Ogrevanje hitrosti učenja s kosinusnim upadanjem v Keras/TensorFlow PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.

Ogrevanje hitrosti učenja s kosinusnim upadanjem v Keras/TensorFlow

Stopnja učenja je pomemben hiperparameter v omrežjih globokega učenja – in neposredno narekuje stopnja za katere se izvajajo posodobitve uteži, za katere se ocenjuje, da minimizirajo določeno funkcijo izgube. V SGD:

$$
teža_{t+1} = teža_t – lr * frac{napaka}{dteža_t}
$$

S stopnjo učenja 0, posodobljena teža je samo nazaj k sebi – težat. Stopnja učenja je dejansko gumb, ki ga lahko zavrtimo, da omogočimo ali onemogočimo učenje, in ima velik vpliv na to, koliko učenja poteka, tako da neposredno nadzira stopnjo posodobitev teže.

Različni optimizatorji različno uporabljajo stopnje učenja – vendar osnovni koncept ostaja enak. Ni treba posebej poudarjati, da so bile stopnje učenja predmet številnih študij, dokumentov in meril uspešnosti praktikov.

Na splošno se skoraj vsi strinjajo, da statična stopnja učenja ne bo zmanjšala, in neka vrsta zmanjšanja stopnje učenja se zgodi pri večini tehnik, ki prilagajajo stopnjo učenja med vadbo – ne glede na to, ali gre za monotono, kosinusno, trikotno ali druge vrste zmanjšanje.

Tehnika, ki se v zadnjih letih vse bolj uveljavlja, je ogrevanje stopnje učenja, ki se lahko kombinira s praktično katero koli drugo tehniko redukcije.

Ogrevanje stopnje učenja

Zamisel za ogrevanje stopnje učenja je preprosta. V najzgodnejših fazah treninga so uteži daleč od idealnega stanja. To pomeni velike posodobitve na vseh področjih, kar je mogoče razumeti kot "prekomerne popravke" za vsako težo – kjer lahko drastična posodobitev druge izniči posodobitev neke druge teže, zaradi česar so začetne faze treninga bolj nestabilne.

Te spremembe se izravnajo, vendar se jim je mogoče izogniti tako, da za začetek uporabite majhno stopnjo učenja, dosežete stabilnejše suboptimalno stanje in nato uporabite višjo stopnjo učenja. Omrežju lahko nekako olajšate posodabljanje, namesto da bi ga udarili z njimi.

To je ogrevanje stopnje učenja! Začnete z nizko (ali 0) stopnjo učenja in povečate na začetno stopnjo učenja (s čimer bi vseeno začeli). To povečanje lahko dejansko sledi kateri koli funkciji, vendar je običajno linearno.

Ko je dosežena začetna stopnja, je mogoče uporabiti druge razporede, kot je kosinusni upad, linearna redukcija itd., za postopno znižanje stopnje do konca vadbe. Ogrevanje hitrosti učenja je običajno del razporeda z dvema urnikoma, pri čemer je prvo ogrevanje LR, medtem ko drugi urnik prevzame, ko hitrost doseže začetno točko.

V tem priročniku bomo izvajali ogrevanje stopnje učenja v Keras/TensorFlow kot keras.optimizers.schedules.LearningRateSchedule podrazred in keras.callbacks.Callback poklicati nazaj. Stopnja učenja se bo povečala od 0 do target_lr in uporabite kosinusni razpad, saj je to zelo pogost sekundarni razpored. Kot običajno Keras poenostavi implementacijo prilagodljivih rešitev na različne načine in njihovo pošiljanje v vaše omrežje.

Opomba: Izvedba je generična in se zgleduje po Tonyjeva izvedba Keras trikov, opisanih v "Vreča trikov za klasifikacijo slik s konvolucijskimi nevronskimi mrežami.

Stopnja učenja s povratnimi klici Keras

Najpreprostejši način za implementacijo katerega koli urnika stopnje učenja je ustvarjanje funkcije, ki prevzame lr parameter (float32), ga prenese skozi neko transformacijo in ga vrne. Ta funkcija se nato prenese na LearningRateScheduler povratni klic, ki uporabi funkcijo za stopnjo učenja.

Zdaj, tf.keras.callbacks.LearningRateScheduler() posreduje številko epohe funkciji, ki jo uporablja za izračun stopnje učenja, ki je precej groba. LR Warmup je treba narediti na vsakem korak (serija), ne epoha, zato bomo morali izpeljati a global_step (v vseh obdobjih), da namesto tega izračunate stopnjo učenja in podrazvrstite Callback razred za ustvarjanje povratnega klica po meri, namesto da bi samo posredovali funkcijo, saj bomo morali posredovati argumente pri vsakem klicu, kar je nemogoče pri samo posredovanju funkcije:

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

Ta pristop je ugoden, če ne želite visoke ravni prilagajanja in ne želite posegati v način, kako Keras obravnava lr, še posebej, če želite uporabiti povratne klice, kot je ReduceLROnPlateau() saj lahko deluje le s plavajočo lr. Izvedimo ogrevanje stopnje učenja z uporabo povratnega klica Keras, začenši s priročno funkcijo:

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

Na vsakem koraku izračunamo stopnjo učenja in stopnjo učenja za ogrevanje (oba elementa urnika), glede na start_lr in target_lr. start_lr se običajno začne ob 0.0, Medtem ko target_lr odvisno od vašega omrežja in optimizatorja – 1e-3 morda ni dobra privzeta vrednost, zato ne pozabite nastaviti cilja, ki se začne z LR, ko kličete metodo.

Če global_step pri usposabljanju je višji od warmup_steps smo nastavili – uporabljamo razpored kosinusnega razpada LR. Če ne, pomeni, da se še ogrevamo, zato se uporablja ogrevalni LR. Če je hold argument je nastavljen, zadržali ga bomo target_lr za to število korakov po ogrevanju in pred razpadom kosinusa. np.where() ponuja odlično sintakso za to:

np.where(condition, value_if_true, value_if_false)

Funkcijo lahko vizualizirate z:

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)

Zdaj bomo to funkcijo želeli uporabiti kot del povratnega klica in prenesti korak optimizatorja kot global_step namesto elementa poljubne matrike – ali pa lahko izvedete izračun znotraj razreda. Podklasirajmo Callback razred:

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)

Oglejte si naš praktični, praktični vodnik za učenje Gita z najboljšimi praksami, standardi, sprejetimi v panogi, in priloženo goljufijo. Nehajte Googlati ukaze Git in pravzaprav naučiti it!

Najprej definiramo konstruktor za razred in spremljamo njegova polja. Pri vsaki končani seriji bomo povečali globalni korak, upoštevali trenutni LR in ga dodali na seznam dosedanjih LR. Na začetku vsake serije – LR bomo izračunali z uporabo lr_warmup_cosine_decay() funkcijo in nastavite ta LR kot trenutni LR optimizatorja. To se naredi z zaledjem set_value().

Ko je to opravljeno – samo izračunajte skupno število korakov (dolžina/velikost_serije*epohe) in vzemite del tega števila za svoj 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)

Končno sestavite svoj model in zagotovite povratni klic v fit() klic:

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

Na koncu usposabljanja lahko pridobite in vizualizirate spremenjene LR prek:

lrs = callback.lrs 
plt.plot(lrs)

Ogrevanje hitrosti učenja s kosinusnim upadanjem v Keras/TensorFlow PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.

Če narišete zgodovino modela, treniranega z ogrevanjem LR in brez njega – boste videli izrazito razliko v stabilnosti treninga:

Ogrevanje hitrosti učenja s kosinusnim upadanjem v Keras/TensorFlow PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.

Stopnja učenja s podrazredom LearningRateSchedule

Druga možnost za ustvarjanje povratnega klica je ustvariti a LearningRateSchedule podrazred, ki ne manipulira z LR – ga nadomesti. Ta pristop vam omogoča, da nekoliko bolj prodrete v zaledje Keras/TensorFlow, vendar ga, ko ga uporabljate, ni mogoče kombinirati z drugimi povratnimi klici, povezanimi z LR, kot je npr. ReduceLROnPlateau(), ki obravnava LR kot števila s plavajočo vejico.

Poleg tega bo uporaba podrazreda zahtevala, da ga naredite serializirajočega (preobremenitev get_config()), ko postane del modela, če želite shraniti uteži modela. Druga stvar, ki jo je treba opozoriti, je, da bo razred pričakoval, da bo delal izključno z tf.Tensors. Na srečo bo edina razlika v našem načinu dela klicanje tf.func() Namesto np.func() saj sta API-ja TensorFlow in NumPy neverjetno podobna in združljiva.

Prepišimo udobje lr_warmup_cosine_decay() funkcijo za uporabo operacij TensorFlow namesto tega:

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

S funkcijo convinience lahko podrazredimo LearningRateSchedule razred. Na vsakem __call__() (batch), bomo izračunali LR s funkcijo in jo vrnili. Seveda lahko izračun zapakirate tudi znotraj razreda podrazreda.

Sintaksa je čistejša od Callback sublcas, predvsem zato, ker dobimo dostop do step polje, namesto da bi ga spremljali sami, vendar je tudi nekoliko težje delati z lastnostmi razreda – zlasti otežuje ekstrahiranje lr od tf.Tensor() v katero koli drugo vrsto, ki jo lahko spremljate na seznamu. Temu se je mogoče tehnično izogniti z izvajanjem v željnem načinu, vendar predstavlja motnjo pri sledenju LR za namene odpravljanja napak in se temu najbolje izogniti:

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

Parametri so enaki in jih je mogoče izračunati na približno enak način kot prej:


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)

In cevovod usposabljanja se razlikuje samo v tem, da nastavimo LR optimizatorja na 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)

Če želite shraniti model, se WarmupCosineDecay urnik bo moral preglasiti get_config() metoda:

    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

Končno, ko nalagate model, boste morali opraviti a WarmupCosineDecay kot predmet po meri:

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

zaključek

V tem priročniku smo si ogledali intuicijo za ogrevanjem hitrosti učenja – običajno tehniko za manipulacijo hitrosti učenja med urjenjem nevronskih mrež.

Implementirali smo ogrevanje stopnje učenja s kosinusnim upadanjem, kar je najpogostejša vrsta zmanjšanja LR v kombinaciji z ogrevanjem. Izvedete lahko katero koli drugo funkcijo za zmanjšanje ali pa sploh ne zmanjšate stopnje učenja – to prepustite drugim povratnim klicem, kot je npr ReduceLROnPlateau(). Implementirali smo ogrevanje stopnje učenja kot povratni klic Keras, kot tudi urnik optimizatorja Keras in narisali stopnjo učenja skozi obdobja.

Časovni žig:

Več od Stackabuse