Je regardais la documentation de tensorflow tf.nn.conv2d
ici . Mais je ne peux pas comprendre ce qu'il fait ou ce qu'il essaie de réaliser. Il dit sur les documents,
# 1: Aplatit le filtre en une matrice 2D avec forme
[filter_height * filter_width * in_channels, output_channels]
.
Maintenant qu'est-ce que cela fait? Est-ce une multiplication élémentaire ou simplement une multiplication matricielle? Je ne pouvais pas non plus comprendre les deux autres points mentionnés dans la documentation. Je les ai écrits ci-dessous:
# 2: Extrait des patchs d'image du tenseur d'entrée pour former un tenseur virtuel de forme
[batch, out_height, out_width, filter_height * filter_width * in_channels]
.# 3: Pour chaque patch, multiplie à droite la matrice de filtre et le vecteur de patch d'image.
Ce serait vraiment utile si quelqu'un pouvait donner un exemple, un morceau de code (extrêmement utile) peut-être et expliquer ce qui se passe là-bas et pourquoi l'opération est comme ça.
J'ai essayé de coder une petite partie et d'imprimer la forme de l'opération. Pourtant, je ne comprends pas.
J'ai essayé quelque chose comme ça:
op = tf.shape(tf.nn.conv2d(tf.random_normal([1,10,10,10]),
tf.random_normal([2,10,10,10]),
strides=[1, 2, 2, 1], padding='SAME'))
with tf.Session() as sess:
result = sess.run(op)
print(result)
Je comprends des morceaux de réseaux de neurones convolutifs. Je les ai étudiés ici . Mais l'implémentation sur tensorflow n'est pas ce à quoi je m'attendais. Cela a donc soulevé la question.
EDIT : Donc, j'ai implémenté un code beaucoup plus simple. Mais je ne peux pas comprendre ce qui se passe. Je veux dire comment les résultats sont comme ça. Il serait extrêmement utile que quelqu'un puisse me dire quel processus produit ce résultat.
input = tf.Variable(tf.random_normal([1,2,2,1]))
filter = tf.Variable(tf.random_normal([1,1,1,1]))
op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
init = tf.initialize_all_variables()
with tf.Session() as sess:
sess.run(init)
print("input")
print(input.eval())
print("filter")
print(filter.eval())
print("result")
result = sess.run(op)
print(result)
production
input
[[[[ 1.60314465]
[-0.55022103]]
[[ 0.00595062]
[-0.69889867]]]]
filter
[[[[-0.59594476]]]]
result
[[[[-0.95538563]
[ 0.32790133]]
[[-0.00354624]
[ 0.41650501]]]]
la source
tf.nn.conv2d()
, donc la méthode en question n'est pas du tout utilisée lorsque nous utilisons TF avec le support GPU, sauf si celause_cudnn_on_gpu=False
est spécifié explicitement.Réponses:
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 par élément et additionnez-les. Mais au lieu que votre noyau / entrée soit un tableau, ce sont ici des matrices.
Dans l'exemple le plus élémentaire, il n'y a pas de rembourrage et stride = 1. Supposons que vous
input
etkernel
êtes:Lorsque vous utilisez votre noyau, vous recevrez la sortie suivante:, qui est calculée de la manière suivante:
La fonction conv2d de TF calcule les convolutions par lots et utilise un format légèrement différent. Pour une entrée, c'est
[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 bon format:Ensuite, la convolution est calculée avec:
Et sera équivalent à celui que nous avons calculé à la main.
Pour des exemples de rembourrage / foulées, jetez un œil ici .
la source
Ok, je pense que c'est la façon la plus simple de tout expliquer.
Votre exemple est 1 image, taille 2x2, avec 1 canal. Vous avez 1 filtre, de taille 1x1, et 1 canal (la taille est hauteur x largeur x canaux x nombre de filtres).
Pour ce cas simple, l'image résultante 2x2, 1 canal (taille 1x2x2x1, nombre d'images x hauteur x largeur xx canaux) est le résultat de la multiplication de la valeur de filtre par chaque pixel de l'image.
Essayons maintenant plus de canaux:
Ici, l'image 3x3 et le filtre 1x1 ont chacun 5 canaux. L'image résultante sera 3x3 avec 1 canal (taille 1x3x3x1), où la valeur de chaque pixel est le produit scalaire sur les canaux du filtre avec le pixel correspondant dans l'image d'entrée.
Maintenant avec un filtre 3x3
Ici, nous obtenons une image 1x1, avec 1 canal (taille 1x1x1x1). La valeur est la somme des 9 produits scalaires à 5 éléments. Mais vous pouvez simplement appeler cela un produit scalaire à 45 éléments.
Maintenant avec une image plus grande
La sortie est une image 3x3 à 1 canal (taille 1x3x3x1). Chacune de ces valeurs est une somme de 9 produits scalaires à 5 éléments.
Chaque sortie est effectuée en centrant le filtre sur l'un des 9 pixels centraux de l'image d'entrée, de sorte qu'aucun filtre ne dépasse. Les
x
s ci-dessous représentent les centres de filtre pour chaque pixel de sortie.Maintenant avec le rembourrage "SAME":
Cela donne une image de sortie 5x5 (taille 1x5x5x1). Cela se fait en centrant le filtre à chaque position sur l'image.
Tous les produits scalaires à 5 éléments où le filtre dépasse le bord de l'image obtiennent une valeur de zéro.
Les coins ne sont donc que des sommes de produits scalaires à 4 ou 5 éléments.
Maintenant avec plusieurs filtres.
Cela donne toujours une image de sortie 5x5, mais avec 7 canaux (taille 1x5x5x7). Où chaque canal est produit par l'un des filtres de l'ensemble.
Maintenant avec des foulées 2,2:
Maintenant, le résultat a toujours 7 canaux, mais n'est que de 3x3 (taille 1x3x3x7).
En effet, au lieu de centrer les filtres en chaque point de l'image, les filtres sont centrés en un autre point de l'image, en effectuant des pas (enjambées) de largeur 2. Les
x
s ci-dessous représentent le centre du filtre pour chaque pixel de sortie, sur l'image d'entrée.Et bien sûr, la première dimension de l'entrée est le nombre d'images afin que vous puissiez l'appliquer sur un lot de 10 images, par exemple:
Cela effectue la même opération, pour chaque image indépendamment, donnant une pile de 10 images comme résultat (taille 10x3x3x7)
la source
Must have strides[0] = strides[3] = 1. For the most common case of the same horizontal and vertices strides, strides = [1, stride, stride, 1].
the 3x3 image and the 1x1 filter each have 5 channels
, je trouve que le résultat est différent du produit scalaire calculé manuellement.Juste pour ajouter aux autres réponses, vous devriez penser aux paramètres dans
comme «5» correspondant au nombre de canaux dans chaque filtre. Chaque filtre est un cube 3D, d'une profondeur de 5. La profondeur de votre filtre doit correspondre à la profondeur de votre image d'entrée. Le dernier paramètre, 7, doit être considéré comme le nombre de filtres dans le lot. Oubliez simplement qu'il s'agit de 4D, et imaginez à la place que vous avez un ensemble ou un lot de 7 filtres. Ce que vous faites est de créer 7 cubes de filtre avec des dimensions (3,3,5).
Il est beaucoup plus facile de visualiser dans le domaine de Fourier puisque la convolution devient une multiplication ponctuelle. Pour une image d'entrée de dimensions (100,100,3), vous pouvez réécrire les dimensions du filtre comme
Afin d'obtenir l'une des 7 cartes d'entités en sortie, nous effectuons simplement la multiplication ponctuelle du cube de filtre avec le cube d'image, puis nous additionnons les résultats à travers la dimension canaux / profondeur (ici c'est 3), en nous effondrant en 2d (100,100) carte des caractéristiques. Faites cela avec chaque cube de filtre et vous obtenez 7 cartes d'entités 2D.
la source
J'ai essayé d'implémenter conv2d (pour mes études). Eh bien, j'ai écrit ça:
J'espère que je l'ai fait correctement. Vérifié sur MNIST, a eu des résultats très proches (mais cette mise en œuvre est plus lente). J'espère que ceci vous aide.
la source
En plus d'autres réponses, l'opération conv2d fonctionne en c ++ (cpu) ou cuda pour les machines gpu qui nécessitent d'aplatir et de remodeler les données d'une certaine manière et d'utiliser la multiplication de matrice gemmBLAS ou cuBLAS (cuda).
la source