Розігрів швидкості навчання з косинусним розпадом у Keras/TensorFlow PlatoBlockchain Data Intelligence. Вертикальний пошук. Ai.

Розминка швидкості навчання з косинусним розпадом у Keras/TensorFlow

Швидкість навчання є важливим гіперпараметром у мережах глибокого навчання, і він безпосередньо визначає ступінь для яких виконуються оновлення вагових коефіцієнтів, які, за оцінками, мінімізують певну функцію втрат. У SGD:

$$
weight_{t+1} = weight_t – lr * frac{derror}{dweight_t}
$$

Зі швидкістю навчання 0, оновлена ​​вага просто повертається до себе – вагаt. Швидкість навчання — це фактично ручка, яку можна повернути, щоб увімкнути або вимкнути навчання, і вона має значний вплив на те, скільки відбувається навчання, безпосередньо контролюючи ступінь оновлення ваги.

Різні оптимізатори по-різному використовують швидкість навчання, але основна концепція залишається незмінною. Зайве говорити, що темпи навчання були об’єктом багатьох досліджень, статей і контрольних показників практиків.

Загалом кажучи, майже всі погоджуються, що статична швидкість навчання не зменшить її, і певний тип зниження швидкості навчання відбувається в більшості технік, які регулюють швидкість навчання під час навчання – будь то монотонний, косинусний, трикутний чи інші типи скорочення.

Техніка, яка останніми роками набуває все більшого поширення темп навчання розминка, який можна поєднувати практично з будь-якою іншою технікою зменшення.

Розминка швидкості навчання

Ідея розминки швидкості навчання проста. На самих ранніх етапах тренувань – ваги далекі від ідеальних станів. Це означає великі оновлення по всіх напрямках, які можна розглядати як «надлишкові корекції» для кожної ваги, де різке оновлення іншої ваги може звести нанівець оновлення іншої ваги, роблячи початкові етапи тренувань більш нестабільними.

Ці зміни згладжуються, але їх можна уникнути, якщо спочатку мати невелику швидкість навчання, досягти більш стабільного субоптимального стану, а потім застосувати більшу швидкість навчання. Ви можете начебто полегшити мережу оновленнями, а не вражати їх.

Це розминка темпу навчання! Починаючи з низької (або нульової) швидкості навчання та збільшуючи до початкової швидкості навчання (з якої ви все одно почали б). Це збільшення насправді може відповідати будь-якій функції, але зазвичай є лінійним.

Після досягнення початкової швидкості можна застосовувати інші графіки, такі як косинусне розпад, лінійне скорочення тощо, щоб поступово знижувати швидкість до кінця навчання. Розминка темпу навчання зазвичай є частиною розкладу з двома розкладами, де розминка LR є першою, а інший розклад починається після досягнення початкової точки.

У цьому посібнику ми реалізуємо розминку швидкості навчання в Keras/TensorFlow як a keras.optimizers.schedules.LearningRateSchedule підклас і keras.callbacks.Callback зворотній дзвінок. Швидкість навчання буде збільшена з 0 до target_lr і застосувати косинусний розпад, оскільки це дуже поширений вторинний графік. Як завжди, Keras дозволяє легко впроваджувати гнучкі рішення різними способами та надсилати їх у вашу мережу.

Примітка: Реалізація є загальною та натхненною Реалізація Keras Тоні трюків, викладених у «Набір хитрощів для класифікації зображень за допомогою згорткових нейронних мереж».

Швидкість навчання за допомогою зворотних викликів Keras

Найпростіший спосіб реалізувати будь-який розклад темпів навчання – створити функцію, яка приймає lr параметр (float32), пропускає його через деяке перетворення та повертає його. Потім ця функція передається LearningRateScheduler зворотний виклик, який застосовує функцію до швидкості навчання.

Тепер, tf.keras.callbacks.LearningRateScheduler() передає номер епохи функції, яку використовує для обчислення швидкості навчання, яка є досить грубою. LR Warmup слід робити на кожному крок (партія), а не епоха, тому нам доведеться вивести a global_step (у всіх епохах), щоб замість цього обчислити швидкість навчання та підкласити Callback для створення власного зворотного виклику, а не просто передачі функції, оскільки нам потрібно буде передавати аргументи під час кожного виклику, що неможливо, якщо просто передати функцію:

def func():
    return ...
    
keras.callbacks.LearningRateScheduler(func)

Цей підхід є сприятливим, якщо ви не бажаєте високого рівня налаштування та не хочете втручатися в те, як Keras обробляє lr, особливо якщо ви хочете використовувати зворотні виклики, як ReduceLROnPlateau() оскільки він може працювати лише з float-based lr. Давайте реалізуємо розминку швидкості навчання за допомогою зворотного виклику Keras, починаючи з функції зручності:

def lr_warmup_cosine_decay(global_step,
                           warmup_steps,
                           hold = 0,
                           total_steps=0,
                           start_lr=0.0,
                           target_lr=1e-3):
    
    learning_rate = 0.5 * target_lr * (1 + np.cos(np.pi * (global_step - warmup_steps - hold) / float(total_steps - warmup_steps - hold)))

    
    warmup_lr = target_lr * (global_step / warmup_steps)

    
    
    if hold > 0:
        learning_rate = np.where(global_step > warmup_steps + hold,
                                 learning_rate, target_lr)
    
    learning_rate = np.where(global_step < warmup_steps, warmup_lr, learning_rate)
    return learning_rate

На кожному кроці ми розраховуємо швидкість навчання та швидкість навчання для розминки (обидва елементи розкладу) з урахуванням start_lr та target_lr. start_lr зазвичай починається о 0.0, У той час target_lr залежить від вашої мережі та оптимізатора – 1e-3 за замовчуванням може бути неправильним, тому обов’язково встановіть цільовий LR під час виклику методу.

Якщо global_step у навчанні вище, ніж warmup_steps ми встановили – ми використовуємо графік розпаду косинуса LR. Якщо ні, це означає, що ми ще розминка, тому використовується розминка LR. Якщо hold аргумент встановлено, ми будемо тримати target_lr для такої кількості кроків після розігріву та до розпаду косинуса. np.where() забезпечує чудовий синтаксис для цього:

np.where(condition, value_if_true, value_if_false)

Ви можете візуалізувати функцію за допомогою:

steps = np.arange(0, 1000, 1)
lrs = []

for step in steps:
  lrs.append(lr_warmup_cosine_decay(step, total_steps=len(steps), warmup_steps=100, hold=10))
plt.plot(lrs)

Тепер ми хочемо використовувати цю функцію як частину зворотного виклику та передати крок оптимізатора як global_step а не елемент довільного масиву – або ви можете виконати обчислення в межах класу. Виділимо підклас Callback клас:

from keras import backend as K

class WarmupCosineDecay(keras.callbacks.Callback):
    def __init__(self, total_steps=0, warmup_steps=0, start_lr=0.0, target_lr=1e-3, hold=0):

        super(WarmupCosineDecay, self).__init__()
        self.start_lr = start_lr
        self.hold = hold
        self.total_steps = total_steps
        self.global_step = 0
        self.target_lr = target_lr
        self.warmup_steps = warmup_steps
        self.lrs = []

    def on_batch_end(self, batch, logs=None):
        self.global_step = self.global_step + 1
        lr = model.optimizer.lr.numpy()
        self.lrs.append(lr)

    def on_batch_begin(self, batch, logs=None):
        lr = lr_warmup_cosine_decay(global_step=self.global_step,
                                    total_steps=self.total_steps,
                                    warmup_steps=self.warmup_steps,
                                    start_lr=self.start_lr,
                                    target_lr=self.target_lr,
                                    hold=self.hold)
        K.set_value(self.model.optimizer.lr, lr)

Ознайомтеся з нашим практичним практичним посібником із вивчення Git з передовими методами, прийнятими в галузі стандартами та включеною шпаргалкою. Припиніть гуглити команди Git і фактично вчитися це!

Спочатку ми визначаємо конструктор для класу та відстежуємо його поля. У кожній закінченій партії ми збільшимо глобальний крок, візьмемо до уваги поточний LR і додамо його до списку LR. На початку кожної партії ми обчислюємо LR за допомогою lr_warmup_cosine_decay() і встановіть цей LR як поточний LR оптимізатора. Це робиться за допомогою бекенда set_value().

Зробивши це, просто обчисліть загальну кількість кроків (довжина/розмір партії*епохи) і візьміть частину цього числа для свого warmup_steps:


total_steps = len(train_set)*config['EPOCHS']



warmup_steps = int(0.05*total_steps)

callback = WarmupCosineDecay(total_steps=total_steps, 
                             warmup_steps=warmup_steps,
                             hold=int(warmup_steps/2), 
                             start_lr=0.0, 
                             target_lr=1e-3)

Нарешті, побудуйте свою модель і надайте зворотний виклик у fit() дзвінок:

model = keras.applications.EfficientNetV2B0(weights=None, 
                                            classes=n_classes, 
                                            input_shape=[224, 224, 3])
  
model.compile(loss="sparse_categorical_crossentropy",
                  optimizer='adam',
                  jit_compile=True,
                  metrics=['accuracy'])

Наприкінці навчання ви можете отримати та візуалізувати змінені LR за допомогою:

lrs = callback.lrs 
plt.plot(lrs)

Розігрів швидкості навчання з косинусним розпадом у Keras/TensorFlow PlatoBlockchain Data Intelligence. Вертикальний пошук. Ai.

Якщо ви побудуєте історію моделі, яка тренується з розминкою LR і без неї, ви побачите явну різницю в стабільності тренування:

Розігрів швидкості навчання з косинусним розпадом у Keras/TensorFlow PlatoBlockchain Data Intelligence. Вертикальний пошук. Ai.

Швидкість навчання з підкласом LearningRateSchedule

Альтернативою створенню зворотного виклику є створення a LearningRateSchedule підклас, який не маніпулює LR – він замінює його. Цей підхід дозволяє вам трохи більше проникнути в серверну частину Keras/TensorFlow, але, якщо він використовується, його не можна комбінувати з іншими зворотними викликами, пов’язаними з LR, такими як ReduceLROnPlateau(), який розглядає LR як числа з плаваючою комою.

Крім того, використання підкласу вимагатиме від вас зробити його серіалізованим (перевантаження get_config()), оскільки він стає частиною моделі, якщо ви хочете зберегти ваги моделі. Ще одна річ, яку слід зазначити, це те, що клас буде працювати виключно з tf.Tensorс. На щастя, єдиною відмінністю в нашій роботі буде дзвінок tf.func() замість np.func() оскільки API TensorFlow і NumPy надзвичайно схожі та сумісні.

Перепишемо зручність lr_warmup_cosine_decay() використовувати замість цього операції TensorFlow:

def lr_warmup_cosine_decay(global_step,
                           warmup_steps,
                           hold = 0,
                           total_steps=0,
                           start_lr=0.0,
                           target_lr=1e-3):
    
    
    learning_rate = 0.5 * target_lr * (1 + tf.cos(tf.constant(np.pi) * (global_step - warmup_steps - hold) / float(total_steps - warmup_steps - hold)))

    
    warmup_lr = target_lr * (global_step / warmup_steps)

    
    
    if hold > 0:
        learning_rate = tf.where(global_step > warmup_steps + hold,
                                 learning_rate, target_lr)
    
    learning_rate = tf.where(global_step < warmup_steps, warmup_lr, learning_rate)
    return learning_rate

За допомогою функції convinience ми можемо підкласити LearningRateSchedule клас. На кожному __call__() (пакет), ми обчислимо LR за допомогою функції та повернемо його. Ви також можете запакувати обчислення в клас підкласу.

Синтаксис чистіший, ніж у Callback підклас, насамперед тому, що ми отримуємо доступ до step замість того, щоб відслідковувати його самостійно, але також дещо ускладнює роботу з властивостями класу – зокрема, це ускладнює вилучення lr від tf.Tensor() у будь-який інший тип для відстеження в списку. Технічно це можна обійти, запустивши в режимі очікування, але це створює неприємності для відстеження LR з метою налагодження, і цього краще уникати:

class WarmUpCosineDecay(keras.optimizers.schedules.LearningRateSchedule):
    def __init__(self, start_lr, target_lr, warmup_steps, total_steps, hold):
        super().__init__()
        self.start_lr = start_lr
        self.target_lr = target_lr
        self.warmup_steps = warmup_steps
        self.total_steps = total_steps
        self.hold = hold

    def __call__(self, step):
        lr = lr_warmup_cosine_decay(global_step=step,
                                    total_steps=self.total_steps,
                                    warmup_steps=self.warmup_steps,
                                    start_lr=self.start_lr,
                                    target_lr=self.target_lr,
                                    hold=self.hold)

        return tf.where(
            step > self.total_steps, 0.0, lr, name="learning_rate"
        )

Параметри ті самі, і їх можна обчислити майже так само, як і раніше:


total_steps = len(train_set)*config['EPOCHS']



warmup_steps = int(0.05*total_steps)

schedule = WarmUpCosineDecay(start_lr=0.0, target_lr=1e-3, warmup_steps=warmup_steps, total_steps=total_steps, hold=warmup_steps)

І навчальний конвеєр відрізняється лише тим, що ми встановлюємо LR оптимізатора на schedule:

model = keras.applications.EfficientNetV2B0(weights=None, 
                                            classes=n_classes, 
                                            input_shape=[224, 224, 3])
  
model.compile(loss="sparse_categorical_crossentropy",
                  optimizer=tf.keras.optimizers.Adam(learning_rate=schedule),
                  jit_compile=True,
                  metrics=['accuracy'])

history3 = model.fit(train_set,
                    epochs = config['EPOCHS'],
                    validation_data=valid_set)

Якщо ви бажаєте зберегти модель, то WarmupCosineDecay розклад доведеться перевизначити get_config() метод:

    def get_config(self):
        config = {
          'start_lr': self.start_lr,
          'target_lr': self.target_lr,
          'warmup_steps': self.warmup_steps,
          'total_steps': self.total_steps,
          'hold': self.hold
        }
        return config

Нарешті, під час завантаження моделі вам доведеться пройти a WarmupCosineDecay як настроюваний об'єкт:

model = keras.models.load_model('weights.h5', 
                                custom_objects={'WarmupCosineDecay', WarmupCosineDecay})

Висновок

У цьому посібнику ми поглянули на інтуїцію, що лежить в основі Learning Rate Warmup – поширеної техніки маніпулювання швидкістю навчання під час навчання нейронних мереж.

Ми реалізували розминку швидкості навчання з косинусним затуханням, найпоширенішим типом зменшення LR у поєднанні з розминкою. Ви можете застосувати будь-яку іншу функцію для зменшення або взагалі не зменшувати швидкість навчання – залишивши це іншим зворотним викликам, таким як ReduceLROnPlateau(). Ми реалізували розминку швидкості навчання як зворотний виклик Keras, а також розклад оптимізатора Keras і побудували швидкість навчання за епохами.

Часова мітка:

Більше від Stackabuse