Comprensione intuitiva delle convoluzioni 1D, 2D e 3D nelle reti neurali convoluzionali


Risposte:


415

Voglio spiegare con foto da C3D .

In poche parole, la direzione convoluzionale e la forma di uscita sono importanti!

inserisci qui la descrizione dell'immagine

↑↑↑↑↑ Convoluzioni 1D - Di base ↑↑↑↑↑

  • solo 1 direzione (asse del tempo) per calcolare conv
  • input = [W], filtro = [k], output = [W]
  • ex) input = [1,1,1,1,1], filtro = [0.25,0.5,0.25], output = [1,1,1,1,1]
  • output-shape è un array 1D
  • esempio) livellamento del grafico

tf.nn.conv1d codice Toy Example

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)

inserisci qui la descrizione dell'immagine

↑↑↑↑↑ Convoluzioni 2D - Di base ↑↑↑↑↑

  • 2 -direzione (x, y) per calcolare conv
  • output-shape è 2D Matrix
  • input = [W, H], filtro = [k, k] output = [W, H]
  • esempio) Sobel Egde Fllter

tf.nn.conv2d - Esempio di giocattolo

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)

inserisci qui la descrizione dell'immagine

↑↑↑↑↑ Convoluzioni 3D - Di base ↑↑↑↑↑

  • 3 -direzione (x, y, z) per calcolare conv
  • output-shape è 3D Volume
  • input = [W, H, L ], filtro = [k, k, d ] output = [W, H, M]
  • d <L è importante! per creare l'uscita del volume
  • esempio) C3D

tf.nn.conv3d - Esempio di giocattolo

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)

inserisci qui la descrizione dell'immagine

↑↑↑↑↑ Convoluzioni 2D con ingresso 3D - LeNet, VGG, ..., ↑↑↑↑↑

  • Anche se l'input è 3D ex) 224x224x3, 112x112x32
  • output-shape non è 3D Volume, ma 2D Matrix
  • perché la profondità del filtro = L deve essere abbinata ai canali di ingresso = L
  • 2 -direzione (x, y) per calcolare conv! non 3D
  • input = [W, H, L ], filtro = [k, k, L ] output = [W, H]
  • output-shape è 2D Matrix
  • e se volessimo addestrare N filtri (N è il numero di filtri)
  • quindi la forma di output è (stacked 2D) 3D = 2D x N matrix.

conv2d - LeNet, VGG, ... per 1 filtro

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, ... per N filtri

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)

inserisci qui la descrizione dell'immagine ↑↑↑↑↑ Bonus 1x1 conv in CNN - GoogLeNet, ..., ↑↑↑↑↑

  • La conversione 1x1 crea confusione quando pensi che questo sia un filtro di immagine 2D come sobel
  • per 1x1 conv in CNN, l'input è in 3D come nell'immagine sopra.
  • calcola il filtraggio in profondità
  • input = [W, H, L], filtro = [1,1, L] output = [W, H]
  • la forma impilata di output è 3D = matrice 2D x N.

tf.nn.conv2d - caso speciale 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)

Animazione (Conv 2D con ingressi 3D)

inserisci qui la descrizione dell'immagine - Link originale: LINK
- L'autore: Martin Görner
- Twitter: @martin_gorner
- Google +: plus.google.com/+MartinGorne

Convoluzioni 1D bonus con input 2D

inserisci qui la descrizione dell'immagine ↑↑↑↑↑ Convoluzioni 1D con ingresso 1D ↑↑↑↑↑

inserisci qui la descrizione dell'immagine ↑↑↑↑↑ Convoluzioni 1D con ingresso 2D ↑↑↑↑↑

  • Anche se l'input è 2D ex) 20x14
  • output-shape non è 2D , ma 1D Matrix
  • perché altezza del filtro = L deve corrispondere all'altezza di input = L
  • 1 -direzione (x) per calcolare conv! non 2D
  • input = [W, L ], filtro = [k, L ] output = [W]
  • output-shape è 1D Matrix
  • e se volessimo addestrare N filtri (N è il numero di filtri)
  • quindi la forma di output è (stacked 1D) 2D = 1D x N matrix.

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

Input e output in Tensorflow

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Sommario

inserisci qui la descrizione dell'immagine


18
Considerando il tuo lavoro e la chiarezza nelle spiegazioni, i voti di 8 sono troppo inferiori.
user3282777

3
La conv 2d con input 3d è un bel tocco. Suggerirei una modifica per includere 1d conv con input 2d (ad esempio un array multicanale) e confrontare la sua differenza con una conv 2d con input 2d.
SumNeuron

2
Risposta incredibile!
Ben

Perché la direzione conv in 2d ↲. Ho visto fonti che affermano che la direzione è per riga 1, quindi per riga 1+stride. La convoluzione stessa è invariante allo spostamento, quindi perché la direzione della convoluzione è importante?
Minh Triet

Grazie per la tua domanda. Sì! la convoluzione stessa è invariante di spostamento. quindi per il calcolo la direzione conv non è importante (puoi calcolare conv 2d con due moltiplicazioni di matrici grandi. Caffe framework lo ha già fatto) ma per capire è meglio spiegare con conv direction. perché conv 2d con input 3d crea confusione senza direzione. ^^
runhani

6

Seguendo la risposta di @runhani aggiungo qualche dettaglio in più per rendere la spiegazione un po 'più chiara e cercherò di spiegarlo un po' di più (e ovviamente con esempi di TF1 e TF2).

Uno dei principali bit aggiuntivi che sto includendo sono,

  • Enfasi sulle applicazioni
  • Utilizzo di tf.Variable
  • Spiegazione più chiara di input / kernel / output convoluzione 1D / 2D / 3D
  • Gli effetti di falcata / imbottitura

Convoluzione 1D

Ecco come potresti eseguire la convoluzione 1D usando TF 1 e TF 2.

E per essere precisi i miei dati hanno le seguenti forme,

  • Vettore 1D - [batch size, width, in channels](eg 1, 5, 1)
  • Kernel - [width, in channels, out channels](eg 5, 1, 4)
  • Uscita - [batch size, width, out_channels](ad esempio 1, 5, 4)

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

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

E 'molto meno lavoro con TF2 come TF2 non ha bisogno Sessione variable_initializer, per esempio.

Come potrebbe apparire nella vita reale?

Quindi capiamo cosa sta facendo usando un esempio di attenuazione del segnale. A sinistra hai l'originale e a destra hai l'output di una Convolution 1D che ha 3 canali di uscita.

inserisci qui la descrizione dell'immagine

Cosa significano più canali?

Più canali sono fondamentalmente più rappresentazioni di caratteristiche di un ingresso. In questo esempio hai tre rappresentazioni ottenute da tre diversi filtri. Il primo canale è il filtro levigante di uguale ponderazione. Il secondo è un filtro che pesa la metà del filtro più dei bordi. Il filtro finale fa l'opposto del secondo. Quindi puoi vedere come questi diversi filtri producono effetti diversi.

Applicazioni di deep learning della convoluzione 1D

La convoluzione 1D è stata utilizzata con successo per l' attività di classificazione delle frasi .

Convoluzione 2D

Off alla convoluzione 2D. Se sei una persona che apprende profondamente, le probabilità che non ti sia imbattuto in una convoluzione 2D sono ... beh, circa zero. Viene utilizzato nelle CNN per la classificazione delle immagini, il rilevamento di oggetti, ecc. Nonché nei problemi di PNL che coinvolgono le immagini (ad esempio, la generazione di didascalie di immagini).

Facciamo un esempio, ho un kernel di convoluzione con i seguenti filtri qui,

  • Kernel rilevamento bordi (finestra 3x3)
  • Sfoca il kernel (finestra 3x3)
  • Affila il kernel (finestra 3x3)

E per essere precisi i miei dati hanno le seguenti forme,

  • Immagine (bianco e nero) - [batch_size, height, width, 1](es. 1, 340, 371, 1)
  • Kernel (aka filtri) - [height, width, in channels, out channels](eg 3, 3, 1, 3)
  • Output (aka mappe di funzionalità) - [batch_size, height, width, out_channels](ad esempio 1, 340, 371, 3)

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

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

Come potrebbe essere nella vita reale?

Qui puoi vedere l'output prodotto dal codice sopra. La prima immagine è l'originale e in senso orario si hanno le uscite del 1 ° filtro, 2 ° filtro e 3 filtri. inserisci qui la descrizione dell'immagine

Cosa significano più canali?

Nel contesto della convoluzione 2D, è molto più facile capire cosa significano questi canali multipli. Dì che stai facendo il riconoscimento facciale. Puoi pensare (questa è una semplificazione molto irrealistica ma fa capire il punto) ogni filtro rappresenta un occhio, una bocca, un naso, ecc. In modo che ogni mappa delle caratteristiche sia una rappresentazione binaria se quella caratteristica è presente nell'immagine che hai fornito . Non credo di dover sottolineare che per un modello di riconoscimento facciale queste sono caratteristiche molto preziose. Maggiori informazioni in questo articolo .

Questa è un'illustrazione di ciò che sto cercando di articolare.

inserisci qui la descrizione dell'immagine

Applicazioni di deep learning della convoluzione 2D

La convoluzione 2D è molto diffusa nel regno del deep learning.

Le CNN (Convolution Neural Networks) utilizzano operazioni di convoluzione 2D per quasi tutte le attività di visione artificiale (ad es. Classificazione di immagini, rilevamento di oggetti, classificazione video).

Convoluzione 3D

Ora diventa sempre più difficile illustrare cosa sta succedendo all'aumentare del numero di dimensioni. Ma con una buona conoscenza di come funziona la convoluzione 1D e 2D, è molto semplice generalizzare tale comprensione alla convoluzione 3D. Quindi ecco qui.

E per essere precisi i miei dati hanno le seguenti forme,

  • Dati 3D (LIDAR) - [batch size, height, width, depth, in channels](eg 1, 200, 200, 200, 1)
  • Kernel - [height, width, depth, in channels, out channels](eg 5, 5, 5, 1, 3)
  • Uscita - [batch size, width, height, width, depth, out_channels](ad esempio 1, 200, 200, 2000, 3)

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

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

Applicazioni di deep learning della convoluzione 3D

La convoluzione 3D è stata utilizzata durante lo sviluppo di applicazioni di machine learning che coinvolgono dati LIDAR (Light Detection and Ranging) di natura tridimensionale.

Cosa ... altro gergo ?: Stride e imbottitura

Va bene, ci sei quasi. Quindi resisti. Vediamo cos'è l'andatura e l'imbottitura. Sono abbastanza intuitivi se ci pensi.

Se percorri un corridoio a grandi passi, ci arrivi più velocemente con meno passaggi. Ma significa anche che hai osservato un ambiente circostante minore rispetto a quando attraversavi la stanza. Rafforziamo ora la nostra comprensione anche con una bella immagine! Comprendiamoli tramite la convoluzione 2D.

Capire il passo

Andatura di convoluzione

Quando si utilizza, tf.nn.conv2dad esempio, è necessario impostarlo come vettore di 4 elementi. Non c'è motivo di lasciarsi intimidire da questo. Contiene solo i passi nel seguente ordine.

  • Convoluzione 2D - [batch stride, height stride, width stride, channel stride]. Qui, batch stride e channel stride hai appena impostato su uno (ho implementato modelli di deep learning per 5 anni e non ho mai dovuto impostarli su nient'altro che uno). Quindi ti restano solo 2 passi da impostare.

  • Convoluzione 3D - [batch stride, height stride, width stride, depth stride, channel stride]. Qui ti preoccupi solo dei passi di altezza / larghezza / profondità.

Capire l'imbottitura

Ora, noti che non importa quanto piccolo sia il tuo passo (es. 1), si verifica un'inevitabile riduzione delle dimensioni durante la convoluzione (es. La larghezza è 3 dopo aver convolto un'immagine di 4 unità). Ciò è indesiderabile soprattutto quando si costruiscono reti neurali di convoluzione profonda. È qui che l'imbottitura viene in soccorso. Esistono due tipi di imbottitura più comunemente usati.

  • SAME e VALID

Di seguito puoi vedere la differenza.

inserisci qui la descrizione dell'immagine

Ultima parola : se sei molto curioso, ti starai chiedendo. Abbiamo appena sganciato una bomba su tutta la riduzione automatica delle dimensioni e ora parliamo di passi diversi. Ma la cosa migliore della falcata è che controlli quando e come ridurre le dimensioni.


2
  1. CNN 1D, 2D o 3D si riferisce alla direzione della convoluzione, piuttosto che alla dimensione dell'input o del filtro.

  2. Per 1 canale di ingresso, CNN2D è uguale a CNN1D è la lunghezza del kernel = lunghezza di ingresso. (1 direzione conv.)


2

In sintesi, in 1D CNN, il kernel si muove in 1 direzione. I dati di input e output di 1D CNN sono bidimensionali. Utilizzato principalmente sui dati delle serie temporali.

Nella CNN 2D, il kernel si muove in 2 direzioni. I dati di input e output della CNN 2D sono tridimensionali. Utilizzato principalmente sui dati di immagine.

Nella CNN 3D, il kernel si muove in 3 direzioni. I dati di input e output della CNN 3D sono quadridimensionali. Utilizzato principalmente su dati di immagini 3D (MRI, scansioni TC).

Puoi trovare maggiori dettagli qui: https://medium.com/@xzz201920/conv1d-conv2d-and-conv3d-8a59182c4d6

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.