Échauffement du taux d'apprentissage avec la décroissance du cosinus dans Keras/TensorFlow PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Réchauffement du taux d'apprentissage avec désintégration du cosinus dans Keras/TensorFlow

Le taux d'apprentissage est un hyperparamètre important dans les réseaux d'apprentissage en profondeur - et il dicte directement le intensité auquel les mises à jour des poids sont effectuées, qui sont estimées pour minimiser une fonction de perte donnée. En SGD :

$$
poids_{t+1} = poids_t – lr * frac{erreur}{dpoids_t}
$$

Avec un taux d'apprentissage de 0, le poids mis à jour revient à lui-même – poidst. Le taux d'apprentissage est en fait un bouton que nous pouvons tourner pour activer ou désactiver l'apprentissage, et il a une influence majeure sur la quantité d'apprentissage en cours, en contrôlant directement le degré de mise à jour du poids.

Différents optimiseurs utilisent différemment les taux d'apprentissage, mais le concept sous-jacent reste le même. Inutile de dire que les taux d'apprentissage ont fait l'objet de nombreuses études, articles et points de repère des praticiens.

De manière générale, à peu près tout le monde convient qu'un taux d'apprentissage statique ne le réduira pas, et un certain type de réduction du taux d'apprentissage se produit dans la plupart des techniques qui règlent le taux d'apprentissage pendant la formation - qu'il s'agisse d'un type monotone, cosinus, triangulaire ou autre. réduction.

Une technique qui s'est imposée ces dernières années est échauffement du taux d'apprentissage, qui peut être associée à pratiquement toutes les autres techniques de réduction.

Échauffement du taux d'apprentissage

L'idée derrière l'échauffement du taux d'apprentissage est simple. Dans les premiers stades de l'entraînement, les poids sont loin de leurs états idéaux. Cela signifie de grandes mises à jour dans tous les domaines, qui peuvent être considérées comme des "surcorrections" pour chaque poids - où la mise à jour drastique d'un autre peut annuler la mise à jour d'un autre poids, rendant les premières étapes de l'entraînement plus instables.

Ces changements s'aplanissent, mais peuvent être évités en ayant un petit taux d'apprentissage pour commencer, en atteignant un état sous-optimal plus stable, puis en appliquant un taux d'apprentissage plus élevé. Vous pouvez en quelque sorte faciliter le réseau dans les mises à jour, plutôt que de le frapper avec eux.

C'est l'échauffement du rythme d'apprentissage ! En commençant par un taux d'apprentissage faible (ou 0) et en augmentant jusqu'à un taux d'apprentissage de départ (ce avec quoi vous commenceriez de toute façon). Cette augmentation peut suivre n'importe quelle fonction, mais elle est généralement linéaire.

Après avoir atteint le taux initial, d'autres programmes tels que la décroissance du cosinus, la réduction linéaire, etc. peuvent être appliqués pour abaisser progressivement le taux jusqu'à la fin de l'entraînement. L'échauffement du taux d'apprentissage fait généralement partie d'un programme à deux programmes, où le préchauffage LR est le premier, tandis qu'un autre programme prend le relais une fois que le taux a atteint un point de départ.

Dans ce guide, nous allons implémenter un échauffement du taux d'apprentissage dans Keras/TensorFlow en tant que keras.optimizers.schedules.LearningRateSchedule sous-classe et keras.callbacks.Callback rappeler. Le taux d'apprentissage passera de 0 à target_lr et appliquer la décroissance cosinus, car il s'agit d'un programme secondaire très courant. Comme d'habitude, Keras simplifie la mise en œuvre de solutions flexibles de différentes manières et les expédie avec votre réseau.

Remarque: L'implémentation est générique et inspirée par Implémentation de Tony's Keras des astuces décrites dans "Sac d'astuces pour la classification d'images avec des réseaux de neurones convolutifs ».

Taux d'apprentissage avec les rappels Keras

La façon la plus simple d'implémenter un programme de taux d'apprentissage est de créer une fonction qui prend le lr paramètre (float32), le fait passer par une transformation et le renvoie. Cette fonction est ensuite transmise au LearningRateScheduler callback, qui applique la fonction au taux d'apprentissage.

Maintenant, l' tf.keras.callbacks.LearningRateScheduler() passe le numéro d'époque à la fonction qu'il utilise pour calculer le taux d'apprentissage, ce qui est assez grossier. L'échauffement LR doit être effectué sur chaque étape (lot), pas d'époque, nous devrons donc dériver un global_step (sur toutes les époques) pour calculer le taux d'apprentissage à la place, et sous-classer le Callback class pour créer un rappel personnalisé plutôt que de simplement passer la fonction, car nous devrons passer des arguments à chaque appel, ce qui est impossible en passant simplement la fonction :

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

Cette approche est favorable lorsque vous ne voulez pas un haut niveau de personnalisation et que vous ne voulez pas interférer avec la façon dont Keras traite le lr, et surtout si vous souhaitez utiliser des rappels comme ReduceLROnPlateau() puisqu'il ne peut fonctionner qu'avec un flotteur lr. Implémentons un préchauffage du taux d'apprentissage à l'aide d'un rappel Keras, en commençant par une fonction pratique :

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

À chaque étape, nous calculons le taux d'apprentissage et le taux d'apprentissage de l'échauffement (les deux éléments de l'horaire), par rapport au start_lr ainsi que target_lr. start_lr commencera généralement à 0.0, Bien que l' target_lr dépend de votre réseau et de votre optimiseur – 1e-3 n'est peut-être pas une bonne valeur par défaut, alors assurez-vous de définir votre cible à partir de LR lors de l'appel de la méthode.

Si la global_step dans la formation est supérieur au warmup_steps nous avons défini - nous utilisons le programme de décroissance cosinus LR. Si ce n'est pas le cas, cela signifie que nous sommes encore en train de nous échauffer, donc le LR d'échauffement est utilisé. Si la hold argument est défini, nous tiendrons le target_lr pour ce nombre d'étapes après l'échauffement et avant la décroissance du cosinus. np.where() fournit une excellente syntaxe pour cela :

np.where(condition, value_if_true, value_if_false)

Vous pouvez visualiser la fonction avec :

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)

Maintenant, nous allons utiliser cette fonction dans le cadre d'un rappel et passer l'étape d'optimisation en tant que global_step plutôt qu'un élément d'un tableau arbitraire - ou vous pouvez effectuer le calcul dans la classe. Sous-classons le Callback classe:

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)

Consultez notre guide pratique et pratique pour apprendre Git, avec les meilleures pratiques, les normes acceptées par l'industrie et la feuille de triche incluse. Arrêtez de googler les commandes Git et en fait apprendre il!

Tout d'abord, nous définissons le constructeur de la classe et gardons une trace de ses champs. Sur chaque lot terminé, nous augmenterons le pas global, prendrons note du LR actuel et l'ajouterons à la liste des LR jusqu'à présent. Au début de chaque lot - nous calculerons le LR en utilisant le lr_warmup_cosine_decay() fonction et définissez ce LR comme le LR actuel de l'optimiseur. Ceci est fait avec le backend set_value().

Une fois cela fait, calculez simplement le nombre total d'étapes (longueur/taille_batch*époques) et prenez une partie de ce nombre pour votre 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)

Enfin, construisez votre modèle et fournissez le rappel dans le fit() appel:

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'])

En fin de formation, vous pouvez obtenir et visualiser les LR modifiés via :

lrs = callback.lrs 
plt.plot(lrs)

Échauffement du taux d'apprentissage avec la décroissance du cosinus dans Keras/TensorFlow PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Si vous tracez l'historique d'un modèle entraîné avec et sans échauffement LR, vous verrez une nette différence dans la stabilité de l'entraînement :

Échauffement du taux d'apprentissage avec la décroissance du cosinus dans Keras/TensorFlow PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Taux d'apprentissage avec la sous-classe LearningRateSchedule

Une alternative à la création d'un rappel consiste à créer un LearningRateSchedule sous-classe, qui ne manipule pas le LR - il le remplace. Cette approche vous permet d'approfondir un peu plus le backend de Keras/TensorFlow, mais lorsqu'elle est utilisée, elle ne peut pas être combinée avec d'autres rappels liés à LR, tels que ReduceLROnPlateau(), qui traite les LR comme des nombres à virgule flottante.

De plus, l'utilisation de la sous-classe vous obligera à la rendre sérialisable (surcharge get_config()) au fur et à mesure qu'il devient une partie du modèle, si vous souhaitez enregistrer les poids du modèle. Une autre chose à noter est que la classe s'attendra à travailler exclusivement avec tf.Tensors. Heureusement, la seule différence dans notre façon de travailler sera d'appeler tf.func() au lieu de np.func() puisque les API TensorFlow et NumPy sont étonnamment similaires et compatibles.

Réécrivons la commodité lr_warmup_cosine_decay() pour utiliser les opérations TensorFlow à la place :

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

Avec la fonction de commodité, on peut sous-classer LearningRateSchedule classer. Sur chaque __call__() (batch), nous calculerons le LR en utilisant la fonction et le renverrons. Vous pouvez naturellement regrouper le calcul dans la classe sous-classée également.

La syntaxe est plus propre que la Callback sublcass, principalement parce que nous avons accès à la step champ, plutôt que d'en garder une trace par nous-mêmes, mais rend également un peu plus difficile le travail avec les propriétés de classe - en particulier, il est difficile d'extraire le lr d'un tf.Tensor() dans n'importe quel autre type pour en garder une trace dans une liste. Cela peut être techniquement contourné en s'exécutant en mode impatient, mais présente une gêne pour le suivi du LR à des fins de débogage et il vaut mieux l'éviter :

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"
        )

Les paramètres sont les mêmes et peuvent être calculés de la même manière qu'auparavant :


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)

Et le pipeline de formation ne diffère que par le fait que nous définissons le LR de l'optimiseur sur le 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)

Si vous souhaitez enregistrer le modèle, le WarmupCosineDecay l'horaire devra remplacer le get_config() méthode:

    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

Enfin, lors du chargement du modèle, vous devrez passer un WarmupCosineDecay en tant qu'objet personnalisé :

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

Conclusion

Dans ce guide, nous avons examiné l'intuition derrière Learning Rate Warmup - une technique courante pour manipuler le taux d'apprentissage lors de la formation de réseaux de neurones.

Nous avons mis en place un échauffement du taux d'apprentissage avec décroissance cosinus, le type le plus courant de réduction LR associé à un échauffement. Vous pouvez implémenter toute autre fonction de réduction, ou ne pas réduire du tout le taux d'apprentissage - en le laissant à d'autres rappels tels que ReduceLROnPlateau(). Nous avons implémenté l'échauffement du taux d'apprentissage en tant que rappel Keras, ainsi qu'un calendrier d'optimisation Keras et tracé le taux d'apprentissage à travers les époques.

Horodatage:

Plus de Stackabuse