Qual é a diferença entre o preenchimento 'SAME' e 'VALID' no tf.nn.max_pool do tensorflow?

309

Qual é a diferença entre o preenchimento 'SAME' e 'VALID' em tf.nn.max_poolof tensorflow?

Na minha opinião, 'VÁLIDO' significa que não haverá preenchimento zero fora das bordas quando fazemos o pool máximo.

De acordo com um guia de aritmética de convolução para aprendizado profundo , ele diz que não haverá preenchimento no operador de pool, ou seja, basta usar 'VALID' de tensorflow. Mas qual é o preenchimento 'MESMO' da piscina máxima tensorflow?

karl_TUM
fonte
3
Verifique tensorflow.org/api_guides/python/… para obter detalhes, é assim que foi feito.
GabrielChu 9/10
3
Aqui está uma resposta bastante detalhada com visualizações .
Rbinnun
4
Confira esses gifs incríveis para entender como funciona o preenchimento e a passada. Link
Deepak
1
@GabrielChu, seu link parece ter morrido e agora é um redirecionamento para uma visão geral.
Matt
À medida que o Tensorflow atualiza para o 2.0, as coisas serão substituídas pelo Keras e acredito que você pode encontrar as informações de pool nas documentações do Keras. @matt
GabrielChu

Respostas:

163

Vou dar um exemplo para deixar mais claro:

  • x: imagem de entrada da forma [2, 3], 1 canal
  • valid_pad: pool máximo com kernel 2x2, stride 2 e preenchimento VÁLIDO.
  • same_pad: pool máximo com kernel 2x2, stride 2 e SAME padding (este é o caminho clássico a seguir)

As formas de saída são:

  • valid_pad: aqui, sem preenchimento, portanto a forma de saída é [1, 1]
  • same_pad: aqui, colocamos a imagem na forma [2, 4] (com -infe depois aplicamos o pool máximo), então a forma de saída é [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.]

Olivier Moindrot
fonte
603

Se você gosta de arte ascii:

  • "VALID" = sem preenchimento:

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

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

Neste exemplo:

  • Largura de entrada = 13
  • Largura do filtro = 6
  • Passo = 5

Notas:

  • "VALID" apenas remove as colunas mais à direita (ou as linhas mais abaixo).
  • "SAME" tenta preencher uniformemente esquerda e direita, mas se a quantidade de colunas a ser adicionada for ímpar, ela adicionará a coluna extra à direita, como é o caso neste exemplo (a mesma lógica se aplica verticalmente: pode haver uma linha extra zeros na parte inferior).

Editar :

Sobre o nome:

  • Com o "SAME"preenchimento, se você usar um passo de 1, as saídas da camada terão as mesmas dimensões espaciais que suas entradas.
  • Com o "VALID"preenchimento, não há entradas de preenchimento "compostas". A camada usa apenas dados de entrada válidos .
MiniQuark
fonte
É justo dizer "MESMO" significa "usar preenchimento zero para garantir que o tamanho do filtro não precise ser alterado se a largura da imagem não for múltipla da largura do filtro ou se a altura da imagem não for múltipla da altura do filtro "? Como em "almofada com zeros até um múltiplo da largura do filtro" se a largura é o problema?
StatsSorceress
2
Respondendo à minha própria pergunta paralela: NÃO, esse não é o ponto de preenchimento zero. Você escolhe o tamanho do filtro para trabalhar com a entrada (incluindo preenchimento zero), mas não escolhe o preenchimento zero após o tamanho do filtro.
StatsSorceress
Não entendo sua própria resposta @StatsSorceress. Parece-me que você adiciona zeros suficientes (da maneira mais simétrica possível) para que todas as entradas sejam cobertas por algum filtro, estou certo?
precisa saber é
2
Ótima resposta, apenas para acrescentar: Caso os valores do tensor possam ser negativos, o preenchimento para max_Widget está com -inf.
Tones29
E se a largura de entrada for um número par quando ksize = 2, stride = 2 e com o mesmo preenchimento? ... então não deve ser preenchido com zero, certo? .... Estou dizendo isso quando procuro um código de darkflow repo , eles estão usando SAME pad, stride = 2, ksize = 2 para maxpool .... depois que a largura da imagem do maxpool foi reduzida para 208 pixels, a partir de 416 pixels, para 208 pixels. Alguém pode esclarecer isso?
K.vindi 04/04
161

Quando strideé 1 (mais típico de convolução do que de pool), podemos pensar na seguinte distinção:

  • "SAME": tamanho da saída é igual ao tamanho da entrada. Isso requer que a janela de filtro deslize para fora do mapa de entrada, daí a necessidade de preenchimento.
  • "VALID": A janela de filtro permanece na posição válida dentro do mapa de entrada, então o tamanho da saída diminui filter_size - 1. Não ocorre preenchimento.
YvesgereY
fonte
65
Isto é finalmente útil. Até este ponto, parecia que SAMEe VALIDpode ter sido chamado fooebar
omatai 25/01
7
Eu acho que "tamanho de saída é o mesmo como o tamanho de entrada" é verdade apenas quando o comprimento do passo é 1.
omsrisagar
92

O exemplo de convolução do TensorFlow fornece uma visão geral sobre a diferença entre SAMEe VALID:

  • Para o SAMEpreenchimento, a altura e a largura da saída são calculadas como:

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

E

  • Para o VALIDpreenchimento, a altura e a largura da saída são calculadas como:

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

O preenchimento é uma operação para aumentar o tamanho dos dados de entrada. No caso de dados unidimensionais, você apenas acrescenta / acrescenta o array com uma constante; em 2-dim você envolve a matriz com essas constantes. Em n-dim, você envolve seu hipercubo n-dim com a constante. Na maioria dos casos, essa constante é zero e é chamada de preenchimento zero.

Aqui está um exemplo de preenchimento zero p=1aplicado ao tensor 2-d: insira a descrição da imagem aqui


Você pode usar preenchimento arbitrário para o kernel, mas alguns dos valores de preenchimento são usados ​​com mais frequência do que outros:

  • Preenchimento VÁLIDO . O caso mais fácil, significa que não há preenchimento. Apenas deixe seus dados da mesma forma que eram.
  • MESMO estofamento às vezes chamado MEIO estofamento . É chamado SAME porque, para uma convolução com passo = 1, (ou para agrupamento), deve produzir saída do mesmo tamanho que a entrada. É chamado MEIO porque, para um kernel de tamanhok insira a descrição da imagem aqui
  • O preenchimento COMPLETO é o preenchimento máximo que não resulta em uma convolução sobre apenas elementos preenchidos. Para um núcleo de tamanho k, esse preenchimento é igual a k - 1.

Para usar preenchimento arbitrário no TF, você pode usar tf.pad()

Salvador Dalí
fonte
32

Explicação Rápida

VALID: Não aplique nenhum preenchimento, ou seja, suponha que todas as dimensões sejam válidas para que a imagem de entrada seja totalmente coberta pelo filtro e pela passada especificada.

SAME: Aplique preenchimento à entrada (se necessário) para que a imagem de entrada fique totalmente coberta pelo filtro e passo especificado. No passo 1, isso garantirá que o tamanho da imagem de saída seja o mesmo da entrada.

Notas

  • Isso se aplica às camadas de conv, bem como ao máximo de camadas de pool da mesma maneira
  • O termo "válido" é um pouco impróprio, porque as coisas não se tornam "inválidas" se você soltar parte da imagem. Às vezes você pode até querer isso. Provavelmente deveria ter sido chamado NO_PADDING.
  • O termo "mesmo" também é um nome impróprio, porque só faz sentido para o passo 1 quando a dimensão de saída é igual à dimensão de entrada. Para o passo 2, as dimensões de saída serão metade, por exemplo. Provavelmente deveria ter sido chamado AUTO_PADDING.
  • No SAME(modo auto-pad), o Tensorflow tentará espalhar o preenchimento igualmente à esquerda e à direita.
  • Em VALID(ou seja, modo sem preenchimento), o Tensorflow deixará cair as células direita e / ou inferior se o filtro e a passada não cobrirem totalmente a imagem de entrada.
Shital Shah
fonte
19

Estou citando esta resposta em documentos oficiais do tensorflow https://www.tensorflow.org/api_guides/python/nn#Convolution Para o preenchimento 'SAME', a altura e a largura da saída são calculadas como:

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

e o preenchimento na parte superior e esquerda são calculados como:

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

Para o preenchimento 'VALID', a altura e a largura da saída são calculadas como:

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

e os valores de preenchimento são sempre zero.

Vaibhav Dixit
fonte
1
Francamente, esta é a única resposta válida e completa, não limitada a passos de 1. E basta uma citação dos documentos. +1
P-Gn
1
Muito útil para ter essa resposta, principalmente porque o link para o qual você aponta não funciona mais e parece que o Google apagou essas informações do site da tf!
Daniel
12

Existem três opções de preenchimento: válido (sem preenchimento), igual (ou metade), completo. Você pode encontrar explicações (em Theano) aqui: http://deeplearning.net/software/theano/tutorial/conv_arithmetic.html

  • Preenchimento válido ou sem preenchimento:

O preenchimento válido não envolve preenchimento zero, portanto, abrange apenas a entrada válida, sem incluir zeros gerados artificialmente. O comprimento da saída é ((o comprimento da entrada) - (k-1)) para o tamanho do kernel k, se o passo s = 1.

  • Mesmo ou meio preenchimento:

O mesmo preenchimento torna o tamanho das saídas igual ao das entradas quando s = 1. Se s = 1, o número de zeros preenchidos é (k-1).

  • Estofamento completo:

O preenchimento completo significa que o kernel roda sobre as entradas inteiras; portanto, nas extremidades, o kernel pode atender à única entrada e zeros a mais. O número de zeros preenchidos é 2 (k-1) se s = 1. O comprimento da saída é ((o comprimento da entrada) + (k-1)) se s = 1.

Portanto, o número de preenchimentos: (válido) <= (mesmo) <= (completo)

Mudar o mundo
fonte
8

Preenchimento on / off. Determina o tamanho efetivo da sua entrada.

VALID:Sem preenchimento. As operações de convolução etc. são realizadas apenas em locais "válidos", ou seja, não muito próximos das bordas do seu tensor.
Com um núcleo de 3x3 e uma imagem de 10x10, você estaria realizando a convolução na área 8x8 dentro das bordas.

SAME:O estofamento é fornecido. Sempre que sua operação faz referência a uma vizinhança (não importa o tamanho), valores zero são fornecidos quando essa vizinhança se estende para fora do tensor original para permitir que essa operação funcione também nos valores de borda.
Com um kernel de 3x3 e uma imagem de 10x10, você estaria realizando convolução em toda a área de 10x10.

Laine Mikael
fonte
8

Preenchimento VÁLIDO : este é com preenchimento zero. Espero que não haja confusão.

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)

MESMO preenchimento: isso é meio complicado de entender, porque precisamos considerar duas condições separadamente, conforme mencionado nos documentos oficiais .

Vamos considerar entrada como , saída como , preenchimento como , passo como e tamanho do kernel como (apenas uma única dimensão é considerada)

Caso 01 ::

Caso 02 ::

é calculado de modo que o valor mínimo que pode ser obtido para o preenchimento. Como o valor de é conhecido, o valor de pode ser encontrado usando esta fórmula .

Vamos elaborar este exemplo:

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)

Aqui a dimensão de x é (3,4). Então, se a direção horizontal for tomada (3):

Se a direção vertical for tomada (4):

Espero que isso ajude a entender como realmente o MESMO preenchimento funciona no TF.

GPrathap
fonte
7

Com base na explicação aqui e no acompanhamento da resposta de Tristan, costumo usar essas funções rápidas para verificações de sanidade.

# 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')
ahmedhosny
fonte
6

Resumindo, preenchimento 'válido' significa sem preenchimento. O tamanho da saída da camada convolucional diminui dependendo do tamanho da entrada e do tamanho do kernel.

Pelo contrário, estofamento 'mesmo' significa usar estofamento. Quando o passo é definido como 1, o tamanho da saída da camada convolucional mantém o tamanho da entrada, acrescentando um certo número de 'borda 0' ao redor dos dados de entrada ao calcular a convolução.

Espero que esta descrição intuitiva ajude.

Frederick HONG
fonte
5

Fórmula geral

Aqui, W e H são largura e altura da entrada, F são dimensões do filtro, P é tamanho do preenchimento (ou seja, número de linhas ou colunas a serem preenchidas)

Para o MESMO preenchimento:

MESMO Preenchimento

Para preenchimento VÁLIDO:

Preenchimento VÁLIDO

Shivam Kushwaha
fonte
2

Complementando a excelente resposta de YvesgereY, achei esta visualização extremamente útil:

Visualização de preenchimento

O preenchimento ' válido ' é a primeira figura. A janela de filtro permanece dentro da imagem.

Preenchimento ' mesmo ' é a terceira figura. A saída é do mesmo tamanho.


Encontrei neste artigo .

zmx
fonte
0

Resposta compatível com Tensorflow 2.0 : Foram fornecidas explicações detalhadas acima, sobre preenchimento "Válido" e "Mesmo".

No entanto, especificarei diferentes Funções de Pool e seus respectivos Comandos Tensorflow 2.x (>= 2.0), para o benefício da comunidade.

Funções no 1.x :

tf.nn.max_pool

tf.keras.layers.MaxPool2D

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

Funções no 2.x :

tf.nn.max_poolSe usado em 2.x e tf.compat.v1.nn.max_pool_v2ou tf.compat.v2.nn.max_pool, se migraram de 1.x para 2.x

tf.keras.layers.MaxPool2D se usado em 2.xe

tf.compat.v1.keras.layers.MaxPool2D ou tf.compat.v1.keras.layers.MaxPooling2D ou tf.compat.v2.keras.layers.MaxPool2Dou tf.compat.v2.keras.layers.MaxPooling2D, se migrado de 1.x para 2.x.

Average Pooling => tf.nn.avg_pool2dou tf.keras.layers.AveragePooling2Dse usado no TF 2.xe

tf.compat.v1.nn.avg_pool_v2ou tf.compat.v2.nn.avg_poolou tf.compat.v1.keras.layers.AveragePooling2Doutf.compat.v1.keras.layers.AvgPool2D ou tf.compat.v2.keras.layers.AveragePooling2Dou tf.compat.v2.keras.layers.AvgPool2D, se migrado de 1.x para 2.x.

Para obter mais informações sobre migração do Tensorflow 1.x para 2.x, consulte este Guia de migração. .

Suporte ao Tensorflow
fonte