Обратный вызов для проверки насыщенности val_acc

#python #machine-learning #deep-learning

#python #машинное обучение #глубокое обучение

Вопрос:

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

Я работаю над настройкой параметров. val_acc очень нестабилен, как показано на рисункеacc_graphs.

 def LSTM_model(X_train, y_train, X_test, y_test, num_classes, batch_size=68, units=128, learning_rate=0.005, epochs=20,
               dropout=0.2, recurrent_dropout=0.2):
    class myCallback(tf.keras.callbacks.Callback):

        def on_epoch_end(self, epoch, logs={}):
            if (logs.get('acc') > 0.90):
                print("nReached 90% accuracy so cancelling training!")
                self.model.stop_training = True

    callbacks = myCallback()
  

Поскольку графики показывают, что val_acc (оранжевый) колеблется в пределах диапазона и на самом деле больше не растет.

Есть ли способ автоматически остановить обучение, как только общая тенденция val_acc перестанет увеличиваться?

Ответ №1:

Вы можете добиться этого с callback помощью такого

 class terminate_on_plateau(keras.callbacks.Callback):
    
    def __init__(self):
        self.patience = 10
        self.val_loss = deque([],self.patience)
        self.std_threshold = 1e-2
        
    def on_epoch_end(self,epoch,logs=None):
        val_loss,val_mae = model.evaluate(x_val,y_val)
        self.val_loss.append(val_loss)
        if len(self.val_loss) >= self.patience:
            std = np.std(self.val_loss)
            if std < self.std_threshold:
                print('nn EarlyStopping on std invoked! nn')
                # clear the deque
                self.val_loss = deque([],self.patience)
                model.stop_training = True
  

Как вы можете видеть, в terminate_on_plateau , val_loss из эпох хранятся в deque максимальной длины self.patience . Как только длина deque будет достигнута self.patience , стандартное отклонение val_loss будет вычисляться для каждой новой эпохи, и процесс обучения будет завершен ( deque оф val_loss также будет очищен), если вычисленное std значение меньше порогового значения.

Ниже приведен простой скрипт, который показывает вам, как использовать это

 from collections import deque
import numpy as np

import tensorflow as tf 
from tensorflow import keras 
import tensorflow.keras.backend as K
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input,Dense

x = np.linspace(0,10,1000)
np.random.shuffle(x)
y = np.sin(x)   x

x_train,x_val,y_train,y_val = train_test_split(x,y,test_size=0.3)

input_x = Input(shape=(1,))
y = Dense(10,activation='relu')(input_x)
y = Dense(10,activation='relu')(y)
y = Dense(1,activation='relu')(y)
model = Model(inputs=input_x,outputs=y)

adamopt = tf.keras.optimizers.Adam(lr=0.01, beta_1=0.9, beta_2=0.999, epsilon=1e-8)

class terminate_on_plateau(keras.callbacks.Callback):
    
    def __init__(self):
        self.patience = 10
        self.val_loss = deque([],self.patience)
        self.std_threshold = 1e-2
        
    def on_epoch_end(self,epoch,logs=None):
        val_loss,val_mae = model.evaluate(x_val,y_val)
        self.val_loss.append(val_loss)
        if len(self.val_loss) >= self.patience:
            std = np.std(self.val_loss)
            if std < self.std_threshold:
                print('nn EarlyStopping on std invoked! nn')
                # clear the deque
                self.val_loss = deque([],self.patience)
                model.stop_training = True
    
model.compile(loss='mse',optimizer=adamopt,metrics=['mae'])
history = model.fit(x_train,y_train,
                    batch_size=8,
                    epochs=100,
                    validation_data=(x_val, y_val),
                    verbose=1,
                    callbacks=[terminate_on_plateau()])
  

Комментарии:

1. Могу ли я узнать, почему вы решили вычислить val_loss вместо val_acc в этом случае, поскольку графики отображали точность? Подробнее, пожалуйста, поправьте меня, если я ошибаюсь. Могу ли я понимать «терпение» как размер окна, используемый для оценки std в течение эпох? например, std вычисляется каждые 10 эпох, если patience равно 10.

2. @Leo Конечно, вы можете выбрать val_acc , при этом вам нужно только изменить это model.evaluate(x_val,y_val) на что-то, что вычисляет val_acc . Я выбираю val_loss просто для простоты. Это patience число val_loss (или val_acc ), которое используется для вычисления стандартного отклонения, я предлагаю вам немного прочитать deque , поскольку я использую его для хранения истории val_loss в моем коде, если patience оно равно 10, std будет вычисляться для каждой эпохи , как только deque достигнет максимальной длины, то есть 10.

3. Да, это имеет смысл. Хотя я столкнулся с проблемой, во второй раз, когда я обучаю модель, она немедленно активирует обратные вызовы, я думаю, это потому, что данные в обратном вызове из последнего обучения не были удалены. Не могли бы вы изменить свои коды, чтобы исправить это?

4. @Leo Конечно, решение простое, просто добавьте эту строку self.val_loss = deque([],self.patience) прямо перед завершением процесса обучения. Ознакомьтесь с моим обновленным сообщением.

Ответ №2:

Приведенный ниже код предназначен для пользовательского обратного вызова, который остановит обучение, когда отслеживаемое количество не улучшится после терпения количества эпох. Установите для параметра acc_or_loss значение «потеря», чтобы отслеживать потери при проверке. Установите для него значение ‘acc’ для контроля точности проверки. Я рекомендую НЕ следить за точностью проверки, поскольку она может сильно колебаться, особенно в ранние эпохи. Я ввел операторы печати, чтобы вы могли видеть, что происходит во время обучения. Вы, конечно, можете удалить их позже. Если вы отслеживаете потерю проверки, обратный вызов останавливает обучение, если для определенного количества эпох потеря проверки превысила наименьшую потерю, обнаруженную в предыдущие эпохи. Если вы отслеживаете точность проверки, обратный вызов останавливает обучение, если для определенного количества эпох точность проверки осталась ниже максимальной точности проверки, зарегистрированной в предыдущие эпохи

 class halt(keras.callbacks.Callback):
    def __init__(self, patience, acc_or_loss):
        self.acc_or_loss=acc_or_loss
        super(halt, self).__init__()
        self.patience=patience # specifies how many epochs without improvement before learning rate is adjusted
        self.lowest_loss=np.inf 
        self.highest_acc=0
        self.count=0
        print ('initializing values ', 'count= ', self.count, '  lowest_loss= ', self.lowest_loss, 'highest acc= ', self.highest_acc)
    def on_epoch_end(self, epoch, logs=None):
        v_loss=logs.get('val_loss')  # get the validation loss for this epoch
        v_acc=logs.get('val_accuracy')
        if self.acc_or_loss=='loss':
            print (' for epoch ', epoch  1, '  v_loss= ', v_loss, ' lowest_loss= ', self.lowest_loss,  'count= ', self.count)
            if v_loss< self.lowest_loss:
                self.lowest_loss=v_loss
                self.count=0
            else:
                self.count=self.count  1
                if self.count>=self.patience:
                    print('There have been ', self.patience, ' epochs with no reduction of validation loss below the lowest loss')
                    print ('Terminating training')
                    self.model.stop_training = True
        else:
            print (' for epoch ', epoch  1, '  v_acc= ', v_acc, ' highest accuracy= ', self.highest_acc,  'count= ', self.count)
            if v_acc>self.highest_acc:
                self.count=0
                self.highest_acc=v_acc
            else:
                self.count=self.count  1
                if self.count>=self.patience:
                    print('There have been ', self.patience, ' epochs with noincrease in validation accuracy')
                    print ('Terminating training')
                    self.model.stop_training = True

patience= 2 #  specify the patience value
acc_or_loss='loss' # specify to monitor validation loss or validation accuracy
callbacks=[halt(patience=patience, acc_or_loss=acc_or_loss)]
# in model.fit include callbacks=callbacks
  

Ответ №3:

Или вы можете просто использовать Keras API в tensorflow : tf.keras.callbacks.EarlyStopping

Учитывая ваш первоначальный вопрос, я не уверен, зачем вам нужен пользовательский callbacks

Вот пример применения:

 history = model.fit([trainX,trainX,trainX],
                    np.array(trainLabels),
                    validation_data = ([testX, testX, testX], np.array(testLabels)),
                    epochs=EPOCH,
                    batch_size=BATCH_SIZE,
                    steps_per_epoch = None,
                    callbacks=[tf.keras.callbacks.EarlyStopping(
                        monitor="val_acc",
                        patience=5,
                        mode="min",
                        restore_best_weights = True)])
  

Комментарии:

1. Режим должен быть «max», если вы хотите прекратить обучение, когда val_acc не увеличивается.

Ответ №4:

Некоторые из приведенных выше ответов немного сложны, вы можете использовать приведенный ниже код.

 opt = tf.optimizers.Adadelta(learning_rate=0.01)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=["accuracy"])
es = EarlyStopping(monitor='val_accuracy', mode='max', patience=20)
# will stop if validation accuracy is not improving till 20 epoches, you can give any number in patience.
ms = ModelCheckpoint('save_model.h5', monitor='val_accuracy', mode='max', save_best_only=True)

training_history = model.fit(x=X_train, y=y_train, validation_split=0.1, batch_size=5, epochs=1000, verbose=1,
                             callbacks = [es, ms])
  

Я только что скопировал этот код из своего проекта, который не предназначен для LSTM, вы можете настроить этот код в соответствии с вашей проблемой / задачей.