Un blog sullo sviluppo Front End, corsi WordPress, Data Science, Machine Leaning e Python.

In questo tutorial, che prende spunto dall'esempio del libro "Generative Deep Learning", andremo ad implementare una rete neurale profonda attraverso il dataset CIFAR-10.

Il dataset CIFAR-10 ha una collezione di 60,000 immagini a colori 32x32 px. Ogni immagine verrà classificata in una delle 10 classi presenti in Figura 1.

Figura 1 - le 10 classi di classificazione immagini per il dataset CIFAR-10

Importiamo le librerie e il dataset CIFAR-10.

import numpy as np
from keras.utils import to_categorical
from keras.datasets import cifar10

Carichiamo il dataset CIFAR-10 associando gli array x_train e x_test di dimensioni [50000, 32, 32, 3] e [10000, 32, 32, 3].

y_train e y_test sono array numpy con dimensioni [50000, 1] e [10000, 1], i quali contengono le etichette da 0 a 9 per le 10 classi di immagini.

(x_train, y_train), (x_test, y_test) = cifar10.load_data()

NUM_CLASSES = 10

Le immagini sono formate da interi con range di valori da 0 a 255 per ogni canale (nel nostro caso essendo immagini a colori abbiamo 3 canali RGB). Le reti neurali lavorano meglio quando ogni input è compreso tra un range di -1 e 1, perciò dividiamo x_train e x_test per 255.

x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0

Cambiamo l'etichetta delle immagini in un vettore one-hot-encoded. Se la classe dell'immagine è i, il corrispondente vettore one-hot-encoded sarà un vettore di lunghezza 10, corrispondente al numero totale delle classi.

Le nuove dimensioni di di y_train e y_test saranno quindi [50000, 10] e [10000, 10].

y_train = to_categorical(y_train, NUM_CLASSES)
y_test = to_categorical(y_test, NUM_CLASSES)

Costruire il modello

In Keras un modello sequenziale è utile per definire rapidamente una pila lineare di livelli (vale a dire, una pila in cui un layer è seguito direttamente dal livello precedente senza ramificazioni).

Molti modelli richiedono che l'output da un layer sia passato a più layer separati al di sotto di esso, o viceversa, che un layer riceva input da più layer al di sopra di esso. Per poter costruire reti con più ramificazioni, dobbiamo usare le functional API. Le functional API danno piena libertà sul design della rete neurale profonda.

from keras.layers import Input, Flatten, Dense 
from keras.models import Model

input_layer = Input(shape=(32,32, 3))
x = Flatten()(input_layer)
x = Dense(units=200, activation = 'relu')(x)
x = Dense(units=150, activation = 'relu')(x)

output_layer = Dense(units=10, activation = 'softmax')(x) 

model = Model(input_layer, output_layer)

Input Layer

Il layer di input è un punto di ingresso nella rete. Indichiamo alla rete la forma di ciascun elemento di dati come tupla. Non viene specificata la dimensione del batch, in quanto possiamo trasferire contemporaneamente qualsiasi numero di immagini nel layer di Input. Non è quindi necessario indicare esplicitamente la dimensione del batch nella definizione del layer di input.

Input Flatten

Viene poi utilizzato un Layer di tipo Flatten, poiché dobbiamo avere come risultato un vettore di lunghezza 3,072 (32 x 32 x 3). Viene utilizzato Flatten poiché Dense richiede un input di tipo flat, anziché un input di tipo array multi dimensionale.

Input Dense

Il Layer Dense viene utilizzato poiché permetter di contenere un dato numero di unità densamente connesse ai layer precedenti. Ogni connessione ha un proprio peso che può essere un valore positivo o negativo.

L'output di una determinata unità è la somma ponderata dell'input che riceve dal layer precedente, che viene quindi passato attraverso una funzione di attivazione non lineare prima di essere inviato al layer successivo.

La funzione di attivazione è fondamentale per garantire che la rete neurale sia in grado di apprendere funzioni complesse e non generi solo una combinazione lineare del suo input.

La funzione di attivazione utilizzata è la ReLU (rectified linear unit).

Nell'esempio viene passato l'input attraverso due strati nascosti densi, il primo con 200 unità e il secondo con 150, entrambi con funzioni di attivazione ReLU.

Un diagramma della rete neurale addestrata sui dati CIFAR-10

Il passaggio finale consiste nel definire il modello stesso, utilizzando la classe Model. In Keras un modello è definito dai livelli di input e output.

Nel nostro caso, abbiamo un livello di input che abbiamo definito in precedenza e il livello di output è il livello Dense finale di 10 unità.

In questo esempio la forma del livello di input corrisponde alla forma di x_train e la forma del ivello di output denso corrisponde alla forma di y_train. Per illustrare questo si usa il metodo model.summary() per vedere la forma della rete su ogni livello.

Compilare il modello

Si compila il modello utilizzando una funzione di optimizer ed una di loss.

from keras.optimizers import Adam

opt = Adam(lr=0.0005) 
model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])

La funzione di loss viene utilizzata dalla rete neurale per confrontare il suo output previsto. Restituisce un singolo numero per ogni osservazione; maggiore è questo numero, peggiore è l'esecuzione della rete.

Vengono passati entrambi i metodi di loss e di optimizer al metodo compile del modello, così come altre metriche durante il training.

Training del modello

Viene ora richiamato il metodo fit.

model.fit(x_train, y_train, batch_size = 32 , epochs = 10, shuffle = True )

Da qui incomincia l'addestramento della rete neurale. Il processo di training inizializza dei pesi con valore random, per poi eseguire una serie di step successivi.

Ad ogni step del training, un batch di immagini viene passato attraverso la rete e gli errori attraverso la backpropagation aggiornano i pesi. Il batch_size determina quante immagini ci sono in ogni step di training. Più grande è il batch_size, maggiore è la stabilità nel calcolo del gradiente, rendendo però ogni step più lento.

La prima epoch viene completata quando tutto il dataset è stato visitato.

Output metodo fit per 10 epoch

Valutare il modello

Il modello ha raggiunto un'accuratezza del 51.1% sul training set. Adesso viene fatto un controllo per vedere le performances su dati che ancora non ha affrontato in precedenza.

model.evaluate(x_test, y_test)
Output del metodo evaluate

L'accuratezza del modello su immagini ancora non viste è del 48.8%. Vengono eseguite quindi delle predizioni sui modelli di test usando il metodo predict.

CLASSES = np.array(['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog'
                       , 'frog', 'horse', 'ship', 'truck'])

preds = model.predict(x_test)
preds_single = CLASSES[np.argmax(preds, axis = -1)] 
actual_single = CLASSES[np.argmax(y_test, axis = -1)]

Vengono visualizzate infine le immagini con le loro etichette e le relative predizioni.

CLASSES = np.array(['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog'
                       , 'frog', 'horse', 'ship', 'truck'])
preds = model.predict(x_test)
preds_single = CLASSES[np.argmax(preds, axis = -1)] 
actual_single = CLASSES[np.argmax(y_test, axis = -1)]

import matplotlib.pyplot as plt 

n_to_show = 10
indices = np.random.choice(range(len(x_test)), n_to_show) 
fig = plt.figure(figsize=(15, 3))
fig.subplots_adjust(hspace=0.4, wspace=0.4)

for i, idx in enumerate(indices):
    img = x_test[idx]
    ax = fig.add_subplot(1, n_to_show, i+1)
    ax.axis('off')
    ax.text(0.5, -0.35, 'pred = ' + str(preds_single[idx]), fontsize=10, ha='center', transform=ax.transAxes)
    ax.text(0.5, -0.7, 'act = ' + str(actual_single[idx]), fontsize=10 , ha='center', transform=ax.transAxes)
    ax.imshow(img)
Predizioni fatte dal modello
Ti sei iscritto con successo al blog di Jacopo Kahl blog
Bentornato! Hai effettuo l'accesso al blog.
Ottimo! Ti sei appena registrato.
Perfetto! Il tuo account è attivo, ed hai l'accesso a tutti i contenuti.