Quelle est la différence entre le remplissage "SAME" et "VALID" dans tf.nn.max_pool de tensorflow?

309

Quelle est la différence entre le rembourrage «SAME» et «VALID» tf.nn.max_poolde tensorflow?

À mon avis, «VALIDE» signifie qu'il n'y aura pas de remplissage nul en dehors des bords lorsque nous ferons la piscine maximale.

Selon Un guide de l'arithmétique de convolution pour l'apprentissage en profondeur , il indique qu'il n'y aura pas de remplissage dans l'opérateur de pool, c'est-à-dire qu'il suffit d'utiliser 'VALIDE' de tensorflow. Mais qu'est-ce que le rembourrage «MÊME» de la piscine max tensorflow?

karl_TUM
la source
3
Consultez tensorflow.org/api_guides/python/… pour plus de détails, c'est ainsi que tf l'a fait.
GabrielChu
3
Voici une réponse assez détaillée avec des visualisations .
rbinnun
4
Découvrez ces gifs incroyables pour comprendre le fonctionnement du rembourrage et de la foulée. Lien
Deepak
1
@GabrielChu, votre lien semble être mort et est maintenant une redirection vers un aperçu général.
mat
Lors de la mise à niveau de Tensorflow vers 2.0, les choses seront remplacées par Keras et je pense que vous pouvez trouver les informations de mise en commun dans les documentations Keras. @matt
GabrielChu

Réponses:

163

Je vais donner un exemple pour le rendre plus clair:

  • x: image d'entrée de forme [2, 3], 1 canal
  • valid_pad: pool max avec noyau 2x2, foulée 2 et rembourrage VALIDE.
  • same_pad: pool max avec noyau 2x2, foulée 2 et même rembourrage (c'est la façon classique de procéder)

Les formes de sortie sont:

  • valid_pad: ici, pas de remplissage donc la forme de sortie est [1, 1]
  • same_pad: ici, nous remplissons l'image à la forme [2, 4] (avec -infpuis appliquons le pool max), donc la forme de sortie est [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
la source
603

Si vous aimez l'art ascii:

  • "VALID" = sans rembourrage:

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

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

Dans cet exemple:

  • Largeur d'entrée = 13
  • Largeur du filtre = 6
  • Foulée = 5

Remarques:

  • "VALID" ne supprime que les colonnes les plus à droite (ou les lignes les plus basses).
  • "SAME" essaie de remplir uniformément à gauche et à droite, mais si le nombre de colonnes à ajouter est impair, il ajoutera la colonne supplémentaire à droite, comme c'est le cas dans cet exemple (la même logique s'applique verticalement: il peut y avoir une ligne supplémentaire de zéros en bas).

Modifier :

À propos du nom:

  • Avec le "SAME"remplissage, si vous utilisez une foulée de 1, les sorties du calque auront les mêmes dimensions spatiales que ses entrées.
  • Avec le "VALID"rembourrage, il n'y a pas d'entrées de remplissage «composées». La couche utilise uniquement des données d'entrée valides .
MiniQuark
la source
Est-il juste de dire que "MÊME" signifie "utiliser un remplissage nul pour vous assurer que la taille du filtre ne doit pas changer si la largeur de l'image n'est pas un multiple de la largeur du filtre ou la hauteur de l'image n'est pas un multiple de la hauteur du filtre "? Comme dans "pad avec des zéros jusqu'à un multiple de la largeur du filtre" si la largeur est le problème?
StatsSorceress
2
Répondre à ma propre question secondaire: NON, ce n'est pas le point de remplissage nul. Vous choisissez la taille de filtre pour travailler avec l'entrée (y compris le remplissage nul), mais vous ne choisissez pas le remplissage zéro après la taille du filtre.
StatsSorceress
Je ne comprends pas votre propre réponse @StatsSorceress. Il me semble que vous ajoutez suffisamment de zéros (de manière aussi symétrique que possible) pour que toutes les entrées soient couvertes par un filtre, ai-je raison?
guillefix
2
Excellente réponse, juste pour ajouter: dans le cas où les valeurs du tenseur peuvent être négatives, le remplissage pour max_pool est avec -inf.
Tones29
Que se passe-t-il si la largeur d'entrée est un nombre pair lorsque ksize = 2, stride = 2 et avec le même remplissage? ... alors elle ne devrait pas être complétée à droite? ... Je dis cela quand je regarde le repo de code darkflow , ils utilisent SAME pad, stride = 2, ksize = 2 pour maxpool .... après la largeur d'image maxpool réduite à 208 pixels au lieu de 416 pixels. Quelqu'un peut-il clarifier cela?
K.vindi
161

Quand strideest 1 (plus typique avec convolution que regroupement), on peut penser à la distinction suivante:

  • "SAME": la taille de sortie est la même que la taille d'entrée. Cela nécessite que la fenêtre de filtre glisse en dehors de la carte d'entrée, d'où la nécessité de remplir.
  • "VALID": La fenêtre de filtre reste à une position valide dans la carte d'entrée, donc la taille de sortie diminue filter_size - 1. Aucun rembourrage ne se produit.
YvesgereY
la source
65
C'est finalement utile. Jusqu'à ce point, il est apparu que SAMEet VALIDaurait aussi bien pu être appelé fooetbar
omatai
7
Je pense que "la taille de sortie est la même que la taille d'entrée" n'est vraie que lorsque la longueur de foulée est 1.
omsrisagar
92

L' exemple de convolution TensorFlow donne un aperçu de la différence entre SAMEet VALID:

  • Pour le SAMErembourrage, la hauteur et la largeur de sortie sont calculées comme suit:

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

Et

  • Pour le VALIDrembourrage, la hauteur et la largeur de sortie sont calculées comme suit:

    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
la source
46

Le remplissage est une opération pour augmenter la taille des données d'entrée. Dans le cas de données à 1 dimension, vous ajoutez / ajoutez simplement le tableau avec une constante, en 2-dim vous entourez la matrice avec ces constantes. Dans n-dim, vous entourez votre hypercube n-dim de la constante. Dans la plupart des cas, cette constante est nulle et elle est appelée remplissage nul.

Voici un exemple de remplissage nul avec un p=1tenseur 2D appliqué: entrez la description de l'image ici


Vous pouvez utiliser un remplissage arbitraire pour votre noyau, mais certaines des valeurs de remplissage sont utilisées plus fréquemment que d'autres:

  • Rembourrage VALIDE . Le cas le plus simple, signifie pas de rembourrage du tout. Laissez simplement vos données telles qu'elles étaient.
  • MÊME rembourrage parfois appelé DEMI rembourrage . Il est appelé SAME car pour une convolution avec une foulée = 1, (ou pour la mise en commun), il doit produire une sortie de la même taille que l'entrée. On l'appelle HALF car pour un noyau de taillek entrez la description de l'image ici
  • Le rembourrage COMPLET est le rembourrage maximal qui n'entraîne pas de convolution sur les éléments simplement rembourrés. Pour un noyau de taille k, ce remplissage est égal à k - 1.

Pour utiliser un remplissage arbitraire dans TF, vous pouvez utiliser tf.pad()

Salvador Dali
la source
32

Explication rapide

VALID: N'appliquez aucun remplissage, c'est-à-dire, supposez que toutes les dimensions sont valides afin que l'image d'entrée soit entièrement couverte par le filtre et la foulée que vous avez spécifiés.

SAME: Appliquez un remplissage à l'entrée (si nécessaire) afin que l'image d'entrée soit entièrement couverte par le filtre et la foulée que vous avez spécifiés. Pour la foulée 1, cela garantira que la taille de l'image de sortie est identique à celle de l'entrée.

Remarques

  • Cela s'applique aux couches conv ainsi qu'aux couches de pool max de la même manière
  • Le terme "valide" est un peu impropre car les choses ne deviennent pas "invalides" si vous déposez une partie de l'image. Parfois, vous pourriez même vouloir cela. Cela aurait probablement dû être appelé à la NO_PADDINGplace.
  • Le terme "même" est également impropre car il n'a de sens que pour la foulée de 1 lorsque la dimension de sortie est identique à la dimension d'entrée. Pour une foulée de 2, les dimensions de sortie seront par exemple la moitié. Cela aurait probablement dû être appelé à la AUTO_PADDINGplace.
  • En SAME(c'est-à-dire en mode auto-pad), Tensorflow essaiera de répartir le rembourrage uniformément à gauche et à droite.
  • En VALID(c'est-à-dire sans mode de remplissage), Tensorflow supprimera les cellules de droite et / ou de bas si votre filtre et votre foulée ne couvrent pas complètement l'image d'entrée.
Shital Shah
la source
19

Je cite cette réponse des documents officiels de tensorflow https://www.tensorflow.org/api_guides/python/nn#Convolution Pour le rembourrage 'SAME', la hauteur et la largeur de sortie sont calculées comme suit:

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

et le rembourrage en haut et à gauche sont calculés comme:

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

Pour le rembourrage «VALIDE», la hauteur et la largeur de sortie sont calculées comme suit:

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

et les valeurs de remplissage sont toujours nulles.

Vaibhav Dixit
la source
1
Franchement, c'est la seule réponse valable et complète, sans se limiter à des pas de 1. Et tout ce qu'il faut, c'est une citation de la documentation. +1
P-Gn
1
Très utile d'avoir cette réponse autour, surtout parce que le lien vers lequel vous pointez ne fonctionne plus et il semble que Google ait effacé ces informations du site tf!
Daniel
12

Il y a trois choix de rembourrage: valide (pas de rembourrage), identique (ou demi), complet. Vous pouvez trouver des explications (dans Theano) ici: http://deeplearning.net/software/theano/tutorial/conv_arithmetic.html

  • Rembourrage valide ou non:

Le remplissage valide n'implique aucun remplissage nul, il ne couvre donc que l'entrée valide, sans inclure les zéros générés artificiellement. La longueur de sortie est ((la longueur d'entrée) - (k-1)) pour la taille du noyau k si la foulée s = 1.

  • Même ou demi rembourrage:

Le même rembourrage rend la taille des sorties identique à celle des entrées lorsque s = 1. Si s = 1, le nombre de zéros remplis est (k-1).

  • Rembourrage complet:

Le remplissage complet signifie que le noyau s'exécute sur toutes les entrées, donc à la fin, le noyau peut rencontrer la seule entrée et des zéros. Le nombre de zéros remplis est 2 (k-1) si s = 1. La longueur de sortie est ((la longueur d'entrée) + (k-1)) si s = 1.

Par conséquent, le nombre de rembourrages: (valide) <= (identique) <= (complet)

Change le monde
la source
8

Rembourrage activé / désactivé. Détermine la taille effective de votre entrée.

VALID:Pas de rembourrage. Les opérations de convolution, etc. ne sont effectuées qu'aux emplacements qui sont "valides", c'est-à-dire pas trop près des frontières de votre tenseur.
Avec un noyau de 3x3 et une image de 10x10, vous effectuez une convolution sur la zone 8x8 à l'intérieur des bordures.

SAME:Le rembourrage est fourni. Chaque fois que votre opération fait référence à un voisinage (quelle que soit sa taille), des valeurs nulles sont fournies lorsque ce voisinage s'étend en dehors du tenseur d'origine pour permettre à cette opération de fonctionner également sur les valeurs de bordure.
Avec un noyau de 3x3 et une image de 10x10, vous effectuez une convolution sur toute la zone 10x10.

Laine Mikael
la source
8

Rembourrage VALIDE : c'est avec un remplissage nul. J'espère qu'il n'y a pas de 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)

MÊME rembourrage: c'est un peu délicat à comprendre en premier lieu car nous devons considérer deux conditions séparément, comme mentionné dans les documents officiels .

Prenons entrée as , sortie as , padding as , stride as et kernel size as (une seule dimension est prise en compte)

Cas 01 ::

Cas 02 ::

est calculé de telle sorte que la valeur minimale qui peut être prise pour le rembourrage. Puisque la valeur de est connue, la valeur de peut être trouvée en utilisant cette formule .

Voyons cet exemple:

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)

Ici, la dimension de x est (3,4). Alors si la direction horizontale est prise (3):

Si la direction verticale est prise (4):

J'espère que cela vous aidera à comprendre comment fonctionne réellement le même rembourrage dans TF.

GPrathap
la source
7

Sur la base de l'explication ici et du suivi de la réponse de Tristan, j'utilise généralement ces fonctions rapides pour les contrôles de santé mentale.

# 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
la source
6

En résumé, un remplissage «valide» signifie qu'il n'y a pas de remplissage. La taille de sortie de la couche convolutionnelle diminue en fonction de la taille d'entrée et de la taille du noyau.

Au contraire, un même rembourrage signifie utiliser un rembourrage. Lorsque la foulée est définie sur 1, la taille de sortie de la couche convolutionnelle reste la taille d'entrée en ajoutant un certain nombre de `` bord 0 '' autour des données d'entrée lors du calcul de la convolution.

J'espère que cette description intuitive vous aidera.

Frederick HONG
la source
5

Formule générale

Ici, W et H sont la largeur et la hauteur de l'entrée, F les dimensions du filtre, P la taille de remplissage (c'est-à-dire le nombre de lignes ou de colonnes à remplir)

Pour le même rembourrage:

MÊME rembourrage

Pour un rembourrage VALIDE:

Rembourrage VALIDE

Shivam Kushwaha
la source
3

En complément de l'excellente réponse d'YvesgereY, j'ai trouvé cette visualisation extrêmement utile:

Visualisation du rembourrage

Rembourrage ' valide » est le premier chiffre. La fenêtre du filtre reste à l'intérieur de l'image.

Le rembourrage « identique » est le troisième chiffre. La sortie est de la même taille.


Trouvé sur cet article .

zmx
la source
0

Réponse compatible avec Tensorflow 2.0 : Des explications détaillées ont été fournies ci-dessus, concernant le remplissage "valide" et "identique".

Cependant, je spécifierai différentes fonctions de regroupement et leurs commandes respectives dans Tensorflow 2.x (>= 2.0), pour le bénéfice de la communauté.

Fonctions en 1.x :

tf.nn.max_pool

tf.keras.layers.MaxPool2D

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

Fonctions en 2.x :

tf.nn.max_poolsi utilisé dans 2.x et tf.compat.v1.nn.max_pool_v2ou tf.compat.v2.nn.max_pool, si migré de 1.x vers 2.x.

tf.keras.layers.MaxPool2D si utilisé dans 2.x et

tf.compat.v1.keras.layers.MaxPool2Dou tf.compat.v1.keras.layers.MaxPooling2Dou tf.compat.v2.keras.layers.MaxPool2Dou tf.compat.v2.keras.layers.MaxPooling2D, en cas de migration de 1.x vers 2.x.

Average Pooling => tf.nn.avg_pool2dou tf.keras.layers.AveragePooling2Dsi utilisé dans TF 2.x et

tf.compat.v1.nn.avg_pool_v2ou tf.compat.v2.nn.avg_poolou tf.compat.v1.keras.layers.AveragePooling2Dou tf.compat.v1.keras.layers.AvgPool2Dou tf.compat.v2.keras.layers.AveragePooling2Dou tf.compat.v2.keras.layers.AvgPool2D, en cas de migration de 1.x vers 2.x.

Pour plus d'informations sur la migration de Tensorflow 1.x vers 2.x, veuillez consulter ce guide de migration .

Prise en charge de Tensorflow
la source