前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >(二)Tensorflow搭建卷积神经网络实现MNIST手写字体识别及预测

(二)Tensorflow搭建卷积神经网络实现MNIST手写字体识别及预测

原创
作者头像
xdq101
修改于 2019-06-03 02:49:19
修改于 2019-06-03 02:49:19
7790
举报
文章被收录于专栏:人工智能项目人工智能项目

1 搭建卷积神经网络

1.0 网络结构

图1.0 卷积网络结构
图1.0 卷积网络结构

1.2 网络分析

序号

网络层

描述

1

卷积层

一张原始图像(28, 28, 1),batch=1,经过卷积处理,得到图像特征(28, 28, 32)

2

下采样

即池化层,最大池化后图像特征(14, 14, 32)

3

卷积层

将池化特征(14, 14, 32)卷积处理后,得到图像特征(14, 14, 64)

4

下采样

最大池化,得到图像特征(7, 7, 64)

5

全连接层

将上一层即池化层的图像特征经过矩阵内积计算,拉成一个向量(7764=3136),特征为(1, 3136)

6

全连接层

继续矩阵计算,得到特征为(1, 512)

7

全连接

高斯矩阵计算,得到特征(1, 10)

2 网络结构

2.1 网络结构可视化

图2.1 网络结构
图2.1 网络结构

2.2 网络结构-源

代码语言:txt
AI代码解释
复制
def conv2d(input_tensor, ksize, strides, pad, name_w, name_b):
    weights = tf.get_variable(name=name_w, shape=ksize, dtype=tf.float32, initializer=tf.truncated_normal_initializer(stddev=0.1))
    biases = tf.get_variable(name=name_b, shape=[ksize[-1]], dtype=tf.float32, initializer=tf.constant_initializer(0.1))
    conv = tf.nn.conv2d(input_tensor, weights, strides=strides, padding=pad)
    conv = tf.nn.relu(conv + biases)
    return conv

def max_pooling(input_tensor, ksize, strides, pad):
    max_pool = tf.nn.max_pool(input_tensor, ksize=ksize, strides=strides, padding=pad)
    return max_pool

def fullc(input_tensor, wsize, name_w, name_b):
    weights = tf.get_variable(name=name_w, shape=wsize, dtype=tf.float32, initializer=tf.truncated_normal_initializer(stddev=0.1))
    biases = tf.get_variable(name=name_b, shape=[wsize[-1]], dtype=tf.float32, initializer=tf.constant_initializer(0.1))
    fullc = tf.matmul(input_tensor, weights) + biases
    return fullc
def inference(inputs, keep_prob):
    with tf.name_scope("conv_1"):
        conv_1 = conv2d(inputs, [5, 5, 1, 32], [1, 1, 1, 1], "SAME", "cw_1", "cb_1")
    with tf.name_scope("max_pool_1"):
        pooling_1 = max_pooling(conv_1, [1, 2, 2, 1], [1, 2, 2, 1], "SAME")
    with tf.name_scope("conv_2"):
        conv_2 = conv2d(pooling_1, [5, 5, 32, 64], [1, 1, 1, 1], "SAME", "cw_2", "cb_2")
    with tf.name_scope("max_pool_2"):
        pooling_2 = max_pooling(conv_2, [1, 2, 2, 1], [1, 2, 2, 1], "SAME")
    feature_shape = pooling_2.get_shape()
    flatten_1 = feature_shape[1].value * feature_shape[2].value * feature_shape[3].value
    feature_reshape = tf.reshape(pooling_2, [-1, flatten_1])
    with tf.name_scope("fc_1"):
        fc_1 = fullc(feature_reshape, [flatten_1, 512], "fw_1", "fb_1")
        fc_1 = tf.nn.dropout(fc_1, keep_prob)
    with tf.name_scope("fc_2"):
        fc_2 = fullc(fc_1, [512, 10], "fw_2", "fb_2")
    return fc_2

3 训练及测试

3.1 载入数据

代码语言:txt
AI代码解释
复制
mnist = input_data.read_data_sets("./mnist_data", one_hot=True)
img_inputs, img_labels = mnist.train.next_batch(BATCH_SIZE)
img_inputs = np.reshape(img_inputs, (BATCH_SIZE, 28, 28, 1))

3.2 训练及保存模型

代码语言:txt
AI代码解释
复制
def train_new(mnist):  
    inputs = tf.placeholder(tf.float32, [None, 28, 28, 1], name="img-inputs")
    labels = tf.placeholder(tf.float32, [None, 10], name="label-outputs")
    prediction = inference(inputs, 0.5)
    loss = loss_cal(prediction, labels)
    accuracy = evaluation(prediction, labels)
    summary_op = tf.summary.merge(tf.get_collection(tf.GraphKeys.SUMMARIES))
    train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(loss)
    saver = tf.train.Saver()
    with tf.Session() as sess:
        init_op = tf.global_variables_initializer()
        sess.run(init_op)
        summary_writer = tf.summary.FileWriter(LOG_DIR, sess.graph)
        for i in range(TRAINING_STEPS):
            img_inputs, img_labels = mnist.train.next_batch(BATCH_SIZE)
            img_inputs = np.reshape(img_inputs, (BATCH_SIZE, 28, 28, 1))
            _, loss_value, acc, summary = sess.run([train_step, loss, accuracy, summary_op], feed_dict={inputs: img_inputs, labels: img_labels})
            # _, loss_value, step, acc = sess.run([train_op, loss, global_step, accuracy], feed_dict={inputs: img_inputs, labels: img_labels})
            if i % 10 == 0:
                print("After {} training steps, loss is {}, accuracy: {}".format(i, loss_value, acc))
                # print("After {} training steps, loss is {}, accuracy: {}".format(step, loss_value, acc)
                saver.save(sess, os.path.join(MODEL_PATH, MODEL_NAME))
            summary_writer.add_summary(summary, i)

4 载入模型及预测

代码语言:txt
AI代码解释
复制
def load_model_only_with_params():
    g_params = tf.Graph()
    with g_params.as_default():
        inputs = tf.placeholder(tf.float32, [None, 28, 28, 1], name="img-inputs")
        labels = tf.placeholder(tf.float32, [None, 10], name="label-outputs")
        prediction = inference(inputs, 0.5)
    mnist = input_data.read_data_sets("./mnist_data", one_hot=True)
    img = mnist.test.images[0]
    img = np.reshape(img, (1, 28, 28, 1))
    img_label = mnist.test.labels[0]
    img_label = np.argmax(img_label)
    with tf.Session(graph=g_params) as sess:
        saver = tf.train.Saver()
        ckpt = tf.train.get_checkpoint_state("./conv_models")
        model_path = ckpt.model_checkpoint_path
        saver.restore(sess, model_path)
        pre = sess.run(prediction, feed_dict={inputs: img})
        pre_num = tf.argmax(pre, 1)
        pre_num = sess.run(pre_num)
        print("prediction: {}, real: {}".format(pre_num[0], img_label))
代码语言:txt
AI代码解释
复制
prediction: 7, real: 7

5 完整程序

代码语言:txt
AI代码解释
复制
import tensorflow as tf 
from tensorflow.examples.tutorials.mnist import input_data
import os
import numpy as np


LEARNING_RATE_BASE = 0.8
LEARNING_RATE_DECAY = 0.99
REGULARAZTION_RATE = 0.0001
LEARNING_RATE = 0.0001
TRAINING_STEPS = 30000
MOVING_AVERAGE_DECAY = 0.99
MODEL_PATH = "./conv_models"
MODEL_NAME = "conv_model.ckpt"
LOG_DIR = "./logs"
BATCH_SIZE = 100
if not os.path.exists(MODEL_PATH):
    os.makedirs(MODEL_PATH)

def conv2d(input_tensor, ksize, strides, pad, name_w, name_b):
    weights = tf.get_variable(name=name_w, shape=ksize, dtype=tf.float32, initializer=tf.truncated_normal_initializer(stddev=0.1))
    biases = tf.get_variable(name=name_b, shape=[ksize[-1]], dtype=tf.float32, initializer=tf.constant_initializer(0.1))
    conv = tf.nn.conv2d(input_tensor, weights, strides=strides, padding=pad)
    conv = tf.nn.relu(conv + biases)
    return conv

def max_pooling(input_tensor, ksize, strides, pad):
    max_pool = tf.nn.max_pool(input_tensor, ksize=ksize, strides=strides, padding=pad)
    return max_pool

def fullc(input_tensor, wsize, name_w, name_b):
    weights = tf.get_variable(name=name_w, shape=wsize, dtype=tf.float32, initializer=tf.truncated_normal_initializer(stddev=0.1))
    biases = tf.get_variable(name=name_b, shape=[wsize[-1]], dtype=tf.float32, initializer=tf.constant_initializer(0.1))
    fullc = tf.matmul(input_tensor, weights) + biases
    return fullc

def inference(inputs, keep_prob):
    with tf.name_scope("conv_1"):
        conv_1 = conv2d(inputs, [5, 5, 1, 32], [1, 1, 1, 1], "SAME", "cw_1", "cb_1")
    with tf.name_scope("max_pool_1"):
        pooling_1 = max_pooling(conv_1, [1, 2, 2, 1], [1, 2, 2, 1], "SAME")
    with tf.name_scope("conv_2"):
        conv_2 = conv2d(pooling_1, [5, 5, 32, 64], [1, 1, 1, 1], "SAME", "cw_2", "cb_2")
    with tf.name_scope("max_pool_2"):
        pooling_2 = max_pooling(conv_2, [1, 2, 2, 1], [1, 2, 2, 1], "SAME")
    feature_shape = pooling_2.get_shape()
    flatten_1 = feature_shape[1].value * feature_shape[2].value * feature_shape[3].value
    feature_reshape = tf.reshape(pooling_2, [-1, flatten_1])
    with tf.name_scope("fc_1"):
        fc_1 = fullc(feature_reshape, [flatten_1, 512], "fw_1", "fb_1")
        fc_1 = tf.nn.dropout(fc_1, keep_prob)
    with tf.name_scope("fc_2"):
        fc_2 = fullc(fc_1, [512, 10], "fw_2", "fb_2")
    return fc_2

def loss_cal(prediction, labels):
    with tf.name_scope("loss"):
        cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=prediction, labels=tf.argmax(labels, 1))
        loss = tf.reduce_mean(cross_entropy)
        tf.summary.scalar("loss", loss)
        return loss


def evaluation(logits, labels):
    with tf.name_scope("accuracy"):
        correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(labels, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
        tf.summary.scalar("accuracy", accuracy)
        return accuracy

def train(mnist):
    inputs = tf.placeholder(tf.float32, [None, 28, 28, 1], name="img-inputs")
    labels = tf.placeholder(tf.float32, [None, 10], name="label-outputs")
    global_step = tf.Variable(0, trainable=False)
    variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
    variables_averages_op = variable_averages.apply(tf.trainable_variables())
    prediction = inference(inputs, 0.5)

    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=prediction, labels=tf.argmax(labels, 1))
    loss = tf.reduce_mean(cross_entropy)
    learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE, global_step, mnist.train.num_examples/BATCH_SIZE, LEARNING_RATE_DECAY)
    train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
    # accuracy = evaluation(prediction, labels)
    with tf.control_dependencies([train_step, variables_averages_op]):
        train_op = tf.no_op(name="train")

    saver = tf.train.Saver()
    with tf.Session() as sess:
        init_op = tf.global_variables_initializer()
        sess.run(init_op)
        for i in range(TRAINING_STEPS):
            img_inputs, img_labels = mnist.train.next_batch(BATCH_SIZE)
            img_inputs = np.reshape(img_inputs, (BATCH_SIZE, 28, 28, 1))
            _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={inputs: img_inputs, labels: img_labels})
            # _, loss_value, step, acc = sess.run([train_op, loss, global_step, accuracy], feed_dict={inputs: img_inputs, labels: img_labels})

            if i % 10 == 0:
                print("After {} training steps, loss is {}".format(step, loss_value))
                # print("After {} training steps, loss is {}, accuracy: {}".format(step, loss_value, acc)
                saver.save(sess, os.path.join(MODEL_PATH, MODEL_NAME), global_step=global_step)

def train_new(mnist):  
    inputs = tf.placeholder(tf.float32, [None, 28, 28, 1], name="img-inputs")
    labels = tf.placeholder(tf.float32, [None, 10], name="label-outputs")
    prediction = inference(inputs, 0.5)
    loss = loss_cal(prediction, labels)
    accuracy = evaluation(prediction, labels)
    summary_op = tf.summary.merge(tf.get_collection(tf.GraphKeys.SUMMARIES))
    train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(loss)
    saver = tf.train.Saver()
    with tf.Session() as sess:
        init_op = tf.global_variables_initializer()
        sess.run(init_op)
        summary_writer = tf.summary.FileWriter(LOG_DIR, sess.graph)
        for i in range(TRAINING_STEPS):
            img_inputs, img_labels = mnist.train.next_batch(BATCH_SIZE)
            img_inputs = np.reshape(img_inputs, (BATCH_SIZE, 28, 28, 1))
            _, loss_value, acc, summary = sess.run([train_step, loss, accuracy, summary_op], feed_dict={inputs: img_inputs, labels: img_labels})
            # _, loss_value, step, acc = sess.run([train_op, loss, global_step, accuracy], feed_dict={inputs: img_inputs, labels: img_labels})
            if i % 10 == 0:
                print("After {} training steps, loss is {}, accuracy: {}".format(i, loss_value, acc))
                # print("After {} training steps, loss is {}, accuracy: {}".format(step, loss_value, acc)
                saver.save(sess, os.path.join(MODEL_PATH, MODEL_NAME))
            summary_writer.add_summary(summary, i)

def load_model_only_with_params():
    g_params = tf.Graph()
    with g_params.as_default():
        inputs = tf.placeholder(tf.float32, [None, 28, 28, 1], name="img-inputs")
        labels = tf.placeholder(tf.float32, [None, 10], name="label-outputs")
        prediction = inference(inputs, 0.5)
    mnist = input_data.read_data_sets("./mnist_data", one_hot=True)
    img = mnist.test.images[0]
    img = np.reshape(img, (1, 28, 28, 1))
    img_label = mnist.test.labels[0]
    img_label = np.argmax(img_label)
    with tf.Session(graph=g_params) as sess:
        saver = tf.train.Saver()
        ckpt = tf.train.get_checkpoint_state("./conv_models")
        model_path = ckpt.model_checkpoint_path
        saver.restore(sess, model_path)
        pre = sess.run(prediction, feed_dict={inputs: img})
        pre_num = tf.argmax(pre, 1)
        pre_num = sess.run(pre_num)
        print("prediction: {}, real: {}".format(pre_num[0], img_label))

def main(argv=None):
    mnist = input_data.read_data_sets("./mnist_data", one_hot=True).
    # 训练
	train_new(mnist)
	# 载入训练模型
    # load_model_only_with_params()

if __name__ == "__main__":
    tf.app.run()

6 训练结果

6.1 损失值

图6.1 损失值
图6.1 损失值

6.2 准确度

图6.1 评估精度
图6.1 评估精度

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
(一)Tensorflow搭建普通神经网络实现MNIST手写字体识别及预测
输入层数据为维度(1, 784),其中1表示数据数量,因为网络一次只处理一张图片,所以为1,784是图像数据维度,将$28\times 28 \times1$的数据处理成一个列向量,便于存储,若向显示,则需要将其回复到源尺寸,参见博客MNIST手写字体数据集解析.
xdq101
2019/05/31
1.2K0
(一)Tensorflow搭建普通神经网络实现MNIST手写字体识别及预测
卷积神经网络处理图像识别(三)
下面是测试Batch的总Loss和验证集上的准确率的收敛趋势图。由于我的电脑性能不好,所以我大幅度削减了待训练参数个数。尽管如此,2000轮训练之后,在验证集上5000个图片的预测正确率已达98.3%。如若不削减参数,准确率可达99.4%。
用户6021899
2019/11/25
8650
卷积神经网络处理图像识别(三)
用Tensorflow实现卷积神经网络CNN
一、数据准备 实验数据使用MNIST数据集。 MNIST 数据集已经是一个被”嚼烂”了的数据集, 很多教程都会对它”下手”, 几乎成为一个 “典范”。 在很多tensorflow教程中,用下面这一句下载mnist数据集: mnist = input_data.read_data_sets('MNIST_data', one_hot=True) 但实际运行时根本无法通过网络下载,解决方案就是手工下载数据,然后直接导入使用。 下载地址:http://yann.lecun.com/exdb/mnist/ 4个
海天一树
2018/04/17
9260
TensorFlow-手写数字识别(三)
本篇文章在上篇TensorFlow-手写数字识别(二)的基础上,将全连接网络改为LeNet-5卷积神经网络,实现手写数字识别。
xxpcb
2020/08/04
1K0
使用 L2 正则化和平均滑动模型的 LeNet-5MNIST 手写数字识别模型
[1]Tensorflow实战Google深度学习框架: https://github.com/caicloud/tensorflow-tutorial/tree/master/Deep_Learning_with_TensorFlow/1.4.0
演化计算与人工智能
2020/08/14
4420
卷积神经网络实现图像识别及过程可视化
样本按照不同类别保存在不同文件夹中,每个文件夹代表一个类别,然后这些文件夹放在同一文件夹中,该文件夹和脚本同一目录下。
全栈程序员站长
2022/06/27
6170
卷积神经网络实现图像识别及过程可视化
美还是丑?这有一个CNN开发的颜值评分器 | 实战
在人工智能的发展越来越火热的今天,其中智能应用也在伴随着我们的生活,其中最具有代表性的便是图像识别,并且其中的应用比比皆是,如车站的人脸识别系统,交通的智能监控车牌号系统等等。而卷积神经网络作为图像识别的首选算法,对于图像的特征提取具有很好的效果,而TensorFlow作为Google的开源框架具有很好的结构化特征,而本篇文章将利用卷积神经网络算法对图像识别进行应用,开发出颜值评分器的功能。
AI科技大本营
2019/11/12
1.2K0
TensorFlow-手写数字识别(二)
本篇文章在上篇TensorFlow-手写数字识别(一)的基础上进行改进,主要实现以下3点:
xxpcb
2020/08/04
8030
深度学习解决手写数字的图片识别
本篇使用TensorFlow框架,利用MNIST手写数字数据集来演示深度学习的入门概念。其训练集共有60000个样本(图片和标签),测试集有10000个样本。手写数字的图片都是尺寸为28*28的二值图:
用户6021899
2019/11/07
1.8K0
深度学习解决手写数字的图片识别
使用tensorflow layers相关API快速构建卷积神经网络
tf.layers包中包含了CNN卷积神经网络的大多数层类型,当前封装支持的层包括:
OpenCV学堂
2018/12/13
1K0
tensorflow2.0手写数字识别_tensorflow手写汉字识别
手写识别的应用场景有很多,智能手机、掌上电脑的信息工具的普及,手写文字输入,机器识别感应输出;还可以用来识别银行支票,如果准确率不够高,可能会引起严重的后果。当然,手写识别也是机器学习领域的一个Hello World任务,感觉每一个初识神经网络的人,搭建的第一个项目十之八九都是它。
全栈程序员站长
2022/10/05
1.7K0
tensorflow2.0手写数字识别_tensorflow手写汉字识别
持久化的基于 L2 正则化和平均滑动模型的 MNIST 手写数字识别模型
[1]Tensorflow实战Google深度学习框架: https://github.com/caicloud/tensorflow-tutorial/tree/master/Deep_Learning_with_TensorFlow/1.4.0
演化计算与人工智能
2020/08/14
4270
tensorflow实现手写体数字识别
之前在人工智能课上自己手动搭建过一个BP神经网络实现MNIST数据集的手写体数字识别,使用的是c++,最终准确率的上限在95%至96%左右(毕竟水平有限)。这次不一样了,使用tensorflow进行实验,准确率确实提高了不少。可能有人会觉得tensorflow有点过时,现在的大企业不怎么用tensorflow了,但我觉得,对于初学者来说,tensorflow还是不错的选择。
luxuantao
2021/02/24
1.1K0
图像识别与卷积神经网络
卷积神经网络是除了全连接神经网络以外另一个常用的网络结果,其在图像识别方面表现十分突出。本文结合Tensorflow:实战Google深度学习框架,讲述卷积神经网络常用数据集,介绍卷积网络的结构思想,以及通过TensorFlow实现其设计。
全栈程序员站长
2022/09/02
1.1K0
图像识别与卷积神经网络
tensorflow学习笔记_03
上一篇使用tensorflow完成一个卷积神经网络,但当时写的代码虽然可以工作,还比较零乱,并且并没有经过参数调优,最终得到的模型准确率也并不是很高。本周花了些时间将代码进行了重构,并且对某些地方进行了调整了,目前得到的准确率就比较高了。 神经网络 神经网络的概念 神经网络只是一个很酷的名词,媒体用来夸大其词的,其实没有生物神经那么高级。神经网络归根结底就是计算图谱,或者说数据流图谱。其实就是一串链在一起的函数,这些函数的操作对象是各种维度的矩阵。 下面这段我总结出来的话很重要,很重要,很重要。 在tens
jeremyxu
2018/05/10
6580
用Tensorflow识别手写体
数据准备 import tensorflow as tfimport tensorflow.examples.tutorials.mnist.input_data as input_datamnist = input_data.read_data_sets("MNIST_data/", one_hot=True) WARNING:tensorflow:From <ipython-input-1-6bfbaa60ed82>:3: read_data_sets (from tensorflow.contrib.
用户3577892
2020/06/12
4.2K0
tensorflow笔记(五)之MNIST手写识别系列二
http://www.cnblogs.com/fydeblog/p/7455233.html
努力努力再努力F
2018/09/11
3290
tensorflow笔记(五)之MNIST手写识别系列二
tensorflow 实现wgan-gp mnist图片生成
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25737169/article/details/76695935
DoubleV
2018/09/12
1.6K0
tensorflow 实现wgan-gp  mnist图片生成
一看就懂的Tensorflow实战(卷积神经网络)
注意,这里只需要给出输入数据,输出通道数,卷积核大小即可。 Pooling layers 模块提供了多个池化方法,这几个池化方法都是类似的,包括 max_pooling1d()、max_pooling2d()、max_pooling3d()、average_pooling1d()、average_pooling2d()、average_pooling3d(),分别代表一维二维三维最大和平均池化方法,它们都定义在 tensorflow/python/layers/pooling.py 中,这里以 > max_pooling2d() 方法为例进行介绍。 max_pooling2d( inputs, pool_size, strides, padding='valid', data_format='channels_last', name=None ) 参数说明如下:
AI异构
2020/07/29
5490
一看就懂的Tensorflow实战(卷积神经网络)
基于tensorflow的MNIST数字识别
MNIST是一个非常有名的手写体数字识别数据集,在很多资料中,这个数据集都会作为深度学习的入门样例。下面大致介绍这个数据集的基本情况,并介绍temsorflow对MNIST数据集做的封装。tensorflow的封装让使用MNIST数据集变得更加方便。MNIST数据集是NIST数据集的一个子集,它包含了60000张图片作为训练数据,10000张图片作为测试数据。在MNIST数据集中的每一张图片都代表了0~9中的一个数字。图片的大小都为28*28,且数字都会出现在图片的正中间。
狼啸风云
2019/03/01
3K0
推荐阅读
相关推荐
(一)Tensorflow搭建普通神经网络实现MNIST手写字体识别及预测
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档