Tensorflow(三)- CNN_MNIST

本文模型为tensorflow官方文档中,构建多层CNN完成MNIST训练的模型,通过复现模型来讲解用到的API以及对CNN进行一个简单的梳理。

权重初始化

tf.truncated_normal() 截断正态分布API
https://www.tensorflow.org/versions/r1.4/api_docs/python/tf/truncated_normal
首先使用正态分布,加入高斯噪声,打破对称性。然后使用截断正态分布是为了限制权重大小,不会太大。
所谓截断正态分布,就是只在我截断的区域里面取值,官方文档说是将取值限制在离均值2倍标准差的范围内。一旦超过这个范围,就重新取值。

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.nn.conv2d(input,filter,strides,padding,use_cudnn_on_gpu=None,data_format=None,name=None)

二维卷积API
https://www.tensorflow.org/api_docs/python/tf/nn/conv2d
首先讲述data_format,只有在规范了数据格式的情况下,才能更好的进行讨论。
data_format是该API的一个带有默认参数的输入,它的意义在于规范卷积的输入输出的数据格式,一般不需要定义。默认为”NHWC”,即[batch, height, width, channels]。
input自然是我们的输入,对于第一层,就是我们的图片,格式为[mini_batch_size, 图片高度,图片宽度,图片通道数]
filter卷积核,它的格式是[filter_height, filter_width, in_channels, out_channels]。
strides步长,是配合卷积核在图片上进行卷积时,移动的步长,它的数据格式对应于input,对于二维卷积来说,strides[0] = strides[3] = 1, 在batch和channel方向上不存在跳步移动的。那么strides[1]就代表卷积核在图片高度上的移动,strides[2]就代表卷积核在图片宽度上的移动。
padding填充,有两种padding方式,’VALID’表示不进行padding,’SAME’表示进行zero padding。这两种padding方式要仔细讲一下,因为它们决定了我们输出的feature map的高度和宽度。

  • VALID
    不进行padding,卷积核在图片上通过strides进行滑动直到卷积核超出图片,卷积结束,无法卷积到的部分会被丢弃。那么输出的图像大小是多少呢,我们来计算一下。(我们只计算宽度方向,高度方向相似)
    filter_width + n × stride in_width < filter_width + ( n + 1 ) × stride in_width filter_width stride < n + 1 in_width filter_width + stride stride

    上述公式中 n 表示卷积核滑动的次数,那么输出的宽度就等于 n+1,因为本身卷积核初始所在位置就已经算一个维度了。所以valid padding下,输出的宽度为:
    为了统一使用ceil,和上面公式对比会发现多加了一个1,这是为了避免ceil取整遇到整数情况,导致取错方向,因为我们的步长是整数且至少是1,所以加1并不影响其他结果而且还修正了(in_width-filter_width)/strides[2]为整数的情况。本身原理没有问题。
out_width = ceil((in_width-filter_width+1)/strides[2])
  • SAME
    进行zero padding,即当图片不够卷积核进行移动时,对图片进行补0,直到卷积核整个移出图片,卷积结束。那么让我们来计算一下输出的特征图大小,同样只计算宽度方向。
    n × stride < in_width ( n + 1 ) × stride in_width stride n + 1 < in_width + stride stride

    同样 n+1 表示输出的宽度,
out_width = ceil(in_width/strides[2])

对于SAME来说,还有一点需要注意,那就是它是怎么补零的,采用左右,上下两边平均补零,如果需要补零的个数为奇数,那多出来的那个零补在右边和下边。

下面为模型定义的卷积函数,可以看到strides步长均为1,即在图像高度,宽度上都按1进行移动,采用SAME进行padding,结合这个strides和padding方式,我们可以算出,输出图片大小等于输入图片大小

def conv2d(x, W):
  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
池化
max_pool(value,ksize,strides,padding,data_format='NHWC',name=None)

最大值池化 API
https://www.tensorflow.org/api_docs/python/tf/nn/max_pool
对于池化来讲,池化的data_format, strides以及padding都和上面讲的卷积的API完全相同。最大值池化实际上就是对相应的区域选取其中的最大值来代表这整个区域,这个区域大小由ksize决定,这个ksize的数据格式也是同data_format一样,那么也就是[batch, height, width, channel]。对于二维卷积来说,ksize[0] = ksize[3] = 1,理由与strides相同,ksize[1]和ksize[2]就共同决定了这个区域大小。同样地,我们来计算一下进行pool输出的图像大小(只计算宽度方向)

  • VALID

    k_width + n × stride in_width < k_width + ( n + 1 ) × stride in_width k_width stride < n + 1 in_width k_width + stride stride

  • SAME

    n × stride < in_width ( n + 1 ) × stride in_width stride n + 1 < in_width + stride stride

对于下面模型定义的max_pool来说,ksize[1] = ksize[2] = 2,那么就相当于对于输入图片的 2 × 2 的区域用其中最大的数值来代表,那就相当于宽度上缩小了2倍,高度上缩小了2倍。

def max_pool_2x2(x):
  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                        strides=[1, 2, 2, 1], padding='SAME')

两层卷积

先上代码

    W_conv1 = weight_variable([5,5,1,32])
    b_conv1 = bias_variable([32])
    x_image = tf.reshape(x, [-1, 28, 28, 1])
    h_conv1 = tf.nn.relu(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)

直接讲述太麻烦,做了个表格,贴出图,更加直观。
1. input中的n,也就是batch,在实际中我们一般会采用mini_batch,这个n的大小就等于mini_batch_size.
2. 关于c,也就是channel,MNIST数据集,黑白图像只有一个通道,所以是1。别的图像可能会有多个渠道,比如RGB就是三通道。
3. 对于参数个数计算,其实很简单,只需要关注kernel就行了,因为参数实际就是kernel的权重。
4. 对于连接数的计算,关于输出和参数个数即可,本质上每张输出的特征图上的每个点,都是通过kernel与对应大小的图片patch相连。
5. 关于池化层的参数个数和连接数个数,博主还没有参透,不敢妄言,一种说法是没有参数,最大值pooling就取最大值,平均值pooling就取平均值,还有一种说法是带有一个bias,即每个特征图一个bias。具体哪种是对的,等我参透,再来修改。
这里写图片描述

全连接层带dropout加输出层

这个部分就没什么好讲的了。
tf.nn.dropout() dropout API
https://www.tensorflow.org/api_docs/python/tf/nn/dropout
tensorflow dropout 自带rescale,也就是我们前面提到过的’inverted dropout’, 这样就不要在test的时候进行rescale。

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)    
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.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)

训练加评估

cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
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, "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})

小tips

博主自己在复现的时候,出现了一些bug,来与大家分享也是希望大家注意。
1. 对输入的图像进行均值归一化,那对于MNIST数据集,我们除以255就可以了,如果不除会有什么后果呢,博主一开始就忘了除,导致输出层softmax接收到的数值太大,可想而知再求个e指数,数值肯定爆炸,最后cost输出为nan。
2. 注意data type,比如tf.argmax默认输出数据类型tf.int64,tf.equal()在进行两者比较时,数据类型不一样,自然是不能比较的。
3. 注意数据维度,到底是最终是[batch, data],还是[data, batch]。tf.argmax()默认是按列取最大值下标,如果想要按行,要么tf.transpose(),要么tf.argmax( _ , 1)。
4. 注意tf.equal()出来的是bool矩阵,需要利用tf.cast()将其进行类型转换。
5. 最后注意一下一定要使用mini_batch,而且注意大小,博主刚从多层神经网络出来,上来就试了一个1024,直接把电脑跑来卡住。最后硬关机重启很心疼啊。

注:本篇博客代码全部来自于tensorflow官方文档,并不是自己的复现代码,当然自己的复现代码也就稍作了一点修改而已。


小广告

淘宝choker、耳饰小店 物理禁止
女程序员编码时和编码之余 都需要一些美美的choker、耳饰来装扮自己
男程序员更是需要常备一些来送给自己心仪的人
淘宝小店开店不易 希望有缘人多多支持 (O ^ ~ ^ O)
本号是本人 只是发则小广告 没有被盗 会持续更新深度学习相关博文和一些翻译
感谢大家 不要拉黑我 ⊙﹏⊙|||°
这里写图片描述

猜你喜欢

转载自blog.csdn.net/mike112223/article/details/78348604