您的位置:首页 > 其它

Deep MNIST for Experts解读(二):卷积与最大池化

2017-08-15 13:23 507 查看
Deep MNIST for Experts解读(二):卷积与池化
https://www.tensorflow.org/get_started/mnist/pros
下面看看那三个遗留问题。

1.卷积

conv2d

max_pool_2x2

Tensorflow提供了两个卷积函数,tf.nn.conv2d和tf.nn.conv3d,并不适合拿来直接演示说明基本概念。

什么是卷积运算?

卷积的感性说明:https://www.zhihu.com/question/22298352

卷积的理性说明:http://blog.csdn.net/anan1205/article/details/12313593。

最直观的说明:http://ufldl.stanford.edu/wiki/index.php/%E5%8D%B7%E7%A7%AF%E7%89%B9%E5%BE%81%E6%8F%90%E5%8F%96 

的卷积的那个动图。

这里说明一下。

绿色矩阵为学习样本,橙色是一个小矩阵,称为卷积核,可以把它视为一个滑窗。用卷积核去覆盖样本矩阵,运算一次,得到一个值。

比如: 被卷积核第一次覆盖的子矩阵(绿色)是

[[1,1,1],

 [0,1,1],

 [0,0,1]],

卷积核(橙色)是:

[[1,0,1],

 [0,1,0],

 [1,0,1]]

对应坐标的值相乘,再各元素相加:

1*1+1*0+1*1+0*0+1*1+1*0+0*1+0*0+1*1=4,得到粉红色矩阵的第一个元素。

移动卷积核,依次得到粉红色矩阵的各个元素,这就是卷积运算。

下面来理解下mnist_deep.py中的conv2d。

def conv2d(x, W):

  """conv2d returns a 2d convolution layer with full stride."""

  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

tf.nn.conv2d给了四个参数:

x:即输入input,格式为:[训练时一个batch的图片数量, 图片高度, 图片宽度, 输入通道数],

[batch, in_height, in_width, in_channels]

源码中的定义为:x_image = tf.reshape(x, [-1, 28, 28, 1]),也就是batch待定,图片是28*28的,入通道数1,这里指单色的,还有一种是三原色的。

W:即卷积核,又名filter,格式为:[卷积核的高度,卷积核的宽度,输入通道数,输出通道数]

[filter_height, filter_width, in_channels, out_channels]

比如卷积层一的W: W_conv1 = weight_variable([5, 5, 1, 32]),卷积核是5*5,入通道数1,出通道数32.

strides=[1, 1, 1, 1]:x不是一个4维矩阵吗,这四个1是对这4维的滑动步长定义。

padding='SAME':把x的周围补0,尽量左右一样多,搞不定的话让右边多一点0。为什么要被0呢。比如stanford的那个例子,图片是5*5,卷积核是3*3,输出是3*3,但如果想要输出与图片的shape(5*5)一样呢,只有把原图片周围补0了。

做点小练习:参见:http://www.cnblogs.com/lovephysics/p/7220111.html

练习一:

import tensorflow as tf

k = tf.constant([

    [1, 0, 1],

    [2, 1, 0],

    [0, 0, 1]

], dtype=tf.float32, name='k')

i = tf.constant([

    [4, 3, 1, 0],

    [2, 1, 0, 1],

    [1, 2, 4, 1],

    [3, 1, 0, 2]

], dtype=tf.float32, name='i')

kernel = tf.reshape(k, [3, 3, 1, 1], name='kernel')

image  = tf.reshape(i, [1, 4, 4, 1], name='image')

res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 1, 1, 1], "VALID"))

# VALID means no padding

with tf.Session() as sess:

   print(sess.run(res))

输出:

[[ 14.   6.]

 [  6.  12.]]

14 = 4 * 1 + 3 * 0 + 1 * 1 + 2 * 2 + 1 * 1 + 0 * 0 + 1 * 0 + 2 * 0 + 4 * 1

6 = 3 * 1 + 1 * 0 + 0 * 1 + 1 * 2 + 0 * 1 + 1 * 0 + 2 * 0 + 4 * 0 + 1 * 1

6 = 2 * 1 + 1 * 0 + 0 * 1 + 1 * 2 + 2 * 1 + 4 * 0 + 3 * 0 + 1 * 0 + 0 * 1

12 = 1 * 1 + 0 * 0 + 1 * 1 + 2 * 2 + 4 * 1 + 1 * 0 + 1 * 0 + 0 * 0 + 2 * 1

练习二:

input = tf.Variable(tf.random_normal([1,5,5,5]))  

filter = tf.Variable(tf.random_normal([3,3,5,1]))  

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')  

sess=tf.Session()

init=tf.global_variables_initializer()

sess.run(init)

print(op)

print(sess.run(op))

输出:

Tensor("Conv2D:0", shape=(1, 3, 3, 1), dtype=float32)

[[[[ -3.14853334]

   [ 16.14700508]

   [ -6.45538139]]

  [[  0.56099153]

   [ -1.47099018]

   [ -4.1546545 ]]

  [[ -5.06771278]

   [  1.38102365]

   [  6.75113153]]]]

shape=(1, 3, 3, 1)是什么含义呢,返回格式未指明的话,指"NHWC",[batch, height, width, channels].(N记为next_batch?好记就先这样记)

可见得到一个3*3的矩阵。VALID,周边没加0嘛。

练习三:同上,把VALID改为SAME。

Tensor("Conv2D:0", shape=(1, 5, 5, 1), dtype=float32)

[[[[  5.71891451]

   [ -2.38102531]

   [ -1.01240289]

   [ -8.67125034]

   [ -0.13419724]]

  [[ -5.37069035]

   [ 16.43548012]

   [  9.87151146]

   [ -1.95723248]

   [ -8.0474062 ]]

  [[ -4.77947903]

   [  9.48705101]

   [  1.43431211]

   [ -4.63299465]

   [  0.04765606]]

  [[  1.12986112]

   [ -6.85580826]

   [  1.7185322 ]

   [ -0.11351717]

   [  1.80565023]]

得到的输出和input的shape是一样的,5*5,计算时input周边补了0.

练习四:输出通道改为7.其它略

input = tf.Variable(tf.random_normal([1,5,5,5]))  

filter = tf.Variable(tf.random_normal([3,3,5,7]))  

输出:Tensor("Conv2D:0", shape=(1, 5, 5, 7), dtype=float32)

2.池化(最大池化)

卷积的问题到这里就差不多了。下面说下池化。

还是先说基本概念

说个比较形象的例子,参见http://m.blog.csdn.net/sinat_32547403/article/details/74937389的图。

第一步不是得到了卷积吗,把这个卷积矩阵划分为一个个小豆腐块,把这个小块中的最大值摘出来,形成一个由最大值元素组成的矩阵,就是最大池化操作。好理解吧。

再看看源码:

def max_pool_2x2(x):

  """max_pool_2x2 downsamples a feature map by 2X."""

  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],

                        strides=[1, 2, 2, 1], padding='SAME')

max_pool仍然四个参数:

x:即value,表现为[batch, height, width, channels],和conv2d的x参数类似。

ksize=[1, 2, 2, 1]:对x的四个维度,指定窗口大小。本例第二、三维是2,也就是把x中的矩阵划为2*2的豆腐块,再选最大值。

strides=[1, 2, 2, 1]:对x的四个维度,指定滑动大小。由于每块豆腐块不重叠,所以第二、三个参数也是2和2。

padding='SAME':补0,同上。

作为练习:

x = tf.Variable(tf.random_normal([1,6,6,1]))  

op = tf.nn.max_pool(x, ksize=[1, 2, 2, 1],

                        strides=[1, 2, 2, 1], padding='SAME')

sess=tf.Session()

init=tf.global_variables_initializer()

sess.run(init)

print('x::', sess.run(x))

print('op::',sess.run(op))

输出x,为了查看上去方便,格式作了下整理:

[[[[ 0.64246583]   [ 0.91550928]   [ 0.8078894 ]   [ 0.2527563 ]   [ 0.16877377]   [ 0.75468737]]

  [[ 1.88294566]   [-0.27354711]   [ 0.891653  ]   [-0.28982827]   [-0.04847682]   [ 1.0516088 ]]

  [[-0.37016639]   [-0.91406488]   [ 0.23838577]   [-0.78308594]   [ 0.58814621]   [ 0.35315144]]

  [[ 0.73015124]   [-1.58307457]   [-0.23730309]   [-0.1681792 ]   [-0.41411981]   [ 0.57433921]]

  [[ 0.25227892]   [ 0.76516557]   [ 0.40843379]   [ 0.43834141]   [ 0.29453221]   [-0.43508232]]

  [[-0.78055584]   [ 0.12396565]   [-0.71946895]   [-0.48513463]   [ 1.21338081]   [-0.9063347 ]]]]

输出op,为了查看上去方便,格式作了下整理:

[[[[ 1.88294566]   [ 0.891653  ]   [ 1.0516088 ]]

  [[ 0.73015124]   [ 0.23838577]   [ 0.58814621]]

  [[ 0.76516557]   [ 0.43834141]   [ 1.21338081]]]]

可以看到,tf.nn.max_pool把x分为了3*3个小块,取了每块的最大值,如第一块最大值是1.88294566。

卷积和最大池化是CNN的关键概念。

下节,讲第二个遗留问题,理解deepnn。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐