Una guida pratica al trasferimento di apprendimento utilizzando PyTorch.

A practical guide to transfer learning using PyTorch.

In questo articolo, impareremo ad adattare modelli pre-addestrati a compiti di classificazione personalizzati utilizzando una tecnica chiamata transfer learning. Lo dimostreremo per un compito di classificazione delle immagini utilizzando PyTorch e comparando il transfer learning su 3 modelli pre-addestrati, Vgg16, ResNet50 e ResNet152.

Scritto in collaborazione con Naresh e Gaurav.

Questo articolo coprirà il cosa, il perché e il come del transfer learning.

  • Cosa è il transfer learning
  • Perché dovresti utilizzare il transfer learning
  • Come puoi utilizzare il transfer learning su un vero compito di classificazione

In particolare, esamineremo i seguenti aspetti del transfer learning.

  • La motivazione alla base dell’idea del transfer learning e i suoi vantaggi.
  • Sviluppare un’intuizione per la selezione del modello base. ( quaderno )
  • Discutere le diverse scelte e i trade-off effettuati lungo il percorso.
  • Implementazione di un compito di classificazione delle immagini con PyTorch. ( quaderno )
  • Confronto delle prestazioni di vari modelli base.
  • Risorse per saperne di più sul transfer learning e sullo stato dell’arte attuale.

Il transfer learning è un campo vasto e in continua crescita e questo articolo copre solo alcuni dei suoi aspetti. Tuttavia, ci sono molte comunità online di deep learning che discutono il transfer learning. Ad esempio, qui c’è un buon articolo su come possiamo sfruttare il transfer learning per raggiungere punteggi più alti rispetto all’addestramento dei modelli da zero.

Pubblico e prerequisiti previsti

  • Conosci i concetti di base del machine learning (ML) come la definizione e l’addestramento dei modelli di classificazione.
  • Conosci PyTorch e torchvision.

Nella sezione successiva, presenteremo formalmente il transfer learning e lo spiegheremo con esempi.

Cos’è il transfer learning?

Dal seguente sito,

“Il transfer learning è un metodo di apprendimento automatico in cui un modello sviluppato per un compito viene riutilizzato come punto di partenza per un modello su un secondo compito”.

Un modello di deep learning è una rete di pesi i cui valori vengono ottimizzati utilizzando una funzione di perdita durante il processo di addestramento. I pesi della rete vengono di solito inizializzati casualmente prima dell’inizio del processo di addestramento. Nel transfer learning, utilizziamo un modello pre-addestrato che è stato addestrato su un compito correlato. Questo ci fornisce un insieme di pesi iniziali che probabilmente si comporteranno meglio rispetto ai pesi inizializzati casualmente. Ottimizziamo ulteriormente i pesi pre-addestrati per il nostro compito specifico.

Jeremy Howard (da fast.ai) afferma che,

“Ovunque sia possibile, si dovrebbe mirare ad iniziare l’addestramento della propria rete neurale con un modello pre-addestrato e affinarlo. Non si vuole davvero iniziare con pesi casuali, perché ciò significa che si sta partendo con un modello che non sa fare assolutamente nulla! Con la preformazione, si può utilizzare il 1000x meno dati rispetto a partire da zero”.

Di seguito, vedremo come si può pensare al concetto di transfer learning in relazione agli esseri umani.

Analogia umana per il transfer learning

  • Addestramento del modello: Dopo che un bambino è nato, ci vuole un po’ di tempo per imparare a stare in piedi, a bilanciarsi e camminare. Durante questo periodo, essi attraversano la fase di costruzione dei muscoli fisici e il loro cervello impara a comprendere e interiorizzare le abilità per stare in piedi, bilanciarsi e camminare. Fanno diversi tentativi, alcuni con successo e altri con insuccesso, per raggiungere una fase in cui riescono a stare in piedi, a bilanciarsi e a camminare con una certa costanza. Questo è simile all’addestramento di un modello di deep learning che richiede molto tempo (epoche di addestramento) per imparare un compito generico (come la classificazione di un’immagine come appartenente ad una delle 1000 classi di ImageNet) quando viene addestrato su quel compito.
  • Transfer learning: Un bambino che ha imparato a camminare trova molto più facile imparare abilità avanzate correlate come saltare e correre. Il Transfer Learning è paragonabile a questo aspetto dell’apprendimento umano in cui viene sfruttato un modello pre-addestrato che ha già imparato abilità generiche per addestrare efficacemente per altri compiti correlati.

Ora che abbiamo costruito una comprensione intuitiva del transfer learning e un’analoga umana con l’apprendimento, diamo un’occhiata a perché si dovrebbe utilizzare il transfer learning per i modelli di machine learning.

Perché dovrei usare il transfer learning?

Molti compiti di visione artificiale come la classificazione delle immagini, la segmentazione delle immagini, la localizzazione o la rilevazione degli oggetti differiscono solo negli oggetti specifici che stanno classificando, segmentando o rilevando. I modelli addestrati su questi compiti hanno imparato le caratteristiche degli oggetti nel loro set di dati di addestramento. Pertanto, possono essere facilmente adattati a compiti correlati. Ad esempio, un modello addestrato per identificare la presenza di una macchina in un’immagine potrebbe essere affinato per identificare un gatto o un cane.

Il principale vantaggio del transfer learning è la capacità di permetterti di ottenere una maggiore accuratezza nei tuoi compiti. Possiamo suddividere i suoi vantaggi come segue:

  • Efficienza di training: Quando si parte da un modello pre-addestrato che ha già appreso le caratteristiche generali dei dati, è sufficiente aggiustare il modello per il tuo compito specifico, il che può essere fatto molto più rapidamente (cioè con meno epoche di training).
  • Accuratezza del modello: Utilizzando il transfer learning puoi ottenere un significativo miglioramento delle prestazioni rispetto all’addestramento di un modello da zero usando la stessa quantità di risorse. È importante scegliere il giusto modello pre-addestrato per il transfer-learning per il tuo compito specifico.
  • Dimensione dei dati di training: Siccome un modello pre-addestrato avrebbe già imparato a identificare molte delle caratteristiche che si sovrappongono con le tue caratteristiche specifiche del compito, puoi addestrare il modello pre-addestrato con meno dati specifici del dominio. Questo è utile se non hai molte etichette per il tuo compito specifico.

Quindi, come si fa a fare il transfer learning nella pratica? La sezione successiva implementa il transfer learning in PyTorch per un compito di classificazione di fiori.

Transfer Learning con PyTorch

Per eseguire il transfer learning con PyTorch, prima dobbiamo selezionare un dataset e un modello di visione pre-addestrato per la classificazione di immagini. Questo articolo si concentra sull’utilizzo di torch-vision (una libreria di dominio utilizzata con PyTorch). Capiremo dove trovare tali modelli pre-addestrati e dataset.

Dove trovare modelli di visione pre-addestrati per la classificazione di immagini?

Ci sono molti siti web che forniscono modelli di classificazione di immagini pre-addestrati di alta qualità. Ad esempio:

  1. Torchvision
  2. Modelli di immagini PyTorch

Per scopi di questo articolo, useremo modelli pre-addestrati da torchvision. Vale la pena conoscere un po’ su come questi modelli sono stati addestrati. Esploreremo questa domanda successivamente!

Su quali dataset sono pre-addestrati i modelli torchvision?

Per i compiti relativi alla visione che coinvolgono immagini, di solito i modelli torchvision sono pre-addestrati sul dataset ImageNet. Il sottoinsieme più popolare di ImageNet utilizzato dai ricercatori e per il pre-addestramento dei modelli di visione contiene circa 1,2 milioni di immagini in 1000 classi. La classificazione di ImageNet è utilizzata come compito di pre-addestramento a causa di:

  1. La sua pronta disponibilità per la comunità di ricerca
  2. La ampiezza e la varietà di immagini che contiene
  3. Il suo utilizzo da parte di vari ricercatori – rendendolo attraente per confrontare i risultati utilizzando un comune denominatore della classificazione Imagenet 1k

Puoi leggere di più sulla storia della sfida ImageNet, sugli antefatti storici e sulle informazioni sul dataset completo in questa pagina di Wikipedia.

Considerazioni legali nell’uso di modelli pre-addestrati

ImageNet è rilasciato solo per scopi di ricerca non commerciali (https://image-net.org/download). Pertanto, non è chiaro se si possano legalmente utilizzare i pesi di un modello pre-addestrato su ImageNet per scopi commerciali. Se hai intenzione di farlo, consulta un avvocato.

Ora che sappiamo dove possiamo trovare i modelli pre-addestrati che utilizzeremo per il transfer learning, diamo un’occhiata a dove possiamo procurarci il dataset che desideriamo utilizzare per il nostro compito di classificazione personalizzato.

Dataset: Oxford Flowers 102

Utilizzeremo il dataset Flowers 102 per illustrare il transfer learning utilizzando PyTorch. Addestreremo un modello per classificare le immagini nel dataset Flowers 102 in una delle 102 categorie. Si tratta di un problema di categorizzazione multi-classe (singola-etichetta) in cui le classi predette sono mutualmente esclusive. Utilizzeremo Torchvision per questo compito, poiché ci fornisce già questo dataset da utilizzare.

Il dataset Flowers 102 è stato ottenuto dal Visual Geometry Group di Oxford. Si prega di vedere la pagina per i termini di licenza per l’uso del dataset.

Successivamente, diamo un’occhiata alle fasi principali coinvolte in questo processo.

Come funziona il transfer learning?

Il transfer learning per i compiti di classificazione delle immagini può essere visto come una sequenza di tre fasi come mostrato in Figura 1. Queste fasi sono le seguenti:

  1. Sostituzione del livello di classificazione: In questa fase, identifichiamo e sostituiamo l’ultimo “classification head” del nostro modello pre-addestrato con il nostro “classification head” che ha il giusto numero di feature di output (102 in questo esempio).
  2. Estrazione delle caratteristiche: In questa fase, congeliamo (rendiamo non addestrabili) tutti i livelli del modello tranne il nuovo livello di classificazione aggiunto, e addestriamo solo questo nuovo livello aggiunto.
  3. Fine-tuning: In questa fase, sblocciamo alcuni sottoinsiemi dei livelli del modello (sbloccare un livello significa renderlo addestrabile). In questo articolo, sbloccheremo tutti i livelli del modello e li addestreremo come faremmo con qualsiasi modello PyTorch di Machine Learning (ML).

Ognuna di queste fasi ha molti dettagli e sfumature aggiuntive di cui dobbiamo essere consapevoli e preoccuparci. Entreremo presto in questi dettagli. Per ora, approfondiamo due delle fasi chiave, ovvero l’estrazione delle caratteristiche e il fine-tuning di seguito.

Estrazione delle caratteristiche e fine-tuning

Puoi trovare ulteriori informazioni sull’estrazione delle caratteristiche e sul fine-tuning qui.

  1. Cosa differenzia l’estrazione delle caratteristiche dal fine-tuning nel transfer learning?
  2. Learning without forgetting

Le diagrammi di seguito illustrano l’estrazione delle caratteristiche e il fine-tuning visivamente.

Ora che abbiamo sviluppato una buona comprensione del compito di classificazione personalizzato, del modello pre-addestrato che utilizzeremo per questo compito e di come funziona il transfer learning, vediamo qualche codice concreto che esegue il transfer learning.

Mostrami il codice

In questa sezione imparerai concetti come l’analisi del modello esplorativo, la selezione del modello iniziale, come definire un modello, implementare le fasi di transfer learning (discusse sopra) e come prevenire l’overfitting. Discuteremo la suddivisione tra train/val/test per questo dataset e interpreteremo i risultati.

Il codice completo per questo esperimento può essere trovato qui (classificazione Flowers102 usando modelli pre-addestrati). La sezione sull’analisi del modello esplorativo si trova in un notebook separato.

Analisi del modello esplorativo

Similmente all’analisi dei dati esplorativi in data science, il primo passaggio nel transfer-learning è l’analisi del modello esplorativo. In questo passaggio, esploriamo tutti i modelli pre-addestrati disponibili per i compiti di classificazione delle immagini e determiniamo come ogni modello è strutturato.

In generale, è difficile sapere quale modello funzionerà meglio per il nostro compito, quindi non è raro provare alcuni modelli che sembrano promettenti o applicabili alla nostra situazione. In questo scenario ipotetico, supponiamo che la dimensione del modello non sia importante (non vogliamo distribuire questi modelli su dispositivi mobili o dispositivi edge simili). Guarderemo prima l’elenco dei modelli di classificazione pre-addestrati disponibili in torchvision.

classification_models = torchvision.models.list_models(module=torchvision.models)

print(len(classification_models), "modelli di classificazione:", classification_models)

Stampa

80 modelli di classificazione: ['alexnet', 'convnext_base', 'convnext_large', 'convnext_small', 'convnext_tiny', 'densenet121', 'densenet161', 'densenet169', 'densenet201', 'efficientnet_b0', 'efficientnet_b1', 'efficientnet_b2', 'efficientnet_b3', 'efficientnet_b4', 'efficientnet_b5', 'efficientnet_b6', 'efficientnet_b7', 'efficientnet_v2_l', 'efficientnet_v2_m', 'efficientnet_v2_s', 'googlenet', 'inception_v3', 'maxvit_t', 'mnasnet0_5', 'mnasnet0_75', 'mnasnet1_0', 'mnasnet1_3', 'mobilenet_v2', 'mobilenet_v3_large', 'mobilenet_v3_small', 'regnet_x_16gf', 'regnet_x_1_6gf', 'regnet_x_32gf', 'regnet_x_3_2gf', 'regnet_x_400mf', 'regnet_x_800mf', 'regnet_x_8gf', 'regnet_y_128gf', 'regnet_y_16gf', 'regnet_y_1_6gf', 'regnet_y_32gf', 'regnet_y_3_2gf', 'regnet_y_400mf', 'regnet_y_800mf', 'regnet_y_8gf', 'resnet101', 'resnet152', 'resnet18', 'resnet34', 'resnet50', 'resnext101_32x8d', 'resnext101_64x4d', 'resnext50_32x4d', 'shufflenet_v2_x0_5', 'shufflenet_v2_x1_0', 'shufflenet_v2_x1_5', 'shufflenet_v2_x2_0', 'squeezenet1_0', 'squeezenet1_1', 'swin_b', 'swin_s', 'swin_t', 'swin_v2_b', 'swin_v2_s', 'swin_v2_t', 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', 'vgg19', 'vgg19_bn', 'vit_b_16', 'vit_b_32', 'vit_h_14', 'vit_l_16', 'vit_l_32', 'wide_resnet101_2', 'wide_resnet50_2']

Wow! Questa è una lista piuttosto ampia di modelli tra cui scegliere! Se ti senti confuso, non preoccuparti: nella sezione successiva, esamineremo i fattori da considerare quando si sceglie l’insieme iniziale di modelli per eseguire il transfer learning.

Selezione del modello iniziale

Ora che abbiamo una lista di 80 modelli candidati tra cui scegliere, dobbiamo ridurla a una manciata di modelli su cui possiamo eseguire esperimenti. La scelta della spina dorsale del modello pre-addestrato è un iper-parametro e possiamo (e dovremmo) esplorare più opzioni eseguendo esperimenti per vedere quale funziona meglio. Eseguire gli esperimenti è costoso e richiede tempo, ed è improbabile che saremo in grado di provare tutti i modelli, motivo per cui cerchiamo di ridurre l’elenco a 3-4 modelli per iniziare.

Abbiamo deciso di iniziare con le seguenti spalle di modelli preaddestrati.

  1. Vgg16: 135M parametri
  2. ResNet50: 23M parametri
  3. ResNet152: 58M parametri

Ecco come / perché abbiamo scelto questi 3 per iniziare.

  1. Non siamo vincolati dalle dimensioni del modello o dalla latenza dell’elaborazione, quindi non dobbiamo trovare i modelli che sono super efficienti. Se desideri uno studio comparativo di vari modelli di visione per dispositivi mobili, leggi il paper intitolato “Comparison and Benchmarking of AI Models and Frameworks on Mobile Devices”.
  2. I modelli che scegliamo sono abbastanza popolari nella comunità di ML di visione e tendono ad essere buone scelte go-to per compiti di classificazione. Potresti usare il conteggio delle citazioni per i documenti su questi modelli come proxy decenti per capire quanto efficaci potrebbero essere questi modelli. Tuttavia, per favore sii consapevole di un potenziale bias in cui i documenti su modelli come AlexNet che esistono da tempo avranno più citazioni anche se non li useresti per alcun serio compito di classificazione come scelta predefinita.
  3. Anche all’interno delle architetture dei modelli, tendono ad esserci molti sapori o dimensioni di modelli. Ad esempio, EfficientNet viene ridotto a dimensioni denominate B0-B7. Si prega di fare riferimento ai documenti sui modelli specifici per i dettagli su cosa significano queste riduzioni.

Conteggio delle citazioni di vari documenti su modelli di classificazione preaddestrati disponibili in torchvision.

  1. Resnet: 165k
  2. AlexNet: 132k
  3. Vgg16: 102k
  4. MobileNet: 19k
  5. Vision Transformers: 16k
  6. EfficientNet: 12k
  7. ShuffleNet: 6k

Se vuoi leggere di più sui fattori che possono influire sulla scelta del modello preaddestrato, leggi i seguenti articoli:

  1. 4 modelli CNN preaddestrati da utilizzare per la visione artificiale con Transfer Learning
  2. Come scegliere il miglior modello preaddestrato per la tua Convolutional Neural Network?
  3. Analisi delle prestazioni di riferimento di architetture rappresentative di reti neurali profonde

Scopriamo le testate di classificazione per questi modelli.

vgg16 = torchvision.models.vgg16_bn(weights=None)

resnet50 = torchvision.models.resnet50(weights=None)

resnet152 = torchvision.models.resnet152(weights=None)

print(“vgg16 \n “, vgg16.classifier)

print(“resnet50 \n “, resnet50.fc)

print(“resnet152 \n “, resnet152.fc)

vgg16

Sequential(

(0): Linear(in_features=25088, out_features=4096, bias=True)

(1): ReLU(inplace=True)

(2): Dropout(p=0.5, inplace=False)

(3): Linear(in_features=4096, out_features=4096, bias=True)

(4): ReLU(inplace=True)

(5): Dropout(p=0.5, inplace=False)

(6): Linear(in_features=4096, out_features=1000, bias=True)

)

resnet50

Linear(in_features=2048, out_features=1000, bias=True)

resnet152

Linear(in_features=2048, out_features=1000, bias=True)

Puoi trovare il notebook completo per l’analisi del modello esplorativo qui.

Dato che eseguiremo esperimenti su 3 modelli pre-addestrati e faremo transfer learning su ciascuno di essi separatamente, definiamo alcune astrazioni e classi che ci aiuteranno ad eseguire e tenere traccia di questi esperimenti.

Definire un modello PyTorch per avvolgere i modelli pre-addestrati

Per consentire una facile esplorazione, definiremo un modello PyTorch chiamato Flowers102Classifier e useremo questo durante tutto l’esercizio. Aggiungeremo progressivamente funzionalità a questa classe finché non raggiungeremo il nostro obiettivo finale. Il notebook completo per il transfer learning per la classificazione Flowers 102 può essere trovato qui.

Le sezioni di seguito approfondiranno ogni uno dei passaggi meccanici necessari per eseguire il transfer learning.

Sostituire la vecchia testa di classificazione con una nuova

La vecchia testa di classificazione per ciascuno di questi modelli, pre-addestrati sul compito di classificazione ImageNet, ha 1000 funzionalità di output. Il nostro compito personalizzato per la classificazione dei fiori ha 102 funzionalità di output. Pertanto, dobbiamo sostituire la testa (strato) di classificazione finale con una nuova che ha 102 funzionalità di output.

Il costruttore per la nostra classe includerà il codice che carica il modello pre-addestrato di interesse da torchvision utilizzando i pesi pre-addestrati e sostituirà la testa di classificazione con una testa di classificazione personalizzata per 102 classi.

def __init__(self, backbone, load_pretrained):
    super().__init__()
    assert backbone in backbones
    self.backbone = backbone
    self.pretrained_model = None
    self.classifier_layers = []
    self.new_layers = []

    if backbone == "resnet50":
        if load_pretrained:
            self.pretrained_model = torchvision.models.resnet50(
                weights=torchvision.models.ResNet50_Weights.IMAGENET1K_V2
            )
        else:
            self.pretrained_model = torchvision.models.resnet50(weights=None)
        # end if

        self.classifier_layers = [self.pretrained_model.fc]
        # Sostituisce l'ultimo strato con un classificatore per 102 classi per il dataset Flowers 102.
        self.pretrained_model.fc = nn.Linear(
            in_features=2048, out_features=102, bias=True
        )
        self.new_layers = [self.pretrained_model.fc]
    elif backbone == "resnet152":
        if load_pretrained:
            self.pretrained_model = torchvision.models.resnet152(
                weights=torchvision.models.ResNet152_Weights.IMAGENET1K_V2
            )
        else:
            self.pretrained_model = torchvision.models.resnet152(weights=None)
        # end if

        self.classifier_layers = [self.pretrained_model.fc]
        # Sostituisce l'ultimo strato con un classificatore per 102 classi per il dataset Flowers 102.
        self.pretrained_model.fc = nn.Linear(
            in_features=2048, out_features=102, bias=True
        )
        self.new_layers = [self.pretrained_model.fc]
    elif backbone == "vgg16":
        if load_pretrained:
            self.pretrained_model = torchvision.models.vgg16_bn(
                weights=torchvision.models.VGG16_BN_Weights.IMAGENET1K_V1
            )
        else:
            self.pretrained_model = torchvision.models.vgg16_bn(weights=None)
        # end if

        self.classifier_layers = [self.pretrained_model.classifier]
        # Sostituisce l'ultimo strato con un classificatore per 102 classi per il dataset Flowers 102.
        self.pretrained_model.classifier[6] = nn.Linear(
            in_features=4096, out_features=102, bias=True
        )
        self.new_layers = [self.pretrained_model.classifier[6]]

Dato che eseguiremo estrazione di funzionalità seguita da sintonizzazione fine, salveremo i nuovi strati aggiunti nella lista self.new_layers. Ciò ci aiuterà a impostare i pesi di quegli strati come addestrabili o non addestrabili a seconda di ciò che stiamo facendo.

Ora che abbiamo sostituito la vecchia testa di classificazione con una nuova testa di classificazione che ha pesi inizializzati casualmente, dovremo addestrare quei pesi in modo che il modello possa effettuare previsioni accurate. Questo include estrazione di funzionalità e sintonizzazione fine e lo esamineremo nel prossimo passaggio.

Transfer Learning (parametri addestrabili e tassi di apprendimento)

Il transfer learning comporta l’esecuzione dell’estrazione di funzionalità e della sintonizzazione fine in quell’ordine specifico. Esaminiamo più da vicino perché devono essere eseguiti in quell’ordine e come possiamo gestire i parametri addestrabili per le diverse fasi del transfer learning.

Estrazione di funzionalità: impostiamo requires_grad su False per i pesi in tutti gli strati del modello e impostiamo requires_grad su True solo per i nuovi strati aggiunti.

Alleniamo il nuovo/i layer per 16 epoche con un tasso di apprendimento di 1e-3. Questo assicura che i nuovi layer siano in grado di regolare e adattare i loro pesi ai pesi nella parte estrattore di funzionalità della rete. È importante congelare il resto dei layer nella rete e addestrare solo il nuovo/i layer, in modo da non far dimenticare alla rete ciò che ha già imparato. Se non congeliamo i layer precedenti, finiremo per riformare i pesi spazzatura che sono stati inizializzati casualmente quando abbiamo aggiunto la nuova testa di classificazione.

Accordatura fine: Impostiamo requires_grad su True per i pesi di tutti i layer del modello. Addestriamo l’intera rete per 8 epoche. Tuttavia, adottiamo una strategia di tasso di apprendimento differenziale in questo caso. Decrementiamo il tasso di apprendimento (LR) in modo che il LR diminuisca man mano che ci muoviamo verso i layer di input (lontano dalla testa di classificazione di output). Decrementiamo il tasso di apprendimento mentre ci muoviamo verso l’alto del modello verso i layer iniziali del modello perché quei layer iniziali hanno appreso caratteristiche di base sull’immagine, che sarebbero comuni per la maggior parte dei compiti di intelligenza artificiale visiva. Pertanto, i layer iniziali sono addestrati con un LR molto basso per evitare di disturbare ciò che hanno imparato. Man mano che ci muoviamo verso il basso del modello verso la testa di classificazione, il modello sta imparando qualcosa di specifico per il compito, quindi ha senso addestrare quei layer successivi con un LR più alto. Qui si possono adottare diverse strategie, e nel nostro caso, ne utilizziamo 2 diverse per illustrare l’efficacia di entrambe.

  1. VGG16: Per la rete vgg16, decrementiamo linearmente il tasso di apprendimento LR = 1e-4 a LR = 1e-7 (1000 volte inferiore rispetto al LR del layer di classificazione). Poiché ci sono 44 layer nella fase di estrazione delle funzionalità, a ciascun layer viene assegnato un LR che è (1e-7 – 1e-4) / 44 = 2,3e-6 inferiore rispetto al layer precedente.
  2. ResNet: per la rete ResNet (50/152), decrementiamo il LR in modo esponenziale a partire da LR = 1e-4. Riduciamo il LR di 3 volte per ogni layer che ci muoviamo verso l’alto.

Il codice per congelare i layer sia per l’estrazione delle funzionalità che per l’accuratazione fine è mostrato nella funzione chiamata fine_tune() di seguito.

def fine_tune(self, what: FineTuneType):
    # Il parametro requires_grad controlla se questo parametro è
    # addestrabile durante l'addestramento del modello.
    m = self.pretrained_model
    for p in m.parameters():
        p.requires_grad = False
    if what is FineTuneType.NEW_LAYERS:
        for l in self.new_layers:
            for p in l.parameters():
                p.requires_grad = True
    elif what is FineTuneType.CLASSIFIER:
        for l in self.classifier_layers:
            for p in l.parameters():
                p.requires_grad = True
    else:
        for p in m.parameters():
            p.requires_grad = True

Snippet di codice: Congelamento e scongelamento dei parametri utilizzando requires_grad durante la fase di estrazione delle funzionalità (NEW_LAYERS) e di accordatura fine (ALL).

In PyTorch, il modo per impostare LRs differenziali per ogni layer è specificare i pesi che necessitano di quel LR all’ottimizzatore che verrà utilizzato durante il trasferimento dell’apprendimento. Nel nostro notebook, utilizziamo l’ottimizzatore Adam. Il metodo get_optimizer_params() di seguito ottiene i parametri dell’ottimizzatore da passare all’Adam (o altro) ottimizzatore che utilizzeremo.

def get_optimizer_params(self):
    """Questo metodo viene utilizzato solo durante l'accuratazione fine del modello
    quando abbiamo bisogno di impostare un tasso di apprendimento (LR) in decrescita lineare o esponenziale
    per i layer nel modello. Decrementiamo il tasso di apprendimento in modo esponenziale
    man mano che ci allontaniamo dall'ultimo layer di output.
    """
    options = []
    if self.backbone == "vgg16":
        # Per vgg16, partiamo con un tasso di apprendimento di 1e-3 per l'ultimo layer e
        # decrementiamo a 1e-7 al primo layer convoluzionale. I tassi intermedi sono
        # decrementati linearmente.
        lr = 0.0001
        options.append(
            {
                "params": self.pretrained_model.classifier.parameters(),
                "lr": lr,
            }
        )
        final_lr = lr / 1000.0
        diff_lr = final_lr - lr
        lr_step = diff_lr / 44.0
        for i in range(43, -1, -1):
            options.append(
                {
                    "params": self.pretrained_model.features[i].parameters(),
                    "lr": lr + lr_step * (44 - i),
                }
            )
        # end for
    elif self.backbone in ["resnet50", "resnet152"]:
        # Per la classe di modelli resnet, decrementiamo il LR in modo esponenziale e riduciamo
        # a un terzo del valore precedente ad ogni passaggio.
        layers = ["conv1", "bn1", "layer1", "layer2", "layer3", "layer4", "fc"]
        lr = 0.0001
        for layer_name in reversed(layers):
            options.append(
                {
                    "params": getattr(self.pretrained_model, layer_name).parameters(),
                    "lr": lr,
                }
            )
            lr = lr / 3.0
        # end for
    # end if
    return options


# end def

Snippet di codice: tassi di apprendimento differenziali per ogni livello durante il fine-tuning del modello.

Una volta ottenuti i parametri del modello con i propri LRs, possiamo passarli all’ottimizzatore con una singola riga di codice. Viene utilizzato un LR predefinito di 1e-8 per i parametri le cui pesi non sono specificati nel dizionario restituito da get_optimizer_params().

optimizer = torch.optim.Adam(fc.get_optimizer_params(), lr=1e-8)

Snippet di codice: passare i parametri con i propri LRs all’ottimizzatore Adam.

Ora che sappiamo come eseguire il transfer learning, diamo un’occhiata ad altre considerazioni che dobbiamo tenere a mente prima di affinare il nostro modello. Questo include i passaggi che dobbiamo fare per prevenire l’overfitting e scegliere la giusta suddivisione tra train/val/test.

Prevenire l’overfitting

Nel nostro notebook, utilizziamo le seguenti tecniche di data augmentation sui dati di training per prevenire l’overfitting e consentire al modello di apprendere le caratteristiche in modo che possa effettuare previsioni su dati non visti in precedenza.

  1. Color Jitter
  2. Horizontal Flip
  3. Rotazione
  4. Shear

Nessuna tecnica di data augmentation è stata applicata alla suddivisione di validazione.

Si dovrebbe anche esplorare la decrescita dei pesi, che è una tecnica di regolarizzazione per prevenire l’overfitting riducendo la complessità del modello.

Train/Val/Test split

Gli autori del dataset Flowers 102 raccomandano una suddivisione train/val/test di dimensioni 1020/1020/6149. Molti autori fanno diversamente. Ad esempio,

  1. Nel paper ResNet strikes back, gli autori utilizzano il train+val (2040 immagini) come set di train e il set di test come set di test. Non è chiaro se ci sia una suddivisione di validazione.
  2. In questo articolo sulla classificazione su Flowers 102, gli autori utilizzano il set di test di dimensioni 6149 come set di train.
  3. In questo notebook, l’autore utilizza una suddivisione train/val/test di dimensioni 6552, 818 e 819 rispettivamente.

L’unico modo per sapere cosa fa ogni autore è leggere i paper o il codice.

Nel nostro notebook (in questo articolo), utilizziamo la suddivisione di dimensioni 6149 come set di train e quella di dimensioni 2040 come set di validazione. Non utilizziamo un set di test, poiché non stiamo realmente cercando di competere qui.

A questo punto, dovresti sentirti in grado di visitare questo notebook che esegue tutti i passaggi sopra descritti e presenta i risultati per te da visualizzare. Sentiti libero di clonare il notebook su Kaggle o Google Colab e eseguirlo tu stesso su una GPU. Se stai utilizzando Google Colab, dovrai sistemare alcuni dei percorsi in cui vengono scaricati i dataset e i modelli pre-addestrati e dove sono memorizzati i migliori pesi per i modelli finetuned.

In basso, esamineremo i risultati dei nostri esperimenti di transfer learning!

Risultati

I risultati hanno alcuni temi comuni che esploreremo di seguito.

  1. Dopo la sola estrazione delle caratteristiche, quasi tutte le reti hanno un’accuratezza compresa tra il 91% e il 94%
  2. Quasi tutte le reti ottengono un’ottima prestazione, raggiungendo un’accuratezza del 96+% dopo la fase di fine-tuning. Ciò dimostra che la fase di fine-tuning aiuta molto durante il transfer learning.

C’è una differenza significativa nel numero di parametri nella nostra rete, con vgg16 a 135M di parametri, ResNet50 a 23M di parametri e ResNet152 a 58M di parametri. Ciò suggerisce che probabilmente possiamo trovare una rete più piccola con un’accuratezza e una prestazione comparabili.

La linea verticale rossa indica l’epoca in cui siamo passati dall’estrazione delle caratteristiche (16 epoche) al fine-tuning (8 epoche). Si può vedere che quando si passa al fine-tuning, tutte le reti mostrano un aumento dell’accuratezza. Ciò dimostra che il fine-tuning dopo l’estrazione delle caratteristiche è molto efficace.

Ricapitolando l’articolo

  1. Il transfer learning è un modo efficiente ed economico per addestrare la rete partendo da una rete pre-addestrata su un compito simile ma non correlato
  2. Torchvision fornisce molti modelli pre-addestrati su ImageNet per i ricercatori da utilizzare durante il transfer learning
  3. Sii attento quando utilizzi modelli pre-addestrati in produzione per assicurarti di non violare alcuna licenza o termini di utilizzo per i dataset su cui sono stati pre-addestrati i modelli
  4. Il transfer learning include l’estrazione delle caratteristiche e il fine-tuning, che devono essere eseguiti in quell’ordine specifico

Vuoi saperne di più?

Ora che sappiamo come eseguire il trasferimento di apprendimento per un compito personalizzato a partire da un modello pre-addestrato su un dataset diverso, non sarebbe fantastico se potessimo evitare di utilizzare un dataset separato per il pre-addestramento (compito di pretesto) e utilizzare il nostro dataset per questo scopo? Sembra che ciò stia diventando fattibile!

Recentemente, ricercatori e professionisti hanno utilizzato l’apprendimento auto-supervisionato come modo per eseguire il pre-addestramento del modello (apprendendo il compito di pretesto), il che ha il vantaggio di addestrare il modello su un dataset con la stessa distribuzione del dataset di destinazione che il modello dovrebbe utilizzare in produzione. Se sei interessato a saperne di più sull’auto-supervised pre-training e sul pretraining gerarchico, consulta questo articolo del 2021 intitolato “self-supervised pretraining improves self-supervised pretraining”.

Se possiedi i dati per il tuo compito specifico, puoi utilizzare l’apprendimento auto-supervisionato per il pre-addestramento del tuo modello e non preoccuparti di utilizzare il dataset ImageNet per la fase di pre-addestramento, rimanendo quindi al riparo per quanto riguarda l’uso del dataset ImageNet.

Glossario dei termini utilizzati

  • Classification head: In PyTorch, questo è uno strato nn.Linear che mappa numerosi input features a un insieme di output features
  • Freeze weights: Rendi i pesi non addestrabili. In PyTorch, ciò viene fatto impostando requires_grad=False
  • Unfreeze (o thaw) weights: Rendi i pesi addestrabili. In PyTorch, ciò viene fatto impostando requires_grad=True
  • Self-supervised learning: Un modo per addestrare un modello di apprendimento automatico in modo che possa essere addestrato su dati senza alcuna etichetta generata dall’uomo. Le etichette potrebbero essere generate automaticamente o tramite macchina

Riferimenti e ulteriori letture

  • Idee su come affinare un modello pre-addestrato in PyTorch
  • Profondità nell’apprendimento: affinamento
  • Apprendimento auto-supervisionato e visione artificiale
  • Un’introduzione gentile al trasferimento di apprendimento per il deep learning
  • Notebook: Analisi esplorativa del modello
  • Notebook: Classificazione Flowers 102 utilizzando il trasferimento di apprendimento

Dhruv Matani è un appassionato di Machine Learning che si concentra su PyTorch, CNN, Vision, Speech e Text AI. È un esperto di AI on-device, ottimizzazione e quantizzazione del modello, infrastruttura di ML e dati. Autore di un capitolo su PyTorch efficiente nel libro Efficient Deep Learning su https://efficientdlbook.com/. Le sue opinioni sono personali e non rappresentano quelle di alcuno dei suoi datori di lavoro (passati, presenti o futuri).

Naresh è profondamente interessato all’aspetto “apprendimento” della rete neurale. Il suo lavoro è incentrato sulle architetture delle reti neurali e su come semplici modifiche topologiche ne migliorino le capacità di apprendimento. Ha ricoperto ruoli di ingegneria presso Microsoft, Amazon e Citrix nel corso della sua carriera professionale decennale. È stato coinvolto nel campo del deep learning negli ultimi 6-7 anni. Puoi trovarlo su Nisoo su https://medium.com/u/1e659a80cffd.

Gaurav è un ingegnere software presso Google Research dove guida progetti di ricerca finalizzati all’ottimizzazione di grandi modelli di apprendimento automatico per l’addestramento ed il riconoscimento efficiente su dispositivi che vanno dai microcontrollori ai server basati su Tensor Processing Unit (TPU). Il suo lavoro ha avuto un impatto positivo su oltre 1 miliardo di utenti attivi su YouTube, Cloud, Ads, Chrome, ecc. È anche autore di un prossimo libro con Manning Publication su Efficient Machine Learning. Prima di Google, Gaurav ha lavorato presso Facebook per 4,5 anni e ha contribuito in modo significativo al sistema di ricerca di Facebook e ai database distribuiti su larga scala. Ha una laurea magistrale in informatica presso la Stony Brook University.