Wie kann man Keras sagen, dass sie das Training basierend auf dem Verlustwert beenden sollen?


82

Derzeit verwende ich den folgenden Code:

callbacks = [
    EarlyStopping(monitor='val_loss', patience=2, verbose=0),
    ModelCheckpoint(kfold_weights_path, monitor='val_loss', save_best_only=True, verbose=0),
]
model.fit(X_train.astype('float32'), Y_train, batch_size=batch_size, nb_epoch=nb_epoch,
      shuffle=True, verbose=1, validation_data=(X_valid, Y_valid),
      callbacks=callbacks)

Es fordert Keras auf, das Training abzubrechen, wenn sich der Verlust in zwei Epochen nicht verbessert hat. Aber ich möchte das Training beenden, nachdem der Verlust kleiner als ein konstantes "THR" geworden ist:

if val_loss < THR:
    break

Ich habe in der Dokumentation gesehen, dass es die Möglichkeit gibt, einen eigenen Rückruf zu tätigen: http://keras.io/callbacks/ Es wurde jedoch nichts gefunden, wie der Trainingsprozess gestoppt werden kann. Ich brauche einen Rat.

Antworten:


85

Ich habe die Antwort gefunden. Ich habe in Keras-Quellen gesucht und Code für EarlyStopping herausgefunden. Darauf basierend habe ich meinen eigenen Rückruf gemacht:

class EarlyStoppingByLossVal(Callback):
    def __init__(self, monitor='val_loss', value=0.00001, verbose=0):
        super(Callback, self).__init__()
        self.monitor = monitor
        self.value = value
        self.verbose = verbose

    def on_epoch_end(self, epoch, logs={}):
        current = logs.get(self.monitor)
        if current is None:
            warnings.warn("Early stopping requires %s available!" % self.monitor, RuntimeWarning)

        if current < self.value:
            if self.verbose > 0:
                print("Epoch %05d: early stopping THR" % epoch)
            self.model.stop_training = True

Und Verwendung:

callbacks = [
    EarlyStoppingByLossVal(monitor='val_loss', value=0.00001, verbose=1),
    # EarlyStopping(monitor='val_loss', patience=2, verbose=0),
    ModelCheckpoint(kfold_weights_path, monitor='val_loss', save_best_only=True, verbose=0),
]
model.fit(X_train.astype('float32'), Y_train, batch_size=batch_size, nb_epoch=nb_epoch,
      shuffle=True, verbose=1, validation_data=(X_valid, Y_valid),
      callbacks=callbacks)

1
Nur wenn es für jemanden nützlich ist - in meinem Fall habe ich monitor = 'loss' verwendet, es hat gut funktioniert.
QtRoS

15
Es scheint, dass Keras aktualisiert wurde. In die EarlyStopping- Rückruffunktion ist jetzt min_delta integriert. Sie müssen den Quellcode nicht mehr hacken, yay! stackoverflow.com/a/41459368/3345375
jkdev

3
Nach dem erneuten Lesen der Fragen und Antworten muss ich mich korrigieren: min_delta bedeutet "Früh aufhören, wenn es nicht genug Verbesserungen pro Epoche (oder pro mehreren Epochen) gibt." Das OP fragte jedoch, wie "frühzeitig gestoppt werden soll, wenn der Verlust ein bestimmtes Niveau unterschreitet".
jkdev

NameError: Name 'Rückruf' ist nicht definiert ... Wie werde ich das beheben?
Alyssaeliyah

2
Eliyah versuchen Sie dies: from keras.callbacks import Callback
ZFTurbo

26

Der Rückruf keras.callbacks.EarlyStopping hat ein min_delta-Argument. Aus der Keras-Dokumentation:

min_delta: Die minimale Änderung der überwachten Menge, um als Verbesserung zu gelten, dh eine absolute Änderung von weniger als min_delta, gilt als keine Verbesserung.


3
Als Referenz finden Sie hier die Dokumente für eine frühere Version von Keras (1.1.0), in der das Argument min_delta noch nicht enthalten war: faroit.github.io/keras-docs/1.1.0/callbacks/#earlystopping
jkdev

Wie könnte ich dafür sorgen, dass es nicht aufhört, bis es min_deltaüber mehrere Epochen andauert?
Zyxue

EarlyStopping hat einen weiteren Parameter: Geduld: Anzahl der Epochen ohne Verbesserung, nach denen das Training abgebrochen wird.
Devin

13

Eine Lösung besteht darin, model.fit(nb_epoch=1, ...)eine for-Schleife aufzurufen. Anschließend können Sie eine break-Anweisung in die for-Schleife einfügen und einen beliebigen anderen benutzerdefinierten Steuerungsfluss ausführen.


Es wäre schön, wenn sie einen Rückruf machen würden, der eine einzige Funktion übernimmt, die das kann.
Ehrlichkeit

8

Ich habe das gleiche Problem mit einem benutzerdefinierten Rückruf gelöst.

Weisen Sie im folgenden benutzerdefinierten Rückrufcode THR den Wert zu, bei dem Sie das Training beenden und den Rückruf Ihrem Modell hinzufügen möchten.

from keras.callbacks import Callback

class stopAtLossValue(Callback):

        def on_batch_end(self, batch, logs={}):
            THR = 0.03 #Assign THR with the value at which you want to stop training.
            if logs.get('loss') <= THR:
                 self.model.stop_training = True

2

Während ich den TensorFlow in der Praxis spezialisierte , lernte ich eine sehr elegante Technik. Nur wenig geändert von der akzeptierten Antwort.

Lassen Sie uns das Beispiel mit unseren bevorzugten MNIST-Daten setzen.

import tensorflow as tf

class new_callback(tf.keras.callbacks.Callback):
    def epoch_end(self, epoch, logs={}): 
        if(logs.get('accuracy')> 0.90): # select the accuracy
            print("\n !!! 90% accuracy, no further training !!!")
            self.model.stop_training = True

mnist = tf.keras.datasets.mnist

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0 #normalize

callbacks = new_callback()

# model = tf.keras.models.Sequential([# define your model here])

model.compile(optimizer=tf.optimizers.Adam(),
          loss='sparse_categorical_crossentropy',
          metrics=['accuracy'])
model.fit(x_train, y_train, epochs=10, callbacks=[callbacks])

Also, hier setze ich die metrics=['accuracy'], und somit wird in der Callback-Klasse die Bedingung gesetzt 'accuracy'> 0.90.

Sie können eine beliebige Metrik auswählen und das Training wie in diesem Beispiel überwachen. Am wichtigsten ist, dass Sie unterschiedliche Bedingungen für unterschiedliche Metriken festlegen und gleichzeitig verwenden können.

Hoffentlich hilft das!


Funktionsname sollte on_epoch_end
xarion

0

Für mich würde das Modell das Training nur beenden, wenn ich eine return-Anweisung hinzufügen würde, nachdem ich den Parameter stop_training auf True gesetzt habe, weil ich nach self.model.evaluate aufgerufen habe. Stellen Sie also entweder sicher, dass stop_training = True am Ende der Funktion steht, oder fügen Sie eine return-Anweisung hinzu.

def on_epoch_end(self, batch, logs):
        self.epoch += 1
        self.stoppingCounter += 1
        print('\nstopping counter \n',self.stoppingCounter)

        #Stop training if there hasn't been any improvement in 'Patience' epochs
        if self.stoppingCounter >= self.patience:
            self.model.stop_training = True
            return

        # Test on additional set if there is one
        if self.testingOnAdditionalSet:
            evaluation = self.model.evaluate(self.val2X, self.val2Y, verbose=0)
            self.validationLoss2.append(evaluation[0])
            self.validationAcc2.append(evaluation[1])enter code here

0

Wenn Sie eine benutzerdefinierte Trainingsschleife verwenden, können Sie collections.dequeeine "rollierende" Liste verwenden, die angehängt werden kann. Die Elemente auf der linken Seite werden angezeigt, wenn die Liste länger als ist maxlen. Hier ist die Zeile:

loss_history = deque(maxlen=early_stopping + 1)

for epoch in range(epochs):
    fit(epoch)
    loss_history.append(test_loss.result().numpy())
    if len(loss_history) > early_stopping and loss_history.popleft() < min(loss_history)
            break

Hier ist ein vollständiges Beispiel:

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
import tensorflow_datasets as tfds
import tensorflow as tf
from tensorflow.keras.layers import Dense
from collections import deque

data, info = tfds.load('iris', split='train', as_supervised=True, with_info=True)

data = data.map(lambda x, y: (tf.cast(x, tf.int32), y))

train_dataset = data.take(120).batch(4)
test_dataset = data.skip(120).take(30).batch(4)

model = tf.keras.models.Sequential([
    Dense(8, activation='relu'),
    Dense(16, activation='relu'),
    Dense(info.features['label'].num_classes)])

loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

train_loss = tf.keras.metrics.Mean()
test_loss = tf.keras.metrics.Mean()

train_acc = tf.keras.metrics.SparseCategoricalAccuracy()
test_acc = tf.keras.metrics.SparseCategoricalAccuracy()

opt = tf.keras.optimizers.Adam(learning_rate=1e-3)


@tf.function
def train_step(inputs, labels):
    with tf.GradientTape() as tape:
        logits = model(inputs, training=True)
        loss = loss_object(labels, logits)

    gradients = tape.gradient(loss, model.trainable_variables)
    opt.apply_gradients(zip(gradients, model.trainable_variables))
    train_loss(loss)
    train_acc(labels, logits)


@tf.function
def test_step(inputs, labels):
    logits = model(inputs, training=False)
    loss = loss_object(labels, logits)
    test_loss(loss)
    test_acc(labels, logits)


def fit(epoch):
    template = 'Epoch {:>2} Train Loss {:.3f} Test Loss {:.3f} ' \
               'Train Acc {:.2f} Test Acc {:.2f}'

    train_loss.reset_states()
    test_loss.reset_states()
    train_acc.reset_states()
    test_acc.reset_states()

    for X_train, y_train in train_dataset:
        train_step(X_train, y_train)

    for X_test, y_test in test_dataset:
        test_step(X_test, y_test)

    print(template.format(
        epoch + 1,
        train_loss.result(),
        test_loss.result(),
        train_acc.result(),
        test_acc.result()
    ))


def main(epochs=50, early_stopping=10):
    loss_history = deque(maxlen=early_stopping + 1)

    for epoch in range(epochs):
        fit(epoch)
        loss_history.append(test_loss.result().numpy())
        if len(loss_history) > early_stopping and loss_history.popleft() < min(loss_history):
            print(f'\nEarly stopping. No validation loss '
                  f'improvement in {early_stopping} epochs.')
            break

if __name__ == '__main__':
    main(epochs=250, early_stopping=10)
Epoch  1 Train Loss 1.730 Test Loss 1.449 Train Acc 0.33 Test Acc 0.33
Epoch  2 Train Loss 1.405 Test Loss 1.220 Train Acc 0.33 Test Acc 0.33
Epoch  3 Train Loss 1.173 Test Loss 1.054 Train Acc 0.33 Test Acc 0.33
Epoch  4 Train Loss 1.006 Test Loss 0.935 Train Acc 0.33 Test Acc 0.33
Epoch  5 Train Loss 0.885 Test Loss 0.846 Train Acc 0.33 Test Acc 0.33
...
Epoch 89 Train Loss 0.196 Test Loss 0.240 Train Acc 0.89 Test Acc 0.87
Epoch 90 Train Loss 0.195 Test Loss 0.239 Train Acc 0.89 Test Acc 0.87
Epoch 91 Train Loss 0.195 Test Loss 0.239 Train Acc 0.89 Test Acc 0.87
Epoch 92 Train Loss 0.194 Test Loss 0.239 Train Acc 0.90 Test Acc 0.87

Early stopping. No validation loss improvement in 10 epochs.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.