¿Cuál es la diferencia entre el relleno 'SAME' y 'VALID' en tf.nn.max_pool de tensorflow?


309

¿Cuál es la diferencia entre 'mismo' y el relleno 'válidos' en el tf.nn.max_poolde tensorflow?

En mi opinión, 'VÁLIDO' significa que no habrá relleno cero fuera de los bordes cuando hagamos el grupo máximo.

De acuerdo con una guía de aritmética de convolución para el aprendizaje profundo , dice que no habrá relleno en el operador de la piscina, es decir, solo use 'VÁLIDO' de tensorflow. Pero, ¿en qué consiste el relleno 'MISMO' del grupo máximo tensorflow?


3
Consulte tensorflow.org/api_guides/python/… para obtener detalles, así es como lo hizo.
GabrielChu

3
Aquí hay una respuesta bastante detallada con visualizaciones .
rbinnun

44
Echa un vistazo a estos increíbles gifs para comprender cómo funciona el relleno y la zancada. Enlace
Deepak

1
@GabrielChu su enlace parece haber muerto y ahora es una redirección a una descripción general.
mate

A medida que Tensorflow se actualice a 2.0, Keras reemplazará las cosas y creo que puede encontrar la información de agrupación en las documentaciones de Keras. @matt
GabrielChu

Respuestas:


163

Daré un ejemplo para aclararlo:

  • x: imagen de entrada de forma [2, 3], 1 canal
  • valid_pad: grupo máximo con 2x2 kernel, stride 2 y relleno VÁLIDO.
  • same_pad: grupo máximo con 2x2 kernel, stride 2 y el mismo relleno (esta es la forma clásica de hacerlo)

Las formas de salida son:

  • valid_pad: aquí, sin relleno, por lo que la forma de salida es [1, 1]
  • same_pad: aquí, rellenamos la imagen a la forma [2, 4] (con -infy luego aplicamos el grupo máximo), por lo que la forma de salida es [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

Si te gusta el arte ascii:

  • "VALID" = sin relleno:

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

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

En este ejemplo:

  • Ancho de entrada = 13
  • Ancho del filtro = 6
  • Zancada = 5

Notas:

  • "VALID" solo deja caer las columnas del extremo derecho (o las filas del extremo inferior).
  • "SAME" intenta rellenar uniformemente hacia la izquierda y hacia la derecha, pero si la cantidad de columnas a agregar es impar, agregará la columna adicional a la derecha, como es el caso en este ejemplo (la misma lógica se aplica verticalmente: puede haber una fila adicional de ceros en la parte inferior).

Editar :

Sobre el nombre

  • Con el "SAME"relleno, si usa una zancada de 1, las salidas de la capa tendrán las mismas dimensiones espaciales que sus entradas.
  • Con el "VALID"relleno, no hay entradas de relleno "inventadas". La capa solo usa datos de entrada válidos .

¿Es justo decir "MISMO" significa "usar relleno cero para asegurarse de que el tamaño del filtro no tenga que cambiar si el ancho de la imagen no es múltiplo del ancho del filtro o si la altura de la imagen no es múltiplo de la altura del filtro "? Como en ", rellena con ceros hasta un múltiplo del ancho del filtro" si el ancho es el problema
EstadísticasSorceress

2
Respondiendo a mi propia pregunta secundaria: NO, ese no es el punto de relleno cero. Usted elige el tamaño del filtro para trabajar con la entrada (incluido el relleno cero), pero no elige el relleno cero después del tamaño del filtro.
StatsSorceress

No entiendo tu propia respuesta @StatsSorceress. Me parece que agrega suficientes ceros (de la manera más simétrica posible) para que todas las entradas estén cubiertas por algún filtro, ¿estoy en lo cierto?
guillefix

2
Gran respuesta, solo para agregar: en caso de que los valores del tensor puedan ser negativos, el relleno para max_pooling es con -inf.
Tones29

¿Qué pasa si el ancho de entrada es un número par cuando ksize = 2, stride = 2 y con el mismo relleno? ... entonces no debería estar rellenado con ceros, ¿verdad? ... Lo digo cuando miro el repositorio de código darkflow , están usando el mismo pad, stride = 2, ksize = 2 para maxpool ... después de que el ancho de la imagen de maxpooling se redujera a 208 píxeles desde un ancho de 416 píxeles. ¿Alguien puede aclarar esto?
K.vindi

161

Cuando stridees 1 (más típico con convolución que agrupación), podemos pensar en la siguiente distinción:

  • "SAME": el tamaño de salida es el mismo que el tamaño de entrada. Esto requiere que la ventana del filtro se deslice fuera del mapa de entrada, de ahí la necesidad de rellenar.
  • "VALID": La ventana de filtro permanece en una posición válida dentro del mapa de entrada, por lo que el tamaño de salida se reduce filter_size - 1. No se produce relleno.

65
Esto finalmente es útil. Hasta este punto, parecía que SAMEy bien VALIDpodría haber sido llamado fooybar
omatai

77
Creo que "el tamaño de salida es el mismo que el tamaño de entrada" solo es cierto cuando la longitud del paso es 1.
omsrisagar

92

El ejemplo de convolución TensorFlow ofrece una visión general sobre la diferencia entre SAMEy VALID:

  • Para el SAMErelleno, la altura y el ancho de salida se calculan como:

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

Y

  • Para el VALIDrelleno, la altura y el ancho de salida se calculan 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]))

46

El relleno es una operación para aumentar el tamaño de los datos de entrada. En el caso de datos unidimensionales, simplemente agrega / antepone la matriz con una constante, en 2-dim rodea la matriz envolvente con estas constantes. En n-dim rodeas tu hipercubo n-dim con la constante. En la mayoría de los casos, esta constante es cero y se llama relleno de cero.

Aquí hay un ejemplo de relleno cero con p=1tensor aplicado a 2-d: ingrese la descripción de la imagen aquí


Puede usar relleno arbitrario para su núcleo, pero algunos de los valores de relleno se usan con más frecuencia que otros:

  • Relleno VÁLIDO . El caso más fácil, significa que no hay relleno en absoluto. Simplemente deje sus datos como estaban.
  • El mismo relleno a veces llamado MEDIO relleno . Se llama MISMO porque para una convolución con una zancada = 1, (o para agrupar) debería producir una salida del mismo tamaño que la entrada. Se llama MEDIO porque para un núcleo de tamañok ingrese la descripción de la imagen aquí
  • El relleno COMPLETO es el relleno máximo que no da como resultado una convolución sobre solo elementos rellenados. Para un núcleo de tamaño k, este relleno es igual a k - 1.

Para usar relleno arbitrario en TF, puede usar tf.pad()


32

Explicacion rapida

VALID: No aplique ningún relleno, es decir, suponga que todas las dimensiones son válidas para que la imagen de entrada quede completamente cubierta por el filtro y la zancada que especificó.

SAME: Aplique relleno a la entrada (si es necesario) para que la imagen de entrada quede completamente cubierta por el filtro y la zancada que especificó. Para el paso 1, esto asegurará que el tamaño de la imagen de salida sea el mismo que el de la entrada.

Notas

  • Esto se aplica a las capas conv, así como a las capas de grupo máximo de la misma manera
  • El término "válido" es un término poco apropiado porque las cosas no se vuelven "inválidas" si suelta parte de la imagen. En algún momento incluso podrías querer eso. Esto probablemente debería haberse llamado en su NO_PADDINGlugar.
  • El término "igual" también es incorrecto porque solo tiene sentido para el paso 1 cuando la dimensión de salida es igual a la dimensión de entrada. Para zancadas de 2, las dimensiones de salida serán la mitad, por ejemplo. Esto probablemente debería haberse llamado en su AUTO_PADDINGlugar.
  • En SAME(es decir, el modo de relleno automático), Tensorflow intentará distribuir el relleno de manera uniforme tanto a la izquierda como a la derecha.
  • En VALID(es decir, sin modo de relleno), Tensorflow colocará las celdas derecha y / o inferior si su filtro y zancada no cubren completamente la imagen de entrada.

19

Cito esta respuesta de los documentos oficiales de Tensorflow https://www.tensorflow.org/api_guides/python/nn#Convolution Para el relleno 'SAME', la altura y el ancho de salida se calculan como:

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

y el relleno en la parte superior e izquierda se calculan 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 el relleno 'VÁLIDO', la altura y el ancho de salida se calculan 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]))

y los valores de relleno son siempre cero.


1
Francamente, esta es la única respuesta válida y completa, no limitada a pasos de 1. Y todo lo que se necesita es una cita de los documentos. +1
P-Gn

1
Muy útil para tener esta respuesta, especialmente porque el enlace al que apunta ya no funciona y parece que Google borró esa información del sitio web de tf.
Daniel

12

Hay tres opciones de relleno: válido (sin relleno), igual (o medio), completo. Puede encontrar explicaciones (en Theano) aquí: http://deeplearning.net/software/theano/tutorial/conv_arithmetic.html

  • Válido o sin relleno:

El relleno válido no implica relleno cero, por lo que cubre solo la entrada válida, sin incluir ceros generados artificialmente. La longitud de salida es ((la longitud de entrada) - (k-1)) para el tamaño de kernel k si la zancada s = 1.

  • Mismo o medio relleno:

El mismo relleno hace que el tamaño de las salidas sea el mismo que el de las entradas cuando s = 1. Si s = 1, el número de ceros rellenos es (k-1).

  • Relleno completo:

El relleno completo significa que el núcleo se ejecuta sobre todas las entradas, por lo que en los extremos, el núcleo puede cumplir con la única entrada y ceros más. El número de ceros rellenos es 2 (k-1) si s = 1. La longitud de salida es ((la longitud de entrada) + (k-1)) si s = 1.

Por lo tanto, el número de rellenos: (válido) <= (igual) <= (completo)


8

Acolchado activado / desactivado. Determina el tamaño efectivo de su entrada.

VALID:Sin relleno. Las operaciones de convolución, etc., solo se realizan en ubicaciones "válidas", es decir, no demasiado cerca de los bordes de su tensor.
Con un núcleo de 3x3 y una imagen de 10x10, estaría realizando una convolución en el área de 8x8 dentro de los bordes.

SAME:Se proporciona relleno. Siempre que su operación haga referencia a un vecindario (no importa cuán grande), se proporcionan valores cero cuando ese vecindario se extiende fuera del tensor original para permitir que esa operación funcione también en valores de borde.
Con un núcleo de 3x3 y una imagen de 10x10, estaría realizando una convolución en el área completa de 10x10.


8

Relleno VÁLIDO : esto es con relleno cero. Espero que no haya confusión.

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)

El mismo relleno: es un poco difícil de entender en primer lugar porque tenemos que considerar dos condiciones por separado como se menciona en los documentos oficiales .

Tomemos input as , output as , padding as , stride as y kernel size as (solo se considera una sola dimensión)

Caso 01 ::

Caso 02 ::

se calcula de tal manera que el valor mínimo que se puede tomar para el relleno. Como se conoce el valor de, se puede encontrar el valor de utilizando esta fórmula .

Analicemos este ejemplo:

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)

Aquí la dimensión de x es (3,4). Entonces, si se toma la dirección horizontal (3):

Si se toma la dirección vertical (4):

Espero que esto ayude a comprender cómo funciona realmente el mismo relleno en TF.


7

Basado en la explicación aquí y siguiendo la respuesta de Tristan, usualmente uso estas funciones rápidas para controles de cordura.

# 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

En resumen, el relleno "válido" significa que no hay relleno. El tamaño de salida de la capa convolucional se reduce según el tamaño de entrada y el tamaño del núcleo.

Por el contrario, 'mismo' relleno significa usar relleno. Cuando el paso se establece en 1, el tamaño de salida de la capa convolucional se mantiene como el tamaño de entrada al agregar un cierto número de 'borde 0' alrededor de los datos de entrada al calcular la convolución.

Espero que esta descripción intuitiva ayude.


5

Formula general

Aquí, W y H son el ancho y la altura de entrada, F son las dimensiones del filtro, P es el tamaño del relleno (es decir, el número de filas o columnas a rellenar)

Para el mismo relleno:

El mismo relleno

Para relleno VÁLIDO:

Relleno VÁLIDO


2

Complementando la gran respuesta de YvesgereY, esta visualización me pareció extremadamente útil:

Visualización de relleno

El relleno ' válido ' es la primera figura. La ventana del filtro permanece dentro de la imagen.

El relleno ' igual ' es la tercera figura. La salida es del mismo tamaño.


Lo encontré en este artículo .


0

Respuesta compatible con Tensorflow 2.0 : se han proporcionado explicaciones detalladas anteriormente, sobre el relleno "válido" y el "mismo".

Sin embargo, especificaré diferentes funciones de agrupación y sus respectivos comandos en Tensorflow 2.x (>= 2.0)beneficio de la comunidad.

Funciones en 1.x :

tf.nn.max_pool

tf.keras.layers.MaxPool2D

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

Funciones en 2.x :

tf.nn.max_poolsi se usa en 2.xy tf.compat.v1.nn.max_pool_v2or tf.compat.v2.nn.max_pool, si se migró de 1.xa 2.x.

tf.keras.layers.MaxPool2D si se usa en 2.xy

tf.compat.v1.keras.layers.MaxPool2Do tf.compat.v1.keras.layers.MaxPooling2Do tf.compat.v2.keras.layers.MaxPool2Do tf.compat.v2.keras.layers.MaxPooling2D, si se migró de 1.xa 2.x.

Average Pooling => tf.nn.avg_pool2do tf.keras.layers.AveragePooling2Dsi se usa en TF 2.xy

tf.compat.v1.nn.avg_pool_v2o tf.compat.v2.nn.avg_poolo tf.compat.v1.keras.layers.AveragePooling2Do tf.compat.v1.keras.layers.AvgPool2Do tf.compat.v2.keras.layers.AveragePooling2Do tf.compat.v2.keras.layers.AvgPool2D, si se migraron de 1.xa 2.x.

Para obtener más información sobre la migración de Tensorflow 1.xa 2.x, consulte esta Guía de migración .

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.