A 6.3. Sztochasztikus és minibatch gradiens alfejezetben a minibatch-gradiens torzítatlanságáról és zajáról volt szó. Ott a lépés közvetlenül a becsült gradiens negatív irányába mutatott. A gyakorlatban az SGD azonban lassan haladhat olyan irányokban, ahol a gradiens kicsi, és erősen fluktuálhat olyan irányokban, ahol a gradiens nagy varianciájú. Ebben az egységben olyan frissítési szabályokat vizsgálunk, amelyek paraméteren kívül állapotváltozókat is tárolnak, és ezekből számítják a tényleges lépést. Az 5.4. Függő képek, seedek és resampling alfejezetből tudjuk, hogy a seed és a minta szerinti bizonytalanságot külön kell kezelni: az optimizer választása az algoritmikus bizonytalanságot befolyásolja, nem magát az adatmintát.
Induló becslés: miért lehet hasznos egy gradiens mozgóátlaga akkor is, ha az egyes gradiensbecslések torzítatlanok? Mit ront el, ha a súlycsökkentést egyszerűen hozzáadjuk a gradienshez egy adaptív normalizálás előtt?
Legyen a paramétervektor , a gradiensbecslés , a tanulási ráta , a momentum-együttható . A PyTorch momentumos SGD-jének állapotváltozója , alakja azonos alakjával, és inicializálása . A frissítés:
Ez nem normalizált exponenciális mozgóátlag: konstans gradiens esetén , ami esetén -hoz tart. Ezért a momentum növeli az effektív lépésnagyságot egy konzisztens gradiensirányban; a tanulási rátát általában kisebbre kell választani, mint sima SGD-nél. A momentum célja heurisztikus: a konzisztens irányt simítja és gyorsítja, a nagy varianciájú, előjelváltó komponenseket részben kiátlagolja. Ez nem bizonyított konvergenciajavítás minden feladatra.
Az Adam három állapotot tart nyilván a paraméter mellett. Legyenek , ; a PyTorch-alapértelmezések szerint gyakran , , . A -edik lépésben a gradiensbecslésből:
ahol elemenkénti négyzet, , alakjuk azonos alakjával. Mivel az inicializálás nulla, a kezdeti lépésekben és szisztematikusan kisebb, mint amit egy régóta futó mozgóátlagtól várnánk. A bias correction ezért:
Az Adam-frissítés:
A nevező műveletei elemenkéntiek, hozzáadása a négyzetgyök után történik a PyTorch-konvenció szerint; más könyvtárak a alakot használhatják, ezért pontos numerikus reprodukció előtt az adott API-t ellenőrizni kell. Az szerepe numerikus: ha valamely koordinátája nulla közelében van, az osztás instabillá válna.
Az Adam tehát két különböző normalizálást végez. Az első momentum a gradiens irányának simítása, a második momentum a gradienskomponensek négyzetes nagyságának simítása; az arányuk az egyes koordinátákra eltérő effektív lépésméretet ad.
A klasszikus L2-regularizálás a veszteséghez ad hozzá tagot, így a gradiens lesz. Az AdamW ehelyett a súlycsökkentést elkülönítve, a momentumoktól függetlenül alkalmazza:
$$ \theta_{t+1} = \theta_t - \eta\Bigl(\lambda\theta_t + \frac{\hat m_t}{\sqrt{\hat v_t}+\epsilon}\Bigr), $$
ahol
a weight decay,
továbbra is csak a
adatgradiensből számítódik. Ezt nevezzük decoupled
decay-nek: a
tag nincs benne sem
-ben,
sem
-ben,
és nincs osztva az adaptív nevezővel. A gondolat forrása [P02]; a
PyTorch AdamW osztálya ezt az alakot valósítja meg, míg a
torch.optim.Adam weight_decay argumentuma a
hagyományos L2-stílusú gradienshozzáadást jelenti a jelen könyv által
feltételezett 2.14 API-ban. Ez utóbbi pontos szakaszát a kézirat
lektorálása előtt ellenőrizni kell [D06].
Állítás (levezetéssel). Legyen , adott adatgradiens és . Az AdamW frissítése
ahol csak -ből képződik. A naiv L2 Adam ezzel szemben a gradienst használja, ezért
és a frissítés
A két szabály csak akkor lenne azonos, ha a tag az adaptív normalizálásból kiemelhető lenne. Nem emelhető ki, mert egyrészt és a korábbi értékeket is tartalmazza, másrészt a L2-tag a nevezővel is osztódik. Ezért a L2 Adam alatti effektív decay nem , hanem egy momentumokkal simított, adaptívan skálázott mennyiség.
Legyen , , , , , . A választás a kézi számolást egyszerűsíti; a PyTorch-alapértelmezés nem nulla, de ebben a példában a nevezők nullától távol vannak, így a különbség csak numerikus apróság.
AdamW, 1. lépés. , , bias korrekció: , ezért , . Ekkor
AdamW, 2. lépés. , , bias korrekció: , ezért . Így
Naiv L2 Adam, 1. lépés. . , , bias korrekcióval , , . A frissítés:
Naiv L2 Adam, 2. lépés. . Ekkor , . A bias korrekció : , , . A hányados majdnem 1, így
A két lépés után AdamW , a L2 Adam . A különbség oka látható: az AdamW-ben a decay tag külön levonódik, a L2 Adam esetén viszont a decay bekerül a momentumokba, és az adaptív normalizálás részben elfedi a nagyságát. A példa nem állítja, hogy az egyik mindig jobb; csak azt mutatja, hogy a két frissítési szabály matematikailag nem azonos.
Munkakönyvtár: lab/n024/. A kód csak CPU-t és NumPy-t
használ; dtype=np.float64 a pontos összehasonlításhoz. A
seed itt nem befolyásolja az eredményt, mert a gradiensértékeket
explicit módon adjuk meg; ha később véletlen gradienst generálunk,
használjuk a np.random.default_rng(20261020)
generátort.
#!/usr/bin/env python3
# lab/n024/optim_steps.py
import numpy as np
def adamw_step_scalar(theta, m, v, g, lr, beta1, beta2, lam, t, eps):
m = beta1 * m + (1.0 - beta1) * g
v = beta2 * v + (1.0 - beta2) * g * g
m_hat = m / (1.0 - beta1 ** t)
v_hat = v / (1.0 - beta2 ** t)
theta_new = (
theta
- lr * lam * theta
- lr * m_hat / (np.sqrt(v_hat) + eps)
)
return theta_new, m, v
def adam_l2_step_scalar(theta, m, v, g, lr, beta1, beta2, lam, t, eps):
grad = g + lam * theta
m = beta1 * m + (1.0 - beta1) * grad
v = beta2 * v + (1.0 - beta2) * grad * grad
m_hat = m / (1.0 - beta1 ** t)
v_hat = v / (1.0 - beta2 ** t)
theta_new = theta - lr * m_hat / (np.sqrt(v_hat) + eps)
return theta_new, m, v
def main():
np.set_printoptions(precision=12, floatmode="fixed")
theta0 = np.float64(2.0)
g = np.float64(1.0)
lr = np.float64(0.1)
beta1 = np.float64(0.5)
beta2 = np.float64(0.5)
lam = np.float64(0.5)
eps = np.float64(0.0)
# AdamW két lépés
theta = theta0
m = np.float64(0.0)
v = np.float64(0.0)
for t in (1, 2):
theta, m, v = adamw_step_scalar(
theta, m, v, g, lr, beta1, beta2, lam, t, eps
)
print(f"AdamW step {t}: theta={theta}, m={m}, v={v}")
# Naiv L2 Adam két lépés
theta = theta0
m = np.float64(0.0)
v = np.float64(0.0)
for t in (1, 2):
theta, m, v = adam_l2_step_scalar(
theta, m, v, g, lr, beta1, beta2, lam, t, eps
)
print(f"L2 Adam step {t}: theta={theta}, m={m}, v={v}")
if __name__ == "__main__":
main()Futtatási parancs a lab/n024/ könyvtárból:
python optim_steps.pyA kézi levezetésből várt kimenet:
AdamW step 1: theta=1.800..., m=0.5, v=0.5
AdamW step 2: theta=1.610..., m=0.75, v=0.75
L2 Adam step 1: theta=1.900..., m=1.0, v=2.0
L2 Adam step 2: theta=1.800..., m=1.475, v=2.90125
Az egyezést rtol=1e-12 mellett érdemes ellenőrizni
np.allclose-zal a kézzel számolt értékekre. A megadott
kimenet levezetett számérték; az ellenőrző kód önállóan futtatható.
Magyarázza el, mi a különbség az Adam első és második momentuma között, és miért szükséges a bias correction. Térjen ki arra, hogy miért torzítja lefelé az első lépéseket. Elvárt eredmény: legfeljebb fél oldal saját magyarázat, benne a tag szerepe.
Igazolja, hogy ha egy -dimenziós feladatban az első gradiens mindegyike ugyanaz a vektor, akkor az Adam bias-korrigált első momentuma , és a második momentum , ahol elemenkénti négyzet. Mondja ki, hogy ez az állítás milyen feltétel mellett érvényes, és mutasson ellenpéldát arra, amikor a gradiensek nem egyenlők, de a bias correction mégsem adja vissza az utolsó gradienst.
A lab/n024/optim_steps.py mellé írjon egy
sgd_momentum_step(theta, buf, g, lr, beta) függvényt a
PyTorch momentumos SGD képlete alapján. Bemenet:
,
,
,
,
dtype=np.float64. Adja meg
-t
és ellenőrizze, hogy
legyen. Elvárt átadandó eredmény: teljes függvénykód, futtatási parancs
és a kapott theta érték.
Az alábbi Adam-implementáció hibás:
def rossz_adam(theta, m, v, g, t, lr, beta1, beta2, eps):
m = beta1 * m + (1-beta1) * g
v = beta2 * v + (1-beta2) * g * g
m_hat = m / (1 - beta1 ** t)
theta = theta - lr * m_hat / (np.sqrt(v) + eps)
return theta, m, vAzonosítsa a hibát, magyarázza el a következményét a második lépésben, és javítsa a kódot. Igazolja a javítást a szakasz kézi példáján. Elvárt eredmény: hibaleírás, javított kód és a javított változat értéke.
Tervezzen egy kis CPU-s protokollt, amely ugyanazon a rögzített modellen és adathalmazon hasonlítja össze az SGD-t, a momentumos SGD-t, az Adamet és az AdamW-t. Határozza meg: a modellt, a veszteséget, a splitet, a metrikát, a tanítási költségkeretet, a seedkezelést és a hiperparaméter-keresést. Az értékelés végén mondja meg, milyen következtetést nem szabad levonni egyetlen futtatásból. Elvárt eredmény: protokollterv és a lehetséges érvényességi korlátok indoklása, futás nélkül.
Az a gradiensvektor mozgóátlaga, ezért elsősorban az irányt simítja; a a gradiens elemenkénti négyzetének mozgóátlaga, ezért a négyzetes nagyságot becsli. A nevezőbeli koordinátánként eltérő léptéket ad. Az inicializálás : az első lépésben , tehát a érték helyett annak csak -szeresét tároljuk. A -edik lépésig a mozgóátlag súlyainak összege , ezért osztunk ezzel. Hasonlóan esetén . A bias correction tehát nem a gradiens torzítását javítja, hanem a nulla kezdőállapotból adódó skálázást.
Ha minden esetén, akkor a rekurziót kibontva:
Ezért . Ugyanígy , tehát . A feltétel az, hogy a gradiensek determinisztikusan azonosak legyenek. Ha például és skalár esetén és , akkor , míg az utolsó gradiens , tehát ; a bias correction nem reprodukálja az időben változó gradienst.
import numpy as np
def sgd_momentum_step(theta, buf, g, lr, beta):
buf = beta * buf + g
theta = theta - lr * buf
return theta, buf
theta = np.float64(0.5)
buf = np.float64(0.0)
g = np.float64(0.2)
lr = np.float64(0.1)
beta = np.float64(0.9)
for _ in range(2):
theta, buf = sgd_momentum_step(theta, buf, g, lr, beta)
print(theta) # 0.442Ellenőrzés: 1. lépésben , ; 2. lépésben , .
A kód a második momentumot nem korrigálja a tényezővel. Emiatt alulbecsült marad, ami túl kicsi nevezőt és túl nagy adaptív lépést okozhat, különösen a kezdeti lépésekben. A javítás:
v_hat = v / (1 - beta2 ** t)
theta = theta - lr * m_hat / (np.sqrt(v_hat) + eps)A szakasz példáján , esetén , a hibás kód , a javított nevezőt ad. A helyes érték a L2 Adam példában; a hibás kód ettől eltérő, nagyobb lépést adna.
Modell: lineáris logisztikus regresszió a GlyphLab-példa
képének előre kivett hisztogram-jellemzőin, vagy egy kis MLP. Veszteség:
cross-entropy. Adat: a GlyphLab data/ könyvtár
train/val/test splitje, csoportosítás scene_id szerint.
Metrika: valószínűségi AUC vagy log loss a validációs halmazon, a végső
jelentés a test halmazon. Költségkeret: legfeljebb például 50 epoch
CPU-n, rögzített batch méret. Seedkezelés: minden optimizerhez legalább
három különböző seed, és a kezdeti súlyok azonos seedű inicializálása.
Keresés: minden optimizer tanulási rátájára kis logaritmikus rács, a
validációs metrika alapján választva. Nem szabad levonni, hogy egyetlen
legjobb seed alapján az egyik optimizer általánosan jobb; a seedek
közötti szórás az algoritmikus bizonytalanság része, és a kis toy
feladat nem reprezentálja a valós CNN-feladatokat.
Gyakori hiba azt hinni, hogy egy optimizer neve önmagában garantál jobb pontosságot vagy konvergenciát. Tekintsük az függvényt, gradiens , . Sima SGD esetén , tehát monoton csökken és lépés után . Ha Adamet használunk , , mellett, akkor a frissítés : minden lépés nagyságú, így lépés kell a nullához, és a pálya lényegesen lassabb. Ez a mesterséges ellenpélda nem bizonyítja, hogy az SGD mindig jobb; azt mutatja, hogy az Adam normalizált lépése bizonyos skálázású feladatoknál elveszítheti a gradiens nagyságából származó előnyt.
Önellenőrzés: írja fel az AdamW frissítést skalár esetben, majd ellenőrizze, hogy a decay tag valóban kívül van-e a nevezőn. Ha a frissítésben az adaptív nevezőn belül szerepel, akkor az L2 Adam, nem AdamW. Visszahívás a 6.3. Sztochasztikus és minibatch gradiens alfejezetből: a minibatch-gradiens torzítatlan becslése a momentum és az Adam állapotváltozóiba kerül, így az ott levezetett variancia csökkenése a lépés zaját, nem feltétlenül az optimizer teljes pályájának stabilizálását írja le. A 2.4. Kis tudományos szoftverprojekt alfejezetből pedig az következik, hogy az optimizer kódját ugyanúgy kis fixture-ökön kell tesztelni, mint bármely más modult.
A momentumos SGD a gradiens rekurzív mozgóátlagát tárolja, és ezzel csökkenti a lépés irányának fluktuációját. Az Adam két állapotot tart: az első momentum a gradiens irányát, a második momentum a koordinátánkénti négyzetes nagyságot becsli; a bias correction a nulla kezdőállapot miatti kezdeti lefelé torzítást korrigálja. Az AdamW a súlycsökkentést elkülönítve alkalmazza: a tag nem kerül a momentumokba, ezért matematikailag nem azonos a naiv L2 Adam-mel. A kézi kétlépéses skalárpélda numerikusan is mutatja az eltérést. Az optimizer neve azonban nem garancia, a választást mindig a feladaton és a seedismétléseken kell ellenőrizni.
A 7.1. Entrópia, cross-entropy és KL alfejezetben az entrópia, a cross-entropy és a KL-divergencia következik. Ott a veszteségfüggvény pontos valószínűségi értelmezését építjük fel; az itt tárgyalt frissítési szabályokat ezután konkrét célfüggvények gradiensére alkalmazzuk.