Προθέρμανση ρυθμού εκμάθησης με αποσύνθεση συνημιτόνου στο Keras/TensorFlow PlatoBlockchain Data Intelligence. Κάθετη αναζήτηση. Ολα συμπεριλαμβάνονται.

Προθέρμανση ρυθμού εκμάθησης με αποσύνθεση συνημιτόνου στο Keras/TensorFlow

Ο ρυθμός εκμάθησης είναι μια σημαντική υπερπαράμετρος στα δίκτυα βαθιάς μάθησης – και υπαγορεύει άμεσα το βαθμός στις οποίες πραγματοποιούνται ενημερώσεις στα βάρη, οι οποίες εκτιμάται ότι ελαχιστοποιούν κάποια δεδομένη συνάρτηση απώλειας. Στο SGD:

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

Με ρυθμό μάθησης 0, το ενημερωμένο βάρος είναι ακριβώς πίσω από μόνο του – βάροςt. Ο ρυθμός μάθησης είναι ουσιαστικά ένα κουμπί που μπορούμε να στραφούμε για να ενεργοποιήσουμε ή να απενεργοποιήσουμε τη μάθηση και έχει μεγάλη επιρροή στο πόσο μάθηση συμβαίνει, ελέγχοντας άμεσα τον βαθμό ενημέρωσης βάρους.

Διαφορετικοί βελτιστοποιητές χρησιμοποιούν διαφορετικά ποσοστά εκμάθησης – αλλά η υποκείμενη ιδέα παραμένει η ίδια. Περιττό να πούμε ότι τα ποσοστά μάθησης έχουν αποτελέσει αντικείμενο πολλών μελετών, εργασιών και σημείων αναφοράς για τους επαγγελματίες.

Σε γενικές γραμμές, σχεδόν όλοι συμφωνούν ότι ένας στατικός ρυθμός μάθησης δεν θα το μειώσει, και κάποιος τύπος μείωσης του ρυθμού μάθησης συμβαίνει στις περισσότερες τεχνικές που συντονίζουν τον ρυθμό μάθησης κατά τη διάρκεια της προπόνησης – είτε πρόκειται για μονοτονικό, συνημίτονο, τριγωνικό ή άλλους τύπους μείωση.

Μια τεχνική που τα τελευταία χρόνια κερδίζει έδαφος είναι προθέρμανση ρυθμού μάθησης, το οποίο μπορεί να συνδυαστεί με πρακτικά οποιαδήποτε άλλη τεχνική μείωσης.

Προθέρμανση ποσοστού εκμάθησης

Η ιδέα πίσω από την προθέρμανση του ρυθμού εκμάθησης είναι απλή. Στα πρώτα στάδια της προπόνησης – τα βάρη απέχουν πολύ από την ιδανική τους κατάσταση. Αυτό σημαίνει μεγάλες ενημερώσεις σε όλα τα επίπεδα, οι οποίες μπορούν να θεωρηθούν ως «υπερδιορθώσεις» για κάθε βάρος – όπου η δραστική ενημέρωση ενός άλλου μπορεί να αναιρεί την ενημέρωση κάποιου άλλου βάρους, καθιστώντας τα αρχικά στάδια της προπόνησης πιο ασταθή.

Αυτές οι αλλαγές εξαντλούνται, αλλά μπορούν να αποφευχθούν έχοντας αρχικά μικρό ρυθμό μάθησης, φτάνοντας σε μια πιο σταθερή υποβέλτιστη κατάσταση και στη συνέχεια εφαρμόζοντας μεγαλύτερο ρυθμό μάθησης. Μπορείτε να διευκολύνετε κατά κάποιο τρόπο το δίκτυο σε ενημερώσεις, αντί να το χτυπήσετε μαζί τους.

Αυτό είναι προθέρμανση ρυθμού εκμάθησης! Ξεκινώντας με χαμηλό (ή 0) ρυθμό μάθησης και αύξηση σε αρχικό ρυθμό εκμάθησης (με αυτό που θα ξεκινούσατε ούτως ή άλλως). Αυτή η αύξηση μπορεί να ακολουθήσει οποιαδήποτε συνάρτηση πραγματικά, αλλά είναι συνήθως γραμμική.

Μετά την επίτευξη του αρχικού ρυθμού, μπορούν να εφαρμοστούν άλλα χρονοδιαγράμματα όπως η διάσπαση συνημιτόνου, η γραμμική αναγωγή κ.λπ. για τη σταδιακή μείωση του ρυθμού μέχρι το τέλος της προπόνησης. Η προθέρμανση του ρυθμού εκμάθησης είναι συνήθως μέρος ενός προγράμματος δύο προγραμμάτων, όπου η προθέρμανση LR είναι η πρώτη, ενώ ένα άλλο πρόγραμμα αναλαμβάνει αφού ο ρυθμός φτάσει σε ένα σημείο εκκίνησης.

Σε αυτόν τον οδηγό, θα εφαρμόσουμε μια προθέρμανση ρυθμού εκμάθησης στο Keras/TensorFlow ως α keras.optimizers.schedules.LearningRateSchedule υποκατηγορία και keras.callbacks.Callback επανάκληση. Το ποσοστό μάθησης θα αυξηθεί από 0 προς την target_lr και εφαρμόστε τη διάσπαση συνημιτόνου, καθώς αυτό είναι ένα πολύ κοινό δευτερεύον πρόγραμμα. Ως συνήθως, η Keras διευκολύνει την εφαρμογή ευέλικτων λύσεων με διάφορους τρόπους και την αποστολή τους με το δίκτυό σας.

Σημείωση: Η υλοποίηση είναι γενική και εμπνευσμένη από Υλοποίηση Tony's Keras από τα κόλπα που περιγράφονται στο "Τσάντα κόλπα για ταξινόμηση εικόνας με συνελικτικά νευρωνικά δίκτυα».

Ποσοστό εκμάθησης με Keras Callbacks

Ο απλούστερος τρόπος για την εφαρμογή οποιουδήποτε χρονοδιαγράμματος ρυθμού εκμάθησης είναι δημιουργώντας μια συνάρτηση που παίρνει το lr παράμετρος (float32), το περνάει από κάποιο μετασχηματισμό και το επιστρέφει. Αυτή η λειτουργία μεταβιβάζεται στη συνέχεια στο LearningRateScheduler επανάκληση, που εφαρμόζει τη συνάρτηση στον ρυθμό εκμάθησης.

Τώρα, το tf.keras.callbacks.LearningRateScheduler() μεταβιβάζει τον αριθμό εποχής στη συνάρτηση που χρησιμοποιεί για τον υπολογισμό του ρυθμού εκμάθησης, ο οποίος είναι αρκετά χονδροειδής. Το LR Warmup θα πρέπει να γίνει σε κάθε ένα βήμα (παρτίδα), όχι εποχή, οπότε θα πρέπει να εξαγάγουμε α 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 στο Google και πραγματικά μαθαίνουν το!

Αρχικά, ορίζουμε τον κατασκευαστή για την κλάση και παρακολουθούμε τα πεδία της. Σε κάθε παρτίδα που ολοκληρώνεται, θα αυξάνουμε το παγκόσμιο βήμα, θα σημειώνουμε το τρέχον LR και θα το προσθέτουμε στη λίστα των LR μέχρι στιγμής. Στην αρχή κάθε παρτίδας – θα υπολογίσουμε το LR χρησιμοποιώντας το lr_warmup_cosine_decay() λειτουργία και ορίστε αυτό το LR ως το τρέχον LR του βελτιστοποιητή. Αυτό γίνεται με το backend's 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. Κάθετη αναζήτηση. Ολα συμπεριλαμβάνονται.

Εάν σχεδιάσετε την ιστορία ενός μοντέλου που έχει εκπαιδευτεί με και χωρίς προθέρμανση LR – θα δείτε μια σαφή διαφορά στη σταθερότητα της προπόνησης:

Προθέρμανση ρυθμού εκμάθησης με αποσύνθεση συνημιτόνου στο Keras/TensorFlow PlatoBlockchain Data Intelligence. Κάθετη αναζήτηση. Ολα συμπεριλαμβάνονται.

Ποσοστό εκμάθησης με υποκατηγορία LearningRateSchedule

Μια εναλλακτική λύση για τη δημιουργία μιας επιστροφής κλήσης είναι να δημιουργήσετε ένα LearningRateSchedule υποκλάση, η οποία δεν χειρίζεται το LR – το αντικαθιστά. Αυτή η προσέγγιση σάς επιτρέπει να προωθήσετε λίγο περισσότερο στο backend του 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 sublcass, κυρίως επειδή έχουμε πρόσβαση στο 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

Τέλος, κατά τη φόρτωση του μοντέλου, θα πρέπει να περάσετε ένα WarmupCosineDecay ως προσαρμοσμένο αντικείμενο:

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

Συμπέρασμα

Σε αυτόν τον οδηγό, ρίξαμε μια ματιά στη διαίσθηση πίσω από το Learning Rate Warmup – μια κοινή τεχνική για τον χειρισμό του ρυθμού εκμάθησης κατά την εκπαίδευση των νευρωνικών δικτύων.

Έχουμε εφαρμόσει μια προθέρμανση ρυθμού εκμάθησης με διάσπαση συνημιτόνου, τον πιο συνηθισμένο τύπο μείωσης LR σε συνδυασμό με προθέρμανση. Μπορείτε να εφαρμόσετε οποιαδήποτε άλλη λειτουργία για μείωση ή να μην μειώσετε καθόλου τον ρυθμό εκμάθησης – αφήνοντάς το σε άλλες επανακλήσεις, όπως π.χ ReduceLROnPlateau(). Έχουμε εφαρμόσει την προθέρμανση του ρυθμού εκμάθησης ως Keras Callback, καθώς και ένα Πρόγραμμα Keras Optimizer και σχεδιάσαμε το ρυθμό εκμάθησης ανά τις εποχές.

Σφραγίδα ώρας:

Περισσότερα από Stackabuse