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?
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.
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?
# 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
etout_channels
sont1
- 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')
où 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 sortied_out
et le poidsw
, avec certains modification. - le gradient de poids
d_w
est la convolution de l'entréex
et le gradient de sortied_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.
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.
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)