What is the difference between 'SAME' and 'VALID' padding in tf.nn.max_pool of tensorflow?

PythonTensorflowDeep Learning

Python Problem Overview


What is the difference between 'SAME' and 'VALID' padding in tf.nn.max_pool of tensorflow?

In my opinion, 'VALID' means there will be no zero padding outside the edges when we do max pool.

According to A guide to convolution arithmetic for deep learning, it says that there will be no padding in pool operator, i.e. just use 'VALID' of tensorflow. But what is 'SAME' padding of max pool in tensorflow?

Python Solutions


Solution 1 - Python

If you like ascii art:

  • "VALID" = without padding:

         inputs:         1  2  3  4  5  6  7  8  9  10 11 (12 13)
                        |________________|                dropped
                                       |_________________|
    
  • "SAME" = with zero padding:

                     pad|                                      |pad
         inputs:      0 |1  2  3  4  5  6  7  8  9  10 11 12 13|0  0
                     |________________|
                                    |_________________|
                                                   |________________|
    

In this example:

  • Input width = 13
  • Filter width = 6
  • Stride = 5

Notes:

  • "VALID" only ever drops the right-most columns (or bottom-most rows).
  • "SAME" tries to pad evenly left and right, but if the amount of columns to be added is odd, it will add the extra column to the right, as is the case in this example (the same logic applies vertically: there may be an extra row of zeros at the bottom).

Edit:

About the name:

  • With "SAME" padding, if you use a stride of 1, the layer's outputs will have the same spatial dimensions as its inputs.
  • With "VALID" padding, there's no "made-up" padding inputs. The layer only uses valid input data.

Solution 2 - Python

When stride is 1 (more typical with convolution than pooling), we can think of the following distinction:

  • "SAME": output size is the same as input size. This requires the filter window to slip outside input map, hence the need to pad.
  • "VALID": Filter window stays at valid position inside input map, so output size shrinks by filter_size - 1. No padding occurs.

Solution 3 - Python

I'll give an example to make it clearer:

  • x: input image of shape [2, 3], 1 channel
  • valid_pad: max pool with 2x2 kernel, stride 2 and VALID padding.
  • same_pad: max pool with 2x2 kernel, stride 2 and SAME padding (this is the classic way to go)

The output shapes are:

  • valid_pad: here, no padding so the output shape is [1, 1]
  • same_pad: here, we pad the image to the shape [2, 4] (with -inf and then apply max pool), so the output shape is [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.]




Solution 4 - Python

The TensorFlow Convolution example gives an overview about the difference between SAME and VALID :

  • For the SAME padding, the output height and width are computed as:

     out_height = ceil(float(in_height) / float(strides[1]))
     out_width  = ceil(float(in_width) / float(strides[2]))
    

And

  • For the VALID padding, the output height and width are computed as:

     out_height = ceil(float(in_height - filter_height + 1) / float(strides[1]))
     out_width  = ceil(float(in_width - filter_width + 1) / float(strides[2]))
    

Solution 5 - Python

Complementing YvesgereY's great answer, I found this visualization extremely helpful:

Padding visualization

Padding 'valid' is the first figure. The filter window stays inside the image.

Padding 'same' is the third figure. The output is the same size.


Found it on this article

Visualization credits: vdumoulin@GitHub

Solution 6 - Python

Padding is an operation to increase the size of the input data. In case of 1-dimensional data you just append/prepend the array with a constant, in 2-dim you surround matrix with these constants. In n-dim you surround your n-dim hypercube with the constant. In most of the cases this constant is zero and it is called zero-padding.

Here is an example of zero-padding with p=1 applied to 2-d tensor: enter image description here


You can use arbitrary padding for your kernel but some of the padding values are used more frequently than others they are:

  • VALID padding. The easiest case, means no padding at all. Just leave your data the same it was.
  • SAME padding sometimes called HALF padding. It is called SAME because for a convolution with a stride=1, (or for pooling) it should produce output of the same size as the input. It is called HALF because for a kernel of size k enter image description here
  • FULL padding is the maximum padding which does not result in a convolution over just padded elements. For a kernel of size k, this padding is equal to k - 1.

To use arbitrary padding in TF, you can use tf.pad()

Solution 7 - Python

Quick Explanation

VALID: Don't apply any padding, i.e., assume that all dimensions are valid so that input image fully gets covered by filter and stride you specified.

SAME: Apply padding to input (if needed) so that input image gets fully covered by filter and stride you specified. For stride 1, this will ensure that output image size is same as input.

Notes

  • This applies to conv layers as well as max pool layers in same way
  • The term "valid" is bit of a misnomer because things don't become "invalid" if you drop part of the image. Sometime you might even want that. This should have probably be called NO_PADDING instead.
  • The term "same" is a misnomer too because it only makes sense for stride of 1 when output dimension is same as input dimension. For stride of 2, output dimensions will be half, for example. This should have probably be called AUTO_PADDING instead.
  • In SAME (i.e. auto-pad mode), Tensorflow will try to spread padding evenly on both left and right.
  • In VALID (i.e. no padding mode), Tensorflow will drop right and/or bottom cells if your filter and stride doesn't full cover input image.

Solution 8 - Python

I am quoting this answer from official tensorflow docs https://www.tensorflow.org/api_guides/python/nn#Convolution For the 'SAME' padding, the output height and width are computed as:

out_height = ceil(float(in_height) / float(strides[1]))
out_width  = ceil(float(in_width) / float(strides[2]))

and the padding on the top and left are computed as:

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

For the 'VALID' padding, the output height and width are computed as:

out_height = ceil(float(in_height - filter_height + 1) / float(strides[1]))
out_width  = ceil(float(in_width - filter_width + 1) / float(strides[2]))

and the padding values are always zero.

Solution 9 - Python

There are three choices of padding: valid (no padding), same (or half), full. You can find explanations (in Theano) here: http://deeplearning.net/software/theano/tutorial/conv_arithmetic.html

  • Valid or no padding:

The valid padding involves no zero padding, so it covers only the valid input, not including artificially generated zeros. The length of output is ((the length of input) - (k-1)) for the kernel size k if the stride s=1.

  • Same or half padding:

The same padding makes the size of outputs be the same with that of inputs when s=1. If s=1, the number of zeros padded is (k-1).

  • Full padding:

The full padding means that the kernel runs over the whole inputs, so at the ends, the kernel may meet the only one input and zeros else. The number of zeros padded is 2(k-1) if s=1. The length of output is ((the length of input) + (k-1)) if s=1.

Therefore, the number of paddings: (valid) <= (same) <= (full)

Solution 10 - Python

VALID padding: this is with zero padding. Hope there is no confusion.

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 padding: This is kind of tricky to understand in the first place because we have to consider two conditions separately as mentioned in the official docs.

Let's take input as https://www.codecogs.com/eqnedit.php?latex=n_i" target="_blank">https://latex.codecogs.com/gif.latex?n_i" title="n_i" /> , output as https://www.codecogs.com/eqnedit.php?latex=n_o" target="_blank">https://latex.codecogs.com/gif.latex?n_o" title="n_o" />, padding as https://www.codecogs.com/eqnedit.php?latex=p_i" target="_blank">https://latex.codecogs.com/gif.latex?p_i" title="p_i" />, stride as https://www.codecogs.com/eqnedit.php?latex=s" target="_blank">https://latex.codecogs.com/gif.latex?s" title="s" /> and kernel size as https://www.codecogs.com/eqnedit.php?latex=k" target="_blank">https://latex.codecogs.com/gif.latex?k" title="k" /> (only a single dimension is considered)

Case 01: https://www.codecogs.com/eqnedit.php?latex=n_i&space;\mod&space;s&space;=&space;0" target="_blank">https://latex.codecogs.com/gif.latex?n_i&space;\mod&space;s&space;=&space;0" title="n_i \mod s = 0" /> :https://www.codecogs.com/eqnedit.php?latex=p_i&space;=&space;max(k-s&space;,0)" target="_blank">https://latex.codecogs.com/gif.latex?p_i&space;=&space;max(k-s&space;,0)" title="p_i = max(k-s ,0)" />

Case 02: https://www.codecogs.com/eqnedit.php?latex=n_i&space;\mod&space;s&space;\neq&space;0" target="_blank">https://latex.codecogs.com/gif.latex?n_i&space;\mod&space;s&space;\neq&space;0" title="n_i \mod s \neq 0" /> : https://www.codecogs.com/eqnedit.php?latex=p_i&space;=&space;max(k&space;-&space;(n_i\mod&space;s)),&space;0)" target="_blank">https://latex.codecogs.com/gif.latex?p_i&space;=&space;max(k&space;-&space;(n_i\mod&space;s)),&space;0)" title="p_i = max(k - (n_i\mod s)), 0)" />

https://www.codecogs.com/eqnedit.php?latex=p_i" target="_blank">https://latex.codecogs.com/gif.latex?p_i" title="p_i" /> is calculated such that the minimum value which can be taken for padding. Since value of https://www.codecogs.com/eqnedit.php?latex=p_i" target="_blank">https://latex.codecogs.com/gif.latex?p_i" title="p_i" /> is known, value of https://www.codecogs.com/eqnedit.php?latex=n_0" target="_blank">https://latex.codecogs.com/gif.latex?n_0" title="n_0" /> can be found using this formula https://www.codecogs.com/eqnedit.php?latex=(n_i&space;-&space;k&space;&plus;&space;2p_i)/2&space;&plus;&space;1&space;=&space;n_0" target="_blank">https://latex.codecogs.com/gif.latex?(n_i&space;-&space;k&space;&plus;&space;2p_i)/2&space;&plus;&space;1&space;=&space;n_0" title="(n_i - k + 2p_i)/2 + 1 = n_0" />.

Let's work out this example:

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)

Here the dimension of x is (3,4). Then if the horizontal direction is taken (3):

https://www.codecogs.com/eqnedit.php?latex=n_i&space;=&space;3,&space;k&space;=2,&space;s&space;=2,&space;p_i&space;=&space;2&space;-&space;(3\mod&space;2)&space;=&space;1,&space;n_0&space;=&space;int&space;(\frac{3-2&plus;2*1}{2}&space;&plus;&space;1)&space;=&space;2" target="_blank">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\mod&space;2)&space;=&space;1,&space;n_0&space;=&space;int&space;(\frac{3-2&plus;2*1}{2}&space;&plus;&space;1)&space;=&space;2" title="n_i = 3, k =2, s =2, p_i = 2 - (3\mod 2) = 1, n_0 = floor (\frac{3-2+2*1}{2} + 1) = 2" />

If the vertial direction is taken (4):

https://www.codecogs.com/eqnedit.php?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;(\frac{3-2&plus;2*0}{2}&space;&plus;&space;1)&space;=&space;2" target="_blank">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;(\frac{3-2&plus;2*0}{2}&space;&plus;&space;1)&space;=&space;2" title="n_i = 4, k =2, s =2, p_i = 2 - 2 = 0, n_0 = floor (\frac{3-2+2*0}{2} + 1) = 2" />

Hope this will help to understand how actually SAME padding works in TF.

Solution 11 - Python

To sum up, 'valid' padding means no padding. The output size of the convolutional layer shrinks depending on the input size & kernel size.

On the contrary, 'same' padding means using padding. When the stride is set as 1, the output size of the convolutional layer maintains as the input size by appending a certain number of '0-border' around the input data when calculating convolution.

Hope this intuitive description helps.

Solution 12 - Python

Based on the explanation here and following up on Tristan's answer, I usually use these quick functions for sanity checks.

# 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')



Solution 13 - Python

Padding on/off. Determines the effective size of your input.

VALID: No padding. Convolution etc. ops are only performed at locations that are "valid", i.e. not too close to the borders of your tensor.
With a kernel of 3x3 and image of 10x10, you would be performing convolution on the 8x8 area inside the borders.

SAME: Padding is provided. Whenever your operation references a neighborhood (no matter how big), zero values are provided when that neighborhood extends outside the original tensor to allow that operation to work also on border values.
With a kernel of 3x3 and image of 10x10, you would be performing convolution on the full 10x10 area.

Solution 14 - Python

General Formula

Here, W and H are width and height of input, F are filter dimensions, P is padding size (i.e., number of rows or columns to be padded)

For SAME padding:

SAME Padding

For VALID padding:

VALID padding

Solution 15 - Python

Tensorflow 2.0 Compatible Answer: Detailed Explanations have been provided above, about "Valid" and "Same" Padding.

However, I will specify different Pooling Functions and their respective Commands in Tensorflow 2.x (>= 2.0), for the benefit of the community.

Functions in 1.x:

tf.nn.max_pool

tf.keras.layers.MaxPool2D

Average Pooling => None in tf.nn, tf.keras.layers.AveragePooling2D

Functions in 2.x:

tf.nn.max_pool if used in 2.x and tf.compat.v1.nn.max_pool_v2 or tf.compat.v2.nn.max_pool, if migrated from 1.x to 2.x.

tf.keras.layers.MaxPool2D if used in 2.x and

tf.compat.v1.keras.layers.MaxPool2D or tf.compat.v1.keras.layers.MaxPooling2D or tf.compat.v2.keras.layers.MaxPool2D or tf.compat.v2.keras.layers.MaxPooling2D, if migrated from 1.x to 2.x.

Average Pooling => tf.nn.avg_pool2d or tf.keras.layers.AveragePooling2D if used in TF 2.x and

tf.compat.v1.nn.avg_pool_v2 or tf.compat.v2.nn.avg_pool or tf.compat.v1.keras.layers.AveragePooling2D or tf.compat.v1.keras.layers.AvgPool2D or tf.compat.v2.keras.layers.AveragePooling2D or tf.compat.v2.keras.layers.AvgPool2D , if migrated from 1.x to 2.x.

For more information about Migration from Tensorflow 1.x to 2.x, please refer to this Migration Guide.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionkarl_TUMView Question on Stackoverflow
Solution 1 - PythonMiniQuarkView Answer on Stackoverflow
Solution 2 - PythonYvesgereYView Answer on Stackoverflow
Solution 3 - PythonOlivier MoindrotView Answer on Stackoverflow
Solution 4 - PythonRoyaumeIXView Answer on Stackoverflow
Solution 5 - PythonzmxView Answer on Stackoverflow
Solution 6 - PythonSalvador DaliView Answer on Stackoverflow
Solution 7 - PythonShital ShahView Answer on Stackoverflow
Solution 8 - PythonVaibhav DixitView Answer on Stackoverflow
Solution 9 - PythonChange-the-worldView Answer on Stackoverflow
Solution 10 - PythonGPrathapView Answer on Stackoverflow
Solution 11 - PythonFrederick HongView Answer on Stackoverflow
Solution 12 - PythonahmedhosnyView Answer on Stackoverflow
Solution 13 - PythonLaine MikaelView Answer on Stackoverflow
Solution 14 - PythonShivam KushwahaView Answer on Stackoverflow
Solution 15 - PythonTensorflow SupportView Answer on Stackoverflow