padding卷积的两种方式“SAME”和“VALID”

conv2d是常用的实现卷积的,Tensorflow调用Conv的api时,常用代码如下:

查看:https://tensorflow.google.cn/api_docs/python/tf/nn/conv2d


tf.nn.conv2d(
    input,
    filter,
    strides,
    padding,
    use_cudnn_on_gpu=True,
    data_format='NHWC',
    dilations=[1, 1, 1, 1],
    name=None
)

对于其中一个参数padding的理解为:

padding: A `string` from: `"SAME", "VALID"`.
      The type of padding algorithm to use.

padding有两个方式可以选择:“SAME” and “VALID”

举两个栗子:

One:padding='SAME'

import tensorflow as tf

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

result = tf.nn.conv2d(input, filter, strides=[1,2,2,1],padding='SAME')
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
print(sess.run(result))
print(result.shape)
sess.close()

结果为: 

[[[[  6.88815355e-01  -1.58929396e+00  -8.13352680e+00   3.47248018e-01
     -2.10637522e+00  -2.47548366e+00  -3.29180861e+00]
   [ -1.50164223e+00  -2.82424307e+00  -2.40781856e+00  -2.55665493e+00
     -3.89841533e+00  -6.71445191e-01   3.10867667e+00]
   [ -3.39479542e+00  -1.40321875e+00   2.29996824e+00  -3.98842275e-01
      7.90905952e-03  -1.71421432e+00  -5.47636747e-01]]

  [[ -1.07995415e+00  -2.21969414e+00  -1.43076777e-01   2.65041399e+00
     -4.38491011e+00  -4.83550358e+00   8.30997753e+00]
   [  1.35791779e+00  -1.38357902e+00  -4.50581169e+00   1.22106361e+00
     -1.36877072e+00  -1.19497585e+00  -3.64005876e+00]
   [ -3.07881045e+00   1.33630781e+01  -4.33032846e+00   1.98507690e+00
     -1.34837186e+00  -3.44964921e-01  -5.76371312e-01]]

  [[ -4.02724743e-01  -3.08082283e-01   1.51205099e+00  -2.11967897e+00
      8.77675891e-01  -3.89271736e-01   1.28933489e+00]
   [  1.05681574e+00   3.83993292e+00   1.46158600e+00   5.12251711e+00
     -4.37659168e+00  -5.88564873e-02   8.72927666e-01]
   [  3.13625002e+00  -2.52725768e+00  -1.89247894e+00  -2.89734745e+00
      2.49475980e+00  -7.85117006e+00   4.73596001e+00]]]]
(1, 3, 3, 7)

Two:padding='VALID'

import tensorflow as tf

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

result = tf.nn.conv2d(input, filter, strides=[1,2,2,1],padding='VALID')
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
print(sess.run(result))
print(result.shape)
sess.close()

结果为:

[[[[  3.30246162e+00   1.00174313e+01   1.02988682e+01  -3.38870287e+00
      3.57620907e+00   9.25950432e+00   1.40226996e+00]
   [  2.39865661e+00   4.90117121e+00   6.27546692e+00  -7.14295626e+00
     -1.87810266e+00   4.73461962e+00  -8.87438393e+00]]

  [[  5.66498578e-01   1.21167574e+01  -2.98488545e+00   2.54433393e+00
     -4.40911025e-01  -4.96578693e-01   2.93070102e+00]
   [ -5.70178509e+00   1.09887476e+01   2.36247849e+00   2.91668701e+00
     -1.77950829e-01   1.17763281e-02  -9.67830420e-01]]]]
(1, 2, 2, 7)

再比如,我们再举个栗子,如果x是一个2×3的矩阵, max pooling窗口为2×2,步长为strides=2

第一次由于窗口可以覆盖,橙色区域做max pooling:

由于步长为2,当向右滑动两步之后,VALID方式发现余下的窗口不到,所以将第3列直接舍弃掉了,所以得到的比原有的小;

而SAME方式与原有尺寸一致,所以需要填充,做补零填充;

另外来讲,我们如何去计算这两种方式?

 If padding == "SAME":
      output_spatial_shape[i] = ceil(input_spatial_shape[i] / strides[i])

    If padding == "VALID":
      output_spatial_shape[i] =
        ceil((input_spatial_shape[i] -
              (spatial_filter_shape[i]-1) * dilation_rate[i])
              / strides[i])

dilation_rate为一个可选的参数,默认为1。

padding = “SAME”:

S是步长。

padding = “VALID”:

输出大小等于输入大小减去滤波器大小加上1,最后再除以步长(f为滤波器的大小,S是步长大小)。

最后,简单的举几个小栗子,说些padding=“SAME”的一些如何补零的情况,挺有趣的:

对于原始图像与卷积核不匹配的情况,就要对图像的边界做一些填充,具体的填充方式和所差的元素个数有关:

1. 28x28的全1图像,卷积核全1的5x5,每个方向的两端各补了一个0,形成30x30,四周都是0的图像

import tensorflow as tf

input = tf.Variable(tf.ones([1,28,28,1]))
filter = tf.Variable(tf.ones([5,5,1,1]))
op = tf.nn.conv2d(input,filter,strides=[1,5,5,1],padding='SAME')

with tf.Session() as sess:
	sess.run(tf.global_variables_initializer())
	res = sess.run(op)
	print("res.shape")
	print(res.shape)
	output = tf.reshape(res,[6,6])
	print(sess.run(output))

结果为:

res.shape
(1, 6, 6, 1)
[[ 16.  20.  20.  20.  20.  16.]
 [ 20.  25.  25.  25.  25.  20.]
 [ 20.  25.  25.  25.  25.  20.]
 [ 20.  25.  25.  25.  25.  20.]
 [ 20.  25.  25.  25.  25.  20.]
 [ 16.  20.  20.  20.  20.  16.]]

 2. 29x29的全1图像

import tensorflow as tf

input = tf.Variable(tf.ones([1,29,29,1]))
filter = tf.Variable(tf.ones([5,5,1,1]))
op = tf.nn.conv2d(input,filter,strides=[1,5,5,1],padding='SAME')

with tf.Session() as sess:
	sess.run(tf.global_variables_initializer())
	res = sess.run(op)
	print("res.shape")
	print(res.shape)
	output = tf.reshape(res,[6,6])
	print(sess.run(output))

每行每列的最后补了一个0,结果为:

res.shape
(1, 6, 6, 1)
[[ 25.  25.  25.  25.  25.  20.]
 [ 25.  25.  25.  25.  25.  20.]
 [ 25.  25.  25.  25.  25.  20.]
 [ 25.  25.  25.  25.  25.  20.]
 [ 25.  25.  25.  25.  25.  20.]
 [ 20.  20.  20.  20.  20.  16.]]

 3. 27x27的全1图像

import tensorflow as tf

input = tf.Variable(tf.ones([1,27,27,1]))
filter = tf.Variable(tf.ones([5,5,1,1]))
op = tf.nn.conv2d(input,filter,strides=[1,5,5,1],padding='SAME')

with tf.Session() as sess:
	sess.run(tf.global_variables_initializer())
	res = sess.run(op)
	print("res.shape")
	print(res.shape)
	output = tf.reshape(res,[6,6])
	print(sess.run(output))

 在每个方向的开头补了一个0,最后补了两个0,结果为:

res.shape
(1, 6, 6, 1)
[[ 16.  20.  20.  20.  20.  12.]
 [ 20.  25.  25.  25.  25.  15.]
 [ 20.  25.  25.  25.  25.  15.]
 [ 20.  25.  25.  25.  25.  15.]
 [ 20.  25.  25.  25.  25.  15.]
 [ 12.  15.  15.  15.  15.   9.]]

4.  26×26的全1图像

import tensorflow as tf

input = tf.Variable(tf.ones([1,26,26,1]))
filter = tf.Variable(tf.ones([5,5,1,1]))
op = tf.nn.conv2d(input,filter,strides=[1,5,5,1],padding='SAME')

with tf.Session() as sess:
	sess.run(tf.global_variables_initializer())
	res = sess.run(op)
	print("res.shape")
	print(res.shape)
	output = tf.reshape(res,[6,6])
	print(sess.run(output))

 首尾各补两个0,结果为:

res.shape
(1, 6, 6, 1)
[[  9.  15.  15.  15.  15.   9.]
 [ 15.  25.  25.  25.  25.  15.]
 [ 15.  25.  25.  25.  25.  15.]
 [ 15.  25.  25.  25.  25.  15.]
 [ 15.  25.  25.  25.  25.  15.]
 [  9.  15.  15.  15.  15.   9.]]

得到的结论为:

当差偶数个元素是首尾各补一半,差奇数个时前边补奇数个,后边补偶数个 

具体差多少元素和选定的卷积核大小以及滑动步长密切相关,那么滑动步长又有哪些影响呢?如果改成滑动步长为1

import tensorflow as tf

input = tf.Variable(tf.ones([1,26,26,1]))
filter = tf.Variable(tf.ones([5,5,1,1]))
op = tf.nn.conv2d(input,filter,strides=[1,1,1,1],padding='SAME')

with tf.Session() as sess:
	sess.run(tf.global_variables_initializer())
	res = sess.run(op)
	print("res.shape")
	print(res.shape)
	output = tf.reshape(res,[26,26])
	print(sess.run(output))

结果为:

res.shape
(1, 26, 26, 1)
[[  9.  12.  15.  15.  15.  15.  15.  15.  15.  15.  15.  15.  15.  15.
   15.  15.  15.  15.  15.  15.  15.  15.  15.  15.  12.   9.]
 [ 12.  16.  20.  20.  20.  20.  20.  20.  20.  20.  20.  20.  20.  20.
   20.  20.  20.  20.  20.  20.  20.  20.  20.  20.  16.  12.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 15.  20.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  20.  15.]
 [ 12.  16.  20.  20.  20.  20.  20.  20.  20.  20.  20.  20.  20.  20.
   20.  20.  20.  20.  20.  20.  20.  20.  20.  20.  16.  12.]
 [  9.  12.  15.  15.  15.  15.  15.  15.  15.  15.  15.  15.  15.  15.
   15.  15.  15.  15.  15.  15.  15.  15.  15.  15.  12.   9.]]

 将其换为padding=“VALID”

import tensorflow as tf

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

with tf.Session() as sess:
	sess.run(tf.global_variables_initializer())
	res = sess.run(op)
	print("res.shape")
	print(res.shape)
	output = tf.reshape(res,[22,22])
	print(sess.run(output))

结果为:

res.shape
(1, 22, 22, 1)
[[ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]
 [ 25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.  25.
   25.  25.  25.  25.  25.  25.  25.  25.]]

 总结一下:

        卷积核大小,滑动步长直接影响最后的卷积结果的大小,且padding为SAME模式时,先对原图像进行填充,再做卷积,填充值须根据卷积核大小及滑动步长决定;而padding为VALID模式时,很简单粗暴直接从原始图像的首段开始卷积,到最后不能匹配卷积核的部分直接舍去。

参考文章:https://blog.csdn.net/wuzqChom/article/details/74785643

参考文章:https://blog.csdn.net/syyyy712/article/details/80272071?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

参考文章:https://blog.csdn.net/zhaozx19950803/article/details/80409502?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

参考文章:https://blog.csdn.net/qq_17272679/article/details/79591540?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

 
发布了128 篇原创文章 · 获赞 132 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/yql_617540298/article/details/105127745