Introduzione.

In questa nota, che mi propongo di aggiornare periodicamente, voglio tener traccia di una serie di tecniche utili che ho scoperto e appreso durante i miei studi sulla Specializzazione in DL di Coursera.

Indice:

 

Codice per disegnare le curve di loss ed accuracy.

Le curve in oggetto sono importanti per valutare l'andamento del training nel caso di un problema di classificazione binaria.

hist = history.history
x_arr = np.arange(len(hist['loss'])) + 1

fig = plt.figure(figsize=(12, 4))
ax = fig.add_subplot(1, 2, 1)
ax.plot(x_arr, hist['loss'], '-o', label='Train loss')
ax.plot(x_arr, hist['val_loss'], '--<', label='Validation loss')
ax.legend(fontsize=15)
ax.set_xlabel('Epoch', size=15)
ax.set_ylabel('Loss', size=15)

ax = fig.add_subplot(1, 2, 2)
ax.plot(x_arr, hist['accuracy'], '-o', label='Train acc.')
ax.plot(x_arr, hist['val_accuracy'], '--<', label='Validation acc.')
ax.legend(fontsize=15)
ax.set_xlabel('Epoch', size=15)
ax.set_ylabel('Accuracy', size=15)
plt.show()

Un esempio del risultato è nella figura seguente:

 

Utilizzo di callbacks in Keras

Le callback sono funzioni che possono essere eseguite in momenti specifici del ciclo di training, ad esempio alla fine di ogni epoch.

Io ho trovato molto utili le seguenti callback, da eseguire ad ogni epoch:

  • Checkpoint: consente di salvare il modello, con i suoi pesi, al verificarsi di specificate condizioni
  • EarlyStopping: consente di fermare il training se le prestazioni del modello non migliorano, dopo un numewro specificato di epochs
  • Reduce Learning Rate on Plateau: consente di attuare una strategia flessibile di training, riducendo il learning rate se le prestazioni del modello non migliorano (plateau)
  • Tensorboard: per usare Tensorboard

Riporto, di seguito, esempi di codice in Python che mostrano come utilizzo usualmente le tre callback indicate

Checkpoint

filepath="weights-{epoch:02d}-{val_accuracy:.2f}.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='val_accuracy', verbose=1, save_best_only=True, mode='max')

La callback salva in un file h5 il modello ogni volta che la val_accuracy migliora. In questo modo possiamo poi scegliere di caricare i pesi che hanno prodotto la migliore val_accuracy ottenuta nel corso del training.

Il modello può essere poi caricato, in un qualsiasi momento, con

model2.load_weights(FILE_BEST)

model2.compile(optimizer="adam", loss="binary_crossentropy", metrics=['accuracy'])

(si noti che dopo aver caricato i pesi del modello, deve essere compilato).

EarlyStopping

earl = EarlyStopping(monitor='val_accuracy', verbose=1, patience=10)

La callback ferma il training se, per 10 epoch consecutive, la metrica sotto monitoraggio (val_accuracy) non migliora.

Reduce Learning Rate on Plateau

rlro = ReduceLROnPlateau(monitor='val_accuracy', factor=0.5, patience=5, verbose=1)

La callback riduce il learning rate, moltiplicandolo per il fattore specificato (nell'esempio dimezza), se la metrica specificata non migliora per un numero di epoch consecutive pari a patience.

Tensorboard

tsboard = TensorBoard(log_dir="log_dir", histogram_freq=1, embeddings_freq=1)

Dopo aver definito le callbacks si deve inserirle in una list e passare la list all'invocazione del metodo fit

callbacks_list = [checkpoint, rlro, earl]

history = model2.fit(train_sequences_padded, train_labels_final, validation_split=0.2, batch_size=1024, epochs=100, callbacks=callbacks_list)

Continuare il training dal punto in cui è terminato.

Se esaminando le curve dell'accuracy e loss decidiamo che è meglio far proseguire il training per ulteriori epoch, possiamo farlo con il codice seguente:

history = model.fit(ds_train, validation_data=ds_valid, epochs=30, initial_epoch=20,
steps_per_epoch=steps_per_epoch)

con 

hist2 = history.history
x_arr = np.arange(len(hist['loss'] + hist2['loss']))

otteniamo i dati per poter mettere in un unico grafico i risultati delle prime 20 e successive 10 epoch

fig = plt.figure(figsize=(12, 4))
ax = fig.add_subplot(1, 2, 1)
ax.plot(x_arr, hist['loss']+hist2['loss'], '-o', label='Train Loss')
ax.plot(x_arr, hist['val_loss']+hist2['val_loss'],'--<', label='Validation Loss')
ax.legend(fontsize=15)


ax = fig.add_subplot(1, 2, 2)
ax.plot(x_arr, hist['accuracy']+hist2['accuracy'], '-o', label='Train Acc.')
ax.plot(x_arr, hist['val_accuracy']+hist2['val_accuracy'], '--<', label='Validation Acc.')
ax.legend(fontsize=15)
plt.show()

Utilizzo di logits in classificazione binaria.

Nel testo Python Machine Learning di S. Raschka et al., nel capitolo 15, è presentata una tecnica interessante che, nel caso di classificazione binaria, non usa per l'ultimo layer Dense come funzione di attivazione una sigmoide.

La tecnica fa si che la CNN produca in output non la probabilità della classe positiva ma il logit. La giustrificazione è che il modello in questo modo ha una migliore stabilitrà numerica ed arriva prima a convergenza.

La tecnica prevede che nell'ultimo layer non si metta nessuna funzione di attivazione

model.add(tf.keras.layers.Dense(1, activation=None))

Nella compilazione però è importante che si specifichi BinaryCrossEntropy, passando il parametro fromLogits=True

model.compile(optimizer=tf.keras.optimizers.Adam(), loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics=['accuracy'])

In questo modo in fase di backpropagation nel calcolo della loss si tiene conto del fatto che in output si hanno le logits.

Ovviamente nel momento in cui si fa inferenza si deve tener conto che la predict non produce in output probabilità e quinsdi, eventualmente, si deve applicare la sigmoide.

pred_logits = model.predict(ds.batch(10))
probas = tf.sigmoid(pred_logits)
probas = probas.numpy().flatten()*100

 

Esempi interessanti di applicazioni del DL.

Image Recognition.

Gender classification.

Nel libro di S. Raschka, nel capitolo 15, è mostrato come, utilizzando il dataset CelebA, parte dei tensorflow_ds, è possibile costruire ed addestrare una rete CNN che è in grado di riconoscere il gender di una persona raffigurata nella foto.

E' interessante che la rete non è molto profonda (4 layer CN + pooling) ed, utilizzando una GPU, è possibile ottenere, effettuando il training sull'intero dataset (+ augmentation) un'accuratezza superiore al 97%. Usando una GPU il tempo richiesto per il training è dell'ordine di 20 min.

(Ci vuole un tempo adeguato per scaricare e decomprimere il dataset, che occupa dopo decompressione circa 4.5 GB).

Il dataset specifica, oltre al gender, per ciascuna immagine numerosi altri attributi e quindi può essere preso di esempio per altri tipi di classificazione su foto di persone.

Penso sia opportuno notare che, se ci si vuole cimentare nell'addestramento di CNN per questi scopi, senza usare pretrained network, è indispensabile l'utilizzo di un ambiente (ad esempio VM in Cloud) con GPU.

YOLO V3.

YOLO è un algoritmo di object detection one-shot. E' nato per essere molto veloce, in modo da poter essere usato realtime sui filmati e quindi la sua implementazione nativa non è in Python.

In molti si sono cimentati nel riprodurre la versione V3 usando Keras.

Nell'articolo alla seguente URL sono descritti i dettagli per la sua implementazione, che io ho riprodotto per Tensorflow2: YOLO V3 in Keras