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

来源:互联网 发布:淘宝代码在线生成 编辑:程序博客网 时间:2024/06/03 17:00
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。
阅读全文
0 0
原创粉丝点击