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。
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。
相关文章推荐
- Deep MNIST for Experts解读(一):概览
- Deep MNIST for Experts解读(三):deepnn源码分析与AdamOptimizer
- TensorFlow学习篇【3】Deep MNIST for Experts
- TensorFlow官方教程学习 Deep MNIST for Experts
- Tensorflow学习笔记(二):Deep MNIST for Experts
- TensorsFlow学习笔记4----面向机器学习专家的深度MNIST教程(Deep MNIST for Experts)
- tensorflow 官网教程 - Deep MNIST for Experts - 代码及注解
- tensorflow 官网教程 - Deep MNIST for Experts - 代码及注解
- Google TensorFlow学习笔记之Deep MNIST for Experts
- tensorflow学习笔记五:mnist实例--卷积神经网络(CNN)(Deep MNIST for Experts)
- Deep MNIST for Experts
- Semantic Segmentation--ENet:A Deep Neural Network Architecture for Real-Time Semantic..论文解读
- 深度学习笔记(一)空间金字塔池化阅读笔记Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition
- VGG-大规模图像识别的深度卷积网络 Very Deep Convolutional Networks for Large-Scale Image Recognition
- 论文PCANet: A Simple Deep Learning Baseline for Image Classification?的matlab源码解读(一)
- [论文解读]VGGNet:Very Deep Convolution Networks for Large-Scale Image Recognition
- [论文解读] MSCNN: A Unified Multi-scale Deep Convolutional Neural Network for Fast Object Detection
- 论文PCANet: A Simple Deep Learning Baseline for Image Classification?的matlab源码解读(四)
- 深度学习论文随记(二)---VGGNet模型解读-2014年(Very Deep Convolutional Networks for Large-Scale Image Recognition)
- 空间金字塔池化 Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition