CNTK - Rete neurale convoluzionale

In questo capitolo, studiamo come costruire una rete neurale convoluzionale (CNN) in CNTK.

introduzione

Anche le reti neurali convoluzionali (CNN) sono costituite da neuroni, che hanno pesi e pregiudizi apprendibili. Ecco perché in questo modo sono come normali reti neurali (NN).

Se ricordiamo il funzionamento delle normali NN, ogni neurone riceve uno o più input, prende una somma ponderata e passa attraverso una funzione di attivazione per produrre l'output finale. Qui, sorge la domanda che se le CNN e le normali NN hanno così tante somiglianze, cosa rende queste due reti diverse tra loro?

Ciò che li rende diversi è il trattamento dei dati di input e dei tipi di livelli? La struttura dei dati di input viene ignorata nel normale NN e tutti i dati vengono convertiti in array 1-D prima di immetterli nella rete.

Tuttavia, l'architettura della rete neurale convoluzionale può considerare la struttura 2D delle immagini, elaborarle e consentirle di estrarre le proprietà specifiche delle immagini. Inoltre, le CNN hanno il vantaggio di avere uno o più strati convoluzionali e strati di pooling, che sono i principali elementi costitutivi delle CNN.

Questi strati sono seguiti da uno o più strati completamente connessi come negli NN multistrato standard. Quindi, possiamo pensare alla CNN, come un caso speciale di reti completamente connesse.

Architettura della rete neurale convoluzionale (CNN)

L'architettura della CNN è fondamentalmente un elenco di livelli che trasforma il volume tridimensionale, ovvero larghezza, altezza e profondità dell'immagine in un volume di output tridimensionale. Un punto importante da notare qui è che ogni neurone nel livello corrente è collegato a una piccola patch dell'output dal livello precedente, che è come sovrapporre un filtro N * N sull'immagine in ingresso.

Utilizza filtri M, che sono fondamentalmente estrattori di funzionalità che estraggono caratteristiche come bordi, angoli e così via. Di seguito sono riportati gli strati [INPUT-CONV-RELU-POOL-FC] che vengono utilizzati per costruire reti neurali convoluzionali (CNN) -

  • INPUT- Come suggerisce il nome, questo livello contiene i valori dei pixel non elaborati. I valori dei pixel grezzi indicano i dati dell'immagine così com'è. Esempio, INPUT [64 × 64 × 3] è un'immagine RGB a 3 canali di larghezza 64, altezza 64 e profondità 3.

  • CONV- Questo livello è uno degli elementi costitutivi delle CNN poiché la maggior parte dei calcoli viene eseguita in questo livello. Esempio - se usiamo 6 filtri sul summenzionato INPUT [64 × 64 × 3], questo potrebbe risultare nel volume [64 × 64 × 6].

  • RELU- Chiamato anche layer lineare rettificato, che applica una funzione di attivazione all'output del layer precedente. In altro modo, una non linearità verrebbe aggiunta alla rete da RELU.

  • POOL- Questo livello, ovvero il livello di pooling, è un altro elemento costitutivo delle CNN. Il compito principale di questo livello è il down-sampling, il che significa che opera indipendentemente su ogni fetta dell'input e lo ridimensiona spazialmente.

  • FC- Si chiama livello completamente connesso o più specificamente il livello di output. Viene utilizzato per calcolare il punteggio della classe di output e l'output risultante è il volume della dimensione 1 * 1 * L dove L è il numero corrispondente al punteggio della classe.

Il diagramma seguente rappresenta l'architettura tipica delle CNN-

Creazione della struttura CNN

Abbiamo visto l'architettura e le basi della CNN, ora costruiremo una rete convoluzionale usando CNTK. Qui, vedremo prima come mettere insieme la struttura della CNN e poi vedremo come allenarne i parametri.

Finalmente vedremo come possiamo migliorare la rete neurale cambiando la sua struttura con varie configurazioni di livelli differenti. Useremo il set di dati immagine MNIST.

Quindi, prima creiamo una struttura CNN. Generalmente, quando costruiamo una CNN per riconoscere i modelli nelle immagini, facciamo quanto segue:

  • Usiamo una combinazione di convoluzione e strati di raggruppamento.

  • Uno o più livelli nascosti alla fine della rete.

  • Infine, finiamo la rete con uno strato softmax a scopo di classificazione.

Con l'aiuto dei seguenti passaggi, possiamo costruire la struttura di rete -

Step 1- Innanzitutto, dobbiamo importare i livelli richiesti per la CNN.

from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling

Step 2- Successivamente, dobbiamo importare le funzioni di attivazione per la CNN.

from cntk.ops import log_softmax, relu

Step 3- Dopodiché, per inizializzare successivamente i livelli convoluzionali, dobbiamo importare il file glorot_uniform_initializer come segue -

from cntk.initializer import glorot_uniform

Step 4- Successivamente, per creare variabili di input importare il file input_variablefunzione. E importadefault_option funzione, per rendere la configurazione di NN un po 'più semplice.

from cntk import input_variable, default_options

Step 5- Ora per memorizzare le immagini di input, creane una nuova input_variable. Conterrà tre canali: rosso, verde e blu. Avrebbe le dimensioni di 28 x 28 pixel.

features = input_variable((3,28,28))

Step 6- Successivamente, dobbiamo crearne un altro input_variable per memorizzare le etichette da prevedere.

labels = input_variable(10)

Step 7- Ora, dobbiamo creare il file default_optionper NN. E dobbiamo usare ilglorot_uniform come funzione di inizializzazione.

with default_options(initialization=glorot_uniform, activation=relu):

Step 8- Successivamente, per impostare la struttura del NN, dobbiamo crearne uno nuovo Sequential set di livelli.

Step 9- Ora dobbiamo aggiungere un file Convolutional2D strato con a filter_shape di 5 e a strides impostazione di 1, all'interno del Sequentialset di livelli. Inoltre, abilita il riempimento, in modo che l'immagine venga riempito per mantenere le dimensioni originali.

model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),

Step 10- Ora è il momento di aggiungere un file MaxPooling strato con filter_shape di 2 e a strides impostazione di 2 per comprimere l'immagine della metà.

MaxPooling(filter_shape=(2,2), strides=(2,2)),

Step 11- Ora, come abbiamo fatto nel passaggio 9, dobbiamo aggiungerne un altro Convolutional2D strato con a filter_shape di 5 e a stridesimpostazione di 1, utilizzare 16 filtri. Inoltre, abilitare la spaziatura interna, in modo che la dimensione dell'immagine prodotta dal precedente livello di raggruppamento debba essere mantenuta.

Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),

Step 12- Ora, come abbiamo fatto nel passaggio 10, aggiungine un altro MaxPooling strato con a filter_shape di 3 e a strides impostazione di 3 per ridurre l'immagine a un terzo.

MaxPooling(filter_shape=(3,3), strides=(3,3)),

Step 13- Infine, aggiungi uno strato denso con dieci neuroni per le 10 classi possibili, la rete può prevedere. Per trasformare la rete in un modello di classificazione, utilizzare un filelog_siftmax funzione di attivazione.

Dense(10, activation=log_softmax)
])

Esempio completo per la creazione di una struttura CNN

from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)

Formazione CNN con immagini

Poiché abbiamo creato la struttura della rete, è il momento di addestrare la rete. Ma prima di iniziare l'addestramento della nostra rete, dobbiamo impostare le sorgenti dei minibatch, perché l'addestramento di un NN che funziona con le immagini richiede più memoria, rispetto alla maggior parte dei computer.

Abbiamo già creato sorgenti minibatch nelle sezioni precedenti. Di seguito è riportato il codice Python per configurare due sorgenti minibatch:

Poiché abbiamo il file create_datasource funzione, ora possiamo creare due origini dati separate (training e testing uno) per addestrare il modello.

train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)

Ora, poiché abbiamo preparato le immagini, possiamo iniziare l'addestramento del nostro NN. Come abbiamo fatto nelle sezioni precedenti, possiamo utilizzare il metodo di allenamento sulla funzione di perdita per dare il via all'allenamento. Di seguito è riportato il codice per questo:

from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
loss = cross_entropy_with_softmax(output, targets)
metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)

Con l'aiuto del codice precedente, abbiamo impostato la perdita e lo studente per NN. Il codice seguente addestrerà e convaliderà NN−

from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
   features: train_datasource.streams.features,
   labels: train_datasource.streams.labels
}
loss.train(train_datasource,
     max_epochs=10,
     minibatch_size=64,
     epoch_size=60000,
        parameter_learners=[learner],
     model_inputs_to_streams=input_map,
     callbacks=[progress_writer, test_config])

Esempio di implementazione completo

from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)
import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
   mapping_file = os.path.join(folder, 'mapping.bin')
   image_transforms = []
   if train:
    image_transforms += [
     xforms.crop(crop_type='randomside', side_ratio=0.8),
     xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
   stream_definitions = StreamDefs(
   features=StreamDef(field='image', transforms=image_transforms),
    labels=StreamDef(field='label', shape=10)
)
   deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)
train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)
from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
   loss = cross_entropy_with_softmax(output, targets)
   metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
   features: train_datasource.streams.features,
   labels: train_datasource.streams.labels
}
loss.train(train_datasource,
     max_epochs=10,
     minibatch_size=64,
     epoch_size=60000,
        parameter_learners=[learner],
     model_inputs_to_streams=input_map,
     callbacks=[progress_writer, test_config])

Produzione

-------------------------------------------------------------------
average  since  average  since  examples
loss     last   metric   last
------------------------------------------------------
Learning rate per minibatch: 0.2
142      142      0.922   0.922    64
1.35e+06 1.51e+07 0.896   0.883    192
[………]

Trasformazioni di immagini

Come abbiamo visto, è difficile addestrare NN utilizzato per il riconoscimento delle immagini e richiedono molti dati anche per il training. Un altro problema è che tendono ad adattarsi eccessivamente alle immagini utilizzate durante l'allenamento. Vediamo con un esempio, quando abbiamo foto di volti in posizione verticale, il nostro modello avrà difficoltà a riconoscere i volti che vengono ruotati in un'altra direzione.

Per superare questo problema, possiamo usare l'aumento delle immagini e CNTK supporta trasformazioni specifiche, quando si creano sorgenti minibatch per le immagini. Possiamo usare diverse trasformazioni come segue:

  • Possiamo ritagliare in modo casuale le immagini utilizzate per l'addestramento con poche righe di codice.

  • Possiamo anche usare una scala e un colore.

Vediamo con l'aiuto del codice Python come possiamo modificare l'elenco delle trasformazioni includendo una trasformazione di ritaglio all'interno della funzione utilizzata per creare il sorgente del minibatch in precedenza.

import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
   mapping_file = os.path.join(folder, 'mapping.bin')
   image_transforms = []
   if train:
   image_transforms += [
     xforms.crop(crop_type='randomside', side_ratio=0.8),
xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
   stream_definitions = StreamDefs(
   features=StreamDef(field='image', transforms=image_transforms),
labels=StreamDef(field='label', shape=10)
)
   deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)

Con l'aiuto del codice sopra, possiamo migliorare la funzione per includere una serie di trasformazioni dell'immagine, in modo che, quando ci formeremo, possiamo ritagliare l'immagine in modo casuale, in modo da ottenere più variazioni dell'immagine.