Încălzirea ratei de învățare cu degradarea cosinusului în Inteligența datelor Keras/TensorFlow PlatoBlockchain. Căutare verticală. Ai.

Încălzirea ratei de învățare cu degradarea cosinusului în Keras/TensorFlow

Rata de învățare este un hiperparametru important în rețelele de învățare profundă – și dictează direct grad la care se efectuează actualizări ale greutăților, care sunt estimate pentru a minimiza o anumită funcție de pierdere. În SGD:

$$
greutate_{t+1} = greutate_t – lr * frac{eroare}{dgreutate_t}
$$

Cu o rată de învățare de 0, greutatea actualizată a revenit la sine - greutatet. Rata de învățare este efectiv un buton pe care îl putem roti pentru a activa sau dezactiva învățarea și are o influență majoră asupra cât de multă învățare are loc, controlând direct gradul de actualizare a greutății.

Diferiți optimizatori utilizează ratele de învățare în mod diferit – dar conceptul de bază rămâne același. Inutil să spun că ratele de învățare au făcut obiectul multor studii, lucrări și repere ale practicienilor.

În general vorbind, aproape toată lumea este de acord că o rată de învățare statică nu o va reduce, iar un anumit tip de reducere a ratei de învățare are loc în majoritatea tehnicilor care reglează rata de învățare în timpul antrenamentului - indiferent dacă acesta este un tip monoton, cosinus, triunghiular sau alte tipuri de reducere.

O tehnică care în ultimii ani a luat amploare este încălzirea ratei de învățare, care poate fi asociat cu practic orice altă tehnică de reducere.

Încălzirea ratei de învățare

Ideea din spatele încălzirii ratei de învățare este simplă. În primele etape ale antrenamentului – greutățile sunt departe de stările lor ideale. Acest lucru înseamnă actualizări mari pe toate planurile, care pot fi văzute ca „supracorecții” pentru fiecare greutate – unde actualizarea drastică a alteia poate anula actualizarea unei alte greutăți, făcând etapele inițiale ale antrenamentului mai instabile.

Aceste schimbări se rezolvă, dar pot fi evitate având pentru început o rată de învățare mică, atingând o stare suboptimă mai stabilă și apoi aplicând o rată de învățare mai mare. Puteți să ușurați rețeaua în actualizări, mai degrabă decât să o faceți cu ele.

Asta înseamnă încălzirea ratei de învățare! Începând cu o rată de învățare scăzută (sau 0) și crescând până la o rată de învățare inițială (cu ce ați începe oricum). Această creștere poate urma cu adevărat orice funcție, dar este de obicei liniară.

După atingerea ratei inițiale, pot fi aplicate alte programe, cum ar fi decăderea cosinusului, reducerea liniară etc., pentru a scădea progresiv rata până la sfârșitul antrenamentului. Încălzirea ratei de învățare face de obicei parte dintr-un program cu două programe, unde încălzirea LR este prima, în timp ce un alt program preia după ce rata a atins un punct de pornire.

În acest ghid, vom implementa o încălzire a ratei de învățare în Keras/TensorFlow ca a keras.optimizers.schedules.LearningRateSchedule subclasa si keras.callbacks.Callback sună din nou. Rata de învățare va crește de la 0 la target_lr și aplicați dezintegrarea cosinusului, deoarece acesta este un program secundar foarte comun. Ca de obicei, Keras simplifică implementarea soluțiilor flexibile în diverse moduri și le trimite împreună cu rețeaua dvs.

Notă: Implementarea este generică și inspirată de Implementarea lui Tony Keras dintre trucurile prezentate în „Pungă de trucuri pentru clasificarea imaginilor cu rețele neuronale convoluționale”.

Rata de învățare cu apelurile Keras

Cea mai simplă modalitate de a implementa orice program al ratei de învățare este prin crearea unei funcții care preia lr parametru (float32), îl trece printr-o transformare și îl returnează. Această funcție este apoi transmisă către LearningRateScheduler callback, care aplică funcția ratei de învățare.

Acum, tf.keras.callbacks.LearningRateScheduler() transmite numărul epocii funcției pe care o folosește pentru a calcula rata de învățare, care este destul de grosieră. Încălzirea LR ar trebui făcută pe fiecare pas (lot), nu epoca, deci va trebui să derivăm a global_step (în toate epocile) pentru a calcula rata de învățare în schimb și subclasați Callback clasă pentru a crea un callback personalizat, mai degrabă decât să transmitem funcția, deoarece va trebui să transmitem argumente la fiecare apel, ceea ce este imposibil atunci când trecem funcția:

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

Această abordare este favorabilă atunci când nu doriți un nivel înalt de personalizare și nu doriți să interferați cu modul în care Keras tratează lr, și mai ales dacă doriți să utilizați apeluri inverse, cum ar fi ReduceLROnPlateau() deoarece poate funcționa numai cu un dispozitiv pe bază de float lr. Să implementăm o încălzire a ratei de învățare folosind un apel Keras, începând cu o funcție de confort:

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

La fiecare pas, calculăm rata de învățare și rata de învățare la încălzire (ambele elemente ale programului), în raport cu start_lr și target_lr. start_lr va începe de obicei la 0.0, În timp ce target_lr depinde de rețea și de optimizator - 1e-3 s-ar putea să nu fie o valoare implicită bună, așa că asigurați-vă că vă setați ținta începând cu LR atunci când apelați metoda.

În cazul în care global_step în formare este mai mare decât cel warmup_steps am stabilit – folosim programul de dezintegrare a cosinusului LR. Dacă nu, înseamnă că încă ne încălzim, deci se folosește încălzirea LR. Dacă hold argumentul este setat, vom reține target_lr pentru acel număr de pași după încălzire și înainte de dezintegrarea cosinusului. np.where() oferă o sintaxă excelentă pentru aceasta:

np.where(condition, value_if_true, value_if_false)

Puteți vizualiza funcția cu:

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)

Acum, vom dori să folosim această funcție ca parte a unui apel invers și să trecem pasul de optimizare drept global_step mai degrabă decât un element al unui tablou arbitrar - sau puteți efectua calculul în cadrul clasei. Să subclss Callback clasă:

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)

Consultați ghidul nostru practic și practic pentru a învăța Git, cu cele mai bune practici, standarde acceptate de industrie și fisa de cheat incluse. Opriți căutarea pe Google a comenzilor Git și de fapt învăţa aceasta!

În primul rând, definim constructorul pentru clasă și urmărim câmpurile acesteia. Pe fiecare lot care s-a încheiat, vom crește pasul global, vom lua notă de LR-ul curent și îl vom adăuga la lista de LR-uri de până acum. La începutul fiecărui lot – vom calcula LR folosind lr_warmup_cosine_decay() funcția și setați acel LR ca LR curent al optimizatorului. Acest lucru se face cu backend-ul set_value().

Cu asta gata - doar calculați pașii totali (lungime/dimensiune_batch*epoch) și luați o parte din acel număr pentru dvs. 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)

În cele din urmă, construiți modelul și furnizați apelul înapoi în fit() apel:

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

La sfârșitul antrenamentului, puteți obține și vizualiza LR-urile modificate prin:

lrs = callback.lrs 
plt.plot(lrs)

Încălzirea ratei de învățare cu degradarea cosinusului în Inteligența datelor Keras/TensorFlow PlatoBlockchain. Căutare verticală. Ai.

Dacă trasezi istoricul unui model antrenat cu și fără încălzire LR - vei vedea o diferență distinctă în stabilitatea antrenamentului:

Încălzirea ratei de învățare cu degradarea cosinusului în Inteligența datelor Keras/TensorFlow PlatoBlockchain. Căutare verticală. Ai.

Rata de învățare cu subclasa LearningRateSchedule

O alternativă la crearea unui apel invers este crearea unui LearningRateSchedule subclasă, care nu manipulează LR – îl înlocuiește. Această abordare vă permite să introduceți puțin mai mult în backend-ul Keras/TensorFlow, dar atunci când este utilizat, nu poate fi combinat cu alte apeluri legate de LR, cum ar fi ReduceLROnPlateau(), care tratează LR-urile ca numere în virgulă mobilă.

În plus, utilizarea subclasei va necesita să o faceți serializabil (supraîncărcare get_config()) pe măsură ce devine parte a modelului, dacă doriți să salvați greutățile modelului. Un alt lucru de remarcat este că clasa se va aștepta să lucreze exclusiv cu tf.Tensors. Din fericire, singura diferență în modul în care lucrăm va fi apelarea tf.func() în loc de np.func() deoarece API-urile TensorFlow și NumPy sunt uimitor de similare și compatibile.

Să rescriem comoditatea lr_warmup_cosine_decay() funcția pentru a utiliza operațiunile TensorFlow în schimb:

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

Cu funcția de convingere, putem subclasa LearningRateSchedule clasă. Pe fiecare __call__() (lot), vom calcula LR folosind funcția și o vom returna. În mod natural, puteți împacheta calculul și în cadrul clasei subclasate.

Sintaxa este mai curată decât cea Callback sublcass, în primul rând pentru că avem acces la step câmp, mai degrabă decât să-l ținem evidența pe cont propriu, dar, de asemenea, îngreunează oarecum lucrul cu proprietățile clasei - în special, face dificilă extragerea lr de la un tf.Tensor() în orice alt tip de urmărit într-o listă. Acest lucru poate fi ocolit din punct de vedere tehnic prin rularea în modul dornic, dar prezintă o supărare pentru urmărirea LR-ului în scopuri de depanare și este cel mai bine evitat:

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

Parametrii sunt aceiași și pot fi calculați în același mod ca înainte:


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)

Și conducta de antrenament diferă doar prin faptul că setăm LR-ul optimizatorului la 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)

Dacă doriți să salvați modelul, WarmupCosineDecay programul va trebui să depășească get_config() metodă:

    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

În cele din urmă, atunci când încărcați modelul, va trebui să treceți un WarmupCosineDecay ca obiect personalizat:

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

Concluzie

În acest ghid, am aruncat o privire asupra intuiției din spatele încălzirii ratei de învățare – o tehnică comună pentru manipularea ratei de învățare în timp ce antrenăm rețelele neuronale.

Am implementat o încălzire a ratei de învățare cu dezintegrare cosinus, cel mai comun tip de reducere LR asociată cu încălzirea. Puteți implementa orice altă funcție pentru reducerea sau nu reduceți deloc rata de învățare - lăsând-o pe seama altor apeluri, cum ar fi ReduceLROnPlateau(). Am implementat încălzirea ratei de învățare ca un Keras Callback, precum și un Keras Optimizer Schedule și am trasat rata de învățare de-a lungul epocilor.

Timestamp-ul:

Mai mult de la Stackabuse