Que fait l'opération "conv2d transpose ()" de TensorFlow?

la documentation relative à l'opération conv2d_transpose() n'explique pas clairement ce qu'elle fait:

La transposition de conv2d.

cette opération est parfois appelée "déconvolution" après réseaux Deconvolutionnels , mais est en fait la transposition (gradient) de conv2d plutôt qu'une véritable déconvolution.

j'ai parcouru le papier sur lequel le doc pointe, mais il n'a pas aidé.

Que fait cette opération ne et quels sont les exemples de pourquoi vous voulez l'utiliser?

25
demandé sur MiniQuark 2016-09-07 17:55:18

4 réponses

C'est la meilleure explication que j'ai vu en ligne comment la convolution transpose fonctionne est ici .

je vais vous donner ma brève description. Il applique la convolution avec une foulée fractionnaire. En d'autres termes espacer les valeurs d'entrée (avec des zéros) pour appliquer le filtre sur une région qui est potentiellement plus petite que la taille du filtre.

le pourquoi vouloir l'utiliser. Il peut être utilisé comme une sorte de suréchantillonnage avec poids appris par opposition à l'interpolation bilinéaire ou à une autre forme fixe de suréchantillonnage.

21
répondu Steven 2017-04-13 12:50:40

voici un autre point de vue du point de vue des" gradients", c'est-à-dire pourquoi la documentation TensorFlow dit conv2d_transpose() est "en fait la transposition ( gradient ) de conv2d plutôt qu'une déconvolution réelle". pour plus de détails sur le calcul réel fait dans conv2d_transpose , je recommande fortement cet article , à partir de la page 19.

Quatre Fonctions Associées

Dans tf.nn , il y a 4 étroitement liés et plutôt déroutant fonctions 2d convolution:

  • tf.nn.conv2d
  • tf.nn.conv2d_backprop_filter
  • tf.nn.conv2d_backprop_input
  • tf.nn.conv2d_transpose

résumé en Une phrase: ils sont tous juste 2d circonvolutions . Leurs différences sont dans l'ordre des arguments d'entrée, la rotation d'entrée ou transpositions, enjambées (y compris les enjambées fractionnaires), coussinets, etc. Avec tf.nn.conv2d en main, on peut implémenter tous les 3 autres ops en transformant les entrées et en changeant les arguments conv2d .

Problème "Paramètres 1519560920"
  • en Avant et en arrière des calculs:
# forward
out = conv2d(x, w)

# backward, given d_out
=> find d_x?
=> find d_w?

dans le calcul avancé, nous calculons la convolution de l'image d'entrée x avec le filtre w , et le résultat est out . Dans le calcul en arrière, supposons que nous sommes donnés d_out , qui est le gradient W. R. T. out . Notre objectif est de trouver d_x et d_w , qui sont le gradient W. R. T. x et w respectivement.

pour faciliter la discussion, nous supposons:

  • Tous foulée de taille 1
  • tous in_channels et out_channels sont 1
  • Utiliser VALID padding
  • nombre impair Taille du filtre, ce qui évite un certain problème de forme asymétrique

Réponse Courte

conceptuellement, avec les hypothèses ci-dessus, nous avons les relations suivantes:

out = conv2d(x, w, padding='VALID')
d_x = conv2d(d_out, rot180(w), padding='FULL')
d_w = conv2d(x, d_out, padding='VALID')

rot180 est une matrice 2d tournée de 180 degrés (un retournement gauche-droite et un retournement du haut vers le bas), FULL signifie "appliquer un filtre partout où il chevauche partiellement l'Entrée" (voir theano docs ). Note que ceci est seulement valide avec les hypothèses ci-dessus , cependant, on peut changer les arguments conv2d pour les généraliser.

La clé de la vente à emporter:

  • le gradient d'entrée d_x est la convolution du gradient de sortie d_out et le poids w , avec certains modification.
  • le gradient de poids d_w est la convolution de l'entrée x et le gradient de sortie d_out , avec quelques modifications.

Longue Réponse

maintenant, donnons un exemple de code de travail réel de la façon d'utiliser les 4 fonctions ci-dessus pour calculer d_x et d_w donné d_out . Cela montre comment conv2d , conv2d_backprop_filter , conv2d_backprop_input , et conv2d_transpose sont liées entre elles. s'il vous Plaît trouver l'intégralité des scripts ici .

calcul d_x de 4 façons différentes:

# Method 1: TF's autodiff
d_x = tf.gradients(f, x)[0]

# Method 2: manually using conv2d
d_x_manual = tf.nn.conv2d(input=tf_pad_to_full_conv2d(d_out, w_size),
                          filter=tf_rot180(w),
                          strides=strides,
                          padding='VALID')

# Method 3: conv2d_backprop_input
d_x_backprop_input = tf.nn.conv2d_backprop_input(input_sizes=x_shape,
                                                 filter=w,
                                                 out_backprop=d_out,
                                                 strides=strides,
                                                 padding='VALID')

# Method 4: conv2d_transpose
d_x_transpose = tf.nn.conv2d_transpose(value=d_out,
                                       filter=w,
                                       output_shape=x_shape,
                                       strides=strides,
                                       padding='VALID')

calcul d_w de 3 façons différentes:

# Method 1: TF's autodiff
d_w = tf.gradients(f, w)[0]

# Method 2: manually using conv2d
d_w_manual = tf_NHWC_to_HWIO(tf.nn.conv2d(input=x,
                                          filter=tf_NHWC_to_HWIO(d_out),
                                          strides=strides,
                                          padding='VALID'))

# Method 3: conv2d_backprop_filter
d_w_backprop_filter = tf.nn.conv2d_backprop_filter(input=x,
                                                   filter_sizes=w_shape,
                                                   out_backprop=d_out,
                                                   strides=strides,
                                                   padding='VALID')

consultez plein de scripts pour la mise en œuvre de tf_rot180 , tf_pad_to_full_conv2d , tf_NHWC_to_HWIO . Dans les scripts, nous vérifions que les valeurs finales de sortie des différentes méthodes sont les mêmes; une implémentation numpy est également disponible.

17
répondu Yixing 2018-06-05 23:59:57

conv2d_transpose () transpose simplement les poids et les fait pivoter de 180 degrés. Puis il applique la norme conv2d(). "Transpose "signifie pratiquement qu'il modifie l'ordre des" colonnes " dans le tenseur de poids. Veuillez consulter l'exemple ci-dessous.

il y a ici un exemple qui utilise des convolutions avec stride=1 et padding='SAME'. C'est un cas simple, mais le même raisonnement pourrait être appliqué à d'autres cas.

Disons que nous avons:

  • Entrée: MNIST image de 28x28x1, de la forme = [28,28,1]
  • couche Convolutionnelle: 32 filtres de 7x7, poids forme = [7, 7, 1, 32], nom = W_conv1

si nous effectuons la convolution de l'entrée alors les activations de la volonté auront la forme: [1,28,28,32].

 activations = sess.run(h_conv1,feed_dict={x:np.reshape(image,[1,784])})

où:

 W_conv1 = weight_variable([7, 7, 1, 32])
 b_conv1 = bias_variable([32])
 h_conv1 = conv2d(x, W_conv1, strides=[1, 1, 1, 1], padding='SAME') + b_conv1

pour obtenir la" déconvolution "ou" convolution transposée " nous peut utiliser conv2d_transpose () sur les activations de convolution de cette façon:

  deconv = conv2d_transpose(activations,W_conv1, output_shape=[1,28,28,1],padding='SAME')

OU à l'aide de conv2d() nous avons besoin de transposer et de retourner le poids:

  transposed_weights = tf.transpose(W_conv1, perm=[0, 1, 3, 2])

ici nous changeons l'ordre des "columns" de [0,1,2,3] à [0,1,3,2].Ainsi, à partir de [7, 7, 1, 32] nous obtiendrons un tenseur de forme=[7,7,3,2,1]. Ensuite, nous renversons les poids:

  for i in range(n_filters):
      # Flip the weights by 180 degrees
      transposed_and_flipped_weights[:,:,i,0] =  sess.run(tf.reverse(transposed_weights[:,:,i,0], axis=[0, 1]))

alors nous pouvons calculer la convolution avec conv2d () comme:

  strides = [1,1,1,1]
  deconv = conv2d(activations,transposed_and_flipped_weights,strides=strides,padding='SAME')

Et nous obtiendrons le même résultat qu'avant. Le même résultat peut aussi être obtenu avec conv2d_backprop_input() en utilisant:

   deconv = conv2d_backprop_input([1,28,28,1],W_conv1,activations, strides=strides, padding='SAME')

les résultats sont affichés ici:

Test de la conv2d(), conv2d_tranposed() et conv2d_backprop_input()

Nous pouvons voir que les résultats sont les mêmes. Pour le voir d'une meilleure façon s'il Vous Plaît vérifier mon code à:

https://github.com/simo23/conv2d_transpose

ici je reproduit la sortie de la fonction conv2d_transpose() en utilisant le conv2d () standard.

8
répondu simo23 2018-06-02 08:47:14

Une application pour conv2d_transpose est upscaling, voici un exemple qui explique comment cela fonctionne:

a = np.array([[0, 0, 1.5],
              [0, 1, 0],
              [0, 0, 0]]).reshape(1,3,3,1)

filt = np.array([[1, 2],
                 [3, 4.0]]).reshape(2,2,1,1)

b = tf.nn.conv2d_transpose(a,
                           filt,
                           output_shape=[1,6,6,1],
                           strides=[1,2,2,1],
                           padding='SAME')

print(tf.squeeze(b))

tf.Tensor(
[[0.  0.  0.  0.  1.5 3. ]
 [0.  0.  0.  0.  4.5 6. ]
 [0.  0.  1.  2.  0.  0. ]
 [0.  0.  3.  4.  0.  0. ]
 [0.  0.  0.  0.  0.  0. ]
 [0.  0.  0.  0.  0.  0. ]], shape=(6, 6), dtype=float64)
0
répondu pentadecagon 2018-09-21 04:27:02