21.2 Learning rate és ütemezés

21.2.1 Cél, előismeret és induló becslés

A 21.1. Inicializálás és varianciaterjedés egységben az inicializálás skálaviszonyait vezettük le; ott a frissítés nagyságát még szabadon hagytuk. Itt a learning rate (tanulási ráta, LR), az η\eta lépésméret kérdését vizsgáljuk: milyen intervallumban stabil, hogyan változtassuk a tanítás során ütemezetten, és hogyan rögzítsük reprodukálhatóan. Induló becslés: ha a cél f(θ)=2θ2f(\theta)=2\theta^2 és θ0=9\theta_0=9, akkor a gradiens f′(9)=36f'(9)=36. Mekkora η\eta választásnál válik a frissítés oszcillálóvá? A pontos határt egy egyszerű kvadratikus kontrollból vezetjük le; ez a lokális görbület és a lépésméret kapcsolatának alapesete lesz.

21.2.2 Frissítési szabály, jelölések és alakok

Legyen θt∈ℝd\theta_t\in\mathbb R^d a paramétervektor a tt-edik frissítés után, dd a paraméterszám. A tt-edik minibatch indexhalmaza BtB_t, mérete |Bt|=B|B_t|=B. A batch-átlag loss:

L̂(θt;Bt)=1B∑i∈Btℓi(θt),\widehat L(\theta_t;B_t)=\frac1B\sum_{i\in B_t}\ell_i(\theta_t),

ahol ℓi\ell_i az ii-edik tanítóminta vesztesége. A teljes gradiensű frissítéshez képest a következő SGD-lépést használjuk:

θt+1=θt−ηt∇θL̂(θt;Bt),ηt>0.\theta_{t+1}=\theta_t-\eta_t\,\nabla_\theta \widehat L(\theta_t;B_t),\qquad \eta_t>0.

A gradiens alakja megegyezik θt\theta_t alakjával, tehát ∇θL̂∈ℝd\nabla_\theta \widehat L\in\mathbb R^d. Az ηt\eta_t általában skalár; a 20.2. Finomhangolás és tanulási ráta finomhangolási egységben láttuk, hogy paramétercsoportonként eltérő is lehet. A következő terminusokat külön kell választani:

A kettő nem felcserélhető, mert a legtöbb schedule a lépések vagy az epochok függvénye.

21.2.3 Kvadratikus kontroll: a schedule által okozott lépések

A levezetési kötelezettségünk a következő állítás.

Állítás (kvadratikus lépésméret-korlát). Legyen f:ℝ→ℝf:\mathbb R\to\mathbb R, f(θ)=a2θ2f(\theta)=\frac a2\theta^2 valamely a>0a>0-ra, és legyen a frissítés teljes gradiensű, rögzített η>0\eta>0 mellett:

θt+1=θt−ηf′(θt).\theta_{t+1}=\theta_t-\eta f'(\theta_t).

Ekkor:

  1. θt=(1−ηa)tθ0\theta_t=(1-\eta a)^t\theta_0 minden t≥0t\ge 0 egészre.
  2. A sorozat pontosan akkor tart nullához, ha 0<η<2/a0<\eta<2/a.
  3. Az egyetlen lépésben pontos optimumot adó választás η=1/a\eta=1/a.

Levezetés. f′(θ)=aθf'(\theta)=a\theta, így

θt+1=θt−ηaθt=(1−ηa)θt.\theta_{t+1}=\theta_t-\eta a\theta_t=(1-\eta a)\theta_t.

Az első állítás t=0t=0-ra triviális, az indukciós lépés pedig a fenti egyenlet. A mértani sorozat (1−ηa)tθ0(1-\eta a)^t\theta_0 pontosan akkor tart nullához, ha |1−ηa|<1|1-\eta a|<1; η>0\eta>0 mellett ez ekvivalens azzal, hogy 0<ηa<20<\eta a<2, azaz 0<η<2/a0<\eta<2/a. A harmadik pont: 1−ηa=01-\eta a=0 akkor és csak akkor, ha η=1/a\eta=1/a. ▫\square

A 2/a2/a érték a lokális görbületből származik; többdimenziós, nem-kvadratikus célfüggvény esetén a Hessian legnagyobb sajátértéke játszik hasonló szerepet, de ez utóbbi kapcsolatot itt csak heurisztikus irányelvnek tekintjük (a spektrális eszközöket 3.4. Sajátérték, SVD és spektrális határ vezette be). A fenti állítás maga teljes, a feltételek szerint bizonyított levezetés.

Kézi számpélda. Legyen a=4a=4, tehát f(θ)=2θ2f(\theta)=2\theta^2, θ0=3\theta_0=3.

Ez a kontroll nem állít semmit a CNN-veszteség felületéről; azt mutatja meg, hogyan számolható ki a stabilitási tartomány egy explicit görbületű feladaton.

21.2.4 Ütemezések: step, cosine és warmup

A learning rate schedule (tanulási ráta ütemezése) egy előre rögzített ηt\eta_t vagy ηe\eta_e pozitív sorozat a tanítási költségvetésre. Három gyakori családot definiálunk.

Step decay. Rögzített η0>0\eta_0>0 kezdőérték, 0<γ<10<\gamma<1 csillapítási tényező és ss lépcsőhossz mellett

ηt=η0γ⌊t/s⌋.\eta_t=\eta_0\,\gamma^{\lfloor t/s\rfloor}.

Epoch-alapú változatban ηe=η0γ⌊e/se⌋\eta_e=\eta_0\,\gamma^{\lfloor e/s_e\rfloor}, ahol ses_e epoch-hossz. A két változatot át kell számolni: az epoch-alak ses_e lépcsője se⌈N/B⌉s_e\lceil N/B\rceil lépésnek felel meg.

Cosine annealing. Rögzített TT össz-lépésszámra

ηt=ηmin+ηmax−ηmin2(1+cosπtT),0≤t≤T.\eta_t=\eta_{\min}+\frac{\eta_{\max}-\eta_{\min}}2\left(1+\cos\frac{\pi t}{T}\right),\qquad 0\le t\le T.

A végpontokban η0=ηmax\eta_0=\eta_{\max} és ηT=ηmin\eta_T=\eta_{\min}. A tt itt lépésindex; epoch-alapú használathoz a TT lépést epochból kell átszámolni.

Warmup. A fő schedule elé lineáris növekedést iktatunk. Az első WW lépésben

ηt=ηtarget⋅tW,t=1,…,W,\eta_t=\eta_{\text{target}}\cdot\frac{t}{W},\qquad t=1,\ldots,W,

majd t>Wt>W-ra a választott ütem következik. A cél, hogy a nagy kezdeti gradiens ne tegye tönkre a frissen inicializált rétegeket; ez heurisztika, és a szükségességét az adott modellen validációval kell igazolni. A BatchNorm-irodalom a nagyobb LR alkalmazhatóságát empirikus előnyként közli [P07], de a mechanizmus vitatott; a running state kérdéseit 18.2. Batch normalization train és eval módban tárgyalja.

Kiszámolt mini-példa. ηmax=0,4\eta_{\max}=0{,}4, ηmin=0,001\eta_{\min}=0{,}001, W=3W=3, T=10T=10. A warmup a t=1,2,3t=1,2,3 lépésekre 0,1333;0,2667;0,40{,}1333;0{,}2667;0{,}4 értéket ad. A cosine-szakasz T′=7T'=7 hosszú, a t′=t−3t'=t-3 átindexeléssel; a t=4t=4 lépésben η=0,001+0,3992(1+cosπ7)≈0,3807\eta=0{,}001+\frac{0{,}399}2(1+\cos\frac{\pi}{7})\approx0{,}3807, a t=10t=10 végpontban ηmin=0,001\eta_{\min}=0{,}001.

Warmup (3 lépés) + cosine schedule szemléltetése - A képletből számított szemléltetés, nem mért futás.

21.2.5 Scheduler állapot, epoch vs step, plateau

A könyvtári API-kban (például a PyTorch torch.optim.lr_scheduler modulja, [D06]) a scheduler állapotot tart nyilván: az aktuális epoch/step számlálót, az utolsó érvényes η\eta értéket, a plateau-változatnál a patience-számlálót és a legjobb validációs metrikát. Ez az állapot a kísérlet része. Ha a checkpointba a modell- és optimizer-state_dict mellett a scheduler-state_dict nem kerül be, a visszatöltés után a schedule nem ott folytatódik, ahol abbamaradt; ez reprodukálhatósági hibát okoz (vö. 12.1.–12.2.). A mentésnek tehát három state_dict-et és az aktuális epochot kell tartalmaznia.

Az epoch vs step megkülönböztetés azért lényeges, mert a step-alapú schedule más számú frissítést ad a batch-mérettől függően, míg az epoch-alapú schedule azonos epochszám mellett más LR-pályát és más számítási költséget jelent.

A ReduceLROnPlateau nem előre rögzített függvény, hanem visszacsatolt döntés: ha a rögzített validációs halmaz mért metrikája patience epochon át nem javul, a scheduler η←η⋅γ\eta\leftarrow\eta\cdot\gamma szorzót hajt végre, és a patience-számlálót nullázza. Mivel a validációs halmaz itt közvetlenül befolyásolja a tanítási pályát, a validációs split a modellválasztás része; a végső tesztet érintetlenül kell hagyni (1.3. Adatfelosztás és a megfigyelési egység). Az adaptív ütem előnye, hogy a csökkentés időzítését az adathoz igazítja; ára, hogy a zajos validációs metrika hamis plateau-t is jelezhet.

21.2.6 Labor: két schedule összehasonlítása azonos update-kerettel

Protokoll. Kérdés: konstans η=0,001\eta=0{,}001 vagy warmup+cosine ad-e alacsonyabb legjobb validációs loss-t a GlyphLabon? Hipotézis: a cosine az utolsó szakaszban csökkentett LR-rel simábban ér be a minimumba. Adat: lab/data/ a GlyphLab-szerződés szerint; a train/val splitet a metadata.csv adja, a scene-szintű csoportosítást az 5.4. Függő képek, seedek és resampling egységben rögzítettük. Modell: a 9.2. MLP és nemlinearitás definícióját követő kis MLP, bemenet 576=24⋅24576=24\cdot24 (NCHW 1×24×241\times24\times24), rejtett 32 ReLU neuron, dropout p=0,05p=0{,}05, kimenet K=3K=3 logits. Optimizer: AdamW lr=1e-3, weight_decay=0.01; a decoupled weight decay [P02] absztraktja szerint a λ\lambda súlycsillapítási tagot nem skálázza az LR, ezért a schedule csak az LR-pályát változtatja. Batch 32, 40 epoch, seed 20261020, CPU float32. Az összehasonlításban azonos modell, optimizer, batch, epoch és seed mellett csak a schedule típusa változik; ez az „azonos update-keret”. A best checkpointot a validációs loss minimuma választja; a végső tesztet a kiválasztás után értékeljük.

Futtatási parancs: cd lab && python n082_schedule_compare.py. A vázlat a 11.3. Dataset és DataLoader a split után adatbetöltési mintáját használja:

# lab/n082_schedule_compare.py
# CPU-futási vázlat; a kimenet JELÖLT SZEMLÉLTETÉS, nem mért futtatás.
import math
import numpy as np
import torch
from torch import nn
from torch import optim

torch.manual_seed(20261020)
device = torch.device("cpu")

X = np.load("data/images.npy")          # (360,1,24,24) float32
y = np.load("data/labels.npy")          # (360,) int64
# metadata.csv: split oszlop; a pontos string-feldolgozást lásd n043.
meta = np.genfromtxt("data/metadata.csv", delimiter=",", names=True)
train_idx = np.where(meta["split"] == b"train")[0]
val_idx   = np.where(meta["split"] == b"val")[0]

B = 32
STEPS_PER_EPOCH = math.ceil(len(train_idx) / B)   # 8
EPOCHS = 40
TOTAL_STEPS = EPOCHS * STEPS_PER_EPOCH
WARMUP_STEPS = 3 * STEPS_PER_EPOCH

def make_model():
    return nn.Sequential(nn.Flatten(), nn.Linear(576, 32), nn.ReLU(),
                         nn.Dropout(0.05), nn.Linear(32, 3)).to(device)

def make_lr_lambda(kind):
    if kind == "const":
        return lambda step: 1.0
    if kind == "cosine":
        def f(step):
            if step < WARMUP_STEPS:
                return 0.2 + 0.8 * step / WARMUP_STEPS
            t = step - WARMUP_STEPS
            T = TOTAL_STEPS - WARMUP_STEPS
            return 0.5 * (1 + math.cos(math.pi * t / T))
        return f
    raise ValueError(kind)

def run(kind):
    model = make_model()
    opt = optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)
    sched = optim.lr_scheduler.LambdaLR(opt, make_lr_lambda(kind))
    lossf = nn.CrossEntropyLoss()
    best_val = math.inf
    Xt = torch.from_numpy(X[train_idx])
    yt = torch.from_numpy(y[train_idx])
    Xv = torch.from_numpy(X[val_idx])
    yv = torch.from_numpy(y[val_idx])
    for epoch in range(EPOCHS):
        model.train()
        perm = torch.randperm(len(yt))
        for i in range(0, len(yt), B):
            xb = Xt[perm[i:i+B]]
            yb = yt[perm[i:i+B]]
            opt.zero_grad()
            lossf(model(xb), yb).backward()
            opt.step()
            sched.step()
        model.eval()
        with torch.no_grad():
            val_loss = lossf(model(Xv), yv).item()
        if val_loss < best_val:
            best_val = val_loss
            torch.save({"model": model.state_dict(), "opt": opt.state_dict(),
                        "sched": sched.state_dict(), "epoch": epoch}, "best.pt")
    return best_val

print("const  best_val_loss:", run("const"))
print("cosine best_val_loss:", run("cosine"))

A szemléltetett kimenet (nem mért adat):

const  best_val_loss: 0.640
cosine best_val_loss: 0.594

Értelmezés: a különbség egyetlen seedből nem bizonyít általános előnyt; ismételt seedek és a közös kiindulás ellenőrzése szükséges. A labor célja a szerződéses kivitelezés, nem egy előre megadott győztes.

21.2.7 Feladatok

F1 (fogalmi, nehézség 1). Definiálja a step és az epoch közötti különbséget, és adja meg a kapcsolatukat drop_last=True esetén. Miért nem elegendő azt közölni, hogy „50 epochig tanítottuk a modellt”, ha valaki reprodukálni akarja a schedule-t?

F2 (levezetési, nehézség 2). Tekintse az f(θ)=32θ2f(\theta)=\frac32\theta^2 függvényt, θ0=2\theta_0=2 kezdőpontot és az ηt=0,5\eta_t=0{,}5 konstans lépésű gradiensmódszert. (a) Adja meg θt\theta_t zárt alakját. (b) Számolja ki θ1,θ2,θ3\theta_1,\theta_2,\theta_3 értékét. (c) Milyen η\eta intervallumban konvergál a módszer? (d) Ellenőrizze, hogy η=1/3\eta=1/3 esetén θ1=0\theta_1=0.

F3 (implementációs, nehézség 2). Írjon Python-függvényt cosine_lr(step, T, eta_max, eta_min, warmup=0) néven, amely a fenti cosine-képletet valósítja meg, warmup>0 esetén a t≤Wt\le W tartományban lineáris növekedéssel. A 0. regressziós teszt: T=100T=100, ηmax=0,1\eta_{\max}=0{,}1, ηmin=10−4\eta_{\min}=10^{-4}; a végpontokra step=0 ad 0,10{,}1 értéket, step=T ad 10−410^{-4} értéket. A relatív tűrés 10−1210^{-12}. Szélsőeset: warmup=0 ne osszon nullával.

F4 (hibakeresési, nehézség 2). Egy kolléga a checkpointba csak model.state_dict() és opt.state_dict() objektumokat ment, a scheduler state_dict-jét nem, majd a visszatöltés után a ReduceLROnPlateau patience-számlálóját újraindulni észlelte. Adja meg a hiba okát, következményét és a javított mentés/betöltés kódját.

F5 (kísérlettervezési/transzfer, nehézség 3). Tervezzen kis CPU-kísérletet a GlyphLabon, amely a step decay (γ=0,1\gamma=0{,}1, lépcső 15 epoch) és a cosine annealing validációs lossát hasonlítja össze. Adja meg: megfigyelési egység, split, baseline, változó tényező, metrika, keresési keret és seed. Indokolja, miért nem vonható le általános következtetés egyetlen seedből, és milyen ismétlést tenne hozzá.

21.2.8 Megoldások és indoklás

F1. A step egy optimizer-frissítés, az epoch az összes tanítóminta egyszeri végighaladása. drop_last=True esetén egy epoch ⌊N/B⌋\lfloor N/B\rfloor stepet tartalmaz, mert az utolsó, BB-nél rövidebb batch kimarad. Az „50 epoch” önmagában nem határozza meg a frissítések számát: B=32B=32 esetén például 400, B=64B=64 esetén 200 lépés lehet. A step-alapú schedule tehát ugyanannyi epoch alatt eltérő LR-pályát jár be; a reprodukcióhoz a batch-méretet, a drop_last értéket és a schedule típusát is meg kell adni.

F2. Itt a=3a=3, tehát f′(θ)=3θf'(\theta)=3\theta, és

θt+1=(1−3η)θt.\theta_{t+1}=(1-3\eta)\theta_t.

  1. θt=(1−3η)tθ0\theta_t=(1-3\eta)^t\theta_0. (b) η=0,5\eta=0{,}5 esetén a szorzó 1−1,5=−0,51-1{,}5=-0{,}5, tehát θ1=−1\theta_1=-1, θ2=0,5\theta_2=0{,}5, θ3=−0,25\theta_3=-0{,}25. (c) A konvergencia feltétele |1−3η|<1|1-3\eta|<1, azaz 0<η<2/30<\eta<2/3. (d) η=1/3\eta=1/3 esetén a szorzó 1−1=01-1=0, tehát θ1=0\theta_1=0. Megjegyzés: a 2/32/3 érték a stabilitási határ, nem az optimális lépés; ott a szorzó −1-1, tehát oszcilláció alakul ki.

F3. A függvény:

import math

def cosine_lr(step, T, eta_max, eta_min, warmup=0):
    if step < warmup:
        return eta_max * step / warmup if warmup > 0 else eta_max
    t = step - warmup
    T_eff = T - warmup
    if T_eff <= 0:
        raise ValueError("warmup < T szükséges")
    return eta_min + 0.5 * (eta_max - eta_min) * (1 + math.cos(math.pi * t / T_eff))

Ellenőrzés: cosine_lr(0, 100, 0.1, 1e-4) a warmup=0 ág miatt a cosine-szakaszba lép, t=0t=0, cos0=1\cos 0=1, az eredmény ηmax=0,1\eta_{\max}=0{,}1. cosine_lr(100, 100, ...) esetén t=100t=100, cosπ=−1\cos\pi=-1, az eredmény ηmin=10−4\eta_{\min}=10^{-4}. A lebegőpontos kerekítés a 10−1210^{-12} relatív tűrésen belül van. A warmup=T eset kizárt a T_eff<=0 ellenőrzéssel.

F4. Ok: a scheduler belső számlálói (epoch, patience, best metrika, utolsó LR) nem részei a modell vagy az optimizer state_dict-jének; önálló objektumban élnek. Következmény: visszatöltéskor a scheduler új példánya nulla patience-számlálóval indul, tehát a plateau-várás elölről kezdődik, és a folytatás LR-pályája eltér a megszakított futásétól. Javítás:

torch.save({"model": model.state_dict(), "opt": opt.state_dict(),
            "sched": sched.state_dict(), "epoch": epoch}, "ckpt.pt")

ckpt = torch.load("ckpt.pt", weights_only=True)
model.load_state_dict(ckpt["model"])
opt.load_state_dict(ckpt["opt"])
sched.load_state_dict(ckpt["sched"])

Ellenőrzés: a mentés utáni sched.get_last_lr() értékét naplózza; a visszatöltés után ugyanazt kell visszaadnia.

F5. Egy lehetséges terv. Megfigyelési egység: egy teljes tanítási futás a rögzített seeddel. Split: a GlyphLab scene-szintű train/val/test felosztása; a validáció a best checkpoint kiválasztására szolgál, a teszt csak a végén fut. Baseline: konstans η=0,001\eta=0{,}001. Változó tényező: a schedule típusa (step decay versus cosine), minden más azonos. Metrika: legjobb validációs loss; záró teszt-accuracy a kiválasztott checkpointra. Keresési keret: 15 epochos lépcső, γ=0,1\gamma=0{,}1, batch 32, AdamW, seed 20261020. Egy seed nem általánosít, mert a súlyinicializálás és a minibatch sorrend realizációja eltérő pályát ad. Ismétlés: három seed (például 20261020, 20261021, 20261022), átlag és seed-szórás, páros seed-különbség a két schedule között; a következtetést a GlyphLabra és a használt modellcsaládra korlátozzuk.

21.2.9 Tipikus tévedés ellenpéldával

„A 40 epochos lépcsős ütem ugyanannyi számítást végez, mint a 40 epochos cosine, mert mindkettő 40 epoch.” Ez hibás. A 240 tanítóképből B=32B=32 mellett 8 batch képződik, B=64B=64 mellett 4; 40 epoch az elsőnél 320, a másodiknál 160 frissítést jelent. A step-alapú step decay az elsőben 320 lépés alatt több lépcsőt hajthat végre, mint a másodikban; a forward/backward hívások száma és a számítási költség is eltér. A költség tehát stephez és batch-mérethez kötött, nem epochhoz.

21.2.10 Önellenőrzés és korábbi fogalom visszahívása

Önellenőrző kérdések. (1) Mi a 0<η<2/a0<\eta<2/a intervallum szerepe a kvadratikus példában? (2) Mely állapotokat kell a checkpointba menteni a scheduler folytatásához? (3) A cosine-képlet t=0t=0 és t=Tt=T végpontjaiban milyen értéket ad?

Visszahívás 21.1. Inicializálás és varianciaterjedés. Az inicializálás varianciaskálája és az LR nem független: a túl nagy kezdeti aktivációskálából adódó nagy gradiens ugyanazt a stabilitási határt feszíti, mint a túl nagy η\eta. A két beállítást együtt kell diagnosztizálni.

Visszahívás 20.2. Finomhangolás és tanulási ráta. A finomhangolásnál a paramétercsoportonkénti LR ugyanazt a frissítési szabályt használja, csak ηt\eta_t csoportonként más; a scheduler ilyenkor minden csoportra azonos ütemben skáláz.

Visszahívás 17.2. AlexNet és a kísérleti kontextus. Az AlexNet-kísérlet tanulsága, hogy a nagy hálózatok tanítási eljárása (LR, momentum, dropout, augmentáció) szétválaszthatatlan az elért teszteredmény kontextusától; a 17.2. AlexNet és a kísérleti kontextus alfejezetben közölt 15,3% top-5 hiba nem a schedule önmagában való dicsérete.

21.2.11 Rövid összefoglaló

A learning rate a gradiensfrissítés skálája; a 2/a2/a korlát mutatja, hogy a lokális görbület felső határt szab a stabil lépésnek. A step decay, a cosine annealing és a warmup előre rögzített ütemek, a ReduceLROnPlateau ezzel szemben validációs visszajelzést használ. A scheduler állapota a reprodukálható tanítás része, ezért a checkpointnak a model, optimizer és scheduler state_dict mellett az epochot is tartalmaznia kell. Az epoch és a step különböző költség- és pályafogalom; azonos epochszám nem jelent azonos számítást. A labor két schedule kis CPU-futását azonos modell, optimizer, seed és split mellett hasonlítja össze.

21.2.12 Következő alkalmazás

A 21.3. Dropout, súlyregularizálás és augmentáció egység a dropout, a súlyregularizálás és az augmentáció kérdését tárgyalja. Ezek a regularizáló eszközök a schedule-rel együtt hatnak a validációs pályára, ezért ott azt is vizsgáljuk, hogyan kell őket kísérletileg szétválasztani.

21.2.13 Felhasznált források