Corso di Keras per machine learning – terza lezione: come addestrare una rete neurale e salvare il modello
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')
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.
Sono amante della tecnologia e delle tante sfumature del mondo IT, ho partecipato, sin dai primi anni di università ad importanti progetti in ambito Internet proseguendo, negli anni, allo startup, sviluppo e direzione di diverse aziende; Nei primi anni di carriera ho lavorato come consulente nel mondo dell’IT italiano, partecipando attivamente a progetti nazionali ed internazionali per realtà quali Ericsson, Telecom, Tin.it, Accenture, Tiscali, CNR. Dal 2010 mi occupo di startup mediante una delle mie società techintouch S.r.l che grazie alla collaborazione con la Digital Magics SpA, di cui sono Partner la Campania, mi occupo di supportare ed accelerare aziende del territorio .
Attualmente ricopro le cariche di :
– CTO MareGroup
– CTO Innoida
– Co-CEO in Techintouch s.r.l.
– Board member in StepFund GP SA
Manager ed imprenditore dal 2000 sono stato,
CEO e founder di Eclettica S.r.l. , Società specializzata in sviluppo software e System Integration
Partner per la Campania di Digital Magics S.p.A.
CTO e co-founder di Nexsoft S.p.A, società specializzata nella Consulenza di Servizi in ambito Informatico e sviluppo di soluzioni di System Integration, CTO della ITsys S.r.l. Società specializzata nella gestione di sistemi IT per la quale ho partecipato attivamente alla fase di startup.
Sognatore da sempre, curioso di novità ed alla ricerca di “nuovi mondi da esplorare“.
Comments