Réponses:
Je veux expliquer avec une image de C3D .
En un mot, la direction convolutionnelle et la forme de sortie sont importantes!
↑↑↑↑↑ Convolutions 1D - Basique ↑↑↑↑↑
import tensorflow as tf
import numpy as np
sess = tf.Session()
ones_1d = np.ones(5)
weight_1d = np.ones(3)
strides_1d = 1
in_1d = tf.constant(ones_1d, dtype=tf.float32)
filter_1d = tf.constant(weight_1d, dtype=tf.float32)
in_width = int(in_1d.shape[0])
filter_width = int(filter_1d.shape[0])
input_1d = tf.reshape(in_1d, [1, in_width, 1])
kernel_1d = tf.reshape(filter_1d, [filter_width, 1, 1])
output_1d = tf.squeeze(tf.nn.conv1d(input_1d, kernel_1d, strides_1d, padding='SAME'))
print sess.run(output_1d)
↑↑↑↑↑ Convolutions 2D - Basique ↑↑↑↑↑
ones_2d = np.ones((5,5))
weight_2d = np.ones((3,3))
strides_2d = [1, 1, 1, 1]
in_2d = tf.constant(ones_2d, dtype=tf.float32)
filter_2d = tf.constant(weight_2d, dtype=tf.float32)
in_width = int(in_2d.shape[0])
in_height = int(in_2d.shape[1])
filter_width = int(filter_2d.shape[0])
filter_height = int(filter_2d.shape[1])
input_2d = tf.reshape(in_2d, [1, in_height, in_width, 1])
kernel_2d = tf.reshape(filter_2d, [filter_height, filter_width, 1, 1])
output_2d = tf.squeeze(tf.nn.conv2d(input_2d, kernel_2d, strides=strides_2d, padding='SAME'))
print sess.run(output_2d)
↑↑↑↑↑ Convolutions 3D - Basique ↑↑↑↑↑
ones_3d = np.ones((5,5,5))
weight_3d = np.ones((3,3,3))
strides_3d = [1, 1, 1, 1, 1]
in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_3d = tf.constant(weight_3d, dtype=tf.float32)
in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])
in_depth = int(in_3d.shape[2])
filter_width = int(filter_3d.shape[0])
filter_height = int(filter_3d.shape[1])
filter_depth = int(filter_3d.shape[2])
input_3d = tf.reshape(in_3d, [1, in_depth, in_height, in_width, 1])
kernel_3d = tf.reshape(filter_3d, [filter_depth, filter_height, filter_width, 1, 1])
output_3d = tf.squeeze(tf.nn.conv3d(input_3d, kernel_3d, strides=strides_3d, padding='SAME'))
print sess.run(output_3d)
↑↑↑↑↑ Convolutions 2D avec entrée 3D - LeNet, VGG, ..., ↑↑↑↑↑
in_channels = 32 # 3 for RGB, 32, 64, 128, ...
ones_3d = np.ones((5,5,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae with in_channels
weight_3d = np.ones((3,3,in_channels))
strides_2d = [1, 1, 1, 1]
in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_3d = tf.constant(weight_3d, dtype=tf.float32)
in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])
filter_width = int(filter_3d.shape[0])
filter_height = int(filter_3d.shape[1])
input_3d = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_3d = tf.reshape(filter_3d, [filter_height, filter_width, in_channels, 1])
output_2d = tf.squeeze(tf.nn.conv2d(input_3d, kernel_3d, strides=strides_2d, padding='SAME'))
print sess.run(output_2d)
in_channels = 32 # 3 for RGB, 32, 64, 128, ...
out_channels = 64 # 128, 256, ...
ones_3d = np.ones((5,5,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae x number of filters = 4D
weight_4d = np.ones((3,3,in_channels, out_channels))
strides_2d = [1, 1, 1, 1]
in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_4d = tf.constant(weight_4d, dtype=tf.float32)
in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])
filter_width = int(filter_4d.shape[0])
filter_height = int(filter_4d.shape[1])
input_3d = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_4d = tf.reshape(filter_4d, [filter_height, filter_width, in_channels, out_channels])
#output stacked shape is 3D = 2D x N matrix
output_3d = tf.nn.conv2d(input_3d, kernel_4d, strides=strides_2d, padding='SAME')
print sess.run(output_3d)
↑↑↑↑↑ Bonus 1x1 conv dans CNN - GoogLeNet, ..., ↑↑↑↑↑
in_channels = 32 # 3 for RGB, 32, 64, 128, ...
out_channels = 64 # 128, 256, ...
ones_3d = np.ones((1,1,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae x number of filters = 4D
weight_4d = np.ones((3,3,in_channels, out_channels))
strides_2d = [1, 1, 1, 1]
in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_4d = tf.constant(weight_4d, dtype=tf.float32)
in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])
filter_width = int(filter_4d.shape[0])
filter_height = int(filter_4d.shape[1])
input_3d = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_4d = tf.reshape(filter_4d, [filter_height, filter_width, in_channels, out_channels])
#output stacked shape is 3D = 2D x N matrix
output_3d = tf.nn.conv2d(input_3d, kernel_4d, strides=strides_2d, padding='SAME')
print sess.run(output_3d)
- Lien d'origine: LINK
- L'auteur: Martin Görner
- Twitter: @martin_gorner
- Google +: plus.google.com/+MartinGorne
↑↑↑↑↑ Convolutions 1D avec entrée 1D ↑↑↑↑↑
↑↑↑↑↑ Convolutions 1D avec entrée 2D ↑↑↑↑↑
in_channels = 32 # 3, 32, 64, 128, ...
out_channels = 64 # 3, 32, 64, 128, ...
ones_4d = np.ones((5,5,5,in_channels))
weight_5d = np.ones((3,3,3,in_channels,out_channels))
strides_3d = [1, 1, 1, 1, 1]
in_4d = tf.constant(ones_4d, dtype=tf.float32)
filter_5d = tf.constant(weight_5d, dtype=tf.float32)
in_width = int(in_4d.shape[0])
in_height = int(in_4d.shape[1])
in_depth = int(in_4d.shape[2])
filter_width = int(filter_5d.shape[0])
filter_height = int(filter_5d.shape[1])
filter_depth = int(filter_5d.shape[2])
input_4d = tf.reshape(in_4d, [1, in_depth, in_height, in_width, in_channels])
kernel_5d = tf.reshape(filter_5d, [filter_depth, filter_height, filter_width, in_channels, out_channels])
output_4d = tf.nn.conv3d(input_4d, kernel_5d, strides=strides_3d, padding='SAME')
print sess.run(output_4d)
sess.close()
1
, puis → pour la ligne 1+stride
. La convolution elle-même est invariante par décalage, alors pourquoi la direction de la convolution est-elle importante?
Suite à la réponse de @runhani, j'ajoute quelques détails supplémentaires pour rendre l'explication un peu plus claire et j'essaierai de l'expliquer un peu plus (et bien sûr avec des exemples de TF1 et TF2).
L'un des principaux éléments supplémentaires que j'inclus est,
tf.Variable
Voici comment faire une convolution 1D en utilisant TF 1 et TF 2.
Et pour être précis, mes données ont les formes suivantes,
[batch size, width, in channels]
(par exemple 1, 5, 1
)[width, in channels, out channels]
(par exemple 5, 1, 4
)[batch size, width, out_channels]
(par exemple 1, 5, 4
)import tensorflow as tf
import numpy as np
inp = tf.placeholder(shape=[None, 5, 1], dtype=tf.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5, 1, 4]), dtype=tf.float32)
out = tf.nn.conv1d(inp, kernel, stride=1, padding='SAME')
with tf.Session() as sess:
tf.global_variables_initializer().run()
print(sess.run(out, feed_dict={inp: np.array([[[0],[1],[2],[3],[4]],[[5],[4],[3],[2],[1]]])}))
import tensorflow as tf
import numpy as np
inp = np.array([[[0],[1],[2],[3],[4]],[[5],[4],[3],[2],[1]]]).astype(np.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5, 1, 4]), dtype=tf.float32)
out = tf.nn.conv1d(inp, kernel, stride=1, padding='SAME')
print(out)
C'est beaucoup moins de travail avec TF2 que TF2 n'en a pas besoin Session
et variable_initializer
par exemple.
Comprenons donc ce que cela fait en utilisant un exemple de lissage de signal. Sur la gauche, vous avez l'original et sur la droite, vous avez la sortie d'un Convolution 1D qui a 3 canaux de sortie.
Les canaux multiples sont essentiellement des représentations d'entités multiples d'une entrée. Dans cet exemple, vous avez trois représentations obtenues par trois filtres différents. Le premier canal est le filtre de lissage à pondération égale. Le second est un filtre qui pondère le milieu du filtre plus que les limites. Le filtre final fait le contraire du second. Vous pouvez donc voir comment ces différents filtres produisent des effets différents.
La convolution 1D a été utilisée avec succès pour la tâche de classification des phrases .
Arrêt à la convolution 2D. Si vous êtes une personne qui apprend en profondeur, les chances que vous n'ayez pas rencontré de convolution 2D sont… enfin nulles. Il est utilisé dans les CNN pour la classification d'images, la détection d'objets, etc. ainsi que dans les problèmes de PNL impliquant des images (par exemple, la génération de légendes d'images).
Essayons un exemple, j'ai un noyau de convolution avec les filtres suivants ici,
Et pour être précis, mes données ont les formes suivantes,
[batch_size, height, width, 1]
(par exemple 1, 340, 371, 1
)[height, width, in channels, out channels]
(par exemple 3, 3, 1, 3
)[batch_size, height, width, out_channels]
(par exemple 1, 340, 371, 3
)import tensorflow as tf
import numpy as np
from PIL import Image
im = np.array(Image.open(<some image>).convert('L'))#/255.0
kernel_init = np.array(
[
[[[-1, 1.0/9, 0]],[[-1, 1.0/9, -1]],[[-1, 1.0/9, 0]]],
[[[-1, 1.0/9, -1]],[[8, 1.0/9,5]],[[-1, 1.0/9,-1]]],
[[[-1, 1.0/9,0]],[[-1, 1.0/9,-1]],[[-1, 1.0/9, 0]]]
])
inp = tf.placeholder(shape=[None, image_height, image_width, 1], dtype=tf.float32)
kernel = tf.Variable(kernel_init, dtype=tf.float32)
out = tf.nn.conv2d(inp, kernel, strides=[1,1,1,1], padding='SAME')
with tf.Session() as sess:
tf.global_variables_initializer().run()
res = sess.run(out, feed_dict={inp: np.expand_dims(np.expand_dims(im,0),-1)})
import tensorflow as tf
import numpy as np
from PIL import Image
im = np.array(Image.open(<some image>).convert('L'))#/255.0
x = np.expand_dims(np.expand_dims(im,0),-1)
kernel_init = np.array(
[
[[[-1, 1.0/9, 0]],[[-1, 1.0/9, -1]],[[-1, 1.0/9, 0]]],
[[[-1, 1.0/9, -1]],[[8, 1.0/9,5]],[[-1, 1.0/9,-1]]],
[[[-1, 1.0/9,0]],[[-1, 1.0/9,-1]],[[-1, 1.0/9, 0]]]
])
kernel = tf.Variable(kernel_init, dtype=tf.float32)
out = tf.nn.conv2d(x, kernel, strides=[1,1,1,1], padding='SAME')
Ici vous pouvez voir la sortie produite par le code ci-dessus. La première image est l'original et dans le sens des aiguilles d'une montre, vous avez les sorties du 1er filtre, 2ème filtre et 3 filtres.
Dans le contexte de la convolution 2D, il est beaucoup plus facile de comprendre ce que signifient ces multiples canaux. Disons que vous faites de la reconnaissance faciale. Vous pouvez penser à (c'est une simplification très irréaliste mais fait passer le point) à chaque filtre représentant un œil, une bouche, un nez, etc. De sorte que chaque carte de caractéristiques soit une représentation binaire de la présence ou non de cette caractéristique dans l'image que vous avez fournie . Je ne pense pas avoir besoin de souligner que pour un modèle de reconnaissance faciale, ce sont des fonctionnalités très précieuses. Plus d'informations dans cet article .
C'est une illustration de ce que j'essaye d'articuler.
La convolution 2D est très répandue dans le domaine de l'apprentissage profond.
Les CNN (Convolution Neural Networks) utilisent l'opération de convolution 2D pour presque toutes les tâches de vision par ordinateur (par exemple, classification d'images, détection d'objets, classification vidéo).
Maintenant, il devient de plus en plus difficile d'illustrer ce qui se passe à mesure que le nombre de dimensions augmente. Mais avec une bonne compréhension du fonctionnement de la convolution 1D et 2D, il est très simple de généraliser cette compréhension à la convolution 3D. Alors voilà.
Et pour être précis, mes données ont les formes suivantes,
[batch size, height, width, depth, in channels]
(par exemple 1, 200, 200, 200, 1
)[height, width, depth, in channels, out channels]
(par exemple 5, 5, 5, 1, 3
)[batch size, width, height, width, depth, out_channels]
(par exemple 1, 200, 200, 2000, 3
)import tensorflow as tf
import numpy as np
tf.reset_default_graph()
inp = tf.placeholder(shape=[None, 200, 200, 200, 1], dtype=tf.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5,5,5,1,3]), dtype=tf.float32)
out = tf.nn.conv3d(inp, kernel, strides=[1,1,1,1,1], padding='SAME')
with tf.Session() as sess:
tf.global_variables_initializer().run()
res = sess.run(out, feed_dict={inp: np.random.normal(size=(1,200,200,200,1))})
import tensorflow as tf
import numpy as np
x = np.random.normal(size=(1,200,200,200,1))
kernel = tf.Variable(tf.initializers.glorot_uniform()([5,5,5,1,3]), dtype=tf.float32)
out = tf.nn.conv3d(x, kernel, strides=[1,1,1,1,1], padding='SAME')
La convolution 3D a été utilisée lors du développement d'applications d'apprentissage automatique impliquant des données LIDAR (Light Detection and Ranging) qui sont de nature tridimensionnelle.
D'accord, vous y êtes presque. Alors attendez. Voyons ce qu'est la foulée et le rembourrage. Ils sont assez intuitifs si vous pensez à eux.
Si vous traversez un couloir, vous y arrivez plus rapidement en moins d'étapes. Mais cela signifie également que vous avez observé un environnement moindre que si vous traversiez la pièce. Renforcez maintenant notre compréhension avec une jolie image aussi! Comprenons-les via la convolution 2D.
Lorsque vous utilisez tf.nn.conv2d
par exemple, vous devez le définir comme un vecteur de 4 éléments. Il n'y a aucune raison d'être intimidé par cela. Il contient simplement les foulées dans l'ordre suivant.
Convolution 2D - [batch stride, height stride, width stride, channel stride]
. Ici, la foulée par lots et la foulée de canal que vous venez de définir sur un (j'implémente des modèles d'apprentissage en profondeur depuis 5 ans et je n'ai jamais eu à les définir sur autre chose). Cela ne vous laisse donc que 2 foulées à définir.
Convolution 3D - [batch stride, height stride, width stride, depth stride, channel stride]
. Ici, vous ne vous souciez que des foulées de hauteur / largeur / profondeur.
Maintenant, vous remarquez que peu importe la taille de votre foulée (c'est-à-dire 1), il y a une réduction de dimension inévitable qui se produit pendant la convolution (par exemple, la largeur est de 3 après la convolution d'une image de 4 unités). Ceci n'est pas souhaitable, en particulier lors de la construction de réseaux de neurones à convolution profonde. C'est là que le rembourrage vient à la rescousse. Il existe deux types de rembourrage les plus couramment utilisés.
SAME
et VALID
Ci-dessous, vous pouvez voir la différence.
Dernier mot : si vous êtes très curieux, vous vous demandez peut-être. Nous venons de lancer une bombe sur toute la réduction automatique des dimensions et parlons maintenant de progrès différents. Mais la meilleure chose à propos de la foulée est que vous contrôlez quand et comment les dimensions sont réduites.
En résumé, dans 1D CNN, le noyau se déplace dans une direction. Les données d'entrée et de sortie de 1D CNN sont à 2 dimensions. Principalement utilisé sur les données de séries chronologiques.
Dans CNN 2D, le noyau se déplace dans 2 directions. Les données d'entrée et de sortie de CNN 2D sont en 3 dimensions. Principalement utilisé sur les données d'image.
Dans 3D CNN, le noyau se déplace dans 3 directions. Les données d'entrée et de sortie du CNN 3D sont en 4 dimensions. Principalement utilisé sur les données d'image 3D (IRM, tomodensitométrie).
Vous pouvez trouver plus de détails ici: https://medium.com/@xzz201920/conv1d-conv2d-and-conv3d-8a59182c4d6