问题描述
利用卷积神经网络将MNIST数据集的28×28像素的灰度手写数字图片识别为相应的数字。
数据描述
MNIST数据集是28×28像素的灰度手写数字图片,其中数字的范围从0到9
具体如下所示(参考自Tensorflow官方文档):
文件 | 内容 |
---|---|
train-images-idx3-ubyte.gz | 训练集图片,55000张训练图片, 5000张验证图片 |
train-labels-idx1-ubyte.gz | 训练集图片对应的数字标签 |
t10k-images-idx3-ubyte.gz | 测试集图片,10000张图片 |
t10k-labels-idx1-ubyte.gz | 测试集图片对应的数字标签 |
网络结构
卷积神经网络一般包含以下几层:
输入层:用于将数据输入到神经网络中
卷积层:使用卷积核提取特征
激励层:对卷积操作的线性运算进行非线性映射
池化层:卷积得到的特征图进行稀疏处理,减少数据量
全连接层:在网络的末端进行重新拟合,恢复特征,减少特征的损失
输出层:输出结果
输入层
卷积神经网络中输入层的结构可以是多维的,例如MNIST数据集中是28×28像素的灰度图片,因此输入为28×28的的二维矩阵。
卷积层
卷积层是使用卷积核提取特征,在卷积层中需要理解局部感受野和共享权值。
局部感受野:类似于一个滑动窗口,以窗口的范围去提取对应范围的神经元携带的特征。
共享权值:根据局部感受野提取特征,原始数据中的一部分神经元与卷积层中的一个神经元相连接,每一条线对应一个权重,而在卷积层中,对于同一个卷积核,权重是相同的。
上图为卷积操作示意图(图片来源于网络,侵删),其中Image表示图片数据矩阵,游走的窗口为卷积核矩阵,x0、x1表示的是权重,一个N×N的图像经过M×M的卷积核卷积后将得到(N-M+1)×(N-M+1)的输出。
卷积后输出的矩阵数据成为特征映射图,一个卷积核输出一个特征映射图,卷积操作是一种线性计算,因此通常在卷积后进行一次非线性映射。
池化层
池化层是将卷积得到的特征映射图进行稀疏处理,减少数据量,操作与卷积基本相似,不同的是卷积操作是一种线性计算,而池化的计算方法更多样化,一般有如下计算方式:
最大池化:取样池中的最大值作为池化结果
均值池化:取样池中的平均值作为池化结果
还有重叠池化、均方池化、归一化池化等方法。
全连接层
在网络的末端对提取后的特征进行恢复,重新拟合,减少因为特征提取而造成的特征丢失。全连接层的神经元数需要根据经验和实验结果进行反复调参。
输出层
输出层用于将最终的结果输出,针对不同的问题,输出层的结构也不相同,例如MNIST数据集识别问题中,输出层为有10个神经元的向量。
示例网络结构
示例模型包括输入层、两个卷积层、两个池化层、全连接层和输出层,其中卷积和池化操作的特征图输出大小计算公式为:
ImageWidth:图片宽度
Padding:边缘补齐像素数
KernelSize:卷积核宽度
Stride:移动步长
具体模型结构如下所示:
程序解读
Tensorflow中使用图来表示计算任务,在会话(Session)中执行图,使用 tensor 表示数据.通过变量(Variable)维护状态,使用 feed 和 fetch 可以为任意的操作赋值或者从其中获取数据.
加载MNIST数据集
1 | mnist =input_data.read_data_sets("MNIST_data", one_hot=True) |
命令会自动下载MNIST数据集,存放在”MNIST_data”目录下,也可以手动下载数据集后放入此目录下。执行read_data_sets()函数后将会返回一个DataSet实例,其中包含训练数据、验证数据和测试数据。
创建Session和占位符
1 | sess =tf.InteractiveSession() |
x和y_都是tensor,其中x表示输入数据,由于是28×28像素的灰度图片,因此输入为784维的向量。y_表示模型输出,为0-9的数字,因此是10维的向量。
定义卷积层1的权重和偏置量
1 | w_conv1 = tf.Variable(tf.truncated_normal([5,5, 1, 32], stddev=0.1)) |
卷积操作的计算公式为:W × X + b
[5, 5, 1,32]表示卷积核的大小为5×5,输出为32,即共有32个卷积核,卷积操作会产生32个特征映射图。
其中w_conv1表示权重W,由正太分布截取得出。b_conv1表示偏置量,初始值均为0.1,由于卷积操作会输出32个特征图,因此偏置量的维度为32。
卷积层1
1 | x_image =tf.reshape(x, [-1,28,28,1]) |
将输入tensor x 调整成为28×28矩阵形式。
1 | r_conv1 = tf.nn.conv2d(x_image,w_conv1, strides=[1, 1, 1, 1], padding='SAME') + b_conv1 |
进行卷积操作W × X + b,得到线性变化的结果r_conv1,再利用Tensorflow的relu规则进行非线性映射,出的卷积的结果h_conv1。
池化层1
1 | h_pool1 = tf.nn.max_pool(h_conv1,ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') |
采用了最大池化方法,其中ksize表示取样池的大小,strides表示步长,padding表示边缘补齐方法,SAME方式会在图片边缘补0,补齐边缘像素为1,最终得出池化结果h_pool1。
定义卷积层2的权重和偏置量
1 | w_conv2 =tf.Variable(tf.truncated_normal([5, 5, 32, 64], stddev=0.1)) |
卷积层2的输入为32张特征映射图,有64个卷积核,最终将输出64个特征映射图。
卷积层2和池化层2
1 | r_conv2 = tf.nn.conv2d(h_pool1,w_conv2, strides=[1, 1, 1, 1], padding='SAME') + b_conv1 |
经过卷积层2和池化层2后,得到64张7×7的特征映射图。
全连接层
1 | W_fc1 =tf.Variable(tf.truncated_normal([7 * 7 * 64, 1024], stddev=0.1)) |
全连接层设有1024个神经元,本层的神经元数需要根据经验和实验结果进行反复调参确定。
1 | h_pool2_flat= tf.reshape(h_pool2, [-1, 7*7*64]) |
将第二层池化后的数据调整为7×7×64的向量,与全连接层的权重进行矩阵相乘,然后进行非线性映射得到1024维的向量。
输出层
1 | W_fc2 = tf.Variable(tf.truncated_normal([1024,10], stddev=0.1)) |
输出层为10维的向量,通过softmax函数输出。
参考资料
深度学习(四)卷积神经网络入门学习(1)
深度学习之卷积神经网络CNN及tensorflow代码实现示例
TensorFlow 官方文档