La Mia Prima Rete Neurale
La Mia Prima Rete Neurale
Alex Alborghetti
Il dataset fashion_mnist, disponibile nella libreria keras, contiene immagini in bianco e nero di
70.000 articoli di moda presenti sul sito di Zalando classificate entro 10 categorie con etichetta
da 0 a 9. Le categorie sono le seguenti:
2: Pullover 6: Camicia
3: Abito 7: Scarpa
# importazione
from keras.datasets import fashion_mnist
import matplotlib.pyplot as plt
# processazione
import numpy as np
from keras.utils import to_categorical
from sklearn.model_selection import train_test_split
# rete neurale
import keras
from tensorflow.keras.layers import Input,BatchNormalization
from keras.models import Sequential,Model
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D, LeakyReLU
Nei set di training e di test si trovano rispettivamente 60.000 e 10.000 immagini con dimensione
28×28, mentre la variabile risposta è data da 60.000 e 10.000 etichette di categoria. Si visualizzano
qui sotto la prima immagine contenuta nel training set e la prima contenuta nel test set. La sintassi
del codice sottostante può essere spiegata nel seguente modo:
plt.figure(figsize=[10,10])
plt.subplot(121)
plt.imshow(train_X[0,:,:], cmap='gray')
plt.title("Ground Truth : {}".format(train_Y[0]))
plt.subplot(122)
plt.imshow(test_X[0,:,:], cmap='gray')
plt.title("Ground Truth : {}".format(test_Y[0]))
Si nota come ora training set e test set siano formati rispettivamente da 60.000 e 10.000 "tensori"
di dimensioni 28×28×1. Per mandare i dati in input alla rete neurale occorre inoltre che il loro
formato sia float32 e che il valore di ogni punto della matrice non vari da 0 a 255 ma da 0 a 1.
Quindi, vengono applicate queste modifiche cambiando il formato dei dati con la funzione astype
e normalizzandoli dividendo per 255.
train_X = train_X.astype('float32')
test_X = test_X.astype('float32')
train_X = train_X / 255.
test_X = test_X / 255.
Bisogna ora trasformare il test set in una forma one-hot encoding. In altre parole, la rete neurale
non è in grado di lavorare direttamente con il valore della categoria (es. categoria 9) ma ha bisogno
per ciascuna immagine di un vettore risposta associato di dimensione pari al numero di categorie
nel quale sono presenti solo degli zeri fuorché un uno quando la categoria è quella di interesse. Ad
esempio, la categoria 9 sarà rappresentata dal vettore (0,0,0,0,0,0,0,0,0,1) . Per svolgere
questa ricofidica dei dati, si utilizza la funzione to_categorical.
Train_Y_one_hot = to_categorical(train_Y)
test_Y_one_hot = to_categorical(test_Y)
Si stampa quanto ottenuto per la categoria riferita alla prima immagine del training set, si ricorda
che tale categoria era la 9.
print('Categoria di appartenenza:', train_Y[0])
print('Codifica one-hot:', train_Y_one_hot[0])
Categoria di appartenenza: 9
Codifica one-hot: [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
Infine, viene suddiviso ulteriormente il training set in due partizioni di proporzioni 80/20%
creando il validation set sul quale verrà testato il modello in fase di addestramento.
train_X,valid_X,train_label,valid_label = train_test_split(train_X, train_Y_one_ho
t, test_size=0.2, random_state=13)
train_X.shape,valid_X.shape,train_label.shape,valid_label.shape
((48000, 28, 28, 1), (12000, 28, 28, 1), (48000, 10), (12000, 10))
Si osserva che il training set ha ora 48.000 immagini, il validation set creato ne contiene quindi
12.000.
Dopo ciascun layer di convoluzione abbiamo un layer di pooling con dimensione 2×2.
Poiché il training set è troppo grande per essere processato tutto in una volta, vengono definiti dei
sottoinsiemi di esso chiamati batch, in questo caso di dimensione 64, in modo da processare 64
immagini alla volta in fase di addestramento. Una volta che tutto il training set è stato processato,
si dice che è trascorsa un’epoca. Il numero di epoche per addestrare il modello è definito a priori,
in questo caso è pari a 20. Infine, in num_classes viene inserito il valore 10 poiché le categorie di
immagini sono 10 in totale (da 0 a 9).
batch_size = 64
epochs = 20
num_classes = 10
Si costruisce ora la rete neurale. Essa viene definita layer per layer: la si inizializza con la funzione
sequential e successivamente si aggiunge ciascun layer con il metodo .add.
fashion_model = Sequential()
fashion_model.add(Conv2D(32, kernel_size=(3, 3),activation='linear',input_shape=(2
8,28,1),padding='same'))
fashion_model.add(LeakyReLU(alpha=0.1))
fashion_model.add(MaxPooling2D((2, 2),padding='same'))
fashion_model.add(Conv2D(64, (3, 3), activation='linear',padding='same'))
fashion_model.add(LeakyReLU(alpha=0.1))
fashion_model.add(MaxPooling2D(pool_size=(2, 2),padding='same'))
fashion_model.add(Conv2D(128, (3, 3), activation='linear',padding='same'))
fashion_model.add(LeakyReLU(alpha=0.1))
fashion_model.add(MaxPooling2D(pool_size=(2, 2),padding='same'))
fashion_model.add(Flatten())
fashion_model.add(Dense(128, activation='linear'))
fashion_model.add(LeakyReLU(alpha=0.1))
fashion_model.add(Dense(num_classes, activation='softmax'))
Ogni layer convoluzionale è aggiunto con la funzione Conv2D, mentre ogni layer di pooling è
aggiunto con la funzione MaxPooling2D. Al termine di ogni layer viene applicata la funzione di
attivazione LeakyReLU che consente di rilevare linee di decisione non lineari. Questa funzione di
attivazione è una delle tante possibili (un'altra, ad esempio, è la sigmoide), ma la più utilizzata nelle
reti neurali convoluzionali. L'ultimo layer è definito con Dense e la funzione di attivazione softmax:
questa combinazione consente di eseguire una classificazione multiclasse come quella del
problema in oggetto. Una volta definita la rete neurale, essa va compilata.
fashion_model.compile(loss = keras.losses.categorical_crossentropy, optimizer = ke
ras.optimizers.Adam(), metrics=['accuracy'])
Uno dei compilatori più utilizzati è Adam. La funzione di perdita è la cross-entropia, molto
utilizzata nella classificazione di immagini. La metrica utilizzata è l'accuratezza. Viene restituito
ora un riassunto della rete neurale appena creata.
fashion_model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 28, 28, 32) 320
leaky_re_lu (LeakyReLU) (None, 28, 28, 32) 0
=================================================================
Total params: 356,234
Trainable params: 356,234
Non-trainable params: 0
_________________________________________________________________
Si notano diversi layer con la loro forma e numero di parametri. Al termine, si è ottenuta una rete
con un numero di parametri totali pari a 356.234.
Si nota che ad ogni epoca vengono processati 750 batch ciascuno contenente 64 immagini (64×750
= 48000, dimensione del training set). Ciò viene fatto per 20 epoche. L'accuracy finale ottenuta sul
training set è pari al 99%, mentre l'accuracy sul validation set è del 92%: sembra quindi essere
presente del sovradattamento. Osserviamo comunque i risultati della rete neurale sul test set.
test_eval = fashion_model.evaluate(test_X, test_Y_one_hot, verbose=0)
print(test_eval)
[0.49416348338127136, 0.9154000282287598]
La funzione di perdita utilizzata vale sul test set 0.49, mentre l'accuracy è del 92%. I risultati sono
buoni, ma la differenza tra i risultati nei due set evidenzia, come già accennato, un potenziale
problema di overfitting. Vengono ora rappresentati graficamente i risultati ad ogni epoca di
addestramento.
accuracy = fashion_train.history['accuracy']
val_accuracy = fashion_train.history['val_accuracy']
loss = fashion_train.history['loss']
val_loss = fashion_train.history['val_loss']
epochs = range(1, len(accuracy)+1)
plt.figure(figsize=[10,5])
plt.subplot(121)
plt.plot(epochs, accuracy, 'b', label='Training accuracy', color = "red")
plt.plot(epochs, val_accuracy, 'b', label='Validation accuracy')
plt.title('Training and validation accuracy')
plt.xlabel("Epoch")
plt.ylabel("Accuracy")
plt.xlim(0,21)
plt.legend()
plt.subplot(122)
plt.plot(epochs, loss, 'b', label='Training loss', color = "red")
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel("Epoch")
plt.ylabel("Funzione di perdita")
plt.xlim(0,21)
plt.legend()
<matplotlib.legend.Legend at 0x7f1196217dc0>
Si nota come la accuracy misurata sul validation set rimanga pressoché uguale dopo 4 o 5 epoche,
mentre la funzione di perdita inizia ad aumentare: tutti segni di potenziale overfitting.
Aggiunta di dropout
Il dropout consiste nel nascondere una certa frazione di neuroni dalla rete in modo casuale
durante la fase di addestramento. Questo serve per evitare il sovradattamento in fase di
allenamento della rete. Si sceglie una frazione del 30% di neuroni da escludere durante
l’addestramento.
Fashion_model = Sequential()
fashion_model.add(Conv2D(32, kernel_size=(3, 3),activation=’linear’,padding=’same’
,input_shape=(28,28,1)))
fashion_model.add(LeakyReLU(alpha=0.1))
fashion_model.add(MaxPooling2D((2, 2),padding=’same’))
fashion_model.add(Dropout(0.25))
fashion_model.add(Conv2D(64, (3, 3), activation=’linear’,padding=’same’))
fashion_model.add(LeakyReLU(alpha=0.1))
fashion_model.add(MaxPooling2D(pool_size=(2, 2),padding=’same’))
fashion_model.add(Dropout(0.25))
fashion_model.add(Conv2D(128, (3, 3), activation=’linear’,padding=’same’))
fashion_model.add(LeakyReLU(alpha=0.1))
fashion_model.add(MaxPooling2D(pool_size=(2, 2),padding=’same’))
fashion_model.add(Dropout(0.4))
fashion_model.add(Flatten())
fashion_model.add(Dense(128, activation=’linear’))
fashion_model.add(LeakyReLU(alpha=0.1))
fashion_model.add(Dropout(0.3))
fashion_model.add(Dense(num_classes, activation=’softmax’))
fashion_model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_3 (Conv2D) (None, 28, 28, 32) 320
=================================================================
Total params: 356,234
Trainable params: 356,234
Non-trainable params: 0
_________________________________________________________________
Si nota che il numero di parametri è rimasto invariato, mentre il dropout è stato aggiunto. Viene
visualizzata la struttura della rete neurale attraverso la funzione layered_view del pacchetto
visualkeras.
Il problema dell'overfitting sembra essersi risolto, dal momento che l'accuratezza sul training set e
sul validation set sono simili e pari al 92.5% circa. Si potrebbe salvare ora il modello, in modo che
possa essere utilizzato in seguito senza aver bisogno di riaddestrarlo ogni volta, dal momento che
il processo di addestramento è piuttosto lungo (ha richiesto circa mezz'ora).
# fashion_model.save("fashion_model_dropout.h5py")
[0.21419931948184967, 0.923799991607666]
L’accuracy è aumentata rispetto a prima e la funzione di perdita è diminuita. L'aggiunta del dropout
ha portato quindi ad un modello più generalizzabile su nuovi dati.
Previsione di categorie
Si mostrano ora alcune previsioni che sono state effettuate sul test set da parte della rete neurale.
Si ricorda che l'output è codificato secondo il paradigma one-hot, di conseguenza si dovrà estrarre
per ciascun vettore in output la posizione della cella con valore 1 per trovare la previsione di
categoria del modello.
predicted_classes = fashion_model.predict(test_X)
predicted_classes = np.argmax(np.round(predicted_classes),axis=1)
predicted_classes[0:2]
array([9, 2])
Si nota, ad esempio, che le prime due immagini del test set sono state associate rispettivamente alla
categoria 9 e alla categoria 2. Si vanno ora ad estrarre le classi predette correttamente
confrontando i valori previsti sul test set con i valori reali e si rappresentano le prime 9 immagini
correttamente classificate nel test set.
correct = np.where(predicted_classes==test_Y)[0]
print("Found %d correct labels" % len(correct))
for i, correct in enumerate(correct[:9]):
plt.subplot(3,3,i+1)
plt.imshow(test_X[correct].reshape(28,28), cmap='gray', interpolation='none')
plt.title("Predicted {}, Class {}".format(predicted_classes[correct], test_Y[c
orrect]))
plt.tight_layout()
https://www.datacamp.com/tutorial/convolutional-neural-networks-python