Käytin muutaman tunnin käymällä /karpathy/autoresearch-repo rivi riviltä. "Tekoälyagentit tekevät tutkimusta" -näkökulma saa kaiken huomion, mutta mielestäni mielenkiintoisempaa on se, mitä koulutusskriptissä oikeasti on ja insinööripäätökset, jotka tekevät hakusilmukasta tiukan. Se on yksi tiiviimmistä yksitiedostoisista koulutusasetelmista, joita olen lukenut. Aloitan siitä, mikä tekee koko projektin mahdolliseksi: aikabudjetti on kiinteä 300 sekunnin seinäkelloon. Ei kiinteitä askeleita, ei kiinteitä tokeneita, ei kiinteitä floppija. seinäkello sekunteja. Tämä kuulostaa pieneltä yksityiskohdalta, mutta se on koko syy siihen, miksi autonominen silmukka toimii. Agentti voi tehdä mallista 3-kertaisen isomman, puolittaa eräkoon, vaihtaa täysin eri arkkitehtuurin, ja tulos on silti suoraan verrattavissa kaikkiin muihin kokeisiin, koska kaikki saivat täsmälleen 5 minuuttia koulutusta samalla näytönohjaimella. Jos korjaisit askeleet sen sijaan, isompi malli saisi vähemmän gradienttipäivityksiä sekunnissa ja rangaistaisit sitä epäoikeudenmukaisesti. Jos korjaisit tokenit, sinulla olisi sama ongelma. Seinän ajan korjaaminen tarkoittaa, että kysyt oikean kysymyksen: tämän laitteiston ja näin pitkän ajan valossa, mikä on paras malli, jonka voit tuottaa? kaikki muu on vapaa muuttuja. Agentti voi tutkia koko Pareto-pintaa mallin koon, läpimenon ja konvergenssin nopeuden välillä ilman, että mikään näistä kompromisseista sekoittuu arviointiprotokollan vuoksi. Mittari valitaan myös huolellisesti. Se on bittiä per tavu, ei ristientropian menetys. Ristientropia riippuu sanavarastosi koosta. Malli, jossa on 32 000 tokenia, ja malli, jossa on 8 000 tokenia, menettää hyvin erilaiset arvot, vaikka ne pakkaisivat datan yhtä hyvin. BPB normalisoi tämän summaamalla token-kohtaisen ristiinentropian NAT-muodossa, summaamalla kohdetokenien UTF-8 tavun pituudet ja muuntamalla NATS-per tavu bitiksi per tavu. Joten vaikka agentti muuttaisi jotain, mikä vaikuttaa tehokkaaseen tokenin jakeluun, vertailu pysyy reiluna. Nämä kaksi vaihtoehtoa, kiinteä seinäaika ja sanastoinvariantti metriikka, muuttavat sotkuisen vertaansa vailla olevan haun puhtaaksi optimointiongelmaksi. Nyt itse malli. Se on GPT, mutta siinä on paljon moderneja niksejä, jotka kannattaa ymmärtää. ensinnäkin RMSnorm kaikkialla. lohkosyötteissä (pre-norm) sekä kyselyissä ja näppäimissä juuri ennen huomiopistetuloa. Tämä QK-normi on tärkeä, koska ilman sitä Q:n ja K:n normit voivat kasvaa rajattomasti koulutuksen aikana, mikä saa huomiologit terävöitymään ja softmaxin kyllästymään. Q:n ja K:n normalisointi pitää DOT-tulot vakaalla alueella riippumatta siitä, kuinka syvä verkko on tai miten harjoitusdynamiikka kehittyy. huomio itsessään kohdistuu FA 3:een, joka on ladattu kernels-kirjaston kautta. Se käyttää Varunnealin toteutusta Hopperilla (sm_90) ja palaa yhteisöpohjaiseen versioon vanhemmille näytönohjainnäytönohjaimille. tarkkaavaisuuskuvio on "SSSL", mikä tarkoittaa kolmea liukuvan ikkunan tarkkaavaisuuskerrosta (ikkuna = puolet sekvenssin pituudesta), jota seuraa yksi kerros täyttä kausaalista tarkkaavaisuutta, joka toistuu. Tämä on harvakseltaan tiheäksi -kuvio, jonka näet Mistralissa ja Gemma2:ssa. paikalliset huomiokerrokset ovat laskennallisesti halpoja, koska huomiomatriisi on kaidattu, ja jaksollinen globaalikerros sallii tiedon kulkea koko kontekstin läpi. Kun on 8 kerrosta ja 4-merkkinen kuvio, saat kerrokset 0,1,2 paikallinen, kerros 3 globaali, kerrokset 4,5,6 paikallinen ja kerros 7 globaali. Viimeinen kerros on pakotettu globaaliksi riippumatta kuviosta. Arvon upottaminen on hienovaraista ja mielestäni aliarvostettua. Jokaisella muulla kerroksella on oma upotustaulukko, joka on täysin erillinen päätoken-upotuksesta, ja joka kuvaa token-ID:t suoraan arvodimensiovektoreihin. Nämä sekoittuvat huomioarvoihin opitun portin kautta: V = V + 2 * Sigmoid(W_gate @ x:32) * ve. Portin paino on nollalla alustettu, joten Sigmoid(0) = 0,5, kertaa 2 antaa 1,0, joka on neutraali lähtöpiste. Ylikouluttamalla malli voi oppia vahvistamaan tai tukahduttamaan arvon upotuksen per luku piilotetun tilan ensimmäisten 32 ulottuvuuden perusteella. tämä on peräisin ResForformer -linjasta, ja intuitio on, että se antaa huomiolle suoran oikotien token-identiteettiin. Arvovektorit voivat kantaa tietoa siitä, "mikä token on tässä paikassa" ilman, että tiedon tarvitsee selviytyä aiemmista kerroksista peräisin olevista jäljellä olevista virtamuunnoksista. Se on käytännössä ohitusyhteys syötteestä suoraan tarkkaavaisuusarvoihin, portittuna niin, että malli voi päättää, milloin se on hyödyllinen. Jäännösvirrassa on myös kerroksikohtaisia opittavia skalaareja: x = lambda_residi * x + lambda_x0i * x0, missä x0 on tasosta 0 normalisoitu upotus. Jokainen kerros voi itsenäisesti hallita, kuinka paljon se kuuntelee käynnissä olevaa jäännöksiä verrattuna alkuperäiseen syötteeseen. Jäännöslambdat alkavat 1,0:sta, X0 Lambdat 0,1:stä. Tämä on pehmeä versio "irrotetun jäännöksen" ideasta. Tavallisessa muuntajassa jäännösvirta on kaikkien aiempien kerroslähtöjen summa, ja se saastuu yhä syvemmälle. Kun jokaiselle kerrokselle annetaan pääsy puhtaaseen alkuperäiseen upotukseen, sen ei tarvitse oppia "kumoamaan" aiempia kerroksia palauttaakseen matalan tason tietoa. Logitit on pehmeästi rajattu 15:een Tanh(Logits/15)*15:n kautta, mikä estää mallia olemasta liian itsevarma koulutuksen alussa, kun esitykset ovat vielä meluisia. Mutta rehellisesti sanottuna koko tiedoston mielenkiintoisin osa on optimoija. MuonAdamW on yhdistetty optimointiohjelma, joka lähettää erilaisia päivityssääntöjä parametriryhmän mukaan. upotukset (token-upotukset, arvoupotukset, upottamis-head) ja kerroskohtaiset skalaarit saavat standardin AdamW:n eri oppimisnopeuksilla jokaiselle ryhmälle. Leviäminen on villi. Upotus LR on 0,6, upottamisen poistaminen LR on 0,004, se on 150-kertainen ero, ja se on tarkoituksellista. Upotusmatriisi näkee jokaisen yksittäisen tokenin ja sen täytyy päivittää aggressiivisesti. Upotusmatriisi on lineaarinen koetin lopullisessa esityksessä ja hyötyy vakaudesta. upotus-, arvo-upotuksen ja upottamisen poistamisen oppimisnopeudet skaalataan (d_model / 768)^(-0.5), mikä on muP-inspiroima korjaus. Mallin leveyden muuttuessa oppimisnopeudet mukautuvat pitämään ominaisuuksien oppimisen dynamiikan skaalausmuuttumattomana. Kerroskohtaisten lambtojen skalaarioppimisnopeudet hoidetaan erikseen, eikä skaalausta ole saatavilla. Transformerin 2D-painomatriisit, huomioprojektiot ja MLP-painot, otetaan Muon, ja tässä kohtaa asia muuttuu aidosti mielenkiintoiseksi. Muon ottaa gradientin, soveltaa Nesterovin liikemäärää ja suorittaa sitten Newton-Schulz-iteraation approksimoidakseen gradienttimatriisin polaarihajotelman. polaarihajotelma faktoroi matriisin G arvoon G = U * S, missä U on ortogonaalinen ja S symmetrinen positiivinen puolimääräinen. Muoni laskee U:n, joka on gradientille lähin ortogonaalinen matriisi, ja käyttää sitä päivityssuuntana. Newton-Schulzin iteraatio on 5 askelta. korkeille matriiseille (enemmän rivejä kuin sarakkeita) A = X^T @ X ja sitten X -> aX + X @ (bA + cA^2). laajoille matriiseille A = X @ X^T sitten X -> aX + (bA + cA^2) @ X. kertoimet on kovakoodattu esilaskennalla. He kutsuvat sitä nimellä "Polar Express". Koko järjestelmä käännetään yhdeksi sulatetuksi ytimeksi torch.compilen kautta. Miksi tämä on tärkeää? Koska painomatriiseissa Frobeniuksen normigradientti (mitä Adam ja SGD käyttävät) on geometrisesti väärä. "Oikea" jyrkin laskusuunta painomatriisille on se, joka minimoi häviön sillä ehdolla, että päivityksellä on yksikköspektrinormi, ei yksikkö Frobeniuksen normi. Ortogonaalinen polaarikerroin antaa juuri tämän. Käytännössä se tarkoittaa, että Muon tekee paljon suurempia ja tehokkaita päivityksiä, koska se ei tuhlaa askelkokoa yksittäisten arvojen skaalaamiseen. Se vain kierrättää niitä. Tästä syystä myoni konvergeetuu merkittävästi nopeammin kuin Adam muuntajapainomatriiseissa. Muon ylläpitää elementtikohtaisia liikemääräpuskureita (sama muoto kuin parametrit, pinottuna kunkin muotoryhmän yli), mutta toisin kuin Adam, se ei seuraa elementtikohtaisia sekuntimomentteja. Toisen momentin arviot ovat riviä tai saraketta kohden ortogonalisoinnin jälkeen, eivät per elementti. siinä kohtaa NorMuon astuu kuvaan. perusmuonin päällä on NorMuon, varianssin vähentämisjärjestelmä. Ortogonalisoinnin jälkeen se laskee riviä kohden (tai sarakkeita kohtaan kuvasuhteesta riippuen) toisen momentin arviot, ylläpitää niiden eksponentiaalista liukuvaa keskiarvoa ja skaalaa päivityksen uudelleen niin, että jokaisella ulostulodimuudella on oma adaptiivinen askelkoko. Se on pohjimmiltaan Adamin adaptiivisuuden idea, mutta sovellettuna ortogonalisoidussa koordinaatistossa raakaparametrin sijaan. Painon lasku on myös epätavanomaista. Se on "varovainen", eli se heikkenee vain parametreja, joissa myonin päivityssuunta vastaa parametrimerkkiä: mask = (g * parameet) >= 0. Tämä välttää tunnetun vikatilan, jossa painon lasku työntää parametreja kohti nollaa päivityksen toiveista huolimatta, mikä voi horjuttaa harjoittelua. Yksi pieni yksityiskohta, josta pidin: jo ensimmäisen koulutusvaiheen jälkeen koodi kutsuu gc.collect(), gc.freeze(), gc.disable() ja sammuttaa Pythonin roskien kerääjän kokonaan. Pythonin GC toimii säännöllisesti ja aiheuttaa ~500 ms pysähtymisiä. Kun kokonaisbudjettisi on 300 sekuntia ja jokainen askel ehkä 300 ms, satunnainen GC-tauko maksaa lähes 2 koulutusvaihetta. He käynnistävät gc.collect() manuaalisesti joka 5000 askeleen välein kompromissina. Tätä oppii vain profiloimalla oikeita harjoitusajoja ja huomaamalla salaperäisiä läpimenon laskuja. Ensimmäiset 11 askelta (0–10) eivät myöskään sisälly aikabudjettiin. siinä vaiheessa torch.compile tekee hommansa ja CUDA-ytimet saavat JIT:n. Ilman tätä poissulkua eri kokeet saisivat eri määrän "todellista" koulutusta riippuen siitä, kuinka kauan kääntäminen kestää kyseiselle mallikonfiguraatiolle. Jälleen suunnitteluvalinta, joka vaikuttaa pieneltä mutta on ratkaisevan tärkeä, jotta kokeet olisivat vertailukelpoisia. Nyt zoomaa ulos. Varsinainen automaattitutkimussilmukka on: agentti lukee program.md (markdown-tiedoston, joka kuvaa sen tehtävän), muokkaa train .py:tä, tekee commitin, ajaa 5 minuuttia, tarkistaa onko val_bpb parantunut, säilyttää tai palauttaa sen, toistaa. program.md sanoo nimenomaan "ÄLÄ KOSKAAN LOPETA." Agentti juoksee loputtomasti, kunnes ihminen tappaa sen. ~12 koetta tunnissa, ~100 yön aikana nukkuessasi. ...