Faza 03 · lecția 09
Scheme ale ratei de învățare și warmup
Scopul lecției: Rata de învățare este cel mai important hiperparametru. Nu arhitectura. Nu dimensiunea setului de date. Nu funcția de activare. Rata de învățare. Dacă nu reglați nimic altceva, reglați-o pe aceasta.
Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.
Cuprinsul lecției
- Obiective de învățare
- Problema
- Conceptul
- Rată de învățare constantă
- Descreștere în trepte
- Amortizare cosinusoidală
- Warmup: de ce porniți cu o valoare mică
- Warmup liniar urmat de descreștere cosinusoidală
- Politica 1cycle
- Formele schemelor
- Diagramă decizională
- Valori reale din modele publicate
- Construiți
- Pasul 1: funcțiile schemelor
- Pasul 2: vizualizarea tuturor schemelor
- Pasul 3: rețeaua pentru antrenare
- Pasul 4: compararea tuturor schemelor
- Pasul 5: LR prea mare sau prea mică
- Folosiți
- Livrați
- Exerciții
- Termeni-cheie
- Lecturi suplimentare
Rata de învățare este cel mai important hiperparametru. Nu arhitectura. Nu dimensiunea setului de date. Nu funcția de activare. Rata de învățare. Dacă nu reglați nimic altceva, reglați-o pe aceasta.
Tip: Construire Limbaje: Python Cerințe preliminare: Lecția 03.06 — Optimizatori; lecția 03.08 — Inițializarea ponderilor Durată: ~90 de minute
Obiective de învățare
- Să implementați de la zero scheme cu rată de învățare constantă, descreștere în trepte, amortizare cosinusoidală, warmup urmat de cosinus și 1cycle.
- Să demonstrați cele trei moduri de eșec ale alegerii ratei de învățare: divergența când este prea mare, stagnarea când este prea mică și oscilația când nu descrește.
- Să explicați de ce warmup-ul este folosit cu optimizatorii bazați pe Adam și cum poate stabiliza începutul antrenării.
- Să comparați viteza de convergență a celor cinci scheme pe aceeași sarcină și să o alegeți pe cea potrivită unui buget de antrenare dat.
Problema
Stabiliți rata de învățare la 0,1. Antrenarea diverge — pierderea ajunge la infinit în trei pași. Stabiliți-o la 0,0001. Antrenarea înaintează foarte lent — după 100 de epoci, modelul abia s-a îndepărtat de starea aleatoare. Stabiliți-o la 0,01. Antrenarea funcționează 50 de epoci, apoi pierderea oscilează în jurul unui minim la care nu poate ajunge, deoarece pașii sunt prea mari.
Rata de învățare optimă nu trebuie să fie constantă. La început sunt utile adesea salturi mari, pentru a avansa rapid. Spre sfârșitul antrenării sunt utile salturi mici, pentru stabilizarea în apropierea unui minim. Diferența dintre un model cu acuratețe de 90% și unul cu 95% poate proveni uneori numai din schemă.
Multe modele importante folosesc o schemă a ratei de învățare. Parametrii nu sunt aleși arbitrar, ci sunt de regulă rezultatul unor experimente costisitoare cu hiperparametri.
Notă tehnică a traducerii: Afirmația originală despre „fiecare model important publicat în ultimii trei ani” nu poate fi susținută universal, iar valorile sale pentru Llama 3 și GPT-3 sunt greșite. Raportul Llama 3 indică pentru modelul 405B o rată maximă de
8e-5, 8.000 de pași de warmup și o descreștere cosinusoidală până la8e-7în 1.200.000 de pași; modelul a fost preantrenat pe 15,6 trilioane de tokenuri. Lucrarea GPT-3 indică pentru modelul 175B o rată de6e-5, warmup liniar pe 375 de milioane de tokenuri și descreștere cosinusoidală până la 10% din valoare pe parcursul a 260 de miliarde de tokenuri.
Trebuie să înțelegeți schemele deoarece valorile implicite nu se vor potrivi neapărat problemei dumneavoastră. Când ajustați fin un model preantrenat, schema potrivită diferă de cea folosită la antrenarea de la zero. Când măriți dimensiunea lotului, poate trebui modificată și perioada de warmup. Când antrenarea se întrerupe la pasul 10.000, trebuie să puteți determina dacă problema provine din schemă sau din altă cauză.
Conceptul
Rată de învățare constantă
Cea mai simplă abordare. Alegeți o valoare și folosiți-o la fiecare pas.
lr(t) = lr_0
Este rareori optimă. Fie este prea mare spre sfârșitul antrenării și provoacă oscilații în jurul minimului, fie este prea mică la început și irosește calculul pe pași minusculi. Poate fi suficientă pentru modele mici și depanare. Este o alegere slabă pentru multe antrenări de lungă durată.
Descreștere în trepte
Abordarea clasică din epoca ResNet. Reduceți rata de învățare cu un factor, de obicei de 10 ori, la epoci fixe.
lr(t) = lr_0 * gamma^(floor(epoch / step_size))
Cu gamma = 0.1 și step_size = 30, rata scade de 10 ori la fiecare 30 de epoci. O rețetă ResNet-50 răspândită folosește lr=0.1 și reduceri de 10 ori la epocile 30, 60 și 90.
Notă tehnică a traducerii: Articolul ResNet original nu fixează pentru ImageNet bornele 30, 60 și 90; el descrie împărțirea ratei la 10 când eroarea se plafonează. Succesiunea 30/60/90 este o rețetă ulterioară frecventă, nu configurația documentată în lucrarea ResNet inițială.
Problema este că punctele optime de descreștere depind de setul de date și de arhitectură. Dacă treceți la altă problemă, trebuie să reglați din nou momentele reducerilor. Tranzițiile sunt abrupte și pot modifica brusc dinamica pierderii.
Amortizare cosinusoidală
Rata descrește lin de la valoarea maximă la una minimă, urmând o curbă cosinusoidală:
lr(t) = lr_min + 0.5 * (lr_max - lr_min) * (1 + cos(pi * t / T))
Unde t este pasul curent, iar T este numărul total de pași.
La t=0, termenul cosinus este 1, deci lr = lr_max. La t=T, termenul cosinus este -1, deci lr = lr_min. Descreșterea este lentă la început, accelerează la mijloc și încetinește din nou spre final.
Aceasta este o alegere implicită frecventă pentru antrenările moderne. Pe lângă lr_max și lr_min, trebuie stabilită și durata T. Forma cosinusoidală menține pași substanțiali în partea centrală a antrenării și îi reduce lin spre final, dar nu este garantat că va fi optimă pentru orice problemă.
Warmup: de ce porniți cu o valoare mică
Adam și alți optimizatori adaptivi mențin estimări mobile ale primului și celui de-al doilea moment al gradientului. La pasul 0, aceste estimări sunt inițializate cu zero. Actualizările timpurii pot avea o scară instabilă în anumite configurații; dacă rata de învățare este mare în această perioadă, modelul poate face pași prea mari și slab orientați.
Warmup-ul poate reduce acest risc. Porniți cu o rată de învățare foarte mică, adesea lr_max / warmup_steps sau chiar zero, și măriți-o liniar până la lr_max pe parcursul primilor N pași. Astfel, optimizatorul și dinamica rețelei evoluează treptat înainte de atingerea ratei complete.
lr(t) = lr_max * (t / warmup_steps) pentru t < warmup_steps
Notă tehnică a traducerii: Adam aplică bias correction estimărilor momentelor, deci nu este corect să spunem pur și simplu că primele actualizări se bazează pe „statistici inutile”. Warmup-ul nu este obligatoriu pentru orice utilizare a lui Adam; utilitatea și durata lui depind de scara actualizărilor, parametrizare, arhitectură, dimensiunea lotului și rata țintă. O analiză dedicată atribuie instabilitatea timpurie mai ales magnitudinii termenului de actualizare, nu doar inițializării cu zero a momentelor.
O euristică întâlnită este ca warmup-ul să ocupe 1–5% din pașii de antrenare, însă aceasta nu este o regulă universală. Llama 3 405B a folosit 8.000 de pași, iar GPT-3 a folosit 375 de milioane de tokenuri de warmup.
Warmup liniar urmat de descreștere cosinusoidală
O combinație modernă frecventă: rata crește liniar, apoi descrește cosinusoidal.
if t < warmup_steps:
lr(t) = lr_max * (t / warmup_steps)
else:
progress = (t - warmup_steps) / (total_steps - warmup_steps)
lr(t) = lr_min + 0.5 * (lr_max - lr_min) * (1 + cos(pi * progress))
Această combinație apare în numeroase antrenări de transformatoare, inclusiv în variante din familiile Llama și GPT. Warmup-ul poate preveni instabilitatea inițială, iar descreșterea cosinusoidală micșorează lin pașii spre final. Rețeta exactă diferă însă între modele și etape de antrenare.
Politica 1cycle
Observația lui Leslie Smith, publicată inițial în 2017 și ulterior în forma finală: măriți rata de învățare de la o valoare mică la una mare în prima parte a antrenării, apoi reduceți-o în partea rămasă. Pare contraintuitiv — de ce ați mări rata de învățare pe parcurs?
Teoria propusă este că o rată mare de învățare acționează ca regularizare prin modificarea traiectoriei de optimizare. Modelul explorează o regiune mai largă a peisajului pierderii în etapa de creștere, iar etapa de descreștere rafinează soluția găsită.
Faza 1 (de la 0 la T/2): lr crește de la lr_max/25 la lr_max
Faza 2 (de la T/2 la T): lr scade de la lr_max la lr_max/10000
1cycle poate antrena mai repede decât amortizarea cosinusoidală în unele probleme și la același buget de calcul. Compromisul este că trebuie să cunoașteți dinainte numărul total de pași.
Notă tehnică a traducerii: Împărțirea exactă în două jumătăți și factorii de mai sus reprezintă o variantă simplificată, nu definiția universală a 1cycle. De exemplu,
OneCycleLRdin PyTorch folosește implicit 30% din ciclu pentru creștere, amortizare cosinusoidală, variația inversă a momentumului,initial_lr = max_lr/25șimin_lr = initial_lr/10000. Performanța mai rapidă nu este garantată pe orice sarcină.
Formele schemelor
Diagramă decizională
Notă tehnică a traducerii: Diagrama oferă euristici, nu reguli generale. Bugetul exprimat numai prin durată nu determină schema optimă, 1cycle nu garantează cea mai rapidă convergență, iar proporția de warmup și raportul
lr_min/lr_maxtrebuie validate pentru configurația concretă.
Valori reale din modele publicate
lr-schedule
Construiți
Pasul 1: funcțiile schemelor
Fiecare funcție primește pasul curent și întoarce rata de învățare pentru acel pas.
import math
def constant_schedule(step, lr=0.01, **kwargs):
return lr
def step_decay_schedule(step, lr=0.1, step_size=100, gamma=0.1, **kwargs):
return lr * (gamma ** (step // step_size))
def cosine_schedule(step, lr=0.01, total_steps=1000, lr_min=1e-5, **kwargs):
if step >= total_steps:
return lr_min
return lr_min + 0.5 * (lr - lr_min) * (1 + math.cos(math.pi * step / total_steps))
def warmup_cosine_schedule(step, lr=0.01, total_steps=1000, warmup_steps=100, lr_min=1e-5, **kwargs):
if total_steps <= warmup_steps:
return lr * (step / max(warmup_steps, 1))
if step < warmup_steps:
return lr * step / warmup_steps
progress = (step - warmup_steps) / (total_steps - warmup_steps)
return lr_min + 0.5 * (lr - lr_min) * (1 + math.cos(math.pi * progress))
def one_cycle_schedule(step, lr=0.01, total_steps=1000, **kwargs):
mid = max(total_steps // 2, 1)
if step < mid:
return (lr / 25) + (lr - lr / 25) * step / mid
else:
progress = (step - mid) / max(total_steps - mid, 1)
return lr * (1 - progress) + (lr / 10000) * progress
Pasul 2: vizualizarea tuturor schemelor
Afișați o diagramă textuală care arată evoluția fiecărei scheme pe parcursul antrenării.
def visualize_schedule(name, schedule_fn, total_steps=500, **kwargs):
steps = list(range(0, total_steps, total_steps // 20))
if total_steps - 1 not in steps:
steps.append(total_steps - 1)
lrs = [schedule_fn(s, total_steps=total_steps, **kwargs) for s in steps]
max_lr = max(lrs) if max(lrs) > 0 else 1.0
print(f"\n{name}:")
for s, lr_val in zip(steps, lrs):
bar_len = int(lr_val / max_lr * 40)
bar = "#" * bar_len
print(f" Step {s:4d}: lr={lr_val:.6f} {bar}")
Pasul 3: rețeaua pentru antrenare
O rețea simplă cu două straturi pe setul de date circular, la fel ca în lecțiile anterioare, însă acum variem schema.
import random
def sigmoid(x):
x = max(-500, min(500, x))
return 1.0 / (1.0 + math.exp(-x))
def relu(x):
return max(0.0, x)
def relu_deriv(x):
return 1.0 if x > 0 else 0.0
def make_circle_data(n=200, seed=42):
random.seed(seed)
data = []
for _ in range(n):
x = random.uniform(-2, 2)
y = random.uniform(-2, 2)
label = 1.0 if x * x + y * y < 1.5 else 0.0
data.append(([x, y], label))
return data
def train_with_schedule(schedule_fn, schedule_name, data, epochs=300, base_lr=0.05, **kwargs):
random.seed(0)
hidden_size = 8
total_steps = epochs * len(data)
std = math.sqrt(2.0 / 2)
w1 = [[random.gauss(0, std) for _ in range(2)] for _ in range(hidden_size)]
b1 = [0.0] * hidden_size
w2 = [random.gauss(0, std) for _ in range(hidden_size)]
b2 = 0.0
step = 0
epoch_losses = []
for epoch in range(epochs):
total_loss = 0
correct = 0
for x, target in data:
lr = schedule_fn(step, lr=base_lr, total_steps=total_steps, **kwargs)
z1 = []
h = []
for i in range(hidden_size):
z = w1[i][0] * x[0] + w1[i][1] * x[1] + b1[i]
z1.append(z)
h.append(relu(z))
z2 = sum(w2[i] * h[i] for i in range(hidden_size)) + b2
out = sigmoid(z2)
error = out - target
d_out = error * out * (1 - out)
for i in range(hidden_size):
d_h = d_out * w2[i] * relu_deriv(z1[i])
w2[i] -= lr * d_out * h[i]
for j in range(2):
w1[i][j] -= lr * d_h * x[j]
b1[i] -= lr * d_h
b2 -= lr * d_out
total_loss += (out - target) ** 2
if (out >= 0.5) == (target >= 0.5):
correct += 1
step += 1
avg_loss = total_loss / len(data)
accuracy = correct / len(data) * 100
epoch_losses.append(avg_loss)
return epoch_losses
Notă tehnică a traducerii: Experimentul folosește actualizări SGD simple, nu Adam, deci nu testează afirmația despre warmup și statisticile unui optimizator adaptiv. În plus,
d_outeste gradientul pierderii0.5 * (out - target) ** 2, în timp ce valoarea raportată omite factorul0.5; aceasta schimbă numai scara gradientului față de pierderea afișată, nu direcția lui.
Notă tehnică a traducerii:
std = sqrt(2/2)este calculat pentru primul strat, cufan_in = 2, dar este reutilizat pentruw2, al căruifan_inestehidden_size = 8. Astfel, comparația dintre scheme poate fi influențată și de inițializarea prea largă a celei de-a doua proiecții; o inițializare He potrivită pentru ea ar folosisqrt(2/hidden_size).
Pasul 4: compararea tuturor schemelor
Antrenați aceeași rețea cu fiecare schemă și comparați pierderea finală și comportamentul convergenței.
def compare_schedules(data):
configs = [
("Constant", constant_schedule, {}),
("Step Decay", step_decay_schedule, {"step_size": 15000, "gamma": 0.1}),
("Cosine", cosine_schedule, {"lr_min": 1e-5}),
("Warmup+Cosine", warmup_cosine_schedule, {"warmup_steps": 3000, "lr_min": 1e-5}),
("1cycle", one_cycle_schedule, {}),
]
print(f"\n{'Schedule':<20} {'Start Loss':>12} {'Mid Loss':>12} {'End Loss':>12} {'Best Loss':>12}")
print("-" * 70)
for name, schedule_fn, extra_kwargs in configs:
losses = train_with_schedule(schedule_fn, name, data, epochs=300, base_lr=0.05, **extra_kwargs)
mid_idx = len(losses) // 2
best = min(losses)
print(f"{name:<20} {losses[0]:>12.6f} {losses[mid_idx]:>12.6f} {losses[-1]:>12.6f} {best:>12.6f}")
Notă tehnică a traducerii: Codul compară numai pierderile de antrenare la sfârșit de epocă. Fără un set de validare, rulări repetate și o măsură definită a timpului sau numărului de pași până la un prag, el nu demonstrează generalizarea și nici viteza de convergență în sens general.
Pasul 5: LR prea mare sau prea mică
Demonstrați cele trei moduri de eșec: o rată prea mare produce divergență, una prea mică înaintează foarte lent, iar una potrivită permite convergența.
def lr_sensitivity(data):
learning_rates = [1.0, 0.1, 0.01, 0.001, 0.0001]
print("\nLR Sensitivity (constant schedule, 100 epochs):")
print(f" {'LR':>10} {'Start Loss':>12} {'End Loss':>12} {'Status':>15}")
print(" " + "-" * 52)
for lr in learning_rates:
losses = train_with_schedule(constant_schedule, f"lr={lr}", data, epochs=100, base_lr=lr)
start = losses[0]
end = losses[-1]
if end > start or math.isnan(end) or end > 1.0:
status = "DIVERGED"
elif end > start * 0.9:
status = "BARELY MOVED"
elif end < 0.15:
status = "CONVERGED"
else:
status = "LEARNING"
end_str = f"{end:.6f}" if not math.isnan(end) else "NaN"
print(f" {lr:>10.4f} {start:>12.6f} {end_str:>12} {status:>15}")
Notă tehnică a traducerii: Etichetele de stare sunt euristici specifice acestui exemplu. Condiția
end > startnu demonstrează matematic divergența, iar pragulend < 0.15nu definește universal convergența.
Folosiți
PyTorch oferă planificatoare în torch.optim.lr_scheduler:
import torch
import torch.optim as optim
from torch.optim.lr_scheduler import CosineAnnealingLR, OneCycleLR, StepLR
model = nn.Sequential(nn.Linear(10, 64), nn.ReLU(), nn.Linear(64, 1))
optimizer = optim.Adam(model.parameters(), lr=3e-4)
scheduler = CosineAnnealingLR(optimizer, T_max=1000, eta_min=1e-5)
for step in range(1000):
loss = train_step(model, optimizer)
scheduler.step()
Notă tehnică a traducerii:
scheduler.step()trebuie apelat dupăoptimizer.step(); exemplul este corect numai dacătrain_stepexecută actualizarea optimizatorului.T_maxnumără apelurile planificatorului, așadar unitatea sa este pasul sau epoca în funcție de locul apelului. Fragmentul este doar ilustrativ și nu poate rula autonom: lipsesc importul sau definiția pentrunnși definiția funcțieitrain_step.
Pentru warmup urmat de cosinus, folosiți un planificator lambda sau get_cosine_schedule_with_warmup din Hugging Face:
from transformers import get_cosine_schedule_with_warmup
scheduler = get_cosine_schedule_with_warmup(
optimizer,
num_warmup_steps=2000,
num_training_steps=100000,
)
Funcția Hugging Face este utilizată în multe scripturi de ajustare fină pentru modele din familiile Llama și GPT. Ca punct de pornire, puteți încerca warmup urmat de cosinus, cu warmup de 3–5% din numărul total de pași, dar trebuie să validați alegerea pe arhitectura, optimizatorul, dimensiunea lotului și bugetul concret; nu există o configurație garantată pentru toate problemele.
Livrați
Această lecție produce:
outputs/prompt-lr-schedule-advisor.md— un prompt care recomandă schema și hiperparametrii potriviți ai ratei de învățare pentru configurația de antrenare.
Exerciții
-
Implementați descreșterea exponențială:
lr(t) = lr_0 * gamma^t, undegamma = 0.999. Comparați-o cu amortizarea cosinusoidală pe setul de date circular. -
Implementați testul intervalului ratei de învățare al lui Leslie Smith: antrenați câteva sute de pași, mărind exponențial LR de la
1e-7la1. Reprezentați grafic pierderea în funcție de LR. Valoarea maximă potrivită se află chiar înainte ca pierderea să înceapă să crească. -
Antrenați cu warmup urmat de cosinus, dar variați lungimea warmup-ului: 0%, 1%, 5%, 10% și 20% din numărul total de pași. Găsiți intervalul în care antrenarea este cea mai stabilă.
-
Implementați amortizarea cosinusoidală cu reporniri calde (SGDR): readuceți rata de învățare la
lr_maxla fiecareTpași și reluați descreșterea. Comparați-o cu schema cosinusoidală standard într-o antrenare mai lungă. -
Construiți un „chirurg al schemei” care monitorizează pierderea în timpul antrenării, trece automat de la warmup la cosinus când pierderea se stabilizează și reduce
lrdacă pierderea stagnează prea mult.
Termeni-cheie
| Termen | Ce spun oamenii | Ce înseamnă de fapt |
|---|---|---|
| Rată de învățare | „Cât de repede învață modelul” | Scalarul care înmulțește gradientul pentru a determina mărimea actualizării parametrilor |
| Schemă | „Modificați LR în timp” | Funcție care asociază fiecărui pas de antrenare o rată de învățare și urmărește îmbunătățirea convergenței |
| Warmup | „Porniți cu o LR mică” | Creșterea liniară a LR de la o valoare apropiată de zero la valoarea-țintă în primii N pași, pentru a stabiliza începutul optimizării |
| Amortizare cosinusoidală | „Descreștere lină a LR” | Reducerea LR pe o curbă cosinusoidală de la lr_max la lr_min pe parcursul antrenării |
| Descreștere în trepte | „Reduceți LR la borne” | Înmulțirea LR cu un factor, de obicei 0,1, la intervale fixe de epoci |
| Politica 1cycle | „În sus, apoi în jos” | Metoda lui Leslie Smith de a crește și apoi a reduce LR într-un singur ciclu, asociată cu super-convergența în anumite configurații |
| Testul intervalului LR | „Găsiți cea mai bună rată de învățare” | Antrenare scurtă cu LR în creștere, pentru a identifica regiunea în care pierderea începe să diverge |
| Cosinus cu reporniri calde | „Reinițializați și repetați” | Readucerea periodică a LR la lr_max, urmată de o nouă descreștere cosinusoidală (SGDR) |
| Eta min | „Pragul inferior pentru LR” | Rata minimă de învățare până la care descrește schema |
| Rată maximă de învățare | „LR maximă” | Cea mai mare LR atinsă în timpul antrenării, de regulă după warmup |
Lecturi suplimentare
- Loshchilov și Hutter, „SGDR: Stochastic Gradient Descent with Warm Restarts” (2017) — a introdus amortizarea cosinusoidală și repornirile calde.
- Smith și Topin, „Super-Convergence: Very Fast Training of Neural Networks Using Large Learning Rates” (2018) — lucrarea despre politica 1cycle.
- Touvron și colaboratorii, „Llama 2: Open Foundation and Fine-Tuned Chat Models” (2023) — documentează o schemă cu warmup și cosinus folosită la scară mare.
- Goyal și colaboratorii, „Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour” (2017) — regula de scalare liniară și warmup-ul pentru antrenarea cu loturi mari.
Sursă: Originalul în limba engleză
Navigare: ← Lecția 03.08 — Inițializarea ponderilor · Faza 3 — Fundamentele învățării profunde · Lecția 03.10 — Construiți-vă propriul mini-framework → · Catalog complet