Corso di Keras per machine learning – terza lezione: come addestrare una rete neurale e salvare il modello

AI, INTELLIGENZA ARTIFICIALE

Nel precedente articolo ho spiegato come usare una struttura sequenziale in keras (model sequential) per sviluppare facilmente una rete neurale. In questo articolo,  farò vedere, invece, come applicare quanto appreso, nel precedente articolo,  su un caso reale  e spiegherò  anche il metodo per salvare il modello addestrato, per consentire di utilizzarlo, successivamente, per  operazioni di inferenza. Allo scopo, in primo istanza, dobbiamo scegliere un argomento e procurarci di conseguenza un dataset sensato. Per questo esempio, ho deciso, dato che implementeremo un classificatore, ovvero una rete neurale in grado di riconoscere un dato ingresso tra n possibilità (classi) ,  di utilizzare come esempio il riconoscimento di piante basate su alcune particolari caratteristiche e non sul riconoscimento dell’immagine.

In particolare proveremo a riconoscere tre categorie di piante:

IRIS VIRGINICA IRIS VERSICOLOR IRIS SETOSA

se non altro perché ho trovato un piccolo dataset utile per il nostro esempio. Ho evitato, per questo primo esercizio, di proporre un esempio basato sul riconoscimento di immagini, forse più  intuitivo,  ma  avrei dovuto per completezza, prima  spiegare le reti neurali CNN (Convolutional Neural Network ) in Keras, oggetto del prossimo articolo.

Iniziamo a dare un occhio al nostro dataset di cui vi lascio il file in formato csv.

lunghezza sepalo larghezza sepalo lunghezza Petalo larghezza Petalo Specie
5.1 3.5 1.4 0.2 I. setosa
4.9 3.0 1.4 0.2 I. setosa
4.7 3.2 1.3 0.2 I. setosa
4.6 3.1 1.5 0.2 I. setosa
5.0 3.6 1.4 0.3 I. setosa
5.4 3.9 1.7 0.4 I. setosa
4.6 3.4 1.4 0.3 I. setosa
5.0 3.4 1.5 0.2 I. setosa
….
6.3 3.4 5.6 2.4 I. virginica
6.4 3.1 5.5 1.8 I. virginica
6.0 3.0 4.8 1.8 I. virginica
6.9 3.1 5.4 2.1 I. virginica
6.7 3.1 5.6 2.4 I. virginica
6.9 3.1 5.1 2.3 I. virginica
5.8 2.7 5.1 1.9 I. virginica
6.8 3.2 5.9 2.3 I. virginica
6.7 3.3 5.7 2.5 I. virginica
6.7 3.0 5.2 2.3 I. virginica

in realtà il  dataset l’ho  riorganizzato  nel seguente modo:

colonna 1 colonna 2 colonna 3 colonna 4 colonna 5 colonna 6 colonna 7
lunghezza sepalo larghezza sepalo lunghezza Petalo larghezza Petalo Setosa Versivolor Virginica
5.1 3.5 1.4 0.2 1 0 0
6.7 3.0 5.2 2.3 0 0 1
5.1 2.5 3.0 1.1 0 1 0

La nostra rete neurale avrà 4 ingressi (Lunghezza sepalo, Larghezza sepalo, Lunghezza Petalo, Larghezza Petalo)

e

3 uscite (Setosa, Versicolor, Virginica)

 

Tra le tre uscite sceglieremo come corretta quella la cui probabilità è prossima a 1, in altre parole sceglieremo quella che ha la maggiore probabilità di correttezza.

N.B Per chi non lo sapesse ( come è successo a me) vi allego una foto per sapere cosa è il sepalo :-).

Implementazione della rete neurale

In prima istanza importiamo quanto necessario:

import numpy as np 
import pandas as pd
from keras.models import Sequential 
from keras.layers import Dense,Dropout
from sklearn.model_selection import train_test_split
from keras.utils import to_categorical

Definiamo il modello, rispetto a quanto spiegato nell’articolo precedente in questo caso ho utilizzato 2 livelli di hidden da 2o neuroni ed  un livello di dropout ( non entriamo in merito a come funziona perché sarà oggetto di un altro articolo, ma sappiate che il dropout ci aiuta ad ottimizzare la fase di trining impostando, in questo caso,  a zero i pesi del 50% dei neuroni )

model = Sequential() 
model.add(Dense(20, input_dim=4, activation='relu',name='hidden1')) # Livello di input 4 neuroni ed hidden di 20 neuroni
model.add(Dense(20, activation='relu',name='hidden2')) # hidden2 di 20 neuroni
model.add(Dropout(0.5) )
model.add(Dense(3, activation='softmax',name='outLayer')) #Livello di output
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy'])
model.summary()

La stampa del summary ci da informazioni sulla complessità della rete neurale e sulla sua struttura.

Model: "sequential_17"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
hidden1 (Dense)              (None, 20)                100       
_________________________________________________________________
hidden2 (Dense)              (None, 20)                420       
_________________________________________________________________
dropout_14 (Dropout)         (None, 20)                0         
_________________________________________________________________
outLayer (Dense)             (None, 3)                 63        
=================================================================
Total params: 583
Trainable params: 583
Non-trainable params: 0

Con la seguente riga andiamo a caricare i da ti dal dataset.csv

dataset = pd.read_csv('./dataset.csv',header=None)

mentre con la seguente trasformiamo i dati in un tensore ( array numpy )

np_dataset = np.asarray(dataset,dtype=np.float32)

Per la fase di training è buona norma normalizzare i dati; ho già affrontato questo tema in questo articolo in cui parlo dei metodi di normalizzazione : feature scaling e mean normalization.

for i in range(0,4):
    minx = np.min( np_dataset[:,i] )
    maxx = np.max( np_dataset[:,i])
    np_dataset[:,i] = (np_dataset[:,i]-minx)/(maxx-minx )

Con la seguente riga vado a dividere il database in:  dataset di traning ed in dataset di validazione o test, in percentuale 80,20.

train, train_val = train_test_split(np_dataset)

a questo punto abbiamo i nostri dati di input, ovvero i dati della matrice dalla colonna 1 alla 4

x_train = train[0:,0:4] 
print(x_train)

esempio:

[[0.44444442 0.41666666 0.5423728  0.5833333 ]
 [0.30555552 0.5833334  0.11864407 0.04166666]
 [0.02777775 0.41666666 0.05084745 0.04166666]
 [0.94444436 0.41666666 0.86440676 0.9166666 ]
 [0.5833333  0.45833328 0.7627118  0.70833325]
 [0.36111104 0.20833333 0.49152544 0.41666666]
 .
 .
 .
 [0.80555546 0.41666666 0.81355935 0.625     ]
 [0.5833333  0.3333333  0.779661   0.875     ]
 [0.02777775 0.37500003 0.0677966  0.04166666]
 [0.16666664 0.45833328 0.08474576 0.        ]
 [0.0555555  0.12499998 0.05084745 0.08333334]
 [0.6944445  0.3333333  0.6440678  0.5416666 ]
 [0.22222215 0.5833334  0.08474576 0.04166666]
 [0.47222218 0.5833334  0.59322035 0.625     ]
 [0.47222218 0.41666666 0.6440678  0.70833325]
 [0.1944444  0.625      0.05084745 0.08333334]]

ed i dati corrispondenti in uscita ->  dalla colonna 5 alla 7.

y_train = train[0:,4:]
print(y_train)

esempio:

[[0. 1. 0.]
 [1. 0. 0.]
 [1. 0. 0.]
 [0. 0. 1.]
 [0. 0. 1.]
 .
 .
 .
 [1. 0. 0.]
 [1. 0. 0.]
 [1. 0. 0.]
 [0. 1. 0.]
 [1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]
 [1. 0. 0.]]

la stesa cosa deve essere fatta per i dati di test.

x_test = train_val[0:,0:4] 
y_test = train_val[0:,4:]

a questo punto non ci resta che lanciare il traning nel modo seguente:

model.fit(x_train, y_train, validation_data=(x_test,y_test),epochs=1000, batch_size=64,verbose=1)

Per ottenere un buon risultato ho fatto alcuni test ed alla fine ho verificato che con 1000 epoche il risultato dell’accuracy arriva intorno al 98%.

.
.
.
Epoch 988/1000
112/112 [==============================] - 0s 56us/step - loss: 0.0284 - accuracy: 0.9732 - val_loss: 0.0304 - val_accuracy: 0.9474
Epoch 989/1000
112/112 [==============================] - 0s 64us/step - loss: 0.0279 - accuracy: 0.9643 - val_loss: 0.0302 - val_accuracy: 0.9474
Epoch 990/1000
112/112 [==============================] - 0s 57us/step - loss: 0.0354 - accuracy: 0.9554 - val_loss: 0.0302 - val_accuracy: 0.9474
Epoch 991/1000
112/112 [==============================] - 0s 60us/step - loss: 0.0237 - accuracy: 0.9821 - val_loss: 0.0301 - val_accuracy: 0.9474
Epoch 992/1000
112/112 [==============================] - 0s 57us/step - loss: 0.0376 - accuracy: 0.9464 - val_loss: 0.0300 - val_accuracy: 0.9474
Epoch 993/1000
112/112 [==============================] - 0s 67us/step - loss: 0.0309 - accuracy: 0.9643 - val_loss: 0.0300 - val_accuracy: 0.9474
Epoch 994/1000
112/112 [==============================] - 0s 61us/step - loss: 0.0331 - accuracy: 0.9554 - val_loss: 0.0301 - val_accuracy: 0.9474
Epoch 995/1000
112/112 [==============================] - 0s 59us/step - loss: 0.0271 - accuracy: 0.9821 - val_loss: 0.0301 - val_accuracy: 0.9474
Epoch 996/1000
112/112 [==============================] - 0s 61us/step - loss: 0.0194 - accuracy: 0.9821 - val_loss: 0.0300 - val_accuracy: 0.9474
Epoch 997/1000
112/112 [==============================] - 0s 63us/step - loss: 0.0336 - accuracy: 0.9554 - val_loss: 0.0299 - val_accuracy: 0.9474
Epoch 998/1000
112/112 [==============================] - 0s 63us/step - loss: 0.0307 - accuracy: 0.9643 - val_loss: 0.0301 - val_accuracy: 0.9474
Epoch 999/1000
112/112 [==============================] - 0s 70us/step - loss: 0.0295 - accuracy: 0.9732 - val_loss: 0.0303 - val_accuracy: 0.9474
Epoch 1000/1000
112/112 [==============================] - 0s 55us/step - loss: 0.0234 - accuracy: 0.9821 - val_loss: 0.0305 - val_accuracy: 0.9474

Salvataggio del modello

A questo punto il modello è pronto e la rete neurale addestrata, Il prossimo passo sarà quello di imparare a salvare il modello ovvero il nuovo “il cervello” 🙂 per poterlo usare in qualsiasi momento, senza dovere ogni volta far ripartire il traning, cosa dire molto sconveniente nei casi in cui il training possa richiedere anche giorni di calcolo.

Con keras questa operazione è abbastanza semplice.

Il salvataggio di un modello  si può fare in due modi, ovvero possono essere salvati solo il pesi pesi calcolati o l’intera  struttura del modello.

Un primo modo per salvare il nostro modello è quello di  serializzare i dati in un formato json e di  salvarli in  file, nel nostro caso “modello.json”.

model_json = model.to_json()

with open("modello.json", "w") as json_file:
    json_file.write(model_json)
from keras.models import model_from_json

json_file = open('modello.json', 'r')
loaded_model_json = json_file.read()
json_file.close()

loaded_model = model_from_json(loaded_model_json)

Se volessimo salvare invece tutta la struttura nel formato h5  sarà sufficiente eseguire le seguente di righe di codice:

from keras.models import load_model
model.save('modello.h5')
mentre per istanziare nuovamente il modello e caricarlo sarà sufficiente eseguire il seguente script:
from keras.models import load_model
model = load_model('modello.h5')

Se volessimo salvare solo i pesi possiamo procedere nel modo seguente:

from keras.models import load_model
model.save_weights("modello.h5")

mentre per caricare il modello partendo dai pesi possiamo fare nel seguente modo:

from keras.models import load_model
model.load_weights('modello.h5')

nel prossimo articolo vedremo come usare un modello salvato per  effettuare una inferenza sui dati di ingresso.

Se vuoi farmi qualche richiesta o contattarmi per un aiuto riempi il seguente form

    Comments