Zephyrnet-logo

Overfitting in ConvNets begrijpen

Datum:

Introductie

Overfitting in ConvNets is een uitdaging bij deep learning en neurale netwerken, waarbij een model te veel leert van trainingsgegevens, wat leidt tot slechte prestaties op nieuwe gegevens. Dit fenomeen komt vooral voor in complexe neurale architecturen, die ingewikkelde relaties kunnen modelleren. Het aanpakken van overfitting in convnet is cruciaal voor het bouwen van betrouwbare neurale netwerkmodellen. Dit artikel biedt een handleiding voor het begrijpen en beperken van overfitting, waarbij diepere oorzaken worden onderzocht, zoals de complexiteit van het model, beperkte trainingsgegevens en luidruchtige functies. Ook worden technieken besproken om overfitting te voorkomen, zoals data-augmentatiestrategieën en regularisatiemethoden.

Ik zou aanraden deze artikelen te lezen voor basiskennis overfitting, onderfitting en bias-variantie-afweging.

Leer doelstellingen

  • Begrijp de oorzaken, gevolgen en scenario’s van overfitting ConvNets.
  • Leercurven interpreteren om overfitting en underfitting in neurale netwerkmodellen te detecteren.
  • Leer verschillende technieken om overfitting tegen te gaan, zoals vroegtijdig stoppen, uitval, batchnormalisatie, regularisatie en gegevensvergroting.
  • Implementeer deze technieken met behulp van TensorFlow en Keras om ConvNets te trainen op de CIFAR-10-dataset.
  • Analyseer de impact van verschillende technieken op de prestaties en generalisatie van modellen.

Inhoudsopgave

Veelvoorkomende scenario's voor overfitting in ConvNet

Laten we eens kijken naar enkele veelvoorkomende scenario's van overfitting in ConvNet:

Scenario 1: Zeer complex model met onvoldoende gegevens

Het gebruik van een zeer complex model, zoals een diep neuraal netwerk, op een kleine dataset kan tot overfitting leiden. Het model kan de trainingsvoorbeelden onthouden in plaats van het algemene patroon te leren. Het trainen van een diep neuraal netwerk met slechts een paar honderd afbeeldingen voor een complexe taak als beeldherkenning zou bijvoorbeeld tot overfitting kunnen leiden.

consequentie

Het model presteert mogelijk heel goed op basis van de trainingsgegevens, maar slaagt er niet in om te generaliseren naar nieuwe, onzichtbare gegevens, wat resulteert in slechte prestaties in toepassingen in de echte wereld.

Hoe dit probleem op te lossen?

Ontvang meer trainingsgegevens, voer beeldvergroting uit om onze dataset te generaliseren. Begin met een minder complex model en als de capaciteit kleiner is, verhoog dan de complexiteit. 

Scenario 2: Overmatige training

Het voortdurend trainen van een model voor te veel tijdperken kan leiden tot overfitting. Omdat het model de trainingsgegevens herhaaldelijk ziet, kan het deze gaan onthouden in plaats van de onderliggende patronen te leren.

consequentie

De prestaties van het model kunnen op basis van onzichtbare gegevens stagneren of zelfs verslechteren naarmate het zich steeds meer specialiseert in de trainingsset.

Hoe dit probleem op te lossen?

Gebruik vroeg stoppen om te voorkomen dat het model te veel past en bewaar het beste model. 

Scenario 3: Het negeren van regularisatie

Regularisatietechnieken, zoals L1- of L2-regularisatie, worden gebruikt om overfitting te voorkomen door complexe modellen te bestraffen. Het negeren of onjuist afstemmen van regularisatieparameters kan leiden tot overfitting.

consequentie

Het model kan te complex worden en niet goed generaliseren naar nieuwe gegevens, wat resulteert in slechte prestaties buiten de trainingsset.

Hoe dit probleem op te lossen?

Implementeer regularisatie, kruisvalidatie, afstemming van hyperparameters. 

Wat is de capaciteit van het model?

De capaciteit van een model verwijst naar de omvang en complexiteit van de patronen die het kan leren. Voor neurale netwerken zal dit grotendeels worden bepaald door het aantal neuronen dat het heeft en hoe ze met elkaar verbonden zijn. Als het erop lijkt dat uw netwerk onvoldoende gegevens levert, kunt u proberen de capaciteit ervan te vergroten.

Je kunt de capaciteit van een netwerk vergroten door het breder te maken (meer eenheden op bestaande lagen) of door het dieper te maken (meer lagen toevoegen). Bredere netwerken kunnen gemakkelijker meer lineaire relaties leren, terwijl diepere netwerken de voorkeur geven aan meer niet-lineaire relaties. Wat beter is, hangt alleen af ​​van de dataset.

Interpretatie van leercurven

Keras biedt de mogelijkheid om terugbelverzoeken te registreren tijdens het trainen van een diep lerend model. Een van de standaard callbacks die worden geregistreerd bij het trainen van alle deep learning-modellen is de History-callback. Het registreert trainingsstatistieken voor elk tijdperk. Dit omvat het verlies en de nauwkeurigheid (voor classificatieproblemen) en het verlies en de nauwkeurigheid van de validatiedataset, als deze is ingesteld.

Het history-object wordt geretourneerd door aanroepen van de fit()-functie die wordt gebruikt om het model te trainen. Metrische gegevens worden opgeslagen in een woordenboek in het geschiedenislid van het geretourneerde object.

U kunt bijvoorbeeld de metrieken weergeven die in een geschiedenisobject zijn verzameld met behulp van het volgende codefragment nadat een model is getraind:

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

Output:

['nauwkeurigheid', 'verlies', 'val_accuracy', 'val_loss']

Informatietype

U kunt de informatie in de trainingsgegevens op twee manieren beschouwen:

  • signaal: Het signaal is het deel dat generaliseert, het deel dat ons model kan helpen voorspellingen te doen op basis van nieuwe gegevens.
  • Lawaai: De ruis is dat deel dat alleen geldt voor de trainingsgegevens; de ruis is alle willekeurige fluctuatie die voortkomt uit gegevens in de echte wereld of alle incidentele, niet-informatieve patronen die het model niet echt kunnen helpen voorspellingen te doen. Het geluid is dat het onderdeel er misschien nuttig uitziet, maar dat in werkelijkheid niet is.

Wanneer we een model trainen, hebben we het verlies per periode van de trainingsset in kaart gebracht. Hieraan voegen we ook een plot van de validatiegegevens toe. Deze plots noemen we de leercurves. Om deep learning-modellen effectief te kunnen trainen, moeten we ze kunnen interpreteren.

Leercurven

In de bovenstaande figuur kunnen we zien dat het trainingsverlies afneemt naarmate de tijdvakken toenemen, maar dat het validatieverlies eerst afneemt en toeneemt naarmate het model de ruis in de dataset begint vast te leggen. Nu gaan we zien hoe we met verschillende technieken overfitting in ConvNets kunnen voorkomen. 

Methoden om overfitting te voorkomen

Nu we enkele scenario's hebben gezien en hoe we leercurves moeten interpreteren om overfitting te detecteren. laten we eens kijken naar enkele methoden om overfitting in een neuraal netwerk te voorkomen:

Methode 1: Gebruik meer data

Door de omvang van uw dataset te vergroten, kan het model beter worden gegeneraliseerd, omdat er meer uiteenlopende voorbeelden zijn waar u van kunt leren. Het model zal belangrijke patronen in de dataset vinden en ruis negeren, aangezien het model zich realiseert dat die specifieke patronen (ruis) niet in de hele dataset aanwezig zijn.

Methode 2: Vroeg stoppen

Vroegtijdig stoppen is een techniek die wordt gebruikt om overfitting te voorkomen door tijdens de training de prestaties van het model op een validatieset te monitoren. De training wordt gestopt wanneer de prestaties van de validatieset beginnen te verslechteren, wat aangeeft dat het model overmatig begint te worden. Normaal gesproken wordt een afzonderlijke validatieset gebruikt om de prestaties te bewaken, en wordt de training gestopt wanneer de prestaties gedurende een bepaald aantal tijdperken niet zijn verbeterd.

Onderfitting en overfitting

Methode 3: Uitval

We weten dat overfitting wordt veroorzaakt doordat het netwerk valse patronen (ruis) in de trainingsgegevens leert. Om deze valse patronen te herkennen, zal een netwerk vaak vertrouwen op zeer specifieke gewichtscombinaties, een soort ‘samenzwering’ van gewichten. Omdat ze zo specifiek zijn, zijn ze vaak kwetsbaar: verwijder er één en de samenzwering valt uiteen.

Dit is het idee achter uitval. Om deze samenzweringen te doorbreken, schrappen we bij elke trainingsstap willekeurig een fractie van de invoereenheden van een laag, waardoor het voor het netwerk veel moeilijker wordt om die valse patronen in de trainingsgegevens te leren. In plaats daarvan moet het zoeken naar brede, algemene patronen, waarvan de gewichtspatronen doorgaans robuuster zijn. 

Je kunt uitval ook zien als het creëren van een soort ensemble van netwerken. De voorspellingen worden niet langer gedaan door één groot netwerk, maar door een commissie van kleinere netwerken. Individuen in de commissie hebben de neiging verschillende soorten fouten te maken, maar hebben tegelijkertijd gelijk, waardoor de commissie als geheel beter is dan welk individu dan ook. (Als je bekend bent met willekeurige bossen als een geheel van beslissingsbomen, is dit hetzelfde idee.)

Overfitting in ConvNets

Methode 4: Batchnormalisatie

De volgende speciale methode die we zullen bekijken, voert “batch-normalisatie” (of “batchnorm”) uit, wat kan helpen bij het corrigeren van training die langzaam of onstabiel is.

Bij neurale netwerken is het over het algemeen een goed idee om al uw gegevens op een gemeenschappelijke schaal te plaatsen, misschien met zoiets als StandardScaler of MinMaxScaler van scikit-learn. De reden is dat SGD de netwerkgewichten zal verschuiven in verhouding tot de omvang van de activering die de gegevens opleveren. Functies die activaties van zeer verschillende groottes veroorzaken, kunnen voor onstabiel trainingsgedrag zorgen.

Als het goed is om de gegevens te normaliseren voordat deze het netwerk ingaan, zou normaliseren binnen het netwerk misschien ook beter zijn! In feite hebben we een speciaal soort laag die dit kan doen, de batchnormalisatielaag. Een batchnormalisatielaag kijkt naar elke batch zoals deze binnenkomt, waarbij eerst de batch wordt genormaliseerd met zijn eigen gemiddelde en standaarddeviatie, en vervolgens de gegevens op een nieuwe schaal worden geplaatst met twee trainbare herschalingsparameters. Batchnorm voert in feite een soort gecoördineerde herschaling van de invoer uit.

Meestal wordt batchnorm toegevoegd als hulpmiddel bij het optimalisatieproces (hoewel het soms ook kan helpen bij het voorspellen van de prestaties). Modellen met batchnorm hebben doorgaans minder tijdvakken nodig om de training te voltooien. Bovendien kan batchnorm ook verschillende problemen oplossen die ervoor kunnen zorgen dat de training ‘vastloopt’. Overweeg om batchnormalisatie aan uw modellen toe te voegen, vooral als u problemen ondervindt tijdens de training.

Methode 5: L1- en L2-regularisatie

L1- en L2-regularisatie zijn technieken die worden gebruikt om overfitting te voorkomen door grote gewichten in het neurale netwerk te bestraffen. L1-regularisatie voegt een strafterm toe aan de verliesfunctie die evenredig is aan de absolute waarde van de gewichten. Het bevordert schaarsheid in de gewichten en kan leiden tot functieselectie. L2-regularisatie, ook bekend als gewichtsverval, voegt een strafterm toe die evenredig is aan het kwadraat van de gewichten aan de verliesfunctie. Het voorkomt dat de gewichten te groot worden en bevordert een gelijkmatigere verdeling van de gewichten.

De keuze tussen L1- en L2-regularisatie hangt vaak af van het specifieke probleem en de gewenste eigenschappen van het model.

Het hebben van grote waarden voor L1/L2-regularisatie zal ervoor zorgen dat het model niet snel leert en een plateau in het leren bereikt, waardoor het model ondermaats wordt. 

Methode 6: Gegevensvergroting

De beste manier om de prestaties van een machine learning-model te verbeteren, is door het op meer gegevens te trainen. Hoe meer voorbeelden het model moet leren, hoe beter het zal kunnen herkennen welke verschillen in afbeeldingen er toe doen en welke niet. Meer gegevens helpen het model beter te generaliseren.

Een eenvoudige manier om meer gegevens te verkrijgen, is door gebruik te maken van de gegevens die u al heeft. Als we de afbeeldingen in onze dataset kunnen transformeren op manieren die de klasse behouden (bijvoorbeeld: MNIST Digit-classificatie, als we augment 6 proberen, zal het moeilijk zijn om onderscheid te maken tussen 6 en 9), kunnen we onze classificator leren dit soort transformaties te negeren. Of een auto op een foto bijvoorbeeld naar links of rechts kijkt, verandert niets aan het feit dat het een auto is en geen vrachtwagen. Dus als we onze trainingsgegevens aanvullen met omgedraaide afbeeldingen, leert onze classificator dat ‘links of rechts’ een verschil is dat hij moet negeren.

En dat is het hele idee achter gegevensvergroting: voeg wat extra valse gegevens toe die redelijk op de echte gegevens lijken, en uw classificator zal verbeteren. 

Houd er rekening mee dat de sleutel tot het voorkomen van overfitting is ervoor te zorgen dat uw model goed generaliseert. Controleer altijd de prestaties van uw model op een validatieset, niet alleen op de trainingsset.

Implementatie van bovenstaande methoden met gegevens

Laten we de implementatiestappen voor bovenstaande methoden verkennen:

Stap 1: benodigde bibliotheken laden

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')

Stap 2: Gegevensset laden en voorbewerken

#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

Stap 3: Dataset leren

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

Output:

uitgang
np.unique(y_train)

Output:

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

Stap 4: Afbeelding uit dataset visualiseren

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()

Laten we nu hyperparameters initialiseren en het model compileren met optimizer, verliesfunctie en evaluatiemetriek.


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'])

Stap6: Trainingsmodel

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))

Stap 7: Evalueer het model

Deze zullen ons de informatie vertellen die zich in het geschiedenisobject bevindt en we gebruiken deze om onze informatiecurven te creëren.

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

Uit de curven kunnen we zien dat de nauwkeurigheid van de validatie een plateau bereikt na het vierde tijdperk en dat het model ruis begint vast te leggen. Daarom zullen we vroegtijdig stoppen implementeren om te voorkomen dat het model overmatig wordt aangepast en de beste gewichten herstellen op basis van val_loss. We zullen val_loss gebruiken om vroegtijdig stoppen te monitoren terwijl ons neurale netwerk verliezen probeert te verminderen met behulp van optimizers. Nauwkeurigheid en validatienauwkeurigheid zijn afhankelijk van de drempel (de kans om klassen te scheiden – meestal 4 voor binaire classificatie), dus als onze dataset onevenwichtig is, zou dit in de meeste gevallen een verlies zijn waar we ons zorgen over moeten maken. 

Stap 8: Vroegtijdig stoppen implementeren

Omdat we ons geen zorgen maken dat ons model overfit raakt, omdat vroegtijdig stoppen zal voorkomen dat ons model werkelijkheid wordt. Het is een goede keuze om een ​​groter aantal tijdperken en een passend geduld te kiezen. Nu zullen we dezelfde modelarchitectuur gebruiken en trainen met vroegtijdige stop-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

Om onze beste gewichten te kennen die het model heeft genomen. 

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

Stap 9: De complexiteit van het model vergroten

Omdat ons model niet goed presteert en onvoldoende past omdat het niet voldoende gegevens kan vastleggen. We moeten de complexiteit van ons model vergroten en evalueren. 

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()
uitgang

We kunnen zien dat er een toename is van de totale parameters. Dit zou helpen bij het vinden van complexere relaties in ons model. Opmerking: onze dataset bestaat uit 32X32 afbeeldingen; dit zijn relatief kleine afbeeldingen. Daarom zal het gebruik van complexere modellen in het begin het model zeker overbelasten, en daarom hebben we de neiging om de complexiteit van ons model langzaam te vergroten.

# 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)

Uit de bovenstaande grafieken kunnen we duidelijk zeggen dat het model overfitting is, daarom zullen we een andere methode gebruiken, genaamd Drop-out-normalisatie en Batch-normalisatie.

Stap 10: Uitvallagen en batchnormalisatielagen gebruiken

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()
uitgang
# 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)

Uit de leergrafieken kunnen we zien dat het model zelfs met batchnormalisatie- en uitvallagen overfitting is. Dus in plaats van de complexiteit te vergroten, maar het aantal filters te vergroten. We zouden meer convolutielagen toevoegen om meer functies te extraheren.

Stap 11: Convolutielagen vergroten

Verlaag de trainbare parameter, maar verhoog de convolutielagen om meer functies te extraheren.

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)
uitgang
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)

Uit de bovenstaande output en leercurve kunnen we afleiden dat het model zeer goed heeft gepresteerd en overfitting heeft vermeden. De trainingsnauwkeurigheid en validatienauwkeurigheid liggen zeer dichtbij. In dit scenario hebben we niet meer methoden nodig om overfitting te verminderen. Toch zullen we L1/L2-regularisatie onderzoeken. 

Stap 12: L1/L2-regularisatie gebruiken

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)

Nu kunnen we zien dat L1/L2-regularisatie, zelfs na gebruik van een lage strafscore van 0.0001, ons model met 4% ondermaats maakte. Daarom is het raadzaam om alle methoden voorzichtig samen te gebruiken. Omdat batchnormalisatie en regularisatie het model op een vergelijkbare manier beïnvloeden, hebben we geen L1/L2-regularisatie nodig. 

Stap 13: Gegevensvergroting

We zullen ImageDataGenerator van tensorflow keras gebruiken.

# 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

Dit zijn vier uitgebreide afbeeldingen en één originele afbeelding.

# 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)
uitgang

We hebben de tqdm-bibliotheek gebruikt om de voortgang van onze augmentatie te kennen.

x_train_augmented.shape, y_train_augmented.shape
uitgang

Dit is onze dataset na augmentatie. Laten we nu deze dataset gebruiken en ons model trainen.

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)

We kunnen zien dat het model algemener is en het verlies afneemt. We hebben ook een betere validatienauwkeurigheid. Daarom heeft data-vergroting de nauwkeurigheid van ons model vergroot. 

Conclusie

Overfitting is een veel voorkomend probleem bij deep learning, vooral bij complexe neurale netwerkarchitecturen zoals ConvNets. Beoefenaars kunnen overfitting in ConvNets voorkomen door de hoofdoorzaken ervan te begrijpen en scenario's te herkennen waarin dit voorkomt. Technieken als vroegtijdig stoppen, drop-out, batchnormalisatie, regularisatie en gegevensvergroting kunnen dit probleem helpen verminderen. Het implementeren van deze technieken op de CIFAR-10-dataset liet significante verbeteringen zien in modelgeneralisatie en prestaties. Het beheersen van deze technieken en het begrijpen van hun principes kan leiden tot robuuste en betrouwbare neurale netwerkmodellen.

Veelgestelde Vragen / FAQ

Q1. Wat is overfitting en waarom is dit een probleem bij deep learning? 

A. Overfitting vindt plaats wanneer een model de trainingsgegevens te goed leert, inclusief de ruis en irrelevante patronen, wat resulteert in slechte prestaties op nieuwe, onzichtbare gegevens. Het is een probleem omdat overfitte modellen er niet in slagen effectief te generaliseren, waardoor hun praktische bruikbaarheid wordt beperkt.

Vraag 2. Hoe kan ik overfitting in mijn neurale netwerkmodel detecteren?

A. U kunt overfitting in ConvNets detecteren door de leercurven te interpreteren, die de trainings- en validatiestatistieken (bijvoorbeeld verlies, nauwkeurigheid) over tijdsperioden uitzetten. Als de validatiestatistieken niet meer verbeteren of beginnen te verslechteren terwijl de trainingsstatistieken blijven verbeteren, is dit een teken van overfitting.

Q3. Wat is vroeg stoppen en hoe helpt het overfitting te voorkomen?

A. Vroegtijdig stoppen is een techniek die de prestaties van het model op een validatieset tijdens de training controleert en het trainingsproces stopt wanneer de prestaties op de validatieset beginnen te verslechteren, wat wijst op overfitting. Het helpt voorkomen dat het model overfitting krijgt door de training op het juiste moment te stoppen.

Q4. Hoe helpt data-augmentatie bij het tegengaan van overfitting? 

A. Gegevensvergroting is het proces waarbij nieuwe, synthetische trainingsgegevens worden gegenereerd door transformaties (bijvoorbeeld omdraaien, roteren, schalen) toe te passen op de bestaande gegevens. Het helpt het model beter te generaliseren door het bloot te stellen aan meer uiteenlopende voorbeelden, waardoor het risico van overfitting in ConvNets op de beperkte trainingsgegevens wordt verkleind.

spot_img

Laatste intelligentie

spot_img