Perchè EfficientNet?

Quando si tratta di mostrare come concretamente attuare il Transfer Learning gli esempi che si trovano sui libri, i tutorial online, utilizzano troppo spesso reti convolutive (CNN) ben note quali: VGG16, Inception, Resnet, DenseNet.

Un esempio lo potete trovare nel bel libro di F. Chollet, Deep Learning In Python, pag. 145, ove, appunto, è utilizzata la rete VGG16.

Si tratta di reti sviluppate alcuni anni fa, che hanno anche vinto competizioni quali la ImageNet Large Scale Visual Recognition Competition (ILSRVC). Reti che hanno fatto la storia, dimostrato, concretamente, le enormi potenzialità del DL e dato l'avvio all'enorme potenziale di investimenti, in soldi ed in termini di potenziale umano, in questo campo.

La lettura di tali esempi è senza dubbio istruttiva, ma se vogliamo usare una rete CNN pre-trained per risolvere un problema concreto conviene oggi usare una rete VGG16 o Inception?

Io penso di no, a meno che lo scopo non sia puramente didattico e, nel concreto, non ci si limiti ad addestrare per poche epochs.

La ragione è, se vogliamo, sempre la stessa: nel campo del DL un paio di anni sono tanti, troppi, il progresso corre molto veloce. Vediamone, approfondendo alcuni dettagli, un concreto esempio.

EfficientNet.

Nel 2019 un team di ricercatori ha presentato una famiglia scalabile di reti CNN (è una famiglia, non una sola rete) molto più efficienti (quindi il nome) delle reti classiche su nominate.

Una sintesi al riguardo si trova in: https://ai.googleblog.com/2019/05/efficientnet-improving-accuracy-and.html

L'articolo completo che presenta i risultati è disponibile, come al solito, su arXiv: https://arxiv.org/abs/1905.11946

Un grafico ci aiuta a spiegare sinteticamente i vantaggi ottenibili adottando le EfficientNet:

La curva in rosso mostra accuratezza e numero di parametri per le reti della famiglia EfficientNet. Da confrontare con i punti relativi alle altre reti.

Il grafico mostra due cose:

  1. E' una famiglia scalabile di reti (da B0 a B7); Con un solo parametro possiamo passare ad un membro più potente della famiglia, partendo da B0;
  2. I punti della curva rossa sono sulla sinistra ed in alto: se confrontate con altre CNN le EfficientNet forniscono una top1-accuracy alta con un numero di parametri molto inferiore e, come spiegato nell'articolo, con una capacità computazionale richiesta significativamente inferiore (ovvero: Teraflops in meno).

Quindi, se, come spesso accade, vogliamo costruire un modello DL partendo da una CNN pre-trained, conviene provare subito ad usare una di queste reti, magari partendo con B0 e passando via via alle altre reti della famiglia, valutando se le prestazioni migliorano e non aumenta eccessivamente l'over-fitting.

La maggiore efficienza vuol dire sicuramente tempi ridotti di training. Dunque, lasciamo stare reti come VGG16 alla loro storia.

Librerie.

Come al (mio) solito, fornisco indicazioni relative all'uso di Tensorflow (che conosco meglio, rispetto a PyTorch)

Una libreria molto usata che consente di utilizzare le EfficientNet e scaricare i pesi (sia ottenuti da Imagenet, sia "NoisyStudent") si può installare semplicemente con

pip install efficientnet

(https://pypi.org/project/efficientnet/)

In TensorFlow 2.3 le EfficientNet sono state incluse. E' utile esaminare questo esempio: 

https://keras.io/examples/vision/image_classification_efficientnet_fine_tuning/

Per le reti EfficientNet oltre ai pesi (weights) ottenuti su Imagenet è disponibile un altro set di pesi, ottenuti con l'approccio "noisy student". In alcune situazioni possono dare migliori prestazioni ed una convergenza più rapida, anche se il suggerimento è, sempre, verificate nel vostro, concreto, caso.

Codice.

L'esempio seguente mostra come usare EfficientNet. Per passare da un membro all'altro della famiglia è sufficiente cambiare il valore del parametro ef.

EFNS = [efn.EfficientNetB0, efn.EfficientNetB1, efn.EfficientNetB2, efn.EfficientNetB3, 
        efn.EfficientNetB4, efn.EfficientNetB5, efn.EfficientNetB6, efn.EfficientNetB7]

# as default it used B0

def build_model(dim = 256, ef = 0):
    inp = tf.keras.layers.Input(shape=(*IMAGE_SIZE, 3))
    
    # introdotta la data augmentation come parte del modello
    x = data_augmentation(inp)
    
    base = EFNS[ef](input_shape=(*IMAGE_SIZE, 3), weights='imagenet', include_top = False)
    
    x = base(x)
    x = tf.keras.layers.GlobalAveragePooling2D()(x)
    x = tf.keras.layers.Dense(NUM_CLASSES, activation='softmax')(x)
    
    model = tf.keras.Model(inputs = inp,outputs = x)
    
    opt = tf.keras.optimizers.Adam(learning_rate = 0.001)
    # loss = tf.keras.losses.BinaryCrossentropy(label_smoothing=0) 
    
    model.compile(optimizer = opt, loss = 'sparse_categorical_crossentropy', metrics=['accuracy'])
    
    return model

Quello che è impressionante (è il Transfer Learning) è che un modello, con poche righe di codice applicativo, si traduce nell'uso di una rete con decine di milioni di parametri, pre-addestrata e che può contribuire in campi complessi (es: Computer Vision in campo medico) ad ottenere accuratezze paragonabili e superiori alle "human level performance".

Un esempio completo di codice è disponibile su uno dei miei repository gitbub: 

https://github.com/luigisaetta/diabetic-retinopathy/blob/master/Diabetic-Retinopathy-512-Classification-Copy6.ipynb

Una nota sul libro Deep Learning in Python.

(aggiunta dopo una prima rilettura del testo di questo post, per rendere merito al lavoro egregio e difficile che fanno le persone come Chollet).

Il libro di Chollet, nella versione 1, è del 2018. E' il più bel libro sul tema DL & Python che abbia mai letto. Del resto l'autore è il progettista, colui che ha inventato Keras. 

Ma il DL è un regno in cui "la bellezza dura relativamente poco" e due anni sono tanti. Chollet sta scrivendo la versione 2 (in MEAP su Manning), ma non è ancora terminata. Nel frattempo, l'innovazione, con i contributi anche suoi, la leggiamo su https://keras.io