tensorflow
Math derrière la convolution 2D avec des exemples avancés en TF
Recherche…
Introduction
La convolution 2D est calculée de la même manière que l'on calculerait la convolution 1D : vous faites glisser votre noyau sur l'entrée, calculez les multiplications élémentaires et les résumez. Mais au lieu que votre noyau / entrée soit un tableau, ce sont des matrices.
Pas de rembourrage, foulées = 1
C'est l'exemple le plus fondamental, avec les calculs les plus simples. Supposons que votre input
et votre kernel
soient:
Lorsque vous aurez votre noyau, vous recevrez la sortie suivante: , qui est calculé de la manière suivante:
- 14 = 4 * 1 + 3 * 0 + 1 * 1 + 2 * 2 + 1 * 1 + 0 * 0 + 1 * 0 + 2 * 0 + 4 * 1
- 6 = 3 * 1 + 1 * 0 + 0 * 1 + 1 * 2 + 0 * 1 + 1 * 0 + 2 * 0 + 4 * 0 + 1 * 1
- 6 = 2 * 1 + 1 * 0 + 0 * 1 + 1 * 2 + 2 * 1 + 4 * 0 + 3 * 0 + 1 * 0 + 0 * 1
- 12 = 1 * 1 + 0 * 0 + 1 * 1 + 2 * 2 + 4 * 1 + 1 * 0 + 1 * 0 + 0 * 0 + 2 * 1
La fonction conv2d de TF calcule les convolutions par lots et utilise un format légèrement différent. Pour une entrée, il s'agit de [batch, in_height, in_width, in_channels]
pour le noyau [filter_height, filter_width, in_channels, out_channels]
. Nous devons donc fournir les données dans le format correct:
import tensorflow as tf
k = tf.constant([
[1, 0, 1],
[2, 1, 0],
[0, 0, 1]
], dtype=tf.float32, name='k')
i = tf.constant([
[4, 3, 1, 0],
[2, 1, 0, 1],
[1, 2, 4, 1],
[3, 1, 0, 2]
], dtype=tf.float32, name='i')
kernel = tf.reshape(k, [3, 3, 1, 1], name='kernel')
image = tf.reshape(i, [1, 4, 4, 1], name='image')
Ensuite, la convolution est calculée avec:
res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 1, 1, 1], "VALID"))
# VALID means no padding
with tf.Session() as sess:
print sess.run(res)
Et sera équivalent à celui que nous avons calculé à la main.
Un peu de rembourrage, foulées = 1
Le remplissage est juste un nom de fantaisie de dire: entourez votre matrice d'entrée avec une certaine constante. Dans la plupart des cas, la constante est zéro et c'est pourquoi les gens l'appellent le remplissage zéro. Donc, si vous voulez utiliser un remplissage de 1 dans notre entrée originale (vérifiez le premier exemple avec padding=0, strides=1
), la matrice ressemblera à ceci:
Pour calculer les valeurs de la convolution, vous faites le même glissement. Notez que dans notre cas, il n'est pas nécessaire de recalculer de nombreuses valeurs au milieu (elles seront les mêmes que dans l'exemple précédent. Je ne montrerai pas non plus tous les calculs ici, car l'idée est simple. Le résultat est:
où
- 5 = 0 * 1 + 0 * 0 + 0 * 1 + 0 * 2 + 4 * 1 + 3 * 0 + 0 * 0 + 0 * 1 + 1 * 1
- ...
- 6 = 4 * 1 + 1 * 0 + 0 * 1 + 0 * 2 + 2 * 1 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 1
TF ne prend pas en charge un remplissage arbitraire dans la fonction conv2d , donc si vous avez besoin d'un remplissage non pris en charge, utilisez tf.pad () . Heureusement pour notre entrée, le remplissage 'SAME' sera égal à padding = 1. Nous devons donc presque rien changer dans notre exemple précédent:
res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 1, 1, 1], "SAME"))
# 'SAME' makes sure that our output has the same size as input and
# uses appropriate padding. In our case it is 1.
with tf.Session() as sess:
print sess.run(res)
Vous pouvez vérifier que la réponse sera la même que celle calculée manuellement.
Rembourrage et foulées (le cas le plus général)
Nous allons maintenant appliquer une convolution stridée à notre exemple de remplissage précédemment décrit et calculer la convolution où p = 1, s = 2
Auparavant, lorsque nous strides = 1
, notre fenêtre glissée se déplaçait de 1 position, avec des strides = s
déplacées de s
positions (vous devez calculer moins de s^2
éléments). Dans notre cas, nous pouvons prendre un raccourci Nous avons déjà calculé les valeurs de s = 1
, dans notre cas, nous pouvons simplement saisir chaque second élément.
Donc, si la solution est le cas de s = 1
était
en cas de s = 2
ce sera:
Vérifiez les positions des valeurs 14, 2, 12, 6 dans la matrice précédente. Le seul changement que nous devons effectuer dans notre code est de changer les foulées de 1 à 2 pour les dimensions largeur et hauteur (2-ème, 3-ème).
res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 2, 2, 1], "SAME"))
with tf.Session() as sess:
print sess.run(res)
Par ailleurs, rien ne nous empêche d’utiliser différentes foulées pour différentes dimensions.