Cosa è il backbone in una rete neurale? e cosa è il transfer learning?

AI, INTELLIGENZA ARTIFICIALE

In questo articolo affronterò un tema molto utile quando si intende addestrare una rete neurale, partendo da un’altra già pre-addestrata, ovvero, parlerò, della tecnica del  transfer learning, del  concetto di backbone di una rete neurale e  del metodo del  freezing.  Inoltre in coda all’articolo lascerò un esempio di utilizzo di una rete neurale.

Iniziamo, innanzitutto, a dire che il backbone  è in sostanza  l’insieme di strati che vengono  estratti da un’altra  rete pre-addestrata e  che saranno inseriti  in testa alla rete neurale che progetteremo,  con lo scopo  di  creare un modulo specializzato all’estrazione  delle features ( motivo per cui a volte il backbone viene chiamato anche feature-extractor )

L’utilizzo della tecnica del Transfer Learning, ci consente di  evitare di intaccare i pesi della rete di testa ( del backbone) attraverso  una operazione di freezing degli stessi allo scopo di non modificare il comportamento della rete iniziale e consentirci di addestrare solamente il pezzo della rete di nostro interesse .

Questa tecnica è molto importante in quanto è alquanto  evidente che  partendo da una rete neurale già pre-addestrata l’operazione di training può convergere più velocemente e può consentire alla rete neurale  di avere una risposta migliore rispetto ad una  addestrata from scratch con un dataset più piccolo.

In definitiva la pratica del transfer learning consente di riutilizzare parte dei pesi di una rete neurale già addestrata in precedenza  e consente di addestrare solo gli ultimi layer, che di solito sono solitamente quelli dedicati alla classificazione e/o alla regressione delle feature ottenute con i layer precedenti.

Questa operazione ci  consente di ottenere due risultati:

  • riutilizzo di una rete già addestrata  e pronta per  estrarre efficacemente feature dai dati di input
  • limitazione dell’elaborazione ad un numero sensibilmente minore di parametri (corrispondenti agli ultimi layer)

Per capire come procedere nella progettazione ed uso di una rete neurale che implementa  la tecnica del transfer learning, facciamo riferimento allo schema seguente:

Come indicato nella figura di cui sopra, quando si procede al riutilizzo di una rete pre addestrata si  manterremo di quest’ultima  solo gli strati iniziali, andando a ridefinire nella nuova rete solamente gli ultimi livelli che si occuperanno della classificazione.

Il metodo come già detto è abbastanza semplice, sarà sufficiente importare il backbone della rete di interesse, i pesi di testa  saranno etichettati come “read-only“ (mediante operazione di freezing) così da consentire alla nostra rete di calcolare in fase di training solo i parametri corrispondenti agli ultimi layer, velocizzando, in tal modo,  i tempi di addestramento e diminuendo sensibilmente la potenza di elaborazione richiesta.

In Internet è possibile trovare diversi modelli già pre-addestrati su dataset standardizzati, in ogni caso vi riporto una lista di dataset e di modelli  al link si uno dei miei vecchi articoli.

Inoltre vi riporto questa tabella presa dal sito di keras che vi tornerà utile perchè elenca  i modelli che potete gestire facilmente.

Model Size Top-1 Accuracy Top-5 Accuracy Parameters Depth
Xception 88 MB 0.790 0.945 22,910,480 126
VGG16 528 MB 0.713 0.901 138,357,544 23
VGG19 549 MB 0.713 0.900 143,667,240 26
ResNet50 98 MB 0.749 0.921 25,636,712
ResNet101 171 MB 0.764 0.928 44,707,176
ResNet152 232 MB 0.766 0.931 60,419,944
ResNet50V2 98 MB 0.760 0.930 25,613,800
ResNet101V2 171 MB 0.772 0.938 44,675,560
ResNet152V2 232 MB 0.780 0.942 60,380,648
InceptionV3 92 MB 0.779 0.937 23,851,784 159
InceptionResNetV2 215 MB 0.803 0.953 55,873,736 572
MobileNet 16 MB 0.704 0.895 4,253,864 88
MobileNetV2 14 MB 0.713 0.901 3,538,984 88
DenseNet121 33 MB 0.750 0.923 8,062,504 121
DenseNet169 57 MB 0.762 0.932 14,307,880 169
DenseNet201 80 MB 0.773 0.936 20,242,984 201
NASNetMobile 23 MB 0.744 0.919 5,326,716
NASNetLarge 343 MB 0.825 0.960 88,949,818
EfficientNetB0 29 MB 5,330,571
EfficientNetB1 31 MB 7,856,239
EfficientNetB2 36 MB 9,177,569
EfficientNetB3 48 MB 12,320,535
EfficientNetB4 75 MB 19,466,823
EfficientNetB5 118 MB 30,562,527
EfficientNetB6 166 MB 43,265,143
EfficientNetB7 256 MB 66,658,687

A questo punto  proviamo a scrivere un esempio funzionante. Innanzi tutto dovete provvedere a crearvi un vostro dataset, inserendo in una directory che chiamerete ad esempio <dataset> le varie cartelle contenti le immagini di cui volete fare la classificazione.

dataset

——–> oggetto1

——–> oggetto2

Per il nostro esempio utilizzeremo come backbone quello di una rete  MobileNet  pre-addestrata con dataset ImageNet.

Per il progetto di test vi consiglio di usare colab di google in tal caso potrete usare come disco su cui creare i vostri dataset il google google drive.

Vediamo come fare.

Prima di tutto dobbiamo importare le librerie necessarie

from keras.applications import MobileNetV2, mobilenet_v2
from keras.layers import Dense
from keras.models import Model
from keras.preprocessing.image import ImageDataGenerator
from keras.optimizers import Adam
import pathlib
import math
from google.colab import drive
montiamo il disco google drive. In tal caso quando eseguirete il seguente blocco vi verrà chiesto di autenticarvi e di copiare il token nella casella di input.
drive.mount('/content/drive')
importiamo il backbone della rete MobilnetV2
backbone = MobileNetV2(weights='imagenet', include_top=False, pooling="avg")
a questo punto  freeziamo i pesi  o meglio rendiamo  i livelli del backbone non trainable, attraverso  il ciclo for che va ad impostare a False la proprietà trainable di ogni layer del backbone. Questa operazione  rende read-only i parametri dei livelli del backbone e  non verranno modificati durante l’addestramento, in altri termini viene evitata la backpropagation per questi layer.
for l in backbone.layers:
  l.trainable = False
creiamo il pezzo della rete  dense che imparerà a classificare utilizzando come input della rete di coda l’output del backbone, nel nsotro caso utilizaimo un rete dense a 3 livelli hidden con 2 livelli da 1024 neuroni ed una da 512
x = backbone.output
x = Dense(1024, activation='relu')(x)
x = Dense(1024, activation='relu')(x)
x = Dense(512, activation='relu')(x)
inseriamo in coda una  softmax che restituisce la valutazione della classificazione in termini di probabilità in questo caso tra due classi
classificatore = Dense(2, activation='softmax')(x)
a questo punto creiamo il modello globale:
model = Model(inputs=backbone.input, outputs=classificatore)
e se mi va provo ad analizzarne  la struttura:
 model.summary()

Stampando il sommario del nostro modello otterremo qualcosa del genere :

.
.
.

Total params: 5,145,154 
Trainable params: 2,887,170 
Non-trainable params: 2,257,984

Come ci si aspettava, abbiamo abbassato notevolmente, in tal caso del 50% il numero di parametri addestrabili.

A questo punto compiliamo  il modello indicando la loss function e l’ottimizzatore da utilizzare:

model.compile(loss="categorical_crossentropy", optimizer=Adam(lr=0.0001), metrics=["accuracy"])

e generiamo i dataset di traning e di validation a partire dalla nostra dir in cui abbiamo inserito le immagini categorizzate dei due oggetti:

definisco il path del dataset che ho inserito su google drive in una dir a mia scelta
train_dataset_dir = pathlib.Path('drive/MyDrive/DATASET/')
creo i dataset utilizzando ImageDataGenerator . Nel mio caso ho indicato di fare anche un pò di augmetation attraverso l’uso di zoom e flip e di dividere  il dataset in uno di training ed uno di validation con un rapporto 80% – 20% attraverso l’uso del parametro validation_split=0.2;
train_datagen = ImageDataGenerator(preprocessing_function=mobilenet_v2.preprocess_input,
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
validation_split=0.2) # set validation split
costruisco il train_data ed il validation_data da utilizzare per il fitting della rete:
train_data = train_datagen.flow_from_directory(train_dataset_dir, target_size=(224, 224),
color_mode='rgb',batch_size=32, class_mode='categorical',shuffle=True, subset='training')

val_data = train_datagen.flow_from_directory(train_dataset_dir, target_size=(224, 224),
color_mode='rgb',batch_size=32, class_mode='categorical',shuffle=True, subset='validation')

Per l’addestramento provate ad utilizzare  10 epoche e vedrete che probabilmente otterrete una accuracy molto elevata.

model.fit(train_data, steps_per_epoch=math.ceil(train_data.n / train_data.batch_size), epochs=30, verbose=1, validation_data=val_data)

tutto qua.

Comments