一直关注DL,从TensorFlow开源就开始尝试,在 [ 极客学院 Wiki ] 看教程。不过三天打鱼两天晒网,雄关漫道真如铁,而今迈步从头越,以期望能够相互讨论不断学习。
CNN
CNN(Convolutional Neural Networks) 卷积神经网络是一个多层的神经网络,每层由多个二维平面组成,而每个平面由多个独立神经元组成。简单讲就是把一个图片的数据传递给CNN,原图层是由RGB组成(是由像素点组成的平面矩阵),然后CNN把它的厚度加厚,长宽变小,每做一层都这样被拉长,最终放入全连接层学习,学习其类别标签,获得对新样本的预测能力。
原理这里就不啰嗦了,推荐看这个博客Deep Learning(深度学习)学习笔记整理系列之(一)
当然有视频教程:google 在 udacity 上的 CNN 教程, 个人感觉视频节奏太慢。
一些主要概念:
局部感知&参数共享
局部感知:每个神经元其实没有必要对全局图像进行感知,只需要对局部进行感知,然后在更高层将局部的信息综合起来就得到了全局的信息。这样可以大大减小参数量。
图2
在上右图中,假如每个神经元只和10×10个像素值相连,那么权值数据为1000000×100个参数,减少为原来的万分之一。而那10×10个像素值对应的10×10个参数,其实就相当于卷积操作。
经过上面操作之后仍然参数较多,就有了权值共享。在上面的局部连接中,每个神经元都对应100个参数,一共1000000个神经元,如果这1000000个神经元的100个参数都是相等的,那么参数数目就变为100了。
卷积核
下图是一个3×3的卷积核在5×5的图像上做卷积的过程。每个卷积都是一种特征提取方式,就像一个筛子,将图像中符合条件(激活值越大越符合条件)的部分筛选出来。
图3
只有一个卷积核的时候,显然特征提取是不充分的,通过添加多卷积核,比如32个,学习得到32个特征
滑动的步长-stride
上面卷积那张图片3从左到右,每次滑动的时候只移动一格,步长为1,如果它一次滑动n格,这就是步长n。
卷积的边界处理-padding
如图3所示,卷积后的矩阵只有3*3,比原来的图片要小了,因为边界没有了,所以要考虑这个边界的问题,网上说卷积的边界处理有两种方式:
- 丢掉边界,也就是就按右边那个缩小的矩阵来。
- 复制边界,也就是把左边的最外层原封不动地复制过去,长宽不变。
tensorflow中conv2d的”padding”参数可以设为两个值SAME,VALID;
SAME:edge_row = (kernel_row - 1) / 2;edge_cols = (kernel_cols - 1) / 2;
VALID:edge_row = edge_cols = 0;
edge_row就是边的行数,kernel_row就是卷积核的行数
Down-pooling
池化很好立即,如上图,池化分为两种,一种是最大池化,在选中区域中找最大的值作为抽样后的值,另一种是平均值池化,把选中的区域中的平均值作为抽样后的值,这样做的,原因是为了后面全连接的时候减少连接数。
全连接
权重最多的地方,每一个输出都可以看成前一层的每一个结点乘以一个权重系数W,最后加上一个偏置值b得到。
由于全连接层参数冗余(仅全连接层参数就可占整个网络参数80%左右),近期一些性能优异的网络模型如ResNet和GoogLeNet等均用全局平均池化(global average pooling,GAP)取代FC来融合学到的深度特征,最后仍用softmax等损失函数作为网络目标函数来指导学习过程。
MNIST上手
MNIST是一个入门级的计算机视觉数据集,它包含各种手写数字图片,也包含每一张图片对应的标签,告诉我们这个是数字几。
这里就不赘述,参考MNIST机器学习入门
关于TensorFlow的安装,windows可以参考:win64安装TensorFlow,ubuntu直接pip install tensorflow。
整体结构
主要参考 Yann LeCun 在 1998 年发表的论文 Gradient-Based Learning Applied to Document Recognition 中所提出的经典的 LeNet5网络
- 输入:28*28的图片,1通道。
- 第1次卷积:5x5,步长1x1,共32个卷积核。out:28x28x32。
- 第1次pooling:2x2核,out:14x14x32
- 第2次卷积:5x5,步长1x1,共64个卷积核。out:14x14x64。
- 第2次pooling:2x2核,out:7x7x64。
- 全连接层1:1024个神经元的全连接层,把池化层输出的张量reshape成一些向量,乘上权重矩阵,加上偏置,然后对其使用ReLU
- dropout:减少过拟合,用一个placeholder
来代表一个神经元的输出在dropout中保持不变的概率。这样我们可以在训练过程中启用dropout,在测试过程中关闭dropout
- 全连接层+softmax层
数据准备
自动下载和安装这个数据集源代码:源代码
import input_data
from tensorflow.examples.tutorials.mnist import input_data
# 读取数据
mnist = input_data.read_data_sets("D:/data/minist/", one_hot=True)
# seesion初始化变量
sess = tf.InteractiveSession()
# 为输入图像和目标输出类别创建节点,来开始构建计算图。x和y是占位符
x = tf.placeholder("float", shape=[None, 784]) # 784=28x28
y_ = tf.placeholder("float", shape=[None, 10])
定义2维的 convolutional 图层
def conv2d(x, W):
# stride [1, x_movement, y_movement, 1]
# Must have strides[0] = strides[3] = 1
# strides 就是跨多大步抽取信息
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
定义pooling
def max_pool_2x2(x):
# stride [1, x_movement, y_movement, 1]
# 用pooling对付跨步大丢失信息问题
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
第一层卷积+pooling
'''1. conv1 layer'''
# 把x_image的厚度1加厚变成了32
W_conv1 = weight_variable([5, 5, 1, 32]) # patch 5x5, in size 1, out size 32
b_conv1 = bias_variable([32])
x_image = tf.reshape(x, [-1, 28, 28, 1]) # 最后一个1表示数据是黑白的
# 构建第一个convolutional层,然后加一个非线性化的处理relu
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # output size 28x28x32
# 经过pooling后,长宽缩小为14x14
h_pool1 = max_pool_2x2(h_conv1) # output size 14x14x32
第二层卷积+pooling
'''2. conv2 layer'''
# 把厚度32加厚变成了64
W_conv2 = weight_variable([5, 5, 32, 64]) # patch 5x5, in size 32, out size 64
b_conv2 = bias_variable([64])
# 构建第二个convolutional层
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # output size 14x14x64
# 经过pooling后,长宽缩小为7x7
h_pool2 = max_pool_2x2(h_conv2) # output size 7x7x64
全连接层+pooling
# 变成1024
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
# [n_samples, 7, 7, 64] ->> [n_samples, 7*7*64]
# 把pooling后的结果变平
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
softmax
# 最后一层,输入1024,输出size 10,用 softmax 计算概率进行分类的处理
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
# 向量化后的图片x和权重矩阵W相乘,加上偏置b,然后计算每个分类的softmax概率值。
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
定义损失函数
# 损失函数定义为交叉熵
cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv))
# 最梯度下降法让交叉熵下降,步长为0.01
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) # 3333333333 test accuracy 0.9922
# train_step = tf.train.GradientDescentOptimizer(1e-3).minimize(cross_entropy)
定义评估模型
# 计算分类的准确率,tf.equal 来检测我们的预测是否真实标签匹配
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
# 计算出匹配结果correct_prediction的平均值为,如[1,0,1,1]为0.75
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess.run(tf.initialize_all_variables())
训练模型
for i in range(20000):
batch = mnist.train.next_batch(50)
if i % 100 == 0:
train_accuracy = accuracy.eval(feed_dict={
x: batch[0], y_: batch[1], keep_prob: 1.0})
print("step %d, training accuracy %g" % (i, train_accuracy))
train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
# 输出最终模型在测试集上的准确率
print("test accuracy %g" % accuracy.eval(feed_dict={
x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
完整代码:mnist_main.py
下一篇:TensorFlow-CIFAR10 CNN代码分析