Transferlernen und Feinabstimmung

Auf TensorFlow.org ansehen Quelle auf GitHub anzeigen Notizbuch herunterladen

Aufstellen

import numpy as np
import tensorflow as tf
from tensorflow import keras

Einführung

Transfer Lernen besteht aus Funktionen gelernt auf ein Problem nehmen und sie auf ein neues, ähnliches Problem nutzen. Zum Beispiel können Merkmale eines Modells, das gelernt hat, Waschbären zu identifizieren, nützlich sein, um ein Modell zu starten, das Tanukis identifizieren soll.

Transferlernen wird normalerweise für Aufgaben durchgeführt, bei denen Ihr Dataset zu wenig Daten enthält, um ein vollständiges Modell von Grund auf zu trainieren.

Die häufigste Inkarnation von Transfer Learning im Kontext von Deep Learning ist der folgende Workflow:

  1. Nehmen Sie Layer aus einem zuvor trainierten Modell.
  2. Frieren Sie sie ein, um zu vermeiden, dass die darin enthaltenen Informationen in zukünftigen Trainingsrunden zerstört werden.
  3. Fügen Sie einige neue, trainierbare Ebenen über den gefrorenen Ebenen hinzu. Sie lernen, die alten Features in Vorhersagen für einen neuen Datensatz umzuwandeln.
  4. Trainieren Sie die neuen Layer in Ihrem Dataset.

Ein letzter, optionaler Schritt, ist die Feinabstimmung, die das gesamte Modell von unfreezing besteht Sie oben erhalten (oder einen Teil davon), und Umschulung auf die neuen Daten mit einer sehr geringen Lernrate. Dadurch können potenziell sinnvolle Verbesserungen erzielt werden, indem die vortrainierten Features inkrementell an die neuen Daten angepasst werden.

Zuerst werden wir die Keras gehen über trainable API im Detail, die die meisten Transfer Lernen & Feinabstimmung Workflows zugrunde liegt.

Anschließend demonstrieren wir den typischen Arbeitsablauf, indem wir ein mit dem ImageNet-Dataset vortrainiertes Modell nehmen und es mit dem Kaggle-Klassifikations-Dataset "Katzen vs. Hunde" erneut trainieren.

Dies ist aus angepasst Deep Learning mit Python und 2016 Blog - Post den Aufbau leistungsfähiger Bildklassifikationsmodelle mit sehr wenig Daten“ .

Freezing Schichten: das Verständnis trainable Attribut

Layer und Modelle haben drei Gewichtsattribute:

  • weights ist die Liste aller Gewichte Variablen der Schicht.
  • trainable_weights ist die Liste derer , die (über Gradientenabfallsaktualisierung) aktualisiert werden sollen , um den Verlust während des Trainings zu minimieren.
  • non_trainable_weights ist die Liste derer , die nicht geschult werden sollen. Normalerweise werden sie vom Modell während des Vorwärtsdurchlaufs aktualisiert.

Beispiel: Die Dense Schicht 2 trainierbar Gewichte (kernel & bias)

layer = keras.layers.Dense(3)
layer.build((None, 4))  # Create the weights

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 2
trainable_weights: 2
non_trainable_weights: 0

Generell sind alle Gewichte trainierbare Gewichte. Die einzige Einbau-Schicht , die nicht-trainierbar Gewichte hat , ist die BatchNormalization Schicht. Es verwendet nicht trainierbare Gewichte, um den Mittelwert und die Varianz seiner Eingaben während des Trainings zu verfolgen. Um zu erfahren , wie die Verwendung nicht-trainierbar Gewichte in Ihre eigenen Schichten finden Sie in der Anleitung zum Schreiben neuer Schichten von Grund auf neu .

Beispiel: Die BatchNormalization Schicht 2 trainierbar Gewichte und 2 nicht trainierbar Gewicht

layer = keras.layers.BatchNormalization()
layer.build((None, 4))  # Create the weights

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 4
trainable_weights: 2
non_trainable_weights: 2

Layers & Modelle verfügen zudem über eine Boolesche Attribut trainable . Sein Wert kann geändert werden. Einstellen layer.trainable auf False bewegt alle Gewichte der Schicht aus trainierbar zu nicht-trainierbar. Dies wird als „Einfrieren“ der Schicht: der Zustand einer gefrorenen Schicht wird während des Trainings nicht aktualisiert werden (entweder beim Training mit fit() oder beim Training mit einer beliebigen benutzerdefinierten Schleife , die auf verlässt sich trainable_weights Gradienten Updates anzuwenden).

Beispiel: Einstellung trainable auf False

layer = keras.layers.Dense(3)
layer.build((None, 4))  # Create the weights
layer.trainable = False  # Freeze the layer

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 2
trainable_weights: 0
non_trainable_weights: 2

Wenn ein trainierbares Gewicht nicht mehr trainierbar wird, wird sein Wert während des Trainings nicht mehr aktualisiert.

# Make a model with 2 layers
layer1 = keras.layers.Dense(3, activation="relu")
layer2 = keras.layers.Dense(3, activation="sigmoid")
model = keras.Sequential([keras.Input(shape=(3,)), layer1, layer2])

# Freeze the first layer
layer1.trainable = False

# Keep a copy of the weights of layer1 for later reference
initial_layer1_weights_values = layer1.get_weights()

# Train the model
model.compile(optimizer="adam", loss="mse")
model.fit(np.random.random((2, 3)), np.random.random((2, 3)))

# Check that the weights of layer1 have not changed during training
final_layer1_weights_values = layer1.get_weights()
np.testing.assert_allclose(
    initial_layer1_weights_values[0], final_layer1_weights_values[0]
)
np.testing.assert_allclose(
    initial_layer1_weights_values[1], final_layer1_weights_values[1]
)
1/1 [==============================] - 1s 640ms/step - loss: 0.0945

Verwechseln Sie nicht das layer.trainable Attribut mit dem Argumente training in layer.__call__() (die steuert , ob die Schicht sollte seinen Vorwärtslauf in Inferenz - Modus oder Trainingsmodus ausgeführt werden ). Weitere Informationen finden Sie in der Keras FAQ .

Rekursive Einstellung des trainable Attribut

Wenn Sie setzen trainable = False auf einem Modell oder auf einer Ebene , die Unterschichten hat, werden alle Kinder Schichten nicht trainierbar als auch.

Beispiel:

inner_model = keras.Sequential(
    [
        keras.Input(shape=(3,)),
        keras.layers.Dense(3, activation="relu"),
        keras.layers.Dense(3, activation="relu"),
    ]
)

model = keras.Sequential(
    [keras.Input(shape=(3,)), inner_model, keras.layers.Dense(3, activation="sigmoid"),]
)

model.trainable = False  # Freeze the outer model

assert inner_model.trainable == False  # All layers in `model` are now frozen
assert inner_model.layers[0].trainable == False  # `trainable` is propagated recursively

Der typische Transfer-Learning-Workflow

Dies führt uns zu der Implementierung eines typischen Transfer-Learning-Workflows in Keras:

  1. Instanziieren Sie ein Basismodell und laden Sie vortrainierte Gewichte hinein.
  2. Frieren Sie alle Ebenen im Basismodell , indem trainable = False .
  3. Erstellen Sie ein neues Modell über der Ausgabe eines (oder mehrerer) Layer aus dem Basismodell.
  4. Trainieren Sie Ihr neues Modell mit Ihrem neuen Dataset.

Beachten Sie, dass ein alternativer, einfacherer Workflow auch sein könnte:

  1. Instanziieren Sie ein Basismodell und laden Sie vortrainierte Gewichte hinein.
  2. Führen Sie Ihr neues Dataset durch und zeichnen Sie die Ausgabe eines (oder mehrerer) Layer aus dem Basismodell auf. Dies wird Merkmalsextraktion genannt.
  3. Verwenden Sie diese Ausgabe als Eingabedaten für ein neues, kleineres Modell.

Ein wesentlicher Vorteil dieses zweiten Workflows besteht darin, dass Sie das Basismodell nur einmal für Ihre Daten ausführen und nicht einmal pro Trainingsepoche. Es ist also viel schneller und billiger.

Ein Problem bei diesem zweiten Workflow besteht jedoch darin, dass Sie die Eingabedaten Ihres neuen Modells während des Trainings nicht dynamisch ändern können, was beispielsweise bei der Datenerweiterung erforderlich ist. Transfer Learning wird normalerweise für Aufgaben verwendet, bei denen Ihr neues Dataset zu wenig Daten enthält, um ein vollständiges Modell von Grund auf zu trainieren, und in solchen Szenarien ist die Datenerweiterung sehr wichtig. Im Folgenden konzentrieren wir uns daher auf den ersten Workflow.

So sieht der erste Workflow in Keras aus:

Instanziieren Sie zunächst ein Basismodell mit vortrainierten Gewichtungen.

base_model = keras.applications.Xception(
    weights='imagenet',  # Load weights pre-trained on ImageNet.
    input_shape=(150, 150, 3),
    include_top=False)  # Do not include the ImageNet classifier at the top.

Frieren Sie dann das Basismodell ein.

base_model.trainable = False

Erstellen Sie oben ein neues Modell.

inputs = keras.Input(shape=(150, 150, 3))
# We make sure that the base_model is running in inference mode here,
# by passing `training=False`. This is important for fine-tuning, as you will
# learn in a few paragraphs.
x = base_model(inputs, training=False)
# Convert features of shape `base_model.output_shape[1:]` to vectors
x = keras.layers.GlobalAveragePooling2D()(x)
# A Dense classifier with a single unit (binary classification)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

Trainieren Sie das Modell mit neuen Daten.

model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=[keras.metrics.BinaryAccuracy()])
model.fit(new_dataset, epochs=20, callbacks=..., validation_data=...)

Feinabstimmung

Sobald Ihr Modell mit den neuen Daten konvergiert ist, können Sie versuchen, das gesamte oder einen Teil des Basismodells aufzuheben und das gesamte Modell mit einer sehr geringen Lernrate von Anfang bis Ende neu zu trainieren.

Dies ist ein optionaler letzter Schritt, der Ihnen potenziell inkrementelle Verbesserungen bringen kann. Es könnte auch möglicherweise zu einer schnellen Überanpassung führen – denken Sie daran.

Es ist entscheidend für diesen Schritt nur zu tun , nachdem das Modell mit gefrorenen Schichten hat Konvergenz geschult. Wenn Sie zufällig initialisierte trainierbare Layer mit trainierbaren Layern mischen, die vortrainierte Features enthalten, verursachen die zufällig initialisierten Layer während des Trainings sehr große Gradientenaktualisierungen, die Ihre vortrainierten Features zerstören.

Es ist auch wichtig, in dieser Phase eine sehr niedrige Lernrate zu verwenden, da Sie ein viel größeres Modell als in der ersten Trainingsrunde mit einem normalerweise sehr kleinen Dataset trainieren. Daher besteht die Gefahr, dass Sie sehr schnell überfitting werden, wenn Sie große Gewichtsaktualisierungen vornehmen. Hier möchten Sie die vortrainierten Gewichte nur schrittweise neu anpassen.

So implementieren Sie die Feinabstimmung des gesamten Basismodells:

# Unfreeze the base model
base_model.trainable = True

# It's important to recompile your model after you make any changes
# to the `trainable` attribute of any inner layer, so that your changes
# are take into account
model.compile(optimizer=keras.optimizers.Adam(1e-5),  # Very low learning rate
              loss=keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=[keras.metrics.BinaryAccuracy()])

# Train end-to-end. Be careful to stop before you overfit!
model.fit(new_dataset, epochs=10, callbacks=..., validation_data=...)

Wichtiger Hinweis zur compile() und trainable

Der Aufruf der compile() auf einem Modell soll „einzufrieren“ das Verhalten dieses Modells. Dies bedeutet , dass die trainable Attributwerte zu der Zeit wird das Modell erstellt während der gesamten Lebensdauer dieses Modells erhalten bleiben, bis die compile erneut aufgerufen wird. Wenn Sie also jede Änderung trainable Wert, stellen Sie sicher , um die Anruf compile() wieder auf dem Modell für die Änderungen berücksichtigt werden.

Wichtige Hinweise zu BatchNormalization Schicht

Viele Bildmodelle enthalten BatchNormalization Schichten. Diese Schicht ist in jeder erdenklichen Hinsicht ein Sonderfall. Hier sind ein paar Dinge zu beachten.

  • BatchNormalization enthält 2 nicht trainierbar Gewichte , die während des Trainings aktualisiert. Dies sind die Variablen, die den Mittelwert und die Varianz der Eingaben verfolgen.
  • Wenn Sie setzen bn_layer.trainable = False , die BatchNormalization wird Schicht in Inferenz - Modus ausgeführt und wird nicht sein Mittelwert & Varianz Statistiken aktualisieren. Dies ist nicht der Fall für andere Schichten im allgemeinen als Gewicht Trainierbarkeit & Inferenz / Trainings - Modi sind zwei orthogonale Konzepte . Aber die beiden sind in dem Fall der gebundenen BatchNormalization Schicht.
  • Wenn Sie ein Modell aufzutauen , die enthält BatchNormalization Schichten, um die Feinabstimmung zu tun, sollten Sie die halten BatchNormalization Schichten in Inferenz - Modus , indem training=False , wenn das Basismodell aufrufen. Andernfalls zerstören die Aktualisierungen, die auf die nicht trainierbaren Gewichtungen angewendet werden, plötzlich, was das Modell gelernt hat.

Im End-to-End-Beispiel am Ende dieses Handbuchs sehen Sie dieses Muster in Aktion.

Übertragen Sie Lernen und Feinabstimmung mit einer benutzerdefinierten Trainingsschleife

Wenn statt fit() , die Sie Ihre eigene Low-Level - Training - Schleife, die Workflow - Aufenthalte im Wesentlichen gleich. Sie sollten vorsichtig sein , um nur die Liste zu berücksichtigen model.trainable_weights wenn Gradient Updates der Anwendung:

# Create base model
base_model = keras.applications.Xception(
    weights='imagenet',
    input_shape=(150, 150, 3),
    include_top=False)
# Freeze base model
base_model.trainable = False

# Create new model on top.
inputs = keras.Input(shape=(150, 150, 3))
x = base_model(inputs, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)
optimizer = keras.optimizers.Adam()

# Iterate over the batches of a dataset.
for inputs, targets in new_dataset:
    # Open a GradientTape.
    with tf.GradientTape() as tape:
        # Forward pass.
        predictions = model(inputs)
        # Compute the loss value for this batch.
        loss_value = loss_fn(targets, predictions)

    # Get gradients of loss wrt the *trainable* weights.
    gradients = tape.gradient(loss_value, model.trainable_weights)
    # Update the weights of the model.
    optimizer.apply_gradients(zip(gradients, model.trainable_weights))

Ebenso zum Feintuning.

Ein End-to-End-Beispiel: Feinabstimmung eines Bildklassifizierungsmodells für einen Katzen- vs.-Hunde-Datensatz

Um diese Konzepte zu festigen, führen wir Sie durch ein konkretes End-to-End-Beispiel für Transferlernen und Feinabstimmung. Wir werden das in ImageNet vortrainierte Xception-Modell laden und auf den Kaggle-Klassifikationsdatensatz „Katzen vs. Hunde“ anwenden.

Abrufen der Daten

Lassen Sie uns zunächst den Datensatz Katzen vs. Hunde mit TFDS abrufen. Wenn Sie Ihre eigene Datenmenge haben, werden Sie wahrscheinlich wollen das Dienstprogramm verwenden tf.keras.preprocessing.image_dataset_from_directory ähnlich markierten Datensatzes aus einem Satz von Bildern auf dem Datenträger - Objekte in klassenspezifischen Ordnern abgelegt zu erzeugen.

Transferlernen ist am nützlichsten, wenn Sie mit sehr kleinen Datensätzen arbeiten. Um unseren Datensatz klein zu halten, verwenden wir 40 % der ursprünglichen Trainingsdaten (25.000 Bilder) zum Training, 10 % zur Validierung und 10 % zum Testen.

import tensorflow_datasets as tfds

tfds.disable_progress_bar()

train_ds, validation_ds, test_ds = tfds.load(
    "cats_vs_dogs",
    # Reserve 10% for validation and 10% for test
    split=["train[:40%]", "train[40%:50%]", "train[50%:60%]"],
    as_supervised=True,  # Include labels
)

print("Number of training samples: %d" % tf.data.experimental.cardinality(train_ds))
print(
    "Number of validation samples: %d" % tf.data.experimental.cardinality(validation_ds)
)
print("Number of test samples: %d" % tf.data.experimental.cardinality(test_ds))
Number of training samples: 9305
Number of validation samples: 2326
Number of test samples: 2326

Dies sind die ersten 9 Bilder im Trainings-Dataset – wie Sie sehen, haben sie alle unterschiedliche Größen.

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 10))
for i, (image, label) in enumerate(train_ds.take(9)):
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(image)
    plt.title(int(label))
    plt.axis("off")

png

Wir können auch sehen, dass Label 1 "Hund" und Label 0 "Katze" ist.

Standardisieren der Daten

Unsere Rohbilder haben verschiedene Größen. Außerdem besteht jedes Pixel aus 3 ganzzahligen Werten zwischen 0 und 255 (RGB-Level-Werte). Dies ist nicht gut geeignet, um ein neuronales Netzwerk zu füttern. Wir müssen 2 Dinge tun:

  • Standardisieren Sie auf eine feste Bildgröße. Wir wählen 150x150.
  • Normalisieren Pixelwerte zwischen -1 und 1. Wir tun dies unter Verwendung einer Normalization selbst Schicht als Teil des Modells.

Im Allgemeinen empfiehlt es sich, Modelle zu entwickeln, die Rohdaten als Eingabe verwenden, im Gegensatz zu Modellen, die bereits vorverarbeitete Daten verwenden. Der Grund dafür ist, dass Sie, wenn Ihr Modell vorverarbeitete Daten erwartet, jedes Mal, wenn Sie Ihr Modell exportieren, um es an anderer Stelle (in einem Webbrowser, in einer mobilen App) zu verwenden, genau dieselbe Vorverarbeitungspipeline erneut implementieren müssen. Das wird sehr schnell sehr knifflig. Daher sollten wir so wenig wie möglich vorverarbeiten, bevor wir das Modell treffen.

Hier werden wir die Bildgröße in der Datenpipeline ändern (da ein tiefes neuronales Netzwerk nur zusammenhängende Datenbatches verarbeiten kann) und die Eingabewertskalierung als Teil des Modells bei der Erstellung vornehmen.

Lassen Sie uns die Größe der Bilder auf 150 x 150 ändern:

size = (150, 150)

train_ds = train_ds.map(lambda x, y: (tf.image.resize(x, size), y))
validation_ds = validation_ds.map(lambda x, y: (tf.image.resize(x, size), y))
test_ds = test_ds.map(lambda x, y: (tf.image.resize(x, size), y))

Lassen Sie uns außerdem die Daten stapeln und Caching & Prefetching verwenden, um die Ladegeschwindigkeit zu optimieren.

batch_size = 32

train_ds = train_ds.cache().batch(batch_size).prefetch(buffer_size=10)
validation_ds = validation_ds.cache().batch(batch_size).prefetch(buffer_size=10)
test_ds = test_ds.cache().batch(batch_size).prefetch(buffer_size=10)

Zufällige Datenerweiterung verwenden

Wenn Sie kein großes Bild-Dataset haben, empfiehlt es sich, die Stichprobendiversität künstlich einzuführen, indem Sie zufällige, aber realistische Transformationen auf die Trainingsbilder anwenden, z. B. zufälliges horizontales Spiegeln oder kleine zufällige Drehungen. Dies hilft dabei, das Modell verschiedenen Aspekten der Trainingsdaten auszusetzen und gleichzeitig die Überanpassung zu verlangsamen.

from tensorflow import keras
from tensorflow.keras import layers

data_augmentation = keras.Sequential(
    [layers.RandomFlip("horizontal"), layers.RandomRotation(0.1),]
)

Lassen Sie uns visualisieren, wie das erste Bild des ersten Stapels nach verschiedenen zufälligen Transformationen aussieht:

import numpy as np

for images, labels in train_ds.take(1):
    plt.figure(figsize=(10, 10))
    first_image = images[0]
    for i in range(9):
        ax = plt.subplot(3, 3, i + 1)
        augmented_image = data_augmentation(
            tf.expand_dims(first_image, 0), training=True
        )
        plt.imshow(augmented_image[0].numpy().astype("int32"))
        plt.title(int(labels[0]))
        plt.axis("off")
2021-09-01 18:45:34.772284: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

png

Modell bauen

Lassen Sie uns nun ein Modell erstellen, das der Blaupause folgt, die wir zuvor erklärt haben.

Beachten Sie, dass:

  • Wir fügen eine Rescaling Schicht Eingabewerte maßstabs (anfänglich in dem [0, 255] Bereich) an den [-1, 1] Bereich.
  • Wir fügen eine Dropout - Schicht vor der Klassifikationsschicht, für Regularisierung.
  • Wir stellen sicher , passieren training=False , wenn das Basismodell nennen, so dass es in Inferenz - Modus ausgeführt wird , so dass batchnorm Statistiken aktualisiert werden nicht einmal , nachdem wir das Basismodell für die Feinabstimmung aufzutauen.
base_model = keras.applications.Xception(
    weights="imagenet",  # Load weights pre-trained on ImageNet.
    input_shape=(150, 150, 3),
    include_top=False,
)  # Do not include the ImageNet classifier at the top.

# Freeze the base_model
base_model.trainable = False

# Create new model on top
inputs = keras.Input(shape=(150, 150, 3))
x = data_augmentation(inputs)  # Apply random data augmentation

# Pre-trained Xception weights requires that input be scaled
# from (0, 255) to a range of (-1., +1.), the rescaling layer
# outputs: `(inputs * scale) + offset`
scale_layer = keras.layers.Rescaling(scale=1 / 127.5, offset=-1)
x = scale_layer(x)

# The base model contains batchnorm layers. We want to keep them in inference mode
# when we unfreeze the base model for fine-tuning, so we make sure that the
# base_model is running in inference mode here.
x = base_model(x, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
x = keras.layers.Dropout(0.2)(x)  # Regularize with dropout
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

model.summary()
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/xception/xception_weights_tf_dim_ordering_tf_kernels_notop.h5
83689472/83683744 [==============================] - 2s 0us/step
83697664/83683744 [==============================] - 2s 0us/step
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_5 (InputLayer)         [(None, 150, 150, 3)]     0         
_________________________________________________________________
sequential_3 (Sequential)    (None, 150, 150, 3)       0         
_________________________________________________________________
rescaling (Rescaling)        (None, 150, 150, 3)       0         
_________________________________________________________________
xception (Functional)        (None, 5, 5, 2048)        20861480  
_________________________________________________________________
global_average_pooling2d (Gl (None, 2048)              0         
_________________________________________________________________
dropout (Dropout)            (None, 2048)              0         
_________________________________________________________________
dense_7 (Dense)              (None, 1)                 2049      
=================================================================
Total params: 20,863,529
Trainable params: 2,049
Non-trainable params: 20,861,480
_________________________________________________________________

Trainiere die oberste Schicht

model.compile(
    optimizer=keras.optimizers.Adam(),
    loss=keras.losses.BinaryCrossentropy(from_logits=True),
    metrics=[keras.metrics.BinaryAccuracy()],
)

epochs = 20
model.fit(train_ds, epochs=epochs, validation_data=validation_ds)
Epoch 1/20
151/291 [==============>...............] - ETA: 3s - loss: 0.1979 - binary_accuracy: 0.9096
Corrupt JPEG data: 65 extraneous bytes before marker 0xd9
268/291 [==========================>...] - ETA: 1s - loss: 0.1663 - binary_accuracy: 0.9269
Corrupt JPEG data: 239 extraneous bytes before marker 0xd9
282/291 [============================>.] - ETA: 0s - loss: 0.1628 - binary_accuracy: 0.9284
Corrupt JPEG data: 1153 extraneous bytes before marker 0xd9
Corrupt JPEG data: 228 extraneous bytes before marker 0xd9
291/291 [==============================] - ETA: 0s - loss: 0.1620 - binary_accuracy: 0.9286
Corrupt JPEG data: 2226 extraneous bytes before marker 0xd9
291/291 [==============================] - 29s 63ms/step - loss: 0.1620 - binary_accuracy: 0.9286 - val_loss: 0.0814 - val_binary_accuracy: 0.9686
Epoch 2/20
291/291 [==============================] - 8s 29ms/step - loss: 0.1178 - binary_accuracy: 0.9511 - val_loss: 0.0785 - val_binary_accuracy: 0.9695
Epoch 3/20
291/291 [==============================] - 9s 30ms/step - loss: 0.1121 - binary_accuracy: 0.9536 - val_loss: 0.0748 - val_binary_accuracy: 0.9712
Epoch 4/20
291/291 [==============================] - 9s 29ms/step - loss: 0.1082 - binary_accuracy: 0.9554 - val_loss: 0.0754 - val_binary_accuracy: 0.9703
Epoch 5/20
291/291 [==============================] - 8s 29ms/step - loss: 0.1034 - binary_accuracy: 0.9570 - val_loss: 0.0721 - val_binary_accuracy: 0.9725
Epoch 6/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0975 - binary_accuracy: 0.9602 - val_loss: 0.0748 - val_binary_accuracy: 0.9699
Epoch 7/20
291/291 [==============================] - 9s 29ms/step - loss: 0.0989 - binary_accuracy: 0.9595 - val_loss: 0.0732 - val_binary_accuracy: 0.9716
Epoch 8/20
291/291 [==============================] - 8s 29ms/step - loss: 0.1027 - binary_accuracy: 0.9566 - val_loss: 0.0787 - val_binary_accuracy: 0.9678
Epoch 9/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0959 - binary_accuracy: 0.9614 - val_loss: 0.0734 - val_binary_accuracy: 0.9729
Epoch 10/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0995 - binary_accuracy: 0.9588 - val_loss: 0.0717 - val_binary_accuracy: 0.9721
Epoch 11/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0957 - binary_accuracy: 0.9612 - val_loss: 0.0731 - val_binary_accuracy: 0.9725
Epoch 12/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0936 - binary_accuracy: 0.9622 - val_loss: 0.0751 - val_binary_accuracy: 0.9716
Epoch 13/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0965 - binary_accuracy: 0.9610 - val_loss: 0.0821 - val_binary_accuracy: 0.9695
Epoch 14/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0939 - binary_accuracy: 0.9618 - val_loss: 0.0742 - val_binary_accuracy: 0.9712
Epoch 15/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0974 - binary_accuracy: 0.9585 - val_loss: 0.0771 - val_binary_accuracy: 0.9712
Epoch 16/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0947 - binary_accuracy: 0.9621 - val_loss: 0.0823 - val_binary_accuracy: 0.9699
Epoch 17/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0947 - binary_accuracy: 0.9625 - val_loss: 0.0718 - val_binary_accuracy: 0.9708
Epoch 18/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0928 - binary_accuracy: 0.9616 - val_loss: 0.0738 - val_binary_accuracy: 0.9716
Epoch 19/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0922 - binary_accuracy: 0.9644 - val_loss: 0.0743 - val_binary_accuracy: 0.9716
Epoch 20/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0885 - binary_accuracy: 0.9635 - val_loss: 0.0745 - val_binary_accuracy: 0.9695
<keras.callbacks.History at 0x7f849a3b2950>

Führen Sie eine Feinabstimmung des gesamten Modells durch

Lassen Sie uns schließlich das Basismodell auftauen und das gesamte Modell mit einer geringen Lernrate von Anfang bis Ende trainieren.

Wichtig ist , obwohl das Basismodell trainierbar wird, ist es immer noch in Inferenz - Modus ausgeführt wird, da wir übergeben training=False beim Aufruf wenn wir das Modell gebaut. Dies bedeutet, dass die Stapelnormalisierungsebenen darin ihre Stapelstatistiken nicht aktualisieren. Wenn sie es täten, würden sie die vom Modell bisher gelernten Darstellungen verwüsten.

# Unfreeze the base_model. Note that it keeps running in inference mode
# since we passed `training=False` when calling it. This means that
# the batchnorm layers will not update their batch statistics.
# This prevents the batchnorm layers from undoing all the training
# we've done so far.
base_model.trainable = True
model.summary()

model.compile(
    optimizer=keras.optimizers.Adam(1e-5),  # Low learning rate
    loss=keras.losses.BinaryCrossentropy(from_logits=True),
    metrics=[keras.metrics.BinaryAccuracy()],
)

epochs = 10
model.fit(train_ds, epochs=epochs, validation_data=validation_ds)
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_5 (InputLayer)         [(None, 150, 150, 3)]     0         
_________________________________________________________________
sequential_3 (Sequential)    (None, 150, 150, 3)       0         
_________________________________________________________________
rescaling (Rescaling)        (None, 150, 150, 3)       0         
_________________________________________________________________
xception (Functional)        (None, 5, 5, 2048)        20861480  
_________________________________________________________________
global_average_pooling2d (Gl (None, 2048)              0         
_________________________________________________________________
dropout (Dropout)            (None, 2048)              0         
_________________________________________________________________
dense_7 (Dense)              (None, 1)                 2049      
=================================================================
Total params: 20,863,529
Trainable params: 20,809,001
Non-trainable params: 54,528
_________________________________________________________________
Epoch 1/10
291/291 [==============================] - 43s 131ms/step - loss: 0.0802 - binary_accuracy: 0.9692 - val_loss: 0.0580 - val_binary_accuracy: 0.9764
Epoch 2/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0542 - binary_accuracy: 0.9792 - val_loss: 0.0529 - val_binary_accuracy: 0.9764
Epoch 3/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0400 - binary_accuracy: 0.9832 - val_loss: 0.0510 - val_binary_accuracy: 0.9798
Epoch 4/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0313 - binary_accuracy: 0.9879 - val_loss: 0.0505 - val_binary_accuracy: 0.9819
Epoch 5/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0272 - binary_accuracy: 0.9904 - val_loss: 0.0485 - val_binary_accuracy: 0.9807
Epoch 6/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0284 - binary_accuracy: 0.9901 - val_loss: 0.0497 - val_binary_accuracy: 0.9824
Epoch 7/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0198 - binary_accuracy: 0.9937 - val_loss: 0.0530 - val_binary_accuracy: 0.9802
Epoch 8/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0173 - binary_accuracy: 0.9930 - val_loss: 0.0572 - val_binary_accuracy: 0.9819
Epoch 9/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0113 - binary_accuracy: 0.9958 - val_loss: 0.0555 - val_binary_accuracy: 0.9837
Epoch 10/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0091 - binary_accuracy: 0.9966 - val_loss: 0.0596 - val_binary_accuracy: 0.9832
<keras.callbacks.History at 0x7f83982d4cd0>

Nach 10 Epochen bringt uns hier die Feinabstimmung eine schöne Verbesserung.