Læringshastighedsopvarmning med Cosinus-decay i Keras/TensorFlow PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Læring af Rate Warmup med Cosinus Decay i Keras/TensorFlow

Læringshastigheden er en vigtig hyperparameter i dybe læringsnetværk - og den dikterer direkte grad hvortil der udføres opdateringer af vægte, som skønnes at minimere en given tabsfunktion. I SGD:

$$
vægt_{t+1} = vægt_t – lr * frac{fejl}{dvægt_t}
$$

Med en indlæringshastighed på 0, den opdaterede vægt er bare tilbage til sig selv – vægtt. Indlæringshastigheden er faktisk en knap, vi kan dreje for at aktivere eller deaktivere læring, og den har stor indflydelse på, hvor meget læring der sker, ved direkte at styre graden af ​​vægtopdateringer.

Forskellige optimeringsværktøjer udnytter læringshastigheder forskelligt - men det underliggende koncept forbliver det samme. Det er overflødigt at sige, at læringshastigheder har været genstand for mange undersøgelser, artikler og praktiserende benchmarks.

Generelt er stort set alle enige om, at en statisk indlæringshastighed ikke vil reducere den, og en eller anden form for reduktion af indlæringshastigheden sker i de fleste teknikker, der justerer indlæringshastigheden under træning - uanset om det er en monotonisk, cosinus, trekantet eller andre typer af reduktion.

En teknik der i de senere år har vundet indpas er opvarmning af indlæringshastighed, som kan parres med praktisk talt enhver anden reduktionsteknik.

Læringshastighedsopvarmning

Ideen bag opvarmning af læringshastighed er enkel. I de tidligste stadier af træning - vægte er langt fra deres ideelle tilstande. Dette betyder store opdateringer over hele linjen, hvilket kan ses som "overkorrektioner" for hver vægt - hvor den drastiske opdatering af en anden kan ophæve opdateringen af ​​en anden vægt, hvilket gør de indledende stadier af træningen mere ustabile.

Disse ændringer udlignes, men kan undgås ved at have en lille indlæringshastighed til at begynde med, nå en mere stabil suboptimal tilstand og derefter anvende en større indlæringshastighed. Du kan på en måde lette netværket til opdateringer i stedet for at ramme det med dem.

Det er opvarmning af læringshastighed! Starter med en lav (eller 0) indlæringshastighed og stiger til en begyndende indlæringsrate (hvad du alligevel ville starte med). Denne stigning kan følge enhver funktion, men er almindeligvis lineær.

Efter at have nået den indledende hastighed, kan andre skemaer såsom cosinus-henfald, lineær reduktion osv. anvendes for gradvist at sænke hastigheden indtil slutningen af ​​træningen. Læringshastighedsopvarmning er normalt en del af et skema med to skemaer, hvor LR-opvarmning er den første, mens et andet skema tager over, efter at satsen har nået et udgangspunkt.

I denne guide implementerer vi en opvarmning af læringshastigheden i Keras/TensorFlow som en keras.optimizers.schedules.LearningRateSchedule underklasse og keras.callbacks.Callback ring tilbage. Læringshastigheden øges fra 0 til target_lr og anvende cosinus-henfald, da dette er et meget almindeligt sekundært skema. Som sædvanlig gør Keras det enkelt at implementere fleksible løsninger på forskellige måder og sende dem med dit netværk.

Bemærk: Implementeringen er generisk og inspireret af Tony's Keras implementering af de tricks, der er beskrevet i "Pose med tricks til billedklassificering med konvolutionelle neurale netværk”.

Læringshastighed med Keras-tilbagekald

Den enkleste måde at implementere enhver læringshastighedsplan er ved at oprette en funktion, der tager lr parameter (float32), sender den gennem en transformation og returnerer den. Denne funktion videregives derefter til LearningRateScheduler callback, som anvender funktionen på indlæringshastigheden.

Nu tf.keras.callbacks.LearningRateScheduler() overfører epoketallet til den funktion, det bruger til at beregne indlæringshastigheden, hvilket er ret groft. LR Warmup bør udføres på hver trin (batch), ikke epoke, så vi bliver nødt til at udlede en global_step (på tværs af alle epoker) for at beregne indlæringshastigheden i stedet, og underklasser Callback klasse for at oprette et tilpasset tilbagekald i stedet for blot at videregive funktionen, da vi bliver nødt til at sende argumenter ind på hvert opkald, hvilket er umuligt, når du bare sender funktionen:

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

Denne tilgang er gunstig, når du ikke ønsker en høj grad af tilpasning, og du ikke ønsker at forstyrre den måde, Keras behandler lr, og især hvis du vil bruge tilbagekald som ReduceLROnPlateau() da det kun kan fungere med en float-baseret lr. Lad os implementere en læringshastighedsopvarmning ved hjælp af et Keras-tilbagekald, startende med en bekvemmelighedsfunktion:

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

På hvert trin beregner vi indlæringshastigheden og opvarmningsindlæringshastigheden (begge elementer af skemaet) med hensyn til start_lr , target_lr. start_lr vil normalt starte kl 0.0, Mens target_lr afhænger af dit netværk og optimizer – 1e-3 er muligvis ikke en god standard, så sørg for at indstille dit mål, der starter LR, når du kalder metoden.

Hvis global_step i uddannelsen er højere end warmup_steps vi har indstillet – vi bruger cosinus-decay-skemaet LR. Hvis ikke, betyder det, at vi stadig varmer op, så opvarmningen LR bruges. Hvis hold argumentet er indstillet, holder vi target_lr for det antal skridt efter opvarmning og før cosinus-henfaldet. np.where() giver en fantastisk syntaks til dette:

np.where(condition, value_if_true, value_if_false)

Du kan visualisere funktionen med:

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)

Nu vil vi gerne bruge denne funktion som en del af et tilbagekald og videregive optimeringstrinnet som global_step i stedet for et element i et vilkårligt array - eller du kan udføre beregningen inden for klassen. Lad os subclss Callback klasse:

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)

Tjek vores praktiske, praktiske guide til at lære Git, med bedste praksis, brancheaccepterede standarder og inkluderet snydeark. Stop med at google Git-kommandoer og faktisk lærer det!

Først definerer vi konstruktøren for klassen og holder styr på dens felter. På hver batch, der er afsluttet, øger vi det globale trin, noterer os den nuværende LR og tilføjer den til listen over LR'er indtil videre. Ved hver batchs begyndelse - beregner vi LR ved hjælp af lr_warmup_cosine_decay() funktion og indstil den LR som optimizerens aktuelle LR. Dette gøres med backend's set_value().

Når det er gjort – beregn bare de samlede trin (længde/batch_size*epoker) og tag en del af dette tal for din 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)

Til sidst skal du konstruere din model og give tilbagekaldet i fit() opkald:

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

Ved afslutningen af ​​træningen kan du få og visualisere de ændrede LR'er via:

lrs = callback.lrs 
plt.plot(lrs)

Læringshastighedsopvarmning med Cosinus-decay i Keras/TensorFlow PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Hvis du plotter historien om en model trænet med og uden LR-opvarmning – vil du se en tydelig forskel i træningens stabilitet:

Læringshastighedsopvarmning med Cosinus-decay i Keras/TensorFlow PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Learning Rate med LearningRateSchedule underklasse

Et alternativ til at oprette et tilbagekald er at oprette en LearningRateSchedule underklasse, som ikke manipulerer LR – den erstatter den. Denne tilgang giver dig mulighed for at proppe lidt mere ind i backend af Keras/TensorFlow, men når den bruges, kan den ikke kombineres med andre LR-relaterede tilbagekald, som f.eks. ReduceLROnPlateau(), som omhandler LR'er som flydende kommatal.

Derudover vil brug af underklassen kræve, at du gør den serialiserbar (overload get_config()), da det bliver en del af modellen, hvis du vil gemme modelvægtene. En anden ting at bemærke er, at klassen vil forvente udelukkende at arbejde med tf.Tensors. Heldigvis vil den eneste forskel i den måde, vi arbejder på, være at ringe tf.func() i stedet for np.func() da TensorFlow og NumPy API'erne er utroligt ens og kompatible.

Lad os omskrive bekvemmeligheden lr_warmup_cosine_decay() funktion for at bruge TensorFlow-operationer i stedet:

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

Med convinience-funktionen kan vi underklassificere LearningRateSchedule klasse. På hver __call__() (batch), beregner vi LR ved hjælp af funktionen og returnerer den. Du kan naturligvis også pakke beregningen inden for den underklassede klasse.

Syntaksen er renere end Callback sublcass, primært fordi vi får adgang til step felt, snarere end at holde styr på det på egen hånd, men gør det også noget sværere at arbejde med klasseejendomme – især gør det det svært at udvinde lr fra en tf.Tensor() til enhver anden type for at holde styr på en liste. Dette kan teknisk omgås ved at køre i ivrig tilstand, men giver en irritation for at holde styr på LR til fejlfindingsformål og undgås bedst:

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

Parametrene er de samme og kan beregnes på nogenlunde samme måde som før:


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)

Og træningspipelinen adskiller sig kun ved, at vi indstiller optimizerens LR til 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)

Hvis du ønsker at gemme modellen, WarmupCosineDecay tidsplanen skal tilsidesætte get_config() metode:

    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

Til sidst, når du indlæser modellen, skal du bestå en WarmupCosineDecay som et brugerdefineret objekt:

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

Konklusion

I denne guide har vi taget et kig på intuitionen bag Learning Rate Warmup – en almindelig teknik til at manipulere indlæringshastigheden, mens du træner neurale netværk.

Vi har implementeret en læringshastighedsopvarmning med cosinus-decay, den mest almindelige type LR-reduktion parret med opvarmning. Du kan implementere en hvilken som helst anden funktion til reduktion eller slet ikke reducere indlæringshastigheden – overlade det til andre tilbagekald som f.eks. ReduceLROnPlateau(). Vi har implementeret opvarmning af indlæringshastighed som et Keras-tilbagekald samt et Keras Optimizer-skema og plottet indlæringshastigheden gennem epokerne.

Tidsstempel:

Mere fra Stablemisbrug