Faza 01 · lecția 08
Optimizare
Scopul lecției: Antrenarea unei rețele neuronale nu înseamnă altceva decât găsirea punctului cel mai de jos al unei văi.
Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.
Cuprinsul lecției
- Obiective de învățare
- Problema
- Conceptul
- Ce înseamnă optimizarea
- Coborârea pe gradient în forma de bază
- Rata de învățare: cel mai important hiperparametru
- SGD, lot complet și mini-lot
- Momentul: bila care se rostogolește la vale
- Adam: rate de învățare adaptive
- Scheme de variație a ratei de învățare
- Funcții convexe și neconvexe
- Vizualizarea peisajului funcției de pierdere
- Construirea soluției
- Pasul 1: Definiți o funcție de test
- Pasul 2: Coborârea pe gradient în forma de bază
- Pasul 3: SGD cu moment
- Pasul 4: Adam
- Pasul 5: Rulați și comparați
- Utilizarea soluției
- Livrarea rezultatului
- Exerciții
- Termeni-cheie
- Lecturi suplimentare
Antrenarea unei rețele neuronale nu înseamnă altceva decât găsirea punctului cel mai de jos al unei văi.
Tip: Construire Limbaj: Python Cerințe preliminare: Faza 1, lecțiile 04–05 (derivate, gradienți) Durată: ~75 de minute
Obiective de învățare
- Implementarea de la zero a coborârii pe gradient în forma de bază, a SGD cu moment și a optimizatorului Adam
- Compararea convergenței optimizatorilor pe funcția Rosenbrock și explicarea modului în care Adam adaptează rata de învățare pentru fiecare pondere
- Deosebirea peisajelor convexe ale funcției de pierdere de cele neconvexe și explicarea rolului punctelor de șa în spații cu multe dimensiuni
- Configurarea schemelor de variație a ratei de învățare, precum scăderea în trepte, atenuarea cosinusoidală și încălzirea (warmup), pentru stabilitatea antrenării
Problema
Aveți o funcție de pierdere. Ea vă arată cât de mult greșește modelul. Aveți gradienți. Aceștia vă indică direcția în care pierderea se agravează. Acum aveți nevoie de o strategie pentru a coborî panta.
Abordarea naivă este simplă: deplasați-vă în direcția opusă gradientului. Ajustați dimensiunea pasului printr-o valoare numită rată de învățare. Repetați. Aceasta este coborârea pe gradient și funcționează. Însă afirmația „funcționează” necesită câteva precizări. Dacă rata de învățare este prea mare, depășiți complet valea și ricoșați între versanți. Dacă este prea mică, vă târâți către rezultat în mii de pași inutili. Dacă ajungeți într-un punct de șa, vă opriți, deși nu ați găsit un minim.
Fiecare optimizator din învățarea profundă răspunde la aceeași întrebare: cum ajungeți în punctul cel mai de jos al văii mai rapid și mai sigur?
Conceptul
Ce înseamnă optimizarea
Optimizarea constă în găsirea valorilor de intrare care minimizează sau maximizează o funcție. În învățarea automată, funcția este pierderea, iar intrările sunt ponderile modelului. Antrenarea este optimizare.
minimizați L(w), unde:
L = funcția de pierdere
w = ponderile modelului (pot exista milioane de parametri)
Coborârea pe gradient în forma de bază
Acesta este cel mai simplu optimizator. Calculați gradientul pierderii în raport cu fiecare pondere. Deplasați fiecare pondere în direcția opusă gradientului său. Scalați pasul cu rata de învățare.
w = w - lr * gradient
Acesta este întregul algoritm. O singură linie.
Rata de învățare: cel mai important hiperparametru
Rata de învățare controlează dimensiunea pasului. Ea determină toate aspectele convergenței.
Nu există o formulă pentru rata de învățare potrivită. O găsiți prin experimentare. Valori inițiale uzuale: 0.001 pentru Adam și 0.01 pentru SGD cu moment.
SGD, lot complet și mini-lot
Coborârea pe gradient în forma de bază calculează gradientul pe întregul set de date înainte de a efectua un pas. Aceasta se numește coborâre pe gradient pe lot complet. Este stabilă, dar lentă.
Coborârea stocastică pe gradient (stochastic gradient descent, SGD) calculează gradientul pe un singur exemplu ales aleatoriu și efectuează imediat un pas. Este zgomotoasă, dar rapidă.
Coborârea pe gradient cu mini-lot (mini-batch) reprezintă un compromis între cele două. Calculați gradientul pe un lot mic, de 32, 64, 128 sau 256 de exemple, apoi efectuați un pas. Aceasta este varianta utilizată în practică.
| Variantă | Dimensiunea lotului | Calitatea gradientului | Viteza per pas | Zgomot |
|---|---|---|---|---|
| GD pe lot complet | Întregul set de date | Exactă | Lentă | Fără zgomot |
| SGD | 1 exemplu | Foarte zgomotoasă | Rapidă | Ridicat |
| Mini-lot | 32–256 | Estimare bună | Echilibrată | Moderat |
Zgomotul din SGD și mini-loturi nu este o eroare. El ajută la ieșirea din minime locale puțin adânci și din puncte de șa.
Momentul: bila care se rostogolește la vale
Coborârea pe gradient în forma de bază ia în considerare numai gradientul curent. Dacă gradientul urmează o traiectorie în zigzag, fenomen frecvent în văile înguste, progresul este lent. Momentul (momentum) corectează această problemă prin acumularea gradienților anteriori într-un termen de viteză.
v = beta * v + gradient
w = w - lr * v
Analogia este cea a unei bile care se rostogolește la vale. Ea nu se oprește și nu pornește din nou la fiecare denivelare. Acumulează viteză în direcțiile persistente și amortizează oscilațiile.
beta, de regulă 0.9, controlează cât de mult istoric se păstrează. O valoare beta mai mare înseamnă mai mult moment și traiectorii mai line, dar și un răspuns mai lent la schimbările de direcție.
Adam: rate de învățare adaptive
Ponderile diferite au nevoie de rate de învățare diferite. O pondere care primește rareori gradienți mari ar trebui să efectueze pași mai mari atunci când îi primește. O pondere care primește constant gradienți foarte mari ar trebui să efectueze pași mai mici.
Adam (Adaptive Moment Estimation, estimarea adaptivă a momentelor) urmărește două mărimi pentru fiecare pondere:
- Primul moment (m): media mobilă a gradienților, asemenea momentului
- Al doilea moment (v): media mobilă a pătratelor gradienților; sqrt(v_hat) estimează scara RMS a gradientului
m = beta1 * m + (1 - beta1) * gradient
v = beta2 * v + (1 - beta2) * gradient^2
m_hat = m / (1 - beta1^t) corecția biasului statistic
v_hat = v / (1 - beta2^t) corecția biasului statistic
w = w - lr * m_hat / (sqrt(v_hat) + epsilon)
Împărțirea la sqrt(v_hat) este ideea esențială. Ponderile cu gradienți mari sunt împărțite la un număr mare, rezultând un pas efectiv mic. Ponderile cu gradienți mici sunt împărțite la un număr mic, rezultând un pas efectiv mare. Fiecare pondere primește propria rată de învățare adaptivă.
Hiperparametrii impliciți sunt lr=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8. Aceste valori funcționează bine pentru majoritatea problemelor.
Scheme de variație a ratei de învățare
O rată de învățare fixă este un compromis. La începutul antrenării sunt de dorit pași mari pentru un progres rapid. Spre sfârșitul antrenării sunt de dorit pași mici, pentru ajustări fine în apropierea minimului.
Scheme uzuale:
| Schemă | Formulă | Caz de utilizare |
|---|---|---|
| Scădere în trepte | lr = lr * factor la fiecare N epoci | Control simplu, manual |
| Scădere exponențială | lr = lr_0 * decay^t | Reducere lină |
| Atenuare cosinusoidală (cosine annealing) | lr = lr_min + 0.5 * (lr_max - lr_min) * (1 + cos(pi * t / T)) | Arhitecturi Transformer, antrenare modernă |
| Încălzire (warmup) + scădere | Creștere liniară, apoi scădere | Modele mari, prevenirea instabilității inițiale |
Funcții convexe și neconvexe
Pentru o funcție convexă, orice minim local este global, dar pot exista mai multe minime globale. Convergența coborârii pe gradient necesită condiții suplimentare, precum netezimea funcției, existența unui minim și o rată de învățare adecvată. O funcție pătratică precum f(x) = x^2 este convexă și are un minim global unic.
Notă tehnică a traducerii: Originalul afirmă că orice funcție convexă are un singur minim și că algoritmul îl găsește întotdeauna. Formularea a fost corectată pentru a include condițiile de convergență și posibilitatea minimelor globale multiple.
Funcțiile de pierdere ale rețelelor neuronale sunt neconvexe. Acestea au numeroase minime locale, puncte de șa și regiuni plate.
În unele peisaje de pierdere cu multe dimensiuni, punctele de șa și regiunile plate pot încetini optimizarea, iar momentul și zgomotul mini-loturilor pot ajuta la părăsirea lor. Nu există însă o garanție generală că toate minimele locale ale rețelelor neuronale sunt apropiate de minimul global sau că punctele de șa reprezintă întotdeauna obstacolul dominant.
Vizualizarea peisajului funcției de pierdere
Pierderea este o funcție a tuturor ponderilor. Pentru un model cu un milion de ponderi, peisajul funcției de pierdere se află într-un spațiu cu 1.000.001 dimensiuni. Îl vizualizăm alegând două direcții aleatorii în spațiul ponderilor și reprezentând pierderea de-a lungul acestora, pentru a obține o suprafață 2D.
Geometria locală a minimului poate fi asociată cu generalizarea, dar noțiunile de minim „ascuțit” și „plat” depind de parametrizare. În anumite configurații, zgomotul SGD favorizează regiuni mai plate și poate oferi o acuratețe finală mai bună decât Adam; aceasta este o observație empirică, nu o regulă universală.
Notă tehnică a traducerii: Originalul prezintă legătura dintre planeitatea minimului, generalizare și avantajul SGD asupra Adam drept universală. Formularea a fost restrânsă deoarece proprietatea nu este invariantă la reparametrizare și depinde de problemă.
gradient-descent
Construirea soluției
Pasul 1: Definiți o funcție de test
Funcția Rosenbrock este un reper clasic pentru optimizare. Minimul său se află în (1, 1), în interiorul unei văi înguste și curbate, care este ușor de găsit, dar greu de urmat.
f(x, y) = (1 - x)^2 + 100 * (y - x^2)^2
def rosenbrock(params):
x, y = params
return (1 - x) ** 2 + 100 * (y - x ** 2) ** 2
def rosenbrock_gradient(params):
x, y = params
df_dx = -2 * (1 - x) + 200 * (y - x ** 2) * (-2 * x)
df_dy = 200 * (y - x ** 2)
return [df_dx, df_dy]
Pasul 2: Coborârea pe gradient în forma de bază
class GradientDescent:
def __init__(self, lr=0.001):
self.lr = lr
def step(self, params, grads):
return [p - self.lr * g for p, g in zip(params, grads)]
Pasul 3: SGD cu moment
class SGDMomentum:
def __init__(self, lr=0.001, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.velocity = None
def step(self, params, grads):
if self.velocity is None:
self.velocity = [0.0] * len(params)
self.velocity = [
self.momentum * v + g
for v, g in zip(self.velocity, grads)
]
return [p - self.lr * v for p, v in zip(params, self.velocity)]
Pasul 4: Adam
class Adam:
def __init__(self, lr=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8):
self.lr = lr
self.beta1 = beta1
self.beta2 = beta2
self.epsilon = epsilon
self.m = None
self.v = None
self.t = 0
def step(self, params, grads):
if self.m is None:
self.m = [0.0] * len(params)
self.v = [0.0] * len(params)
self.t += 1
self.m = [
self.beta1 * m + (1 - self.beta1) * g
for m, g in zip(self.m, grads)
]
self.v = [
self.beta2 * v + (1 - self.beta2) * g ** 2
for v, g in zip(self.v, grads)
]
m_hat = [m / (1 - self.beta1 ** self.t) for m in self.m]
v_hat = [v / (1 - self.beta2 ** self.t) for v in self.v]
return [
p - self.lr * mh / (vh ** 0.5 + self.epsilon)
for p, mh, vh in zip(params, m_hat, v_hat)
]
Pasul 5: Rulați și comparați
def optimize(optimizer, func, grad_func, start, steps=5000):
params = list(start)
history = [params[:]]
for _ in range(steps):
grads = grad_func(params)
params = optimizer.step(params, grads)
history.append(params[:])
return history
start = [-1.0, 1.0]
gd_history = optimize(GradientDescent(lr=0.0005), rosenbrock, rosenbrock_gradient, start)
sgd_history = optimize(SGDMomentum(lr=0.0001, momentum=0.9), rosenbrock, rosenbrock_gradient, start)
adam_history = optimize(Adam(lr=0.01), rosenbrock, rosenbrock_gradient, start)
for name, history in [("GD", gd_history), ("SGD+M", sgd_history), ("Adam", adam_history)]:
final = history[-1]
loss = rosenbrock(final)
print(f"{name:6s} -> x={final[0]:.6f}, y={final[1]:.6f}, loss={loss:.8f}")
Rezultatul așteptat: Adam converge cel mai repede. SGD cu moment urmează o traiectorie mai lină. GD în forma de bază avansează lent de-a lungul văii înguste.
Utilizarea soluției
În practică, utilizați optimizatoarele din PyTorch sau JAX. Acestea gestionează grupurile de parametri, reducerea ponderilor (weight decay), tăierea gradienților (gradient clipping) și accelerarea pe GPU.
import torch
model = torch.nn.Linear(784, 10)
sgd = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
adam = torch.optim.Adam(model.parameters(), lr=0.001)
adamw = torch.optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.01)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(adam, T_max=100)
Reguli orientative:
- Începeți cu Adam (lr=0.001). Funcționează pentru majoritatea problemelor fără reglaje suplimentare.
- Treceți la SGD cu moment (lr=0.01, momentum=0.9) atunci când aveți nevoie de cea mai bună acuratețe finală și vă puteți permite mai multe reglaje.
- Utilizați AdamW, adică Adam cu reducerea decuplată a ponderilor (decoupled weight decay), pentru arhitecturile Transformer.
- Utilizați întotdeauna o schemă de variație a ratei de învățare pentru antrenări care durează mai mult de câteva epoci.
- Dacă antrenarea este instabilă, reduceți rata de învățare. Dacă antrenarea este prea lentă, măriți-o.
Livrarea rezultatului
Această lecție produce un prompt pentru alegerea optimizatorului potrivit. Consultați outputs/prompt-optimizer-guide.md.
Clasele de optimizatori construite aici reapar în faza 3, când vom antrena de la zero o rețea neuronală.
Exerciții
-
Explorarea ratei de învățare. Rulați coborârea pe gradient în forma de bază pe funcția Rosenbrock cu ratele de învățare [0.0001, 0.0005, 0.001, 0.005, 0.01]. Reprezentați grafic sau afișați pierderea finală după 5000 de pași pentru fiecare valoare. Găsiți cea mai mare rată de învățare pentru care algoritmul încă converge.
-
Compararea momentului. Rulați SGD cu valorile momentului [0.0, 0.5, 0.9, 0.99] pe funcția Rosenbrock. Urmăriți pierderea la fiecare pas. Pentru ce valoare a momentului convergența este cea mai rapidă? Care valoare duce la depășirea minimului?
-
Comportamentul lângă un punct de șa. Definiți funcția
f(x, y) = x^2 - y^2, care are un punct de șa în origine și nu este mărginită inferior. Pornind exact din (0, 0), observați că metodele deterministe de ordinul întâi rămân pe loc deoarece gradientul este zero. Repetați din (0,01, 0,01) și comparați deplasarea GD, SGD cu moment și Adam de-a lungul direcției de curbură negativă.
Notă tehnică a traducerii: Originalul pornește din (0,01, 0,01), care nu este punctul de șa, și întreabă care algoritm „iese” din acesta. Exercițiul a fost reformulat pentru a separa pornirea exactă de o perturbare mică.
- Implementarea scăderii ratei de învățare. Adăugați clasei GradientDescent o schemă de scădere exponențială:
lr = lr_0 * 0.999^step. Comparați convergența cu și fără scădere pe funcția Rosenbrock.
Termeni-cheie
| Termen | Formulare uzuală | Ce înseamnă de fapt |
|---|---|---|
| Coborâre pe gradient | „Coborâți panta” | Actualizarea ponderilor prin scăderea gradientului scalat cu rata de învățare. Cel mai simplu optimizator. |
| Rată de învățare | „Dimensiunea pasului” | Un scalar care controlează cât de mult deplasează fiecare actualizare ponderile. O valoare prea mare provoacă divergență. O valoare prea mică irosește resurse de calcul. |
| Moment | „Continuați să vă deplasați” | Acumularea gradienților anteriori într-un vector de viteză. Amortizează oscilațiile și accelerează deplasarea în direcțiile persistente. |
| SGD | „Eșantionare aleatorie” | Coborârea stocastică pe gradient. Gradientul este calculat pe o submulțime aleatorie, nu pe întregul set de date. În practică, denumește aproape întotdeauna SGD cu mini-loturi. |
| Mini-lot | „O porțiune de date” | O submulțime mică a datelor de antrenare, între 32 și 256 de exemple, utilizată pentru estimarea gradientului. Echilibrează viteza și precizia gradientului. |
| Adam | „Optimizatorul implicit” | Estimarea adaptivă a momentelor. Urmărește pentru fiecare pondere mediile mobile ale gradienților și ale pătratelor acestora, pentru a atribui fiecărei ponderi propria rată de învățare. |
| Corecția biasului statistic | „Remedierea pornirii la rece” | Primul și al doilea moment din Adam sunt inițializate cu zero. Corecția împarte la (1 - beta^t) pentru a compensa biasul statistic din pașii inițiali; nu se referă la termenul liber al unui neuron. |
| Schemă de variație a ratei de învățare | „Modificarea lr în timp” | O funcție care ajustează rata de învățare în timpul antrenării: pași mari la început și pași mici spre sfârșit. |
| Funcție convexă | „O singură vale” | O funcție în care orice minim local este global, fără ca minimul să fie neapărat unic. Convergența coborârii pe gradient depinde de ipotezele funcției și de alegerea pasului. Funcțiile de pierdere ale rețelelor neuronale nu sunt, în general, convexe. |
| Punct de șa | „Plat, dar nu este un minim” | Un punct în care gradientul este zero, dar care reprezintă un minim în unele direcții și un maxim în altele. Este frecvent în spații cu multe dimensiuni. |
| Peisajul funcției de pierdere | „Relieful” | Funcția de pierdere reprezentată în spațiul ponderilor. Este vizualizată prin secționarea de-a lungul a două direcții aleatorii. |
| Convergență | „Ați ajuns la destinație” | Optimizatorul a ajuns într-un punct în care pașii următori nu mai reduc semnificativ pierderea. |
Lecturi suplimentare
- Sebastian Ruder: An overview of gradient descent optimization algorithms — prezentare amplă a tuturor optimizatoarelor principale
- Why Momentum Really Works (Distill) — vizualizare interactivă a dinamicii momentului
- Adam: A Method for Stochastic Optimization (Kingma & Ba, 2014) — lucrarea originală despre Adam, accesibilă și concisă
- Visualizing the Loss Landscape of Neural Nets (Li et al., 2018) — lucrarea care a evidențiat diferența dintre minimele ascuțite și cele plate
Sursă: Optimization — originalul Navigare: înapoi: 01.07 — Teorema lui Bayes · Faza 1 — Fundamente matematice · Catalog complet · în continuare: 01.09 — Teoria informației.