200 votes

Compréhension intuitive des convolutions 1D, 2D et 3D dans les réseaux neuronaux convolutifs.

Quelqu'un peut-il expliquer clairement la différence entre les convolutions 1D, 2D et 3D dans les réseaux de neurones convolutifs (dans l'apprentissage profond) à l'aide d'exemples ?

591voto

runhani Points 2919

Je veux expliquer avec une photo de C3D .

En un mot, sens convolutif & forme de sortie est important !

enter image description here

Convolutions 1D - Base

  • juste 1 -direction (axe du temps) pour calculer le conv.
  • entrée = [W], filtre = [k], sortie = [W].
  • ex) entrée = [1,1,1,1,1], filtre = [0.25,0.5,0.25], sortie = [1,1,1,1,1].
  • la forme de la sortie est un tableau 1D
  • exemple) lissage des graphiques

tf.nn.conv1d code Toy Exemple

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)

enter image description here

Convolutions 2D - Base

  • 2 -direction (x,y) pour calculer la conv.
  • La forme de la sortie est 2D Matrice
  • entrée = [W, H], filtre = [k,k] sortie = [W,H].
  • exemple) Sobel Egde Fllter

tf.nn.conv2d - Exemple de jouet

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)

enter image description here

Convolutions 3D - Base

  • 3 -direction (x,y,z) pour calculer le conv.
  • La forme de la sortie est 3D Volume
  • entrée = [W,H, L ], filtre = [k,k, d ] sortie = [W,H,M]
  • d < L est important ! pour faire une sortie de volume
  • exemple) C3D

tf.nn.conv3d - Exemple de jouet

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)

enter image description here

Convolutions 2D avec entrée 3D - LeNet, VGG, ...,

  • Même si l'entrée est en 3D ex) 224x224x3, 112x112x32
  • la forme de la sortie n'est pas 3D Volume, mais 2D Matrice
  • parce que la profondeur du filtre = L doivent être appariés avec les canaux d'entrée = L
  • 2 -direction (x,y) pour calculer le conv ! pas 3D
  • entrée = [W,H, L ], filtre = [k,k, L Sortie = [W,H]
  • La forme de la sortie est 2D Matrice
  • Que faire si nous voulons former N filtres (N est le nombre de filtres) ?
  • alors la forme de sortie est (2D empilé) 3D = 2D x N matrice.

conv2d - LeNet, VGG, ... pour 1 filtre

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)

conv2d - LeNet, VGG, ... pour N filtres

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)

enter image description here Bonus 1x1 conv en CNN - GoogLeNet, ...,

  • 1x1 conv est confus quand vous pensez que c'est un filtre d'image 2D comme sobel.
  • pour 1x1 conv dans CNN, l'entrée est une forme 3D comme l'image ci-dessus.
  • il calcule le filtrage en profondeur
  • entrée = [W,H,L], filtre = [1,1,L] sortie = [W,H]
  • la forme empilée de sortie est 3D = 2D x N matrice.

tf.nn.conv2d - cas particulier 1x1 conv

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)

Animation (Conv. 2D avec entrées 3D)

enter image description here

  • Original Lnk : LIEN
  • L'auteur : Martin Görner
  • Twitter : @martin_gorner
  • Google + : plus.google.com/+MartinGorne

Bonus 1D Convolutions avec entrée 2D

enter image description here Convolutions 1D avec entrée 1D

enter image description here Convolutions 1D avec entrée 2D

  • Même si l'entrée est en 2D ex) 20x14
  • la forme de la sortie n'est pas 2D mais 1D Matrice
  • parce que la hauteur du filtre = L doit correspondre à la hauteur d'entrée = L
  • 1 -direction (x) pour calculer conv ! pas 2D
  • entrée = [W, L ], filtre = [k, L Sortie = [W]
  • La forme de la sortie est 1D Matrice
  • Que faire si nous voulons former N filtres (N est le nombre de filtres) ?
  • alors la forme de sortie est (empilée 1D) 2D = 1D x N matrice.

Bonus C3D

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()

Entrée et sortie dans Tensorflow

enter image description here

enter image description here

Résumé

enter image description here

32 votes

Considérant votre travail et la clarté de vos explications, des upvotes de 8 sont trop peu.

4 votes

La convocation en 2d avec une entrée en 3d est une touche agréable. Je suggérerais une modification pour inclure un conv avec une entrée 2d (par exemple un réseau multicanal) et comparer la différence avec un conv 2d avec une entrée 2d.

2 votes

Réponse étonnante !

24voto

thushv89 Points 356

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 exmaples de TF1 et TF2).

L'un des principaux éléments supplémentaires que j'inclus sont ,

  • Accent mis sur les applications
  • Utilisation de tf.Variable
  • Explication plus claire des entrées/noyaux/sorties Convolution 1D/2D/3D
  • Les effets de la foulée/du patinage

Convolution 1D

Voici comment vous pourriez faire une convolution 1D en utilisant TF 1 et TF 2.

Et pour être plus précis, mes données ont les formes suivantes,

  • Vecteur 1D - [batch size, width, in channels] (par exemple 1, 5, 1 )
  • Noyau - [width, in channels, out channels] (par exemple 5, 1, 4 )
  • Sortie - [batch size, width, out_channels] (par exemple 1, 5, 4 )

Exemple de TF1

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]]])}))

Exemple de TF2

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 bien moins de travail avec TF2 car TF2 n'a pas besoin de Session et variable_initializer par exemple.

À quoi cela pourrait-il ressembler dans la vie réelle ?

Comprenons donc ce que cela fait en utilisant un exemple de lissage de signal. A gauche, vous avez l'original et à droite, vous avez la sortie d'une Convolution 1D qui a 3 canaux de sortie.

enter image description here

Qu'entend-on par "canaux multiples" ?

Les canaux multiples sont en fait des représentations 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 deuxième est un filtre qui pondère davantage le milieu du filtre que les limites. Le dernier filtre fait l'inverse du second. Vous pouvez donc voir comment ces différents filtres produisent des effets différents.

Applications d'apprentissage profond de la convolution 1D

La convolution 1D a été utilisée avec succès pour l'analyse de l'ADN. classification des phrases tâche.

Convolution 2D

En route pour la convolution 2D. Si vous êtes un adepte de l'apprentissage profond, les chances que vous n'ayez pas rencontré la convolution 2D sont à peu près nulles. Elle est utilisée dans les CNN pour la classification d'images, la détection d'objets, etc. ainsi que dans les problèmes de TAL qui impliquent des images (par exemple, la génération de légendes d'images).

Essayons un exemple, j'ai obtenu un noyau de convolution avec les filtres suivants ici,

  • Noyau de détection des bords (fenêtre 3x3)
  • Noyau de flou (fenêtre 3x3)
  • Noyau d'accentuation (fenêtre 3x3)

Et pour être plus précis, mes données ont les formes suivantes,

  • Image (noir et blanc) - [batch_size, height, width, 1] (par exemple 1, 340, 371, 1 )
  • Noyau (alias filtres) - [height, width, in channels, out channels] (par exemple 3, 3, 1, 3 )
  • Sortie (alias cartes de caractéristiques) - [batch_size, height, width, out_channels] (par exemple 1, 340, 371, 3 )

Exemple de TF1,

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)})

Exemple de TF2

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')

À quoi cela pourrait-il ressembler dans la vie réelle ?

Vous pouvez voir ici la sortie produite par le code ci-dessus. La première image est l'original et en allant dans le sens des aiguilles d'une montre, vous avez les sorties du 1er filtre, du 2ème filtre et du 3ème filtre. enter image description here

Qu'entend-on par "canaux multiples" ?

Dans le contexte de la convolution 2D, il est beaucoup plus facile de comprendre la signification de ces canaux multiples. Disons que vous faites de la reconnaissance faciale. Vous pouvez penser (c'est une simplification très irréaliste mais qui fait passer le message) que chaque filtre représente un œil, une bouche, un nez, etc. Ainsi, chaque carte de caractéristiques serait une représentation binaire de la présence de cette caractéristique dans l'image que vous avez fournie. Je ne pense pas devoir insister sur le fait que, pour un modèle de reconnaissance des visages, ces caractéristiques sont très précieuses. Plus d'informations dans ce article .

Ceci est une illustration de ce que j'essaie d'exprimer.

enter image description here

Applications d'apprentissage profond de la convolution 2D

La convolution 2D est très répandue dans le domaine de l'apprentissage profond.

Les CNN (réseaux de neurones à convolution) utilisent l'opération de convolution 2D pour presque toutes les tâches de vision par ordinateur (par exemple, la classification d'images, la détection d'objets, la classification vidéo).

Convolution 3D

Or, il devient de plus en plus difficile d'illustrer ce qui se passe à mesure que le nombre de dimensions augmente. Mais si l'on comprend bien comment fonctionnent les convolutions 1D et 2D, il est très facile de généraliser cette compréhension à la convolution 3D. C'est donc parti.

Et pour être plus précis, mes données ont les formes suivantes,

  • Données 3D (LIDAR) [batch size, height, width, depth, in channels] (par exemple 1, 200, 200, 200, 1 )
  • Noyau - [height, width, depth, in channels, out channels] (par exemple 5, 5, 5, 1, 3 )
  • Sortie - [batch size, width, height, width, depth, out_channels] (par exemple 1, 200, 200, 2000, 3 )

Exemple de TF1

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))})

Exemple de TF2

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') 

Applications d'apprentissage profond de la convolution 3D

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 tridimensionnelles par nature.

Quoi... encore du jargon? : Enjambement et rembourrage

Bon, vous y êtes presque. Alors, tenez bon. Voyons ce que sont la foulée et le rembourrage. C'est assez intuitif si vous y pensez.

Si vous traversez un couloir à grandes enjambées, vous y arrivez plus rapidement en moins de pas. Mais cela signifie également que vous observez moins l'environnement que si vous traversez la pièce à pied. Renforçons maintenant notre compréhension avec une jolie image ! Comprenons-les via la convolution 2D.

Comprendre la foulée

Convolution stride

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 de se laisser intimider par cela. Il suffit de contenir les strides dans l'ordre suivant.

  • Convolution 2D - [batch stride, height stride, width stride, channel stride] . Ici, le stride du lot et le stride du canal sont simplement réglés sur un (j'implémente des modèles d'apprentissage profond depuis 5 ans et je n'ai jamais eu à les régler sur autre chose qu'un). Il ne vous reste donc que 2 strides à définir.

  • Convolution 3D - [batch stride, height stride, width stride, depth stride, channel stride] . Ici, vous vous préoccupez uniquement des foulées de hauteur/largeur/profondeur.

Comprendre le rembourrage

Vous remarquez maintenant que, quelle que soit la taille de votre pas (c'est-à-dire 1), une réduction inévitable de la dimension se produit pendant la convolution (par exemple, la largeur est de 3 après la convolution d'une image de 4 unités de largeur). Ceci n'est pas souhaitable, en particulier lors de la construction de réseaux de neurones à convolution profonds. C'est là que le padding vient à la rescousse. Il existe deux types de rembourrage les plus couramment utilisés.

  • SAME et VALID

Vous pouvez voir la différence ci-dessous.

enter image description here

Mot de la fin : Si vous êtes très curieux, vous vous demandez peut-être. Nous venons de lâcher une bombe sur la réduction automatique des dimensions et nous parlons maintenant d'avoir différents strides. Mais la meilleure chose avec le stride est que vous contrôlez quand, où et comment les dimensions sont réduites.

5voto

zz x Points 69

En résumé, dans le CNN 1D, le noyau se déplace dans une seule direction. Les données d'entrée et de sortie de 1D CNN sont bidimensionnelles. Principalement utilisé sur les données de séries temporelles.

Dans le CNN 2D, le noyau se déplace dans 2 directions. Les données d'entrée et de sortie du CNN 2D sont tridimensionnelles. Principalement utilisé pour les données d'image.

Dans le CNN 3D, le noyau se déplace dans 3 directions. Les données d'entrée et de sortie du CNN 3D sont en 4 dimensions. Il est surtout utilisé pour les données d'images 3D (IRM, tomodensitométrie).

Vous pouvez trouver plus de détails ici : https://medium.com/@xzz201920/conv1d-conv2d-and-conv3d-8a59182c4d6

1 votes

Il est peut-être important de mentionner que, souvent, dans les architectures CNN, les couches intermédiaires ont des sorties en 2D même si l'entrée n'est qu'en 1D au départ.

1voto

Jerry Liu Points 39
  1. CNN 1D,2D, ou 3D fait référence à la direction de la convolution, plutôt qu'à la dimension de l'entrée ou du filtre.

  2. Pour une entrée à 1 canal, CNN2D est égal à CNN1D, c'est-à-dire longueur du noyau = longueur de l'entrée. (1 sens conv)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X