tensorflow
的 tf.nn.max_pool
中的“SAME”和“VALID”填充有什么区别?
在我看来,“有效”意味着当我们做最大池时,边缘之外不会有零填充。
根据A guide to convolution arithmetic for deep learning,它表示池运算符中不会有填充,即只使用tensorflow
的'VALID'。但是 tensorflow
中最大池的“相同”填充是什么?
如果你喜欢 ascii 艺术:
“有效” = 无填充:输入:1 2 3 4 5 6 7 8 9 10 11 (12 13) |________________|掉了|_________________|
"SAME" = 零填充:pad| |填充输入:0 |1 2 3 4 5 6 7 8 9 10 11 12 13|0 0 |________________| |_________________| |________________|
在这个例子中:
输入宽度 = 13
过滤器宽度 = 6
步幅 = 5
笔记:
“VALID”只删除最右边的列(或最底部的行)。
“SAME”试图左右均匀填充,但如果要添加的列数是奇数,它会将额外的列添加到右侧,就像本例中的情况一样(垂直应用相同的逻辑:可能有底部有一行额外的零)。
编辑:
关于名称:
使用“SAME”填充,如果您使用 1 的步幅,则图层的输出将具有与其输入相同的空间维度。
使用“有效”填充,没有“虚构”填充输入。该层仅使用有效的输入数据。
当 stride
为 1(卷积比池化更典型)时,我们可以想到以下区别:
“SAME”:输出大小与输入大小相同。这需要过滤器窗口滑出输入映射,因此需要填充。
“VALID”:过滤器窗口保持在输入映射内的有效位置,因此输出大小缩小 filter_size - 1。不发生填充。
SAME
和 VALID
也可能被称为 foo
和 bar
我将举一个例子使其更清楚:
x: 形状为 [2, 3] 的输入图像,1 通道
valid_pad:具有 2x2 内核、步幅 2 和 VALID 填充的最大池。
same_pad:具有 2x2 内核、步幅 2 和 SAME 填充的最大池(这是经典的方法)
输出形状为:
valid_pad:这里没有填充,所以输出形状是 [1, 1]
same_pad:在这里,我们将图像填充到形状 [2, 4](使用 -inf 然后应用 max pool),因此输出形状为 [1, 2]
x = tf.constant([[1., 2., 3.],
[4., 5., 6.]])
x = tf.reshape(x, [1, 2, 3, 1]) # give a shape accepted by tf.nn.max_pool
valid_pad = tf.nn.max_pool(x, [1, 2, 2, 1], [1, 2, 2, 1], padding='VALID')
same_pad = tf.nn.max_pool(x, [1, 2, 2, 1], [1, 2, 2, 1], padding='SAME')
valid_pad.get_shape() == [1, 1, 1, 1] # valid_pad is [5.]
same_pad.get_shape() == [1, 1, 2, 1] # same_pad is [5., 6.]
TensorFlow Convolution 示例概述了 SAME
和 VALID
之间的区别:
对于相同的填充,输出高度和宽度计算为:out_height = ceil(float(in_height) / float(strides[1])) out_width = ceil(float(in_width) / float(strides[2]))
和
对于 VALID 填充,输出高度和宽度计算如下: out_height = ceil(float(in_height - filter_height + 1) / float(strides[1])) out_width = ceil(float(in_width - filter_width + 1) / float(跨步[2]))
作为 YvesgereY 出色答案的补充,我发现这种可视化非常有帮助:
https://i.stack.imgur.com/0rs9l.gif
填充“有效”是第一个数字。过滤器窗口停留在图像内。
填充“相同”是第三个数字。输出大小相同。
在这个 article 上找到它
可视化学分:vdumoulin@GitHub
填充是增加输入数据大小的操作。如果是一维数据,您只需在数组中附加/前置一个常数,在二维中,您可以用这些常数包围矩阵。在 n-dim 中,你用常数包围你的 n-dim 超立方体。在大多数情况下,该常数为零,称为零填充。
https://i.stack.imgur.com/84NMg.png
您可以为内核使用任意填充,但某些填充值的使用频率高于其他值:
有效的填充。最简单的情况,意味着根本没有填充。只需让您的数据保持原样即可。
相同的填充有时称为半填充。之所以称为 SAME,是因为对于步幅 = 1 的卷积(或对于池化),它应该产生与输入大小相同的输出。之所以称为 HALF,是因为对于大小为 k 的内核
FULL padding 是最大填充,不会导致仅填充元素的卷积。对于大小为 k 的内核,此填充等于 k - 1。
要在 TF 中使用任意填充,您可以使用 tf.pad()
快速说明
VALID
:不应用任何填充,即假设所有尺寸都有效,以便输入图像完全被您指定的过滤器和步幅覆盖。
SAME
:对输入应用填充(如果需要),以便输入图像完全被您指定的过滤器和步幅覆盖。对于步幅 1,这将确保输出图像大小与输入图像大小相同。
笔记
这同样适用于 conv 层和 max pool 层
术语“有效”有点用词不当,因为如果您丢弃部分图像,事情不会变得“无效”。有时你甚至可能想要那个。这可能应该被称为 NO_PADDING 。
术语“相同”也是用词不当,因为当输出维度与输入维度相同时,只有步幅为 1 才有意义。例如,对于 2 的步幅,输出尺寸将是一半。这可能应该被称为 AUTO_PADDING 。
在 SAME(即自动填充模式)下,Tensorflow 将尝试在左右两侧均匀分布填充。
在 VALID(即无填充模式)下,如果您的过滤器和步幅未完全覆盖输入图像,Tensorflow 将删除右侧和/或底部单元格。
我从官方 tensorflow 文档中引用了这个答案https://www.tensorflow.org/api_guides/python/nn#Convolution 对于“相同”填充,输出高度和宽度计算为:
out_height = ceil(float(in_height) / float(strides[1]))
out_width = ceil(float(in_width) / float(strides[2]))
顶部和左侧的填充计算为:
pad_along_height = max((out_height - 1) * strides[1] +
filter_height - in_height, 0)
pad_along_width = max((out_width - 1) * strides[2] +
filter_width - in_width, 0)
pad_top = pad_along_height // 2
pad_bottom = pad_along_height - pad_top
pad_left = pad_along_width // 2
pad_right = pad_along_width - pad_left
对于“有效”填充,输出高度和宽度计算为:
out_height = ceil(float(in_height - filter_height + 1) / float(strides[1]))
out_width = ceil(float(in_width - filter_width + 1) / float(strides[2]))
并且填充值始终为零。
填充有三种选择:有效(无填充)、相同(或一半)、完整。您可以在此处找到解释(在 Theano 中):http://deeplearning.net/software/theano/tutorial/conv_arithmetic.html
有效或无填充:
有效填充不涉及零填充,因此它仅涵盖有效输入,不包括人为生成的零。如果步幅 s=1,则对于内核大小 k,输出长度为 ((输入长度) - (k-1))。
相同或半填充:
当 s=1 时,相同的填充使输出的大小与输入的大小相同。如果 s=1,则填充的零数为 (k-1)。
全填充:
完全填充意味着内核运行在整个输入上,因此在最后,内核可能会遇到唯一的一个输入而其他输入为零。如果 s=1,则填充的零数为 2(k-1)。如果 s=1,则输出的长度为 ((输入的长度) + (k-1))。
因此,填充的数量:(有效)<=(相同)<=(完整)
有效填充:这是零填充。希望没有混乱。
x = tf.constant([[1., 2., 3.], [4., 5., 6.],[ 7., 8., 9.], [ 7., 8., 9.]])
x = tf.reshape(x, [1, 4, 3, 1])
valid_pad = tf.nn.max_pool(x, [1, 2, 2, 1], [1, 2, 2, 1], padding='VALID')
print (valid_pad.get_shape()) # output-->(1, 2, 1, 1)
SAME 填充:首先这有点难以理解,因为我们必须分别考虑 official docs 中提到的两个条件。
https://latex.codecogs.com/gif.latex?n_i
https://latex.codecogs.com/gif.latex?n_i&space;%5Cmod&space;s&space;=&space;0
https://latex.codecogs.com/gif.latex?n_i&space;%5Cmod&space;s&space;%5Cneq&space;0
https://latex.codecogs.com/gif.latex?p_i
让我们来看看这个例子:
x = tf.constant([[1., 2., 3.], [4., 5., 6.],[ 7., 8., 9.], [ 7., 8., 9.]])
x = tf.reshape(x, [1, 4, 3, 1])
same_pad = tf.nn.max_pool(x, [1, 2, 2, 1], [1, 2, 2, 1], padding='SAME')
print (same_pad.get_shape()) # --> output (1, 2, 2, 1)
这里 x 的维度是 (3,4)。那么如果取水平方向(3):
https://latex.codecogs.com/gif.latex?n_i&space;=&space;3,&space;k&space;=2,&space;s&space;=2,&space;p_i&space;=&space;2&space;-&space;(3%5Cmod&space;2)&space;=&space;1,&space;n_0&space;=&space;int&space;(%5Cfrac%7B3-2+2*1%7D%7B2%7D&space;+&space;1)&space;=&space;2
如果取垂直方向(4):
https://latex.codecogs.com/gif.latex?n_i&space;=&space;4,&space;k&space;=2,&space;s&space;=2,&space;p_i&space;=&space;2&space;-&space;2&space;=&space;0,&space;n_0&space;=&space;int&space;(%5Cfrac%7B3-2+2*0%7D%7B2%7D&space;+&space;1)&space;=&space;2
希望这将有助于理解 SAME 填充在 TF 中的实际工作原理。
总而言之,“有效”填充意味着没有填充。卷积层的输出大小根据输入大小和内核大小而缩小。
相反,“相同”填充意味着使用填充。当 stride 设置为 1 时,卷积层的输出大小通过在计算卷积时在输入数据周围附加一定数量的 '0-border' 来保持输入大小。
希望这个直观的描述有所帮助。
根据解释 here 并跟进 Tristan 的回答,我通常使用这些快速功能进行健全性检查。
# a function to help us stay clean
def getPaddings(pad_along_height,pad_along_width):
# if even.. easy..
if pad_along_height%2 == 0:
pad_top = pad_along_height / 2
pad_bottom = pad_top
# if odd
else:
pad_top = np.floor( pad_along_height / 2 )
pad_bottom = np.floor( pad_along_height / 2 ) +1
# check if width padding is odd or even
# if even.. easy..
if pad_along_width%2 == 0:
pad_left = pad_along_width / 2
pad_right= pad_left
# if odd
else:
pad_left = np.floor( pad_along_width / 2 )
pad_right = np.floor( pad_along_width / 2 ) +1
#
return pad_top,pad_bottom,pad_left,pad_right
# strides [image index, y, x, depth]
# padding 'SAME' or 'VALID'
# bottom and right sides always get the one additional padded pixel (if padding is odd)
def getOutputDim (inputWidth,inputHeight,filterWidth,filterHeight,strides,padding):
if padding == 'SAME':
out_height = np.ceil(float(inputHeight) / float(strides[1]))
out_width = np.ceil(float(inputWidth) / float(strides[2]))
#
pad_along_height = ((out_height - 1) * strides[1] + filterHeight - inputHeight)
pad_along_width = ((out_width - 1) * strides[2] + filterWidth - inputWidth)
#
# now get padding
pad_top,pad_bottom,pad_left,pad_right = getPaddings(pad_along_height,pad_along_width)
#
print 'output height', out_height
print 'output width' , out_width
print 'total pad along height' , pad_along_height
print 'total pad along width' , pad_along_width
print 'pad at top' , pad_top
print 'pad at bottom' ,pad_bottom
print 'pad at left' , pad_left
print 'pad at right' ,pad_right
elif padding == 'VALID':
out_height = np.ceil(float(inputHeight - filterHeight + 1) / float(strides[1]))
out_width = np.ceil(float(inputWidth - filterWidth + 1) / float(strides[2]))
#
print 'output height', out_height
print 'output width' , out_width
print 'no padding'
# use like so
getOutputDim (80,80,4,4,[1,1,1,1],'SAME')
填充开/关。确定输入的有效大小。
VALID:
无填充。卷积等操作仅在“有效”的位置执行,即不太靠近张量的边界。
使用 3x3 的内核和 10x10 的图像,您将在边界内的 8x8 区域上执行卷积。
SAME:
提供了填充。每当您的操作引用一个邻域(无论有多大)时,当该邻域延伸到原始张量之外时,将提供零值,以允许该操作也适用于边界值。
使用 3x3 的内核和 10x10 的图像,您将在整个 10x10 区域上执行卷积。
https://i.stack.imgur.com/uamYe.png
这里,W 和 H 是输入的宽度和高度,F 是过滤器尺寸,P 是填充大小(即要填充的行数或列数)
对于相同的填充:
https://i.stack.imgur.com/Q1X1b.png
对于有效的填充:
https://i.stack.imgur.com/CaSbW.png
Tensorflow 2.0 兼容答案:上面已经提供了关于“有效”和“相同”填充的详细说明。
但是,为了社区的利益,我将在 Tensorflow 2.x (>= 2.0)
中指定不同的池化函数及其各自的命令。
1.x 中的功能:
tf.nn.max_pool
tf.keras.layers.MaxPool2D
Average Pooling => None in tf.nn, tf.keras.layers.AveragePooling2D
2.x 中的功能:
tf.nn.max_pool
(如果在 2.x 中使用)和 tf.compat.v1.nn.max_pool_v2
或 tf.compat.v2.nn.max_pool
(如果从 1.x 迁移到 2.x)。
tf.keras.layers.MaxPool2D
如果用于 2.x 和
tf.compat.v1.keras.layers.MaxPool2D
或 tf.compat.v1.keras.layers.MaxPooling2D
或 tf.compat.v2.keras.layers.MaxPool2D
或 tf.compat.v2.keras.layers.MaxPooling2D
,如果从 1.x 迁移而来到 2.x。
Average Pooling => tf.nn.avg_pool2d
或 tf.keras.layers.AveragePooling2D
(如果用于 TF 2.x 和
tf.compat.v1.nn.avg_pool_v2
或 tf.compat.v2.nn.avg_pool
或 tf.compat.v1.keras.layers.AveragePooling2D
或 tf.compat.v1.keras.layers.AvgPool2D
或 tf.compat.v2.keras.layers.AveragePooling2D
或 tf.compat.v2.keras.layers.AvgPool2D
(如果从 1.x 迁移到 2.x)。
有关从 TensorFlow 1.x 迁移到 2.x 的更多信息,请参阅此Migration Guide。
有效的填充是没有填充。相同的填充是以输出与输入具有相同大小的方式填充。
不定期副业成功案例分享
-inf
。