手写数字识别(2)---- CNN网络模型
2016-08-01 08:46
369 查看
这是之前的文章《手写数字识别(1)---- Softmax回归模型》的姊妹篇,一些基本的输入与环境与上一篇一致。
权重初始化
要构建一个多层网络,需要初始化很多权重变量。在这里要说明一些有关权重初始化需要注意的事项。权重不能初始化为全部相同的值,要将参数进行随机初始化,而不是全部置为0。如果所有参数都用相同的值作为初始值,那么所有隐藏层单元最终会得到与输入值有关的、相同的函数(也就是说同一层的所有结点都会有相同的激活函数)。随机初始化的目的是为了使对称失效(symmetry breaking)。
同时在tensorflow的深度网络里面,他们使用了ReLU(Rectified Linear Unites)作为激活函数,为了避免出现神经元不能被激活(dead neurons)的情况,在这次的训练网络中又加入了一些偏置量。
在这里,构造两个函数来生成变量。
def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial)
tf.truncated_normal,这个函数返回一个变量组件,里面的值由一个截断正态分布函数(truncated normal distribution)生成。对于这个,我们只需要知道,该变量的初始值是由正太分布函数生成的即可。
shape即该变量的维度信息。
stddev即,standard deviation。
使用方法可以看下面的例子:
>>> import tensorflow as tf >>> sess = tf.Session() >>> initial = tf.truncated_normal(shape=[2,2], stddev=0.1) >>> sess.run(initial) array([[ 0.01293811, 0.15602824], [ 0.13170491, 0.01664901]], dtype=float32) >>> sess.run(initial) array([[ 0.06780983, -0.14649697], [-0.09865334, 0.04494597]], dtype=float32)
卷积和池化(convolution and pooling)
卷积
关于什么是卷积已经有很多文章可以看,我在下面也放了一些我认为不错的资料。做卷积时,需要注意下面这些问题:
卷积边界怎么处理?
每次移动卷积模版的步长是多少?
这次实验中,选用了0边界,步长为1的参数,以保障输入与输出为同一大小。
池化
关于池化,把它理解成取矩阵里的不相交的区域里面的一个特殊值的过程(类似降采样,不过它取出的值可以是经过计算得出的,如均值)。在这个实验中,池化操作采用了取小区域最大值的方法。实现
def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
tf.nn.conv2d,这个就是卷积操作,
x是输入数据,
W是过滤模板或者说核函数。
x是一个有四个维度的量,包括
[batch, in_height, in_width, in_channels].
W也是一个四维的量,包括
[filter_height, filter_width, in_channels, out_channels]
strides里面的每一量对应
W在
x上的移动步长,比如
strides = [1, 2, 3, 4]批,每次移动batch的个数是1;每次移动in_height的数目是2;每次移动in_width的数目是3;每次移动in_channels的数目是4。当然,每次只应该移动一个量。注意,batch和in_channels一般每次只会移动1。所以一般形式是
strides = [1, stride, stride, 1]。
padding,暂不理解。说是和它的内部选择算法有关。
max_pool,池化操作。
x是输入矩阵,一般是卷积之后得到的结果。包括四个维度
[batch, in_height, in_width, in_channels]
ksize指对
x的四个维度做池化时的大小。如
ksize=[1, 2, 2, 1],池化的模板的每次一个batch,一个channel,长为2,宽为2。
strides和上面的一个意思。
第一层卷积
第一层卷积将在含有一个通道的图像的5 X 5的小区域内做卷积,并计算出32个特征。所以,
初始化权重与偏置
W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32])
reshape输入
x_image = tf.reshape(x, [-1, 28, 28, 1])
-1表示,它的大小信息由其它几组值确定。
28表示,长与宽的信息
1表示,图像的channel(R、G、B)
详细reshape
输入卷积结点并池化
h_conv1 = tf.nn.relu( 4000 conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1)
第二层
第二层与第一层类似W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2)
全连接层
原图片大小是28*28的,经过2次池化,池化使用的是2*2的模板。第一次池化之后大小为14*14,第二次池化之后为7*7。此时第二层的64个输出设置一个全连接层。W_fc1 = weight_variable([7 * 7 * 64, 1024]) b_fc1 = bias_variable([1024]) 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)
Dropout层
这一层的功能还是比较清楚的。为了使网络有更好的适应性,同时也是为了防止过拟合,dropout层在训练的时候会开启(使结点以特定的概率被激活)。在测试的时候,就关掉它们。keep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.dropout(h_fc1, keep_prob)
这部分现在还不熟
输出层
这部分就比较好理解了。W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
训练与测试
这部分与使用softmax回归的部分基本一致,不同的地方有以下几点:使用更精密的ADAM最优化方法代替了梯度下降最优化方法。
在输入数据中加入了keep_prob,来控制训练与测试过程中drop的概率值。
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv), reduction_indices=[1])) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) 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}))
结果
其实,大约训练几千次就能等到接近最终准确率的时候了。下面这是训练了一万次的结果。(tensorflow) cslzy (master *) mnist $ python cnn_mnist.py Extracting MNIST_data/train-images-idx3-ubyte.gz Extracting MNIST_data/train-labels-idx1-ubyte.gz Extracting MNIST_data/t10k-images-idx3-ubyte.gz Extracting MNIST_data/t10k-labels-idx1-ubyte.gz training step 0, the accuracy: 0.06 training step 100, the accuracy: 0.86 training step 200, the accuracy: 0.92 training step 300, the accuracy: 0.86 training step 400, the accuracy: 1 training step 500, the accuracy: 0.92 training step 600, the accuracy: 1 ... ... training step 9400, the accuracy: 1 training step 9500, the accuracy: 0.98 training step 9600, the accuracy: 1 training step 9700, the accuracy: 1 training step 9800, the accuracy: 1 training step 9900, the accuracy: 0.98 the test accuracy: 0.9911
总结
通过这次模仿实现,从实践角度学习了CNN的框架与原理。同时也有一些新的知识点需要学习与深化,包括:dropout层的原理与使用范围。
对CNN的实现细节方面还需要进一步熟悉。
本次实验代码放在here
注意事项
sess.run(tf.initialize_all_variables())要放在训练之前,模型构建之后。不然会出错。
参考
ReLU激活函数对称失效(symmetry breaking)
截断正态分布函数(truncated normal distribution)
直观理解卷积:Understanding Convolutions博主用了一些直观的图和例子来给我们描述了什么是卷积,比较不错。
卷积原理与过程:Conv Nets: A Modular Perspective
池化
中文教程
详细reshape
相关文章推荐
- Tensorflow手写数字识别之简单神经网络分类与CNN分类效果对比
- 学习笔记CB009:人工神经网络模型、手写数字识别、多层卷积网络、word2vec
- 【深度学习】笔记3_caffe自带的第一个例子,Mnist手写数字识别所使用的LeNet网络模型的详细解释
- [置顶] 【tensorflow CNN】构建cnn网络,识别mnist手写数字识别
- CNN:人工智能之神经网络算法进阶优化,六种不同优化算法实现手写数字识别逐步提高,应用案例自动驾驶之捕捉并识别周围车牌号—Jason niu
- 《神经网络和深度学习》系列文章五:用简单的网络结构解决手写数字识别
- Tensorflow的Helloword:使用简单Softmax Regression模型来识别Mnist手写数字
- 手把手入门神经网络系列(2)_74行代码实现手写数字识别
- Tensorflow - Tutorial (4) :基于CNN的手写数字识别
- 手把手入门神经网络系列(2)_74行代码实现手写数字识别
- TensorFlow学习笔记(3)----CNN识别MNIST手写数字
- 读书笔记-神经网络与深度学习(一)-使用神经网络识别手写数字
- 手把手入门神经网络系列(2)_74行代码实现手写数字识别
- 《神经网络与深度学习》第一章 使用神经网络来识别手写数字(三)- 用Python代码实现
- cnn 识别手写数字的实现
- 4 机器学习实践之手写数字识别- 神经网络识别
- 手把手入门神经网络系列(2)_74行代码实现手写数字识别
- 《神经网络和深度学习》系列文章一:使用神经网络识别手写数字
- 卷积神经网络(cnn) 手写数字识别
- 卷积神经网络CNN 手写数字识别