В чем разница между заполнением 'SAME' и 'VALID' в tf.nn.max_pool of tenorflow?


309

В чем разница между «SAME» и «ДЕЙСТВИТЕЛЬНО» обивкой в tf.nn.max_poolиз tensorflow?

По моему мнению, «VALID» означает, что после краев не будет нуля.

Согласно Руководству по арифметике свертки для глубокого обучения , в нем говорится, что в операторе пула не будет заполнения, т.е. просто используйте 'VALID' of tensorflow. Но что такое «ТАКОЕ ЖЕ» заполнение максимального пула tensorflow?


3
Проверьте тензорный поток.org/api_guides/python/… для деталей, вот как это сделано.
ГабриэльЧу,


4
Посмотрите эти удивительные гифки, чтобы понять, как работает отступ и шаг. Ссылка
Дипак

1
@GabrielChu ваша ссылка, кажется, умерла и теперь перенаправляет на общий обзор.
матовый

По мере обновления Tensorflow до 2.0 все будет заменено Keras, и я думаю, что вы можете найти информацию о пуле в документации Keras. @ Matt
GabrielChu

Ответы:


163

Я приведу пример, чтобы прояснить это:

  • x: входное изображение формы [2, 3], 1 канал
  • valid_pad: максимальный пул с ядром 2x2, шаг 2 и VALID.
  • same_pad: максимальный пул с ядром 2x2, шагом 2 и ЖЕ ЗАПОЛНЕНИЕМ (это классический путь)

Выходные формы:

  • valid_pad: здесь нет отступов, поэтому выходная форма равна [1, 1]
  • same_pad: здесь мы дополняем изображение до фигуры [2, 4] (с -infпоследующим применением максимального пула, а затем применяем максимальный пул), поэтому выходная форма равна [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.]


603

Если тебе нравится ascii art:

  • "VALID" = без дополнения:

       inputs:         1  2  3  4  5  6  7  8  9  10 11 (12 13)
                      |________________|                dropped
                                     |_________________|
  • "SAME" = с нулевым заполнением:

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

В этом примере:

  • Ширина ввода = 13
  • Ширина фильтра = 6
  • Страйд = 5

Ноты:

  • "VALID" только когда-либо удаляет самые правые столбцы (или самые нижние строки).
  • "SAME" пытается равномерно заполнить левую и правую части, но если количество столбцов, которые будут добавлены, будет нечетным, будет добавлен дополнительный столбец справа, как в случае с этим примером (та же логика применяется вертикально: может быть дополнительная строка нулей внизу).

Редактировать :

О названии:

  • С "SAME"отступом, если вы используете шаг 1, выходы слоя будут иметь те же пространственные измерения, что и входы.
  • С "VALID"отступом нет «готовых» вводов заполнения. Слой использует только правильные входные данные.

Справедливо ли сказать, что «ОДИН И ТОТ» означает «использовать заполнение нулями, чтобы убедиться, что размер фильтра не должен изменяться, если ширина изображения не кратна ширине фильтра или высота изображения не кратна высоте фильтра» «? Как, например, «заполнить нулями до кратного ширины фильтра», если ширина - это проблема?
StatsSorceress

2
Отвечая на мой собственный вопрос: НЕТ, это не точка заполнения нулями. Вы выбираете размер фильтра для работы со входом (включая заполнение нулями), но вы не выбираете заполнение нулями после размера фильтра.
StatsSorceress

Я не понимаю ваш собственный ответ @StatsSorceress. Мне кажется, что вы добавляете достаточно нулей (как можно более симметрично), чтобы все входы были покрыты каким-то фильтром, я прав?
Guillefix

2
Отличный ответ, просто добавлю: В случае, если значения тензора могут быть отрицательными, заполнение для max_pooling - с -inf.
Tones29

Что если ширина ввода является четным числом, когда ksize = 2, stride = 2 и с ОДНИМ ЖЕ дополнением? ... тогда это не должно быть заполнено нулями, верно? .... Я говорю это, когда смотрю репозиторий darkflow code repo , они используют SAME pad, stride = 2, ksize = 2 для maxpool .... после maxpooling ширина изображения уменьшена до 208 пикселей с 416 пикселей ширины. Кто-нибудь может уточнить это?
К.винди

161

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

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

65
Это, наконец, полезно. До этого момента казалось, что SAMEи с VALIDтаким же успехом можно было назвать fooиbar
omatai

7
Я думаю, что «выходной размер совпадает с входным размером», верно только тогда, когда длина шага равна 1.
omsrisagar

92

TensorFlow Свертка пример дает представление о разнице между 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]))

46

Заполнение - это операция для увеличения размера входных данных. В случае одномерных данных вы просто добавляете / добавляете массив к константе, в 2-мерной матрице вы окружаете эти константы. В n-dim вы окружаете свой гиперкуб n-dim константой. В большинстве случаев эта константа равна нулю и называется заполнением нулями.

Вот пример заполнения нулями с p=1применением к 2-му тензору: введите описание изображения здесь


Вы можете использовать произвольное заполнение для своего ядра, но некоторые из значений заполнения используются чаще, чем другие:

  • ДЕЙСТВИТЕЛЬНЫЕ дополнения . Самый простой случай, означает отсутствие заполнения вообще. Просто оставьте свои данные такими же, как были.
  • ТО ЖЕ САМОЕ заполнение иногда называют ПОЛОВИННЫМ заполнением . Он называется SAME, потому что для свертки с шагом = 1 (или для объединения) он должен производить выходные данные того же размера, что и входные. Это называется HALF, потому что для размера ядраk введите описание изображения здесь
  • FULL - это максимальное заполнение, которое не приводит к свертке только для дополняемых элементов. Для ядра размера kэтот отступ равен k - 1.

Чтобы использовать произвольный отступ в TF, вы можете использовать tf.pad()


32

Быстрое объяснение

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

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

Ноты

  • Это относится как к слоям конв, так и к максимальным слоям пула таким же образом
  • Термин «действительный» является немного неправильным, потому что вещи не становятся «недействительными», если вы удаляете часть изображения. Иногда вы можете даже этого хотеть. Это, вероятно, должно быть вызвано NO_PADDINGвместо этого.
  • Термин «то же самое» также является неправильным, потому что имеет смысл только для шага 1, когда выходное измерение совпадает с входным измерением. Например, для шага 2 выходные размеры будут вдвое меньше. Это, вероятно, должно быть вызвано AUTO_PADDINGвместо этого.
  • В SAME(то есть режиме автозаполнения) Tensorflow будет пытаться равномерно распределить отступы как слева, так и справа.
  • В VALID(т. Е. Без режима заполнения) Tensorflow будет отбрасывать правую и / или нижнюю ячейки, если ваш фильтр и шаг не полностью покрывают входное изображение.

19

Я цитирую этот ответ в официальных документах тензорного потока 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

Для заполнения '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]))

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


1
Откровенно говоря, это единственный действительный и полный ответ, не ограничиваясь шагами 1. И все, что нужно, это цитата из документов. +1
P-Gn

1
Очень полезно иметь этот ответ, особенно потому, что ссылка, на которую вы указываете, больше не работает, и кажется, что Google стер эту информацию с веб-сайта tf!
Даниил

12

Существует три варианта заполнения: действительный (без заполнения), одинаковый (или наполовину), полный. Вы можете найти объяснения (в Theano) здесь: http://deeplearning.net/software/theano/tutorial/conv_arithmetic.html

  • Допустимо или нет заполнения:

Допустимое заполнение не включает в себя заполнение нулями, поэтому оно охватывает только действительный ввод, не включая искусственно сгенерированные нули. Длина вывода равна ((длина ввода) - (k-1)) для размера ядра k, если шаг s = 1.

  • То же или наполовину:

При одинаковом заполнении размер выходных данных совпадает с размером входных данных при s = 1. Если s = 1, число дополняемых нулей равно (k-1).

  • Полный отступ:

Полное заполнение означает, что ядро ​​работает над целыми входами, поэтому в конце ядро ​​может встретить только один вход и нули в другом. Число дополняемых нулей равно 2 (k-1), если s = 1. Длина вывода равна ((длина ввода) + (k-1)), если s = 1.

Следовательно, количество отступов: (действительное) <= (одинаковое) <= (полное)


8

Заполнение вкл / выкл. Определяет эффективный размер вашего ввода.

VALID:Нет дополнения. Операции свертки и т. Д. Выполняются только в «допустимых» местах, т.е. не слишком близко к границам вашего тензора.
С ядром 3x3 и изображением 10x10 вы будете выполнять свертку в области 8x8 внутри границ.

SAME:Обивка предоставляется. Всякий раз, когда ваша операция ссылается на окрестность (независимо от ее размера), нулевые значения предоставляются, когда эта окрестность выходит за пределы исходного тензора, чтобы эта операция могла работать и с граничными значениями.
С ядром 3x3 и изображением 10x10 вы будете выполнять свертку на всей площади 10x10.


8

VALID padding: это с нулевым заполнением. Надеюсь, что нет путаницы.

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)

ТО ЖЕ ВРЕМЯ: Это довольно сложно понять, во-первых, потому что мы должны рассматривать два условия отдельно, как указано в официальных документах .

Давайте возьмем ввод как , вывод как , заполнение как , шаг как и размер ядра как (рассматривается только одно измерение)

Дело 01 :

Дело 02 :

рассчитывается таким образом, чтобы минимальное значение, которое может быть принято для заполнения. Поскольку значение известно, значение можно найти с помощью этой формулы .

Давайте разберем этот пример:

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)

Здесь размерность х равна (3,4). Тогда, если взять горизонтальное направление (3):

Если вертикальное направление принято (4):

Надеюсь, что это поможет понять, как на самом деле работает то же самое в TF.


7

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

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

6

Подводя итог, «действительный» отступ означает отсутствие заполнения. Размер вывода сверточного слоя уменьшается в зависимости от размера ввода и размера ядра.

Напротив, «одно и то же» заполнение означает использование заполнения. Когда для шага установлено значение 1, выходной размер сверточного слоя сохраняется в качестве входного размера, добавляя определенное количество «0-границы» вокруг входных данных при вычислении свертки.

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


5

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

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

Для ТО ЖЕ САМОГО дополнения:

ЖЕ САД

Для VALID дополнения:

VALID padding


3

В дополнение к прекрасному ответу Ивсегерея, я нашел эту визуализацию чрезвычайно полезной:

Визуализация заполнения

Заполнение ' valid ' является первой цифрой. Окно фильтра остается внутри изображения.

Заполнение "то же самое " является третьей цифрой. Выход такой же размер.


Нашел это по этой статье .


0

Ответ, совместимый с 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, пожалуйста, обратитесь к этому Руководству по миграции .

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.