Logo Zephyrnet

Comprendere l'overfitting in ConvNets

Data:

Introduzione

L'overfitting in ConvNets è una sfida nel deep learning e nelle reti neurali, in cui un modello impara troppo dai dati di training, portando a scarse prestazioni sui nuovi dati. Questo fenomeno è particolarmente diffuso nelle architetture neurali complesse, che possono modellare relazioni complesse. Affrontare l'overfitting in convnet è fondamentale per creare modelli di rete neurale affidabili. Questo articolo fornisce una guida per comprendere e mitigare l'overfitting, esaminando le cause principali come la complessità del modello, i dati di training limitati e le funzionalità rumorose. Vengono inoltre discusse le tecniche per prevenire l'overfitting, come strategie di aumento dei dati e metodi di regolarizzazione.

Consiglierei di leggere questi articoli per una comprensione di base adattamento eccessivo, adattamento insufficiente ed compromesso della varianza di bias.

Impara gli obiettivi

  • Comprendere le cause, le conseguenze e gli scenari del sovradattamento ConvNet.
  • Interpretare le curve di apprendimento per rilevare l'overfitting e l'underfitting nei modelli di rete neurale.
  • Apprendi varie tecniche per mitigare l'overfitting, come l'arresto anticipato, l'abbandono, la normalizzazione batch, la regolarizzazione e l'aumento dei dati.
  • Implementa queste tecniche utilizzando TensorFlow e Keras per addestrare ConvNets sul set di dati CIFAR-10.
  • Analizzare l'impatto di diverse tecniche sulle prestazioni e sulla generalizzazione del modello.

Sommario

Scenari comuni di overfitting in ConvNet

Esaminiamo alcuni scenari comuni di overfitting in ConvNet:

Scenario 1: modello altamente complesso con dati insufficienti

L'utilizzo di un modello molto complesso, come una rete neurale profonda, su un set di dati di piccole dimensioni può portare a un overfitting. Il modello può memorizzare gli esempi di formazione invece di apprendere lo schema generale. Ad esempio, addestrare una rete neurale profonda con solo poche centinaia di immagini per un compito complesso come il riconoscimento delle immagini potrebbe portare a un overfitting.

conseguenza

Il modello può funzionare molto bene sui dati di addestramento ma non riesce a generalizzare a dati nuovi e invisibili, con conseguenti prestazioni scadenti nelle applicazioni del mondo reale.

Come risolvere questo problema?

Ottieni più dati di addestramento, esegui l'aumento delle immagini per generalizzare il nostro set di dati. Inizia con un modello meno complesso e se la capacità è inferiore aumenta la complessità. 

Scenario 2: formazione eccessiva

L'addestramento continuo di un modello per troppe epoche può portare a un adattamento eccessivo. Poiché il modello vede ripetutamente i dati di addestramento, potrebbe iniziare a memorizzarli anziché apprendere i modelli sottostanti.

conseguenza

Le prestazioni del modello possono stabilizzarsi o addirittura peggiorare su dati invisibili man mano che diventa sempre più specializzato per l'insieme di addestramento.

Come risolvere questo problema?

Utilizzare l'arresto anticipato per evitare che il modello si adatti eccessivamente e salvare il modello migliore. 

Scenario 3: ignorare la regolarizzazione

Le tecniche di regolarizzazione, come la regolarizzazione L1 o L2, vengono utilizzate per prevenire l'overfitting penalizzando i modelli complessi. Ignorare o ottimizzare in modo errato i parametri di regolarizzazione può portare a un overfitting.

conseguenza

Il modello potrebbe diventare eccessivamente complesso e non riuscire a generalizzare bene ai nuovi dati, con conseguenti prestazioni scadenti al di fuori del set di addestramento.

Come risolvere questo problema?

Implementare la regolarizzazione, la convalida incrociata e l'ottimizzazione dei parametri Hyper. 

Qual è la capacità del modello?

La capacità di un modello si riferisce alla dimensione e alla complessità dei modelli che è in grado di apprendere. Per le reti neurali, ciò sarà in gran parte determinato da quanti neuroni possiede e da come sono collegati tra loro. Se sembra che la tua rete non sia in grado di accogliere dati, dovresti provare ad aumentarne la capacità.

È possibile aumentare la capacità di una rete rendendola più ampia (più unità ai livelli esistenti) o rendendola più profonda (aggiungendo più livelli). Le reti più ampie hanno più facilità ad apprendere relazioni più lineari, mentre le reti più profonde preferiscono quelle più non lineari. Quale è meglio dipende solo dal set di dati.

Interpretazione delle curve di apprendimento

Keras offre la possibilità di registrare i callback durante l'addestramento di a modello di apprendimento profondo. Uno dei callback predefiniti registrati durante l'addestramento di tutti i modelli di deep learning è il callback della cronologia. Registra le metriche di allenamento per ogni epoca. Ciò include la perdita e l'accuratezza (per problemi di classificazione) e la perdita e l'accuratezza per il set di dati di convalida, se impostato.

L'oggetto cronologia viene restituito dalle chiamate alla funzione fit() utilizzata per addestrare il modello. Le metriche vengono archiviate in un dizionario nel membro della cronologia dell'oggetto restituito.

Ad esempio, puoi elencare i parametri raccolti in un oggetto cronologia utilizzando il seguente snippet di codice dopo l'addestramento di un modello:

# list all data in history
print(history.history.keys())

Produzione:

['precisione', 'perdita', 'val_precisione', 'val_loss']

Tipo di informazioni

Potresti pensare che le informazioni nei dati di addestramento siano di due tipi:

  • segnale: Il segnale è la parte che generalizza, la parte che può aiutare il nostro modello a fare previsioni a partire da nuovi dati.
  • Rumore: Il rumore è quella parte che è vera solo per i dati di addestramento; il rumore è tutta la fluttuazione casuale che proviene dai dati nel mondo reale o tutti i modelli incidentali e non informativi che non possono effettivamente aiutare il modello a fare previsioni. Il rumore è che la parte potrebbe sembrare utile ma in realtà non lo è.

Quando addestriamo un modello, tracciamo la perdita sul set di addestramento epoca per epoca. A questo aggiungeremo anche un grafico dei dati di convalida. Questi grafici li chiamiamo curve di apprendimento. Per addestrare efficacemente i modelli di deep learning, dobbiamo essere in grado di interpretarli.

Curve di apprendimento

Nella figura sopra possiamo vedere che la perdita di addestramento diminuisce all'aumentare delle epoche, ma la perdita di convalida diminuisce inizialmente e aumenta man mano che il modello inizia a catturare il rumore presente nel set di dati. Ora vedremo come evitare l'overfitting in ConvNets attraverso varie tecniche. 

Metodi per evitare il sovradattamento

Ora che abbiamo visto alcuni scenari e come interpretare le curve di apprendimento per rilevare l'overfitting. esaminiamo alcuni metodi per evitare l'overfitting in una rete neurale:

Metodo 1: utilizzare più dati

Aumentare la dimensione del set di dati può aiutare il modello a generalizzarsi meglio poiché contiene esempi più diversi da cui imparare. Il modello troverà modelli importanti presenti nel set di dati e ignorerà il rumore poiché il modello si rende conto che quei modelli specifici (rumore) non sono presenti in tutto il set di dati.

Metodo 2: arresto anticipato

L'arresto anticipato è una tecnica utilizzata per prevenire l'overfitting monitorando le prestazioni del modello su un set di validazione durante l'addestramento. L'addestramento viene interrotto quando le prestazioni del set di convalida iniziano a peggiorare, indicando che il modello sta iniziando ad adattarsi eccessivamente. In genere, viene utilizzato un set di convalida separato per monitorare le prestazioni e l'addestramento viene interrotto quando le prestazioni non sono migliorate per un numero specificato di epoche.

Sottoadattamento e sovraadattamento

Metodo 3: abbandono

Sappiamo che l'adattamento eccessivo è causato dalla rete che apprende modelli spuri (rumore) nei dati di addestramento. Per riconoscere questi modelli spuri, una rete spesso si affida a combinazioni di peso molto specifiche, una sorta di “cospirazione” dei pesi. Essendo così specifici, tendono ad essere fragili: togline uno e il complotto crolla.

Questa è l'idea alla base dell'abbandono. Per sventare queste cospirazioni, eliminiamo casualmente una frazione delle unità di input di un livello in ogni fase dell'addestramento, rendendo molto più difficile per la rete apprendere questi modelli spuri nei dati di addestramento. Deve invece cercare modelli ampi e generali, i cui modelli di peso tendono ad essere più robusti. 

Si potrebbe anche pensare all’abbandono scolastico come alla creazione di una sorta di insieme di reti. I pronostici non verranno più fatti da una grande rete, ma da un comitato di reti più piccole. Gli individui nel comitato tendono a commettere diversi tipi di errori, ma allo stesso tempo hanno ragione, rendendo il comitato nel suo insieme migliore di qualsiasi individuo. (Se hai familiarità con le foreste casuali come insieme di alberi decisionali, è la stessa idea.)

Overfitting in ConvNets

Metodo 4: normalizzazione batch

Il prossimo metodo speciale che vedremo esegue la “normalizzazione batch” (o “batchnorm”), che può aiutare a correggere l'allenamento lento o instabile.

Con le reti neurali, in genere è una buona idea mettere tutti i dati su una scala comune, magari con qualcosa come StandardScaler o MinMaxScaler di scikit-learn. Il motivo è che SGD sposterà i pesi della rete in proporzione all’ampiezza dell’attivazione prodotta dai dati. Caratteristiche che tendono a produrre attivazioni di dimensioni molto diverse possono rendere instabile il comportamento di allenamento.

Ora, se è bene normalizzare i dati prima che entrino nella rete, forse sarebbe meglio normalizzarli anche all'interno della rete! In effetti, abbiamo un tipo speciale di livello che può farlo, il livello di normalizzazione batch. Un livello di normalizzazione batch esamina ciascun batch non appena arriva, normalizzando prima il batch con la propria media e deviazione standard, quindi inserendo i dati su una nuova scala con due parametri di riscalamento addestrabili. Batchnorm, in effetti, esegue una sorta di riscalamento coordinato dei suoi input.

Molto spesso, batchnorm viene aggiunto come aiuto al processo di ottimizzazione (sebbene a volte possa anche aiutare a prevedere le prestazioni). I modelli con batchnorm tendono a richiedere meno epoche per completare l'addestramento. Inoltre, batchnorm può anche risolvere vari problemi che possono causare il “blocco” della formazione. Prendi in considerazione l'aggiunta della normalizzazione batch ai tuoi modelli, soprattutto se riscontri problemi durante l'addestramento.

Metodo 5: Regolarizzazione L1 e L2

La regolarizzazione L1 e L2 sono tecniche utilizzate per prevenire l'overfitting penalizzando grandi pesi nella rete neurale. La regolarizzazione L1 aggiunge un termine di penalità alla funzione di perdita proporzionale al valore assoluto dei pesi. Incoraggia la scarsità dei pesi e può portare alla selezione delle funzionalità. La regolarizzazione L2, nota anche come decadimento del peso, aggiunge un termine di penalità proporzionale al quadrato dei pesi alla funzione di perdita. Impedisce che i pesi diventino troppo grandi e favorisce una distribuzione più uniforme dei pesi.

La scelta tra la regolarizzazione L1 e L2 dipende spesso dal problema specifico e dalle proprietà desiderate del modello.

Avere valori elevati per la regolarizzazione L1/L2 farà sì che il modello non apprenda velocemente e raggiunga un plateau nell'apprendimento causando un adattamento inadeguato del modello. 

Metodo 6: aumento dei dati

Il modo migliore per migliorare le prestazioni di un modello di machine learning è addestrarlo su più dati. Quanti più esempi il modello deve imparare, tanto meglio sarà in grado di riconoscere quali differenze nelle immagini contano e quali no. Più dati aiutano il modello a generalizzare meglio.

Un modo semplice per ottenere più dati è utilizzare i dati che già possiedi. Se possiamo trasformare le immagini nel nostro set di dati in modi che preservino la classe (esempio: classificazione delle cifre MNIST se proviamo ad aumentare 6 sarà difficile distinguere tra 6 e 9), possiamo insegnare al nostro classificatore a ignorare questo tipo di trasformazioni. Ad esempio, se un'auto è rivolta a sinistra o a destra in una foto non cambia il fatto che si tratti di un'auto e non di un camion. Quindi, se aumentiamo i nostri dati di addestramento con immagini capovolte, il nostro classificatore imparerà che “sinistra o destra” è una differenza che dovrebbe ignorare.

E questa è l'idea alla base dell'aumento dei dati: aggiungi alcuni dati extra falsi che assomigliano ragionevolmente ai dati reali e il tuo classificatore migliorerà. 

Ricorda, la chiave per evitare un overfitting è assicurarsi che il tuo modello si generalizzi bene. Controlla sempre le prestazioni del tuo modello su un set di validazione, non solo sul set di training.

Implementazione dei metodi di cui sopra con i dati

Esploriamo i passaggi di implementazione per i metodi sopra indicati:

Passaggio 1: caricamento delle librerie necessarie

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.callbacks import ModelCheckpoint
import keras
from keras.preprocessing import image
from keras import models, layers, regularizers
from tqdm import tqdm
import warnings
warnings.filterwarnings(action='ignore')

Passaggio 2: caricamento del set di dati e preelaborazione

#Here all the images are in the form of a numpy array
cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train / 255.0
x_test = x_test / 255.0

Passaggio 3: apprendimento del set di dati

x_train.shape, y_train.shape, x_test.shape, y_test.shape 

Produzione:

Uscita
np.unique(y_train)

Produzione:

Uscita
#These labels are in the order and taken from the documentaion
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck']

Passaggio 4: visualizzazione dell'immagine dal set di dati

def show_image(IMG_INDEX):
    plt.imshow(x_train[20] ,cmap=plt.cm.binary)
    plt.xlabel(class_names[y_train[IMG_INDEX][0]])
    plt.show()
show_image(20)
Overfitting in ConvNets
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10))
model.summary()

Inizializziamo ora gli iperparametri e compiliamo il modello con l'ottimizzatore, la funzione di perdita e la metrica di valutazione.


train_hyperparameters_config={'optim':keras.optimizers.Adam(learning_rate=0.001),
                             'epochs':20,
                              'batch_size':16
                             }
model.compile(optimizer=train_hyperparameters_config['optim'],
                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                  metrics=['accuracy'])

Passaggio 6: modello di formazione

history = model.fit(x_train, y_train, 
                        epochs=train_hyperparameters_config['epochs'], 
                        batch_size=train_hyperparameters_config['batch_size'], 
                        verbose=1,
                        validation_data=(x_test, y_test))

Passaggio 7: valutare il modello

Questi ci diranno le informazioni contenute nell'oggetto storia e le utilizzeremo per creare le nostre curve informative.

print(history.history.keys()) 
def learning_curves(history):
# Plotting Accuracy
    plt.figure(figsize=(14, 5))  # Adjust the figure size as needed
    plt.subplot(1, 2, 1)  # Subplot with 1 row, 2 columns, and index 1
    plt.plot(history.history['accuracy'], label='train_accuracy', marker='s', markersize=4)
    plt.plot(history.history['val_accuracy'], label='val_accuracy', marker='*', markersize=4)
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.legend(loc='lower right')

    # Plotting Loss
    plt.subplot(1, 2, 2)  # Subplot with 1 row, 2 columns, and index 2
    plt.plot(history.history['loss'], label='train_loss', marker='s', markersize=4)
    plt.plot(history.history['val_loss'], label='val_loss', marker='*', markersize=4)
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.legend(loc='lower right')

    plt.show()
learning_curves(history)
Overfitting in ConvNets

Dalle curve possiamo vedere che l'accuratezza della validazione raggiunge un plateau dopo la 4a epoca e il modello inizia a catturare il rumore. Pertanto implementeremo l'arresto anticipato per evitare l'overfitting del modello e ripristinare i pesi migliori in base a val_loss. Utilizzeremo val_loss per monitorare l'arresto anticipato mentre la nostra rete neurale tenta di ridurre la perdita utilizzando gli ottimizzatori. L'accuratezza e l'accuratezza della convalida dipendono dalla soglia (una probabilità di separare le classi – solitamente 0.5 per la classificazione binaria), quindi se il nostro set di dati è sbilanciato sarebbe una perdita di cui dovremmo preoccuparci nella maggior parte dei casi. 

Fase 8: implementazione dell'arresto anticipato

Poiché non siamo preoccupati che il nostro modello si adatti eccessivamente, poiché l'arresto anticipato eviterà che il nostro modello si realizzi. È una buona scelta scegliere un numero maggiore di epoche e una pazienza adeguata. Ora utilizzeremo la stessa architettura del modello e ci addestreremo con l'arresto anticipato del callback. 

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10))
model.summary()

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)

def model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config):
    model.compile(optimizer=train_hyperparameters_config['optim'],
                      loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                      metrics=['accuracy'])
    ht = model.fit(x_train, y_train, 
                            epochs=train_hyperparameters_config['epochs'], 
                            batch_size=train_hyperparameters_config['batch_size'],
                            callbacks=[callback],
                            verbose=1,
                            validation_data=(x_test, y_test))
    return ht

ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
Overfitting in ConvNets

Per conoscere i nostri migliori pesi che la modella ha assunto. 

print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

Passaggio 9: aumento della complessità del modello

Poiché il nostro modello non funziona bene e non è in grado di acquisire dati sufficienti. Dovremmo aumentare la complessità del nostro modello e valutare. 

model = models.Sequential()

model.add(layers.Conv2D(128, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
model.summary()
Uscita

Possiamo vedere che c'è un aumento dei parametri totali. Ciò aiuterebbe a trovare relazioni più complesse nel nostro modello. Nota: il nostro set di dati è composto da immagini 32X32; queste sono immagini relativamente piccole. Pertanto, l'utilizzo di modelli più complessi all'inizio sicuramente si adatterà eccessivamente al modello, quindi tendiamo ad aumentare lentamente la complessità del nostro modello.

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)

learning_curves(ht)
Overfitting in ConvNets
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

Dai grafici sopra possiamo dire chiaramente che il modello è overfitting, quindi utilizzeremo un altro metodo chiamato Normalizzazione Drop out e Normalizzazione Batch.

Passaggio 10: utilizzo dei livelli di esclusione e dei livelli di normalizzazione batch

model = models.Sequential()

model.add(layers.Conv2D(128, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.3))

model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.3))

model.add(layers.Dense(10, activation='softmax'))
model.summary()
Uscita
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
Overfitting in ConvNets
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

Dai grafici di apprendimento possiamo vedere che il modello si sta adattando eccessivamente anche con livelli di normalizzazione batch e di abbandono. Quindi invece di aumentare la complessità ma aumentare il numero di filtri. Aggiungeremmo più livelli di convoluzione per estrarre più funzionalità.

Passaggio 11: aumento dei livelli di convoluzione

Diminuire il parametro addestrabile ma aumentare i livelli di convoluzione per estrarre più funzionalità.

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))

model.add(layers.Dense(10, activation='softmax'))

model.summary()
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
produzione
learning_curves(ht)
Overfitting in ConvNets
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

Dall'output di cui sopra e dalla curva di apprendimento possiamo dedurre che il modello ha funzionato molto bene e ha evitato un adattamento eccessivo. L'accuratezza dell'addestramento e l'accuratezza della convalida sono molto vicine. In questo scenario non avremo bisogno di ulteriori metodi per ridurre l’overfitting. Tuttavia esploreremo la regolarizzazione L1/L2. 

Passaggio 12: utilizzo della regolarizzazione L1/L2

from tensorflow.keras import regularizers

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l2(0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu', kernel_regularizer=regularizers.l1_l2(0.0005, 0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(10, activation='softmax'))

model.summary()
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 7,
    'epochs': 70,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
Overfitting in ConvNets
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

Ora possiamo vedere che la regolarizzazione L1/L2, anche dopo aver utilizzato un punteggio di penalità basso pari a 0.0001, ha reso il nostro modello sottoadattato del 4%. Pertanto è consigliabile utilizzare con cautela tutti i metodi insieme. Poiché la normalizzazione e la regolarizzazione batch influiscono sul modello in modo simile, non avremmo bisogno della regolarizzazione L1/L2. 

Passaggio 13: aumento dei dati

Utilizzeremo ImageDataGenerator di tensorflow keras.

# creates a data generator object that transforms images
datagen = ImageDataGenerator(
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')

# pick an image to transform
test_img = x_train[20]
img = image.img_to_array(test_img)  # convert image to numpy arry
img = img.reshape((1,) + img.shape)  # reshape image

i = 0

for batch in datagen.flow(img, save_prefix='test', save_format='jpeg'):  # this loops runs forever until we break, saving images to current directory with specified prefix
    plt.figure(i)
    plot = plt.imshow(image.img_to_array(batch[0]))
    i += 1
    if i > 4:  # show 4 images
        break

plt.show()
Overfitting in ConvNets

Queste sono quattro immagini aumentate e un'immagine originale.

# Create an instance of the ImageDataGenerator
datagen = ImageDataGenerator(
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest'
)

# Create an iterator for the data generator
data_generator = datagen.flow(x_train, y_train, batch_size=32)

# Create empty lists to store the augmented images and labels
augmented_images = []
augmented_labels = []

# Loop over the data generator and append the augmented data to the lists
num_batches = len(x_train) // 32
progress_bar = tqdm(total=num_batches, desc="Augmenting data", unit="batch")

for i in range(num_batches):
    batch_images, batch_labels = next(data_generator)
    augmented_images.append(batch_images)
    augmented_labels.append(batch_labels)
    progress_bar.update(1)

progress_bar.close()

# Convert the lists to NumPy arrays
augmented_images = np.concatenate(augmented_images, axis=0)
augmented_labels = np.concatenate(augmented_labels, axis=0)

# Combine the original and augmented data
x_train_augmented = np.concatenate((x_train, augmented_images), axis=0)
y_train_augmented = np.concatenate((y_train, augmented_labels), axis=0)
produzione

Abbiamo utilizzato la libreria tqdm per conoscere lo stato di avanzamento del nostro potenziamento.

x_train_augmented.shape, y_train_augmented.shape
Uscita

Questo è il nostro set di dati dopo l'aumento. Ora utilizziamo questo set di dati e addestriamo il nostro modello.

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))

model.add(layers.Dense(10, activation='softmax'))

model.summary()

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 10,
    'epochs': 70,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)

ht=model_train(model, x_train_augmented, y_train_augmented, x_test, y_test, train_hyperparameters_config)


learning_curves(ht)
Overfitting in ConvNets
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

Possiamo vedere che il modello è più generalizzato e una diminuzione delle perdite. Abbiamo anche una migliore precisione di convalida. Pertanto l’aumento dei dati ha aumentato la precisione del nostro modello. 

Conclusione

L'overfitting è un problema comune nel deep learning, in particolare con architetture di reti neurali complesse come ConvNets. I professionisti possono prevenire l'overfitting in ConvNet comprendendone le cause profonde e riconoscendo gli scenari in cui si verifica. Tecniche come l'arresto anticipato, l'abbandono, la normalizzazione batch, la regolarizzazione e l'aumento dei dati possono aiutare a mitigare questo problema. L'implementazione di queste tecniche sul set di dati CIFAR-10 ha mostrato miglioramenti significativi nella generalizzazione e nelle prestazioni del modello. Padroneggiare queste tecniche e comprenderne i principi può portare a modelli di rete neurale robusti e affidabili.

Domande frequenti

Q1. Cos’è l’overfitting e perché è un problema nel deep learning? 

R. L'overfitting si verifica quando un modello apprende troppo bene i dati di addestramento, compresi il rumore e i modelli irrilevanti, con conseguenti prestazioni scadenti su dati nuovi e invisibili. È un problema perché i modelli sovradimensionati non riescono a generalizzare in modo efficace, limitando la loro utilità pratica.

Q2. Come posso rilevare il sovradattamento nel mio modello di rete neurale?

R. È possibile rilevare l'overfitting in ConvNet interpretando le curve di apprendimento, che tracciano i parametri di training e convalida (ad esempio, perdita, precisione) nel corso delle epoche. Se i parametri di convalida smettono di migliorare o iniziano a peggiorare mentre i parametri di addestramento continuano a migliorare, è un segno di overfitting.

Q3. Cos’è l’arresto anticipato e come aiuta a prevenire l’overfitting?

R. L'arresto anticipato è una tecnica che monitora le prestazioni del modello su un set di validazione durante l'addestramento e interrompe il processo di addestramento quando le prestazioni sul set di validazione iniziano a peggiorare, indicando un overfitting. Aiuta a prevenire l'overfitting del modello interrompendo l'addestramento al momento giusto.

Q4. In che modo l'aumento dei dati aiuta a mitigare l'overfitting? 

R. L'aumento dei dati è il processo di generazione di nuovi dati di addestramento sintetici applicando trasformazioni (ad esempio capovolgimento, rotazione, ridimensionamento) ai dati esistenti. Aiuta il modello a generalizzarsi meglio esponendolo a esempi più diversi, riducendo il rischio di adattamento eccessivo in ConvNet ai dati di training limitati.

spot_img

L'ultima intelligenza

spot_img