Zephyrnet-Logo

Überanpassung in ConvNets verstehen

Datum:

Einleitung

Überanpassung in ConvNets ist eine Herausforderung bei Deep Learning und neuronalen Netzen, wo ein Modell zu viel aus Trainingsdaten lernt, was zu einer schlechten Leistung bei neuen Daten führt. Dieses Phänomen tritt besonders häufig in komplexen neuronalen Architekturen auf, die komplexe Beziehungen modellieren können. Die Beseitigung der Überanpassung in Convnet ist für den Aufbau zuverlässiger neuronaler Netzwerkmodelle von entscheidender Bedeutung. Dieser Artikel bietet einen Leitfaden zum Verständnis und zur Abschwächung von Überanpassung und untersucht die Grundursachen wie Modellkomplexität, begrenzte Trainingsdaten und verrauschte Funktionen. Außerdem werden Techniken zur Verhinderung einer Überanpassung erörtert, wie z. B. Strategien zur Datenerweiterung und Regularisierungsmethoden.

Ich würde empfehlen, diese Artikel zu lesen, um ein grundlegendes Verständnis zu erlangen Überanpassung, Unteranpassung und Bias-Varianz-Kompromiss.

Lernziele

  • Verstehen Sie die Ursachen, Folgen und Szenarien einer Überanpassung ConvNets.
  • Interpretieren Sie Lernkurven, um Überanpassung und Unteranpassung in neuronalen Netzwerkmodellen zu erkennen.
  • Lernen Sie verschiedene Techniken zur Minderung von Überanpassungen kennen, z. B. vorzeitiges Stoppen, Aussetzen, Batch-Normalisierung, Regularisierung und Datenerweiterung.
  • Implementieren Sie diese Techniken mit TensorFlow und Keras, um ConvNets auf dem CIFAR-10-Datensatz zu trainieren.
  • Analysieren Sie die Auswirkungen verschiedener Techniken auf die Modellleistung und -verallgemeinerung.

Inhaltsverzeichnis

Häufige Szenarien für Überanpassung in ConvNet

Schauen wir uns einige häufige Szenarien einer Überanpassung in ConvNet an:

Szenario 1: Hochkomplexes Modell mit unzureichenden Daten

Die Verwendung eines sehr komplexen Modells, beispielsweise eines tiefen neuronalen Netzwerks, für einen kleinen Datensatz kann zu einer Überanpassung führen. Das Modell merkt sich möglicherweise die Trainingsbeispiele, anstatt das allgemeine Muster zu lernen. Beispielsweise könnte das Training eines tiefen neuronalen Netzwerks mit nur wenigen hundert Bildern für eine komplexe Aufgabe wie die Bilderkennung zu einer Überanpassung führen.

Folge

Das Modell schneidet bei den Trainingsdaten möglicherweise sehr gut ab, lässt sich jedoch nicht auf neue, unbekannte Daten verallgemeinern, was zu einer schlechten Leistung in realen Anwendungen führt.

Wie kann ich dieses Problem beheben?

Erhalten Sie mehr Trainingsdaten und führen Sie eine Bildvergrößerung durch, um unseren Datensatz zu verallgemeinern. Beginnen Sie mit einem weniger komplexen Modell und erhöhen Sie die Komplexität, wenn die Kapazität geringer ist. 

Szenario 2: Übermäßiges Training

Das kontinuierliche Training eines Modells über zu viele Epochen kann zu einer Überanpassung führen. Da das Modell die Trainingsdaten wiederholt sieht, fängt es möglicherweise an, sie sich zu merken, anstatt die zugrunde liegenden Muster zu lernen.

Folge

Die Leistung des Modells kann bei nicht sichtbaren Daten ein Plateau erreichen oder sich sogar verschlechtern, wenn es zunehmend auf den Trainingssatz spezialisiert wird.

Wie kann ich dieses Problem beheben?

Verwenden Sie ein frühes Anhalten, um eine Überanpassung des Modells zu vermeiden und das beste Modell zu speichern. 

Szenario 3: Ignorieren der Regularisierung

Regularisierungstechniken wie die L1- oder L2-Regularisierung werden verwendet, um eine Überanpassung durch Bestrafung komplexer Modelle zu verhindern. Das Ignorieren oder falsche Anpassen von Regularisierungsparametern kann zu einer Überanpassung führen.

Folge

Das Modell wird möglicherweise übermäßig komplex und lässt sich nicht gut auf neue Daten verallgemeinern, was zu einer schlechten Leistung außerhalb des Trainingssatzes führt.

Wie kann ich dieses Problem beheben?

Implementieren Sie Regularisierung, Kreuzvalidierung und Hyperparameteroptimierung. 

Was ist die Kapazität des Modells?

Die Kapazität eines Modells bezieht sich auf die Größe und Komplexität der Muster, die es lernen kann. Bei neuronalen Netzen hängt dies weitgehend davon ab, wie viele Neuronen es hat und wie sie miteinander verbunden sind. Wenn es den Anschein hat, dass Ihr Netzwerk nicht über ausreichend Daten verfügt, sollten Sie versuchen, seine Kapazität zu erhöhen.

Sie können die Kapazität eines Netzwerks erhöhen, indem Sie es entweder breiter machen (mehr Einheiten zu vorhandenen Schichten) oder tiefer machen (mehr Schichten hinzufügen). Größere Netzwerke haben es leichter, linearere Beziehungen zu erlernen, während tiefere Netzwerke eher nichtlineare Beziehungen bevorzugen. Was besser ist, hängt nur vom Datensatz ab.

Interpretation von Lernkurven

Keras bietet die Möglichkeit, Rückrufe beim Training von a zu registrieren Deep-Learning-Modell. Einer der Standardrückrufe, die beim Training aller Deep-Learning-Modelle registriert werden, ist der Verlaufsrückruf. Es zeichnet Trainingsmetriken für jede Epoche auf. Dazu gehören der Verlust und die Genauigkeit (für Klassifizierungsprobleme) sowie der Verlust und die Genauigkeit für den Validierungsdatensatz, sofern einer festgelegt ist.

Das Verlaufsobjekt wird von Aufrufen der Funktion fit() zurückgegeben, die zum Trainieren des Modells verwendet wird. Metriken werden in einem Wörterbuch im Verlaufsmitglied des zurückgegebenen Objekts gespeichert.

Beispielsweise können Sie die in einem Verlaufsobjekt gesammelten Metriken mithilfe des folgenden Codeausschnitts auflisten, nachdem ein Modell trainiert wurde:

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

Ausgang:

['accuracy', 'loss', 'val_accuracy', 'val_loss']

Informationstyp

Sie könnten sich die Informationen in den Trainingsdaten als zwei Arten vorstellen:

  • Signal: Das Signal ist der Teil, der verallgemeinert, der Teil, der unserem Modell helfen kann, Vorhersagen aus neuen Daten zu treffen.
  • Noise: Das Rauschen ist der Teil, der nur auf die Trainingsdaten zutrifft; Das Rauschen besteht aus allen zufälligen Schwankungen, die aus Daten in der realen Welt resultieren, oder aus allen zufälligen, nicht informativen Mustern, die dem Modell nicht wirklich dabei helfen können, Vorhersagen zu treffen. Das Geräusch ist, dass das Teil vielleicht nützlich aussieht, es aber nicht wirklich ist.

Wenn wir ein Modell trainieren, zeichnen wir den Verlust des Trainingssatzes Epoche für Epoche auf. Dazu fügen wir auch ein Diagramm der Validierungsdaten hinzu. Diese Diagramme nennen wir Lernkurven. Um Deep-Learning-Modelle effektiv zu trainieren, müssen wir sie interpretieren können.

Lernkurven

In der obigen Abbildung können wir sehen, dass der Trainingsverlust mit zunehmender Epoche abnimmt, der Validierungsverlust jedoch zunächst abnimmt und zunimmt, wenn das Modell beginnt, im Datensatz vorhandenes Rauschen zu erfassen. Jetzt werden wir sehen, wie wir durch verschiedene Techniken eine Überanpassung in ConvNets vermeiden können. 

Methoden zur Vermeidung von Überanpassung

Jetzt haben wir einige Szenarien gesehen und erfahren, wie man Lernkurven interpretiert, um eine Überanpassung zu erkennen. Schauen wir uns einige Methoden an, um eine Überanpassung in einem neuronalen Netzwerk zu vermeiden:

Methode 1: Mehr Daten verwenden

Durch Erhöhen der Größe Ihres Datensatzes kann das Modell besser verallgemeinert werden, da es über vielfältigere Beispiele verfügt, aus denen man lernen kann. Das Modell findet wichtige Muster im Datensatz und ignoriert Rauschen, da das Modell erkennt, dass diese spezifischen Muster (Rauschen) nicht im gesamten Datensatz vorhanden sind.

Methode 2: Frühzeitiges Stoppen

Beim frühen Stoppen handelt es sich um eine Technik zur Verhinderung einer Überanpassung, indem die Leistung des Modells anhand eines Validierungssatzes während des Trainings überwacht wird. Das Training wird gestoppt, wenn die Leistung des Validierungssatzes nachzulassen beginnt, was darauf hindeutet, dass das Modell zu überanpassen beginnt. Typischerweise wird zur Überwachung der Leistung ein separater Validierungssatz verwendet und das Training wird gestoppt, wenn sich die Leistung über eine bestimmte Anzahl von Epochen nicht verbessert hat.

Unter- und Überanpassung

Methode 3: Ausstieg

Wir wissen, dass eine Überanpassung dadurch verursacht wird, dass das Netzwerk falsche Muster (Rauschen) in den Trainingsdaten lernt. Um diese falschen Muster zu erkennen, greift ein Netzwerk häufig auf ganz bestimmte Gewichtskombinationen zurück, eine Art „Verschwörung“ von Gewichten. Da sie so spezifisch sind, neigen sie dazu, fragil zu sein: Entfernen Sie einen und die Verschwörung bricht zusammen.

Das ist die Idee hinter Dropout. Um diese Verschwörungen aufzudecken, lassen wir bei jedem Trainingsschritt zufällig einen Bruchteil der Eingabeeinheiten einer Schicht weg, was es für das Netzwerk viel schwieriger macht, diese falschen Muster in den Trainingsdaten zu lernen. Stattdessen muss nach breiten, allgemeinen Mustern gesucht werden, deren Gewichtsmuster tendenziell robuster sind. 

Man könnte sich Dropout auch als eine Art Ensemble von Netzwerken vorstellen. Die Vorhersagen werden nicht mehr von einem großen Netzwerk getroffen, sondern von einem Gremium kleinerer Netzwerke. Einzelne Mitglieder des Ausschusses neigen dazu, unterschiedliche Arten von Fehlern zu machen, haben aber gleichzeitig Recht, wodurch der Ausschuss als Ganzes besser ist als jeder Einzelne. (Wenn Sie mit Random Forests als Ensemble von Entscheidungsbäumen vertraut sind, handelt es sich um dieselbe Idee.)

Überanpassung in ConvNets

Methode 4: Batch-Normalisierung

Die nächste spezielle Methode, die wir uns ansehen, führt eine „Batch-Normalisierung“ (oder „Batchnorm“) durch, die dabei helfen kann, langsames oder instabiles Training zu korrigieren.

Bei neuronalen Netzen ist es im Allgemeinen eine gute Idee, alle Ihre Daten auf eine gemeinsame Skala zu bringen, vielleicht mit etwas wie StandardScaler oder MinMaxScaler von scikit-learn. Der Grund dafür ist, dass SGD die Netzwerkgewichte proportional dazu verschiebt, wie groß die Aktivierung der Daten ist. Funktionen, die dazu neigen, sehr unterschiedlich große Aktivierungen hervorzurufen, können zu einem instabilen Trainingsverhalten führen.

Wenn es nun gut ist, die Daten zu normalisieren, bevor sie in das Netzwerk gelangen, wäre es vielleicht besser, auch innerhalb des Netzwerks zu normalisieren! Tatsächlich verfügen wir über eine spezielle Art von Schicht, die dies tun kann, die Batch-Normalisierungsschicht. Eine Stapelnormalisierungsschicht betrachtet jeden eingehenden Stapel, normalisiert den Stapel zunächst mit seinem eigenen Mittelwert und seiner eigenen Standardabweichung und bringt die Daten dann mit zwei trainierbaren Neuskalierungsparametern auf eine neue Skala. Tatsächlich führt Batchnorm eine Art koordinierte Neuskalierung seiner Eingaben durch.

Am häufigsten wird Batchnorm als Hilfsmittel zum Optimierungsprozess hinzugefügt (obwohl es manchmal auch die Vorhersageleistung verbessern kann). Modelle mit Batchnorm benötigen tendenziell weniger Epochen, um das Training abzuschließen. Darüber hinaus kann Batchnorm auch verschiedene Probleme beheben, die dazu führen können, dass das Training „hängen bleibt“. Erwägen Sie, Ihren Modellen eine Batch-Normalisierung hinzuzufügen, insbesondere wenn beim Training Probleme auftreten.

Methode 5: L1- und L2-Regularisierung

Bei der L1- und L2-Regularisierung handelt es sich um Techniken zur Verhinderung einer Überanpassung durch Bestrafung großer Gewichte im neuronalen Netzwerk. Die L1-Regularisierung fügt der Verlustfunktion einen Strafterm hinzu, der proportional zum absoluten Wert der Gewichte ist. Es fördert die Sparsamkeit bei den Gewichten und kann zu einer Feature-Auswahl führen. Die L2-Regularisierung, auch als Gewichtsabfall bekannt, fügt der Verlustfunktion einen Strafterm hinzu, der proportional zum Quadrat der Gewichte ist. Es verhindert, dass die Gewichte zu groß werden und fördert eine gleichmäßigere Gewichtsverteilung.

Die Wahl zwischen L1- und L2-Regularisierung hängt häufig vom spezifischen Problem und den gewünschten Eigenschaften des Modells ab.

Hohe Werte für die L1/L2-Regularisierung führen dazu, dass das Modell nicht schnell lernt und beim Lernen ein Plateau erreicht, was zu einer Unteranpassung des Modells führt. 

Methode 6: Datenerweiterung

Der beste Weg, die Leistung eines maschinellen Lernmodells zu verbessern, besteht darin, es auf mehr Daten zu trainieren. Je mehr Beispiele das Modell lernen muss, desto besser kann es erkennen, welche Unterschiede in den Bildern wichtig sind und welche nicht. Mehr Daten helfen dem Modell, besser zu verallgemeinern.

Eine einfache Möglichkeit, mehr Daten zu erhalten, besteht darin, die Daten zu verwenden, die Sie bereits haben. Wenn wir die Bilder in unserem Datensatz so transformieren können, dass die Klasse erhalten bleibt (Beispiel: MNIST-Ziffernklassifizierung, wenn wir Augment 6 versuchen, wird es schwierig sein, zwischen 6 und 9 zu unterscheiden), können wir unserem Klassifikator beibringen, diese Art von Transformationen zu ignorieren. Ob ein Auto beispielsweise auf einem Foto nach links oder rechts zeigt, ändert nichts an der Tatsache, dass es sich um ein Auto und nicht um einen LKW handelt. Wenn wir also unsere Trainingsdaten mit gespiegelten Bildern erweitern, lernt unser Klassifikator, dass „links oder rechts“ ein Unterschied ist, den er ignorieren sollte.

Und das ist die ganze Idee hinter der Datenerweiterung: Fügen Sie einige zusätzliche gefälschte Daten hinzu, die den echten Daten einigermaßen ähneln, und Ihr Klassifikator wird verbessert. 

Denken Sie daran, dass der Schlüssel zur Vermeidung einer Überanpassung darin besteht, sicherzustellen, dass Ihr Modell gut verallgemeinert werden kann. Überprüfen Sie die Leistung Ihres Modells immer anhand eines Validierungssatzes, nicht nur des Trainingssatzes.

Implementierung der oben genannten Methoden mit Daten

Lassen Sie uns die Implementierungsschritte für die oben genannten Methoden untersuchen:

Schritt 1: Laden der erforderlichen Bibliotheken

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

Schritt 2: Datensatz laden und vorverarbeiten

#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

Schritt 3: Datensatz lernen

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

Ausgang:

Output
np.unique(y_train)

Ausgang:

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

Schritt 4: Visualisieren des Bildes aus dem Datensatz

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

Lassen Sie uns nun Hyperparameter initialisieren und das Modell mit Optimierer, Verlustfunktion und Bewertungsmetrik kompilieren.


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

Schritt 6: Trainingsmodell

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

Schritt 7: Bewerten Sie das Modell

Diese teilen uns die im Verlaufsobjekt enthaltenen Informationen mit und wir verwenden diese, um unsere Informationskurven zu erstellen.

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)
Überanpassung in ConvNets

Anhand der Kurven können wir erkennen, dass die Validierungsgenauigkeit nach der 4. Epoche ein Plateau erreicht und das Modell beginnt, Rauschen zu erfassen. Daher werden wir ein frühes Stoppen implementieren, um eine Überanpassung des Modells zu vermeiden und die besten Gewichte basierend auf val_loss wiederherzustellen. Wir werden val_loss verwenden, um das frühe Stoppen zu überwachen, während unser neuronales Netzwerk versucht, Verluste mithilfe von Optimierern zu reduzieren. Genauigkeit und Validierungsgenauigkeit hängen vom Schwellenwert ab (einer Wahrscheinlichkeit, Klassen zu trennen – normalerweise 0.5 für die binäre Klassifizierung). Wenn unser Datensatz also unausgeglichen ist, wäre das in den meisten Fällen ein Verlust, über den wir uns Sorgen machen sollten. 

Schritt 8: Frühzeitiges Stoppen implementieren

Da wir uns keine Sorgen über eine Überanpassung unseres Modells machen, wird durch ein frühes Stoppen verhindert, dass unser Modell eintritt. Es ist eine gute Wahl, eine höhere Anzahl an Epochen und eine entsprechende Geduld zu wählen. Jetzt werden wir dieselbe Modellarchitektur verwenden und mit einem Rückruf zum frühen Stoppen trainieren. 

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)
Überanpassung in ConvNets

Um unsere besten Gewichte zu erfahren, die das Modell genommen hat. 

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

Schritt 9: Modellkomplexität erhöhen

Da unser Modell nicht gut funktioniert und nicht in der Lage ist, genügend Daten zu erfassen. Wir sollten die Komplexität unseres Modells erhöhen und bewerten. 

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

Wir können sehen, dass die Gesamtparameter zunehmen. Dies würde dabei helfen, komplexere Beziehungen in unserem Modell zu finden. Hinweis: Unser Datensatz besteht aus 32 x 32 Bildern. das sind relativ kleine Bilder. Daher führt die Verwendung komplexerer Modelle zu Beginn sicherlich zu einer Überanpassung des Modells, weshalb wir dazu neigen, die Komplexität unseres Modells langsam zu erhöhen.

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

Anhand der obigen Grafiken können wir eindeutig sagen, dass das Modell überangepasst ist. Daher werden wir eine andere Methode namens Drop-Out-Normalisierung und Batch-Normalisierung verwenden.

Schritt 10: Verwenden von Dropout-Ebenen und Batch-Normalisierungsebenen

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()
Output
# 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)
Überanpassung 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)

Aus den Lerndiagrammen können wir erkennen, dass das Modell selbst mit Batchnormalisierungs- und Dropout-Ebenen überpasst. Daher erhöht sich nicht die Komplexität, sondern die Anzahl der Filter. Wir würden weitere Faltungsschichten hinzufügen, um mehr Features zu extrahieren.

Schritt 11: Faltungsschichten erhöhen

Verringern Sie den trainierbaren Parameter, erhöhen Sie jedoch die Faltungsschichten, um mehr Merkmale zu extrahieren.

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)
Signalausgangsmöglichkeiten:
learning_curves(ht)
Überanpassung 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)

Aus der obigen Ausgabe- und Lernkurve können wir schließen, dass das Modell sehr gut funktioniert und eine Überanpassung vermieden hat. Die Trainingsgenauigkeit und Validierungsgenauigkeit liegen sehr nahe beieinander. In diesem Szenario benötigen wir keine weiteren Methoden, um die Überanpassung zu verringern. Dennoch werden wir die L1/L2-Regularisierung untersuchen. 

Schritt 12: Verwendung der L1/L2-Regularisierung

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

Jetzt können wir sehen, dass die L1/L2-Regularisierung selbst nach Verwendung eines niedrigen Strafwerts von 0.0001 dazu führte, dass unser Modell um 4 % unterschritten wurde. Daher ist es ratsam, alle Methoden vorsichtig zusammen anzuwenden. Da sich Batch-Normalisierung und -Regularisierung auf ähnliche Weise auf das Modell auswirken, benötigen wir keine L1/L2-Regularisierung. 

Schritt 13: Datenerweiterung

Wir werden ImageDataGenerator von Tensorflow Keras verwenden.

# 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()
Überanpassung in ConvNets

Dabei handelt es sich um vier erweiterte Bilder und ein Originalbild.

# 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)
Signalausgangsmöglichkeiten:

Wir haben die tqdm-Bibliothek verwendet, um den Fortschritt unserer Erweiterung zu ermitteln.

x_train_augmented.shape, y_train_augmented.shape
Output

Dies ist unser Datensatz nach der Erweiterung. Lassen Sie uns nun diesen Datensatz verwenden und unser Modell trainieren.

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

Wir können sehen, dass das Modell allgemeiner ist und der Verlust abnimmt. Wir haben auch eine bessere Validierungsgenauigkeit erreicht. Daher hat die Datenerweiterung die Genauigkeit unseres Modells erhöht. 

Zusammenfassung

Überanpassung ist ein häufiges Problem beim Deep Learning, insbesondere bei komplexen neuronalen Netzwerkarchitekturen wie ConvNets. Praktiker können eine Überanpassung in ConvNets verhindern, indem sie die Grundursachen verstehen und Szenarien erkennen, in denen sie auftritt. Techniken wie frühes Stoppen, Abbruch, Batch-Normalisierung, Regularisierung und Datenerweiterung können helfen, dieses Problem zu mildern. Die Implementierung dieser Techniken auf den CIFAR-10-Datensatz zeigte erhebliche Verbesserungen bei der Modellverallgemeinerung und -leistung. Die Beherrschung dieser Techniken und das Verständnis ihrer Prinzipien können zu robusten und zuverlässigen neuronalen Netzwerkmodellen führen.

Häufig gestellte Fragen

Q1. Was ist Überanpassung und warum ist sie beim Deep Learning ein Problem? 

A. Überanpassung tritt auf, wenn ein Modell die Trainingsdaten, einschließlich ihres Rauschens und irrelevanter Muster, zu gut lernt, was zu einer schlechten Leistung bei neuen, unsichtbaren Daten führt. Dies stellt ein Problem dar, da überangepasste Modelle nicht effektiv verallgemeinern können, was ihren praktischen Nutzen einschränkt.

Q2. Wie kann ich eine Überanpassung in meinem neuronalen Netzwerkmodell erkennen?

A. Sie können eine Überanpassung in ConvNets erkennen, indem Sie die Lernkurven interpretieren, die die Trainings- und Validierungsmetriken (z. B. Verlust, Genauigkeit) über Epochen darstellen. Wenn sich die Validierungsmetriken nicht mehr verbessern oder sich verschlechtern, während sich die Trainingsmetriken weiter verbessern, ist dies ein Zeichen für eine Überanpassung.

Q3. Was ist ein frühzeitiges Stoppen und wie hilft es, eine Überanpassung zu verhindern?

A. Beim frühen Stoppen handelt es sich um eine Technik, die die Leistung des Modells auf einem Validierungssatz während des Trainings überwacht und den Trainingsprozess stoppt, wenn die Leistung auf dem Validierungssatz nachzulassen beginnt, was auf eine Überanpassung hinweist. Es trägt dazu bei, eine Überanpassung des Modells zu verhindern, indem das Training zum richtigen Zeitpunkt gestoppt wird.

Q4. Wie hilft die Datenerweiterung dabei, eine Überanpassung abzumildern? 

A. Datenerweiterung ist der Prozess der Generierung neuer, synthetischer Trainingsdaten durch Anwenden von Transformationen (z. B. Spiegeln, Drehen, Skalieren) auf die vorhandenen Daten. Dadurch lässt sich das Modell besser verallgemeinern, indem es vielfältigeren Beispielen ausgesetzt wird, wodurch das Risiko einer Überanpassung in ConvNets an die begrenzten Trainingsdaten verringert wird.

spot_img

Neueste Intelligenz

spot_img