05 · Treenimine ja genereerimine: tsükkel, mis sulgeb microGPT ringi
Neli õppetundi masinavärki, ja mitte ükski neist ei selgitanud, kust need
4000 nupuasendit tulid. See õppetund teeb seda: mudel, mis alustab puhta
Gaussi mürana, segatud loend inimeste nimedest, ja tsükkel — edasisuund,
kadu, tagasilevi, nügimine — korratuna tuhat korda, kuni müra on end ümber
korraldanud millekski, mis suudab pursata karia, dell, aanna: nimesid,
mida ei eksisteeri, aga võiks. See on õppetund, kus kõik senine end ära
tasub. Kõik jookseb.
Enne sukeldumist. Treenimine on lehe 00 · Alusmõisted §4 retsept ellu viiduna: arvuta, kui palju mudel eksib (kadu), hangi iga nupu gradient õppetunni 02 tagasilevi kaudu ja nügi siis iga nuppu väikese sammu võrra allamäge — tuhandeid kordi. Adam on sama retsept amortisaatoritega: ta silub iga nupu hiljutisi gradiente, et üks mürarikas samm nuppu siia-sinna ei rapsiks. Ja temperatuur on §5 trikk: jaga skoorid enne softmax’i, et muuta genereerimine julgemaks või ettevaatlikumaks. Kui oled lugenud 00 ja 02, pole selles õppetunnis midagi uut — siin hakkab lihtsalt kõik lõpuks tööle.
Teooria
Õppetunnid 01–04 ehitasid edasisuuna. See õppetund on faili ülejäänud osa:
kuidas kaalud õpitakse (treenimine) ja kuidas treenitud mudel purskab
uusi nimesid (genereerimine). Mõlemad on lühikesed ja mõlemad tulevad otse
failist src/microgpt_annotated.py.
Andmed ja tokeniseerija
Andmestik docs on segatud nimede loend. Sõnavara on lihtsalt sorditud
hulk tähemärke, mis seal esinevad:
uchars = sorted(set(''.join(docs))) # the characters → token ids 0..n-1
BOS = len(uchars) # one extra sentinel id, AFTER the chars
vocab_size = len(uchars) + 1 # chars + 1 (the BOS)BOS on üksainus spetsiaalne token. Iga treeningdokument mähitakse sellega
mõlemast otsast ja iga positsiooni siht (target) on lihtsalt
järgmine token:
tokens = [BOS] + [uchars.index(ch) for ch in doc] + [BOS]
# predict tokens[pos+1] from tokens[pos]Alguse BOS on „START”; lõpu BOS on see, mida mudel peab õppima ennustama, et öelda „see nimi on läbi”.
Treenimine: edasisuund → kadu → tagasilevi → Adam
Iga positsiooni jaoks toodab mudel logitid, softmax muudab need tõenäosusteks ja kadu on negatiivne logaritm tõenäosusest, mille mudel omistas tegelikule järgmisele tokenile, keskmistatuna üle dokumendi:
probs = softmax(logits)
loss_t = -probs[target_id].log()
loss = (1 / n) * sum(losses) # mean cross-entropy
loss.backward() # the lesson-02 autograd, run on the whole graphSeejärel uuendab Adam (mitte tavaline SGD) iga parameetri tema
gradiendi põhjal. Adam hoiab parameetri kohta kahte jooksvat puhvrit —
esimest momenti m (silutud gradient) ja teist momenti v (silutud
ruudus gradient) —, korrigeerib nende nihke ja kahandab õpisammu
treenimise jooksul lineaarselt:
learning_rate, beta1, beta2, eps_adam = 0.01, 0.85, 0.99, 1e-8
lr_t = learning_rate * (1 - step / num_steps) # linear LR decay
for i, p in enumerate(params):
m[i] = beta1 * m[i] + (1 - beta1) * p.grad
v[i] = beta2 * v[i] + (1 - beta2) * p.grad ** 2
m_hat = m[i] / (1 - beta1 ** (step + 1)) # bias correction
v_hat = v[i] / (1 - beta2 ** (step + 1))
p.data -= lr_t * m_hat / (v_hat ** 0.5 + eps_adam)
p.grad = 0 # reset for the next stepGenereerimine: mudel purskab vastu
Pärast treenimist genereerib mudel tähthaaval, alustades BOS-ist
ja söötes iga ennustuse tagasi järgmise sisendina. Karpathy Pythonis kogub
kasvav KV-vahemälu (keys, values) varasemat konteksti, nii et iga
uus token teeb tööd ainult iseenda jaoks (brauseri port arvutab seda
teisiti — vt märkust kommenteeritud koodi all). Temperatuur jagab
logitid enne softmax’i — madal temperatuur koondab jaotuse (fokuseeritud,
korduv), kõrge lamendab selle (juhuslik, loov). Genereerimine peatub, kui
loositakse uuesti BOS:
temperature = 0.5 # in (0, 1]
for pos_id in range(block_size):
logits = gpt(token_id, pos_id, keys, values)
probs = softmax([l / temperature for l in logits])
token_id = random.choices(range(vocab_size), weights=[p.data for p in probs])[0]
if token_id == BOS:
break
sample.append(uchars[token_id])Kommenteeritud kood
Treeningtsükkel on faili src/microgpt_annotated.py 5. jaotis (alajaotis
overview-training-step, py read 194–226); genereerimine on 6. jaotis (py
read 234–247). Adami hüperparameetrid learning_rate=0.01, beta1=0.85, beta2=0.99, eps=1e-8 ja num_steps=1000 on väärtused, millega kaasapandud
kaalud treeniti.
Märkus selle kohta, mida see liivakast teeb ja mida ei tee. Need ~89 KB kaale treeniti ette ära Pythoni failiga; brauser mudelit uuesti ei treeni.
Režiim Train arvutab ühe päris gradiendi ja Adam-uuenduse ühe
LM-headi parameetri jaoks (ülejäänud võrk hoitakse fikseerituna): see
tokeniseerib sinu dokumendi, jooksutab kao jaoks ehtsa edasisuuna, kutsub
loss.backward() läbi õppetunni 02 Value-mootori, et saada tõeline
gradient, ja arvutab täpselt ülaltoodud Adami valemi. Iga arv on päris —
aga arvutatud uuendus ainult kuvatakse, seda ei salvestata laaditud
mudelisse, ja iga sisendimuutus alustab uuesti värskete Adami puhvritega
sammult 0. See on uuenduse arvutus, mitte salvestatud treeningsamm, ja
mudel, millest režiimis Generate valimeid võtad, jääb muutmata.
Režiim Generate jooksutab päris mudelit autoregressiivselt sinu valitud temperatuuril. Üks täitmise detail, milles täpne olla: Pythoni viitekood kasutab kasvavat KV-vahemälu, samas kui brauseri TypeScripti port arvutab igal genereerimissammul kogu põhjusliku algusjada uuesti. Tulemuseks saadavad logitid kasutavad sama matemaatikat, aga täitmisstrateegia on erinev — brauser ei pea inkrementaalset KV-vahemälu.
Liivakast
- Generate — vaata, kuidas mudel ehitab nime alates BOS-ist. Lohista temperature’i ja järgmise tähemärgi tõenäosustulbad muudavad kuju reaalajas (madal = fokuseeritud, kõrge = juhuslik); resample loosib uue nime. Genereerimine peatub, kui mudel ennustab vahimärgi STOP. (Iga samm arvutab kogu algusjada uuesti — samad logitid, ilma inkrementaalse KV-vahemäluta.)
- Train — trüki dokument ja vaata ühte päris gradiendi + Adam-uuenduse
arvutust: andmed → edasisuund → kadu → tagasilevi → Adam. Paneel näitab
tõelist keskmist ristentroopiat ja tõelisi
m / v / m̂ / v̂ / lr_t / Δväärtusi ühe LM-headi parameetri kohta, otse valemist. Uuendus on näidatud, mitte salvestatud — muuda sisendit ja kõik arvutatakse uuesti värskete Adami puhvritega sammult 0.
Proovi ise.
- Sea režiimis Generate temperatuur miinimumi ja loosi viis korda. Siis lükka see maksimumi ja loosi veel viis. Sa viisid mudeli just ühe liuguriga seisundist „igav, aga kindel” seisundisse „loov, aga ohjeldamatu” — täpselt sama kompromiss, mida timmib iga juturobot.
- Jälgi tõenäosustulpasid temperatuuri lohistamise ajal. Tulpade järjestus ei muutu kunagi — ainult kontrast. Miks? (Vihje: konstandiga jagamine ei saa skooride järjekorda muuta.)
- Anna režiimis Train mudelile
emmaja pane kadu kirja. Nüüd annaxqzzv. Kumb dokument üllatab mudelit rohkem, ja mida see ütleb nimede kohta, mille keskel ta üles kasvas?- Vaata LM-headi parameetri päris
Δ-d. See on tibatilluke. Treenimine on tuhat tibatillukest nügimist — mitte üks suur ilmutus.