Keras: EarlyStopping использует потерю проверки необученной сети вместо Inf

Я реализовал раннюю остановку с помощью «ModelCheckpoint». Цель состоит в том, чтобы выбрать наиболее обученную версию модели и вернуть соответствующий номер эпохи.

early_stopping = EarlyStopping(monitor='val_loss', patience=earlyStoppingEpochs, restore_best_weights=False) 
checkpoint_filepath = '/tmp/checkpoint.weights.h5'
checkpoint = ModelCheckpoint(filepath=checkpoint_filepath, save_weights_only=True, monitor = "val_loss", mode = "min", save_best_only=True, verbose=verbose)
history = model.fit(Train_Data, Train_Label, epochs=epochs, batch_size=batch_size, validation_data=(Val_Data, Val_Label), verbose=verbose, callbacks=[early_stopping, checkpoint])
hist = model.history.history['val_loss']
finalEpochs = np.argmin(hist) + 1 
model.load_weights(checkpoint_filepath)

Все работает. Единственный момент, который меня еще смущает, это то, что не учитываются потери при валидации необученной модели. Вместо этого первоначальная потеря устанавливается на Inf. «Эпоха 1: значение val_loss улучшено с **inf ** до 2,35898, модель сохранена в /tmp/checkpoint.weights.h5»

Однако в моем приложении возможно, что даже первая эпоха обучения приведет к ухудшению потерь при проверке. Затем он должен вернуть необученную модель как лучшую модель и идеальный номер эпохи, равный 0.

Знаете ли вы, можно ли настроить поведение так, чтобы в начале EarlyStopping использовалась не inf, а потеря валидации необученной модели?

Оптимизация производительности модели: Руководство по настройке гиперпараметров в Python с Keras
Оптимизация производительности модели: Руководство по настройке гиперпараметров в Python с Keras
Настройка гиперпараметров - это процесс выбора наилучшего набора гиперпараметров для модели машинного обучения с целью оптимизации ее...
Определение пород собак с помощью конволюционных нейронных сетей (CNN)
Определение пород собак с помощью конволюционных нейронных сетей (CNN)
В рамках финального проекта Udacity Data Scietist Nanodegree я разработал алгоритм с использованием конволюционных нейронных сетей (CNN) для...
0
0
70
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В Керасе, согласно этому документу, она:

https://keras.io/api/callbacks/early_stopping/

Вы действительно можете указать параметр start_from_epoch=0 (по умолчанию он равен 0). Чтобы избежать -inf, возможно, вы могли бы начать через несколько эпох (например, 2); в противном случае вам придется реализовать свой собственный обратный вызов для достижения желаемого.

Вот полный пример, протестированный с последней версией keras==3.4.1

from keras.callbacks import Callback
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Flatten
from keras.utils import to_categorical

# Custom EarlyStoppingCallback class definition
class CustomEarlyStoppingCallback(Callback):
    def __init__(self, patience=0, min_delta=0, initial_best=float('inf')):
        super(CustomEarlyStoppingCallback, self).__init__()
        self.patience = patience
        self.min_delta = min_delta
        self.best_weights = None
        self.wait = 0
        self.stopped_epoch = 0
        self.best = initial_best

    def on_train_begin(self, logs=None):
        self.wait = 0
        self.stopped_epoch = 0
        # self.best = self.best (already set during initialization)
        self.best_weights = None

    def on_epoch_end(self, epoch, logs=None):
        current_loss = logs.get("val_loss")
        if current_loss is None:
            return
        
        if current_loss < self.best - self.min_delta:
            self.best = current_loss
            self.wait = 0
            self.best_weights = self.model.get_weights()
        else:
            self.wait += 1
            if self.wait >= self.patience:
                self.stopped_epoch = epoch
                self.model.stop_training = True
                self.model.set_weights(self.best_weights)

    def on_train_end(self, logs=None):
        if self.stopped_epoch > 0:
            print(f'Restoring model weights from the end of the best epoch: {self.stopped_epoch - self.patience + 1}')

# Load and prepare the MNIST dataset
(x_train, y_train), (x_val, y_val) = mnist.load_data()

# Normalize the data
x_train = x_train.astype('float32') / 255.0
x_val = x_val.astype('float32') / 255.0

# One-hot encode the labels
y_train = to_categorical(y_train, 10)
y_val = to_categorical(y_val, 10)

# Create a simple model
model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(64, activation='relu'),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])

model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
custom_early_stopping = CustomEarlyStoppingCallback(patience=3, min_delta=0.001, initial_best=2.64)
history = model.fit(x_train, y_train, epochs=100, validation_data=(x_val, y_val), callbacks=[custom_early_stopping])

Спасибо за ваш ответ. Если я правильно понимаю, я могу использовать этот параметр, чтобы указать, что он не должен выполнять EarlySopping в первые n эпох. К сожалению, в этом случае мою проблему это не решит. Я бы хотел уже проверить EarlyStopping от эпохи 0 до эпохи 1.

xxxyyy 12.07.2024 14:08

Боюсь, что в противном случае вам придется реализовать свой собственный обратный вызов для достижения желаемого.

Timbus Calin 12.07.2024 14:14

Я обновил ответ примером, с которого вы можете начать: D

Timbus Calin 12.07.2024 14:23

Большое спасибо за ваш комментарий. Это самое «чистое» решение.

xxxyyy 15.07.2024 18:54

Если вам это подходит, вы можете принять ответ как тот, который решил вашу проблему. Спасибо

Timbus Calin 15.07.2024 20:09

Могу ли я добавить callbacks=[early_stopping, checkpoint], чтобы получить лучшую модель? То есть, если Early_stopping останавливается после первой эпохи, потому что необученная модель является лучшей, возвращает ли контрольная точка необученную модель?

xxxyyy 17.07.2024 09:16
Ответ принят как подходящий

Вы можете вручную оценить модель перед началом обучения и установить ее в качестве базовой линии для ранней остановки. Вот как вы можете настроить свой код:

  1. Оцените необученную модель в наборе проверки, чтобы получить первоначальные потери при проверке.
  2. Измените обратный вызов EarlyStopping, чтобы использовать эту первоначальную потерю при проверке в качестве базовой линии.

Пример:

initial_val_loss = model.evaluate(Val_Data, Val_Label, verbose=0)

class CustomEarlyStopping(EarlyStopping):
    def __init__(self, initial_val_loss, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.initial_val_loss = initial_val_loss
        self.best = initial_val_loss

Надеюсь, это поможет.

Большое спасибо. Звучит многообещающе. Тогда мне просто нужно написать что-то вроде FinalEpochs = np.argmin([initial_val_loss, hist]). Если в течение первой эпохи потери усилятся, будет ли model.load_weights(checkpoint_filepath) автоматически возвращать модель до первой эпохи?

xxxyyy 15.07.2024 18:52

Я уже попробовал ваш код, но, похоже, он не работает. Я пытался:

xxxyyy 17.07.2024 08:46

startLoss = 0.00001 Early_stopping = CustomEarlyStopping(monitor='val_loss', терпение=earlyStoppingEpochs, restre_best_weights=False, Initial_val_loss=startLoss) И я получаю: Эпоха 1: val_loss улучшено с inf до 0,30686 Эпоха 2: val_loss улучшено с 0,30686 до 0,2 3336

xxxyyy 17.07.2024 08:58

Вам пришлось выполнить дополнительное редактирование в классе: # if self.wait >= self.patience and epoch > 0: ИЗМЕНИТЬ НА if self.wait >= self.patience and epoch >= 0: Теперь это работает, большое спасибо много!

xxxyyy 22.07.2024 13:15

Другие вопросы по теме