Какова разница между набивкой 'SAME' и 'VALID' в tf.nn.max_pool из tensorflow?

По моему мнению, "VALID" означает, что при выполнении max pool не будет нулевых подкладок за краями.

Согласно A guide to convolution arithmetic for deep learning, там говорится, что в операторе pool не будет padding, т.е. просто используйте 'VALID' из tensorflow. Но что такое 'SAME' padding для max pool в tensorflow?

karl_TUM

Ответы (15)

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.]

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.

Когда stride равен 1 (что более характерно для свертки, чем для объединения), мы можем представить следующее различие:

  • "SAME": выходной размер такой же, как и входной. Это требует, чтобы окно фильтра выходило за пределы входной карты, следовательно, необходима прокладка.
  • "VALID": Окно фильтра остается в валидной позиции внутри карты ввода, поэтому размер вывода уменьшается на filter_size - 1. Никаких подкладок не происходит.

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)

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()

Краткое объяснение

VALID: Не применяйте никаких прокладок, т.е. считайте, что все размеры действительны, чтобы входное изображение полностью покрывалось указанными вами фильтром и stride.

SAME: Примените подкладку к входному изображению (если необходимо), чтобы входное изображение полностью покрывалось фильтром и указанным вами stride. Для stride 1 это гарантирует, что размер выходного изображения будет таким же, как и входного.

Примечания

  • Это относится как к слоям conv, так и к слоям max pool в равной степени
  • Термин "валидный" - это немного неправильный термин, потому что вещи не становятся "невалидными", если вы отбрасываете часть изображения. Иногда вы можете даже захотеть этого. Возможно, вместо этого следовало бы назвать NO_PADDING.
  • Термин "same" - это тоже неправильное название, потому что он имеет смысл только для stride 1, когда размеры вывода совпадают с размерами ввода. Для stride 2, например, выходная размерность будет в два раза меньше. Возможно, вместо этого следовало бы назвать AUTO_PADDING.
  • В режиме SAME (т.е. в режиме auto-pad) Tensorflow будет пытаться равномерно распределить padding как слева, так и справа.
  • В режиме VALID (т.е. без подбивки) Tensorflow будет отбрасывать правые и/или нижние ячейки, если ваш фильтр и stride не полностью покрывают входное изображение.

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 , output as , padding as , stride as and kernel size as (only a single dimension is considered)

Case 01: :

Case 02: :

is calculated such that the minimum value which can be taken for padding. Since value of is known, value of can be found using this formula .

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):

If the vertial direction is taken (4):

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

Общая формула

Здесь W и H - ширина и высота входа, F - размеры фильтра, P - размер прокладки (т.е. количество строк или столбцов для прокладки)

Для САМОЙ набивки:

SAME Padding

Для ВАЛИДНОЙ прокладки:

VALID padding

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.

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

На основании объяснения здесь и следуя ответу Тристана, я обычно использую эти быстрые функции для проверки здравомыслия.

# 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.

Подводя итог, можно сказать, что "действительный" паддинг означает отсутствие падинга. Размер выхода конволюционного слоя уменьшается в зависимости от размера входа & размера ядра.

Напротив, 'same' padding означает использование padding. Если параметр stride равен 1, выходной размер сверточного слоя сохраняется равным входному размеру за счет добавления определенного количества "0-границ" вокруг входных данных при вычислении свертки.

Надеемся, что это интуитивное описание поможет.

Я цитирую этот ответ из официальной документации tensorflow https://www.tensorflow.org/api_guides/python/nn#Convolution. Для 'SAME' padding высота и ширина вычисляются следующим образом:

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

Для набивки 'VALID' высота и ширина вычисляются следующим образом:

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

и значения прокладок всегда равны нулю.

Пример TensorFlow Convolution дает представление о разнице между SAME и VALID :

.
  • Для набивки SAME высота и ширина вычисляются следующим образом:

     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(strides[2]))
    

2022 WebDevInsider