Jeg brukte noen timer på å gå gjennom /karpathy/autoresearch repo linje for linje. Vinkelen «AI-agenter som gjør forskning» er det som får all oppmerksomheten, men jeg synes det mest interessante er hva som faktisk ligger inne i treningsskriptet og de tekniske beslutningene som gjør søkeloopen stram i prosessen. Det er et av de mest tette enkeltfil-treningsoppsettene jeg har lest om. La meg begynne med det som gjør hele prosjektet mulig: tidsbudsjettet er fastsatt til 300 sekunders veggklokke. Ikke faste trinn, ikke faste tokens, ikke faste flopper. Veggklokkesekunder. Dette høres ut som en liten detalj, men det er hele grunnen til at den autonome løkken fungerer. Agenten kan gjøre modellen tre ganger større, halvere batchen, bytte inn en helt annen arkitektur, og resultatet er fortsatt direkte sammenlignbart med alle andre eksperimenter fordi de alle fikk nøyaktig 5 minutters trening på samme GPU. Hvis du løste trinn i stedet, ville en større modell få færre gradientoppdateringer per sekund, og du ville straffe den urettferdig. Hvis du fikset tokens, ville du hatt samme problem. Å fikse veggtid betyr at du stiller det riktige spørsmålet: Med denne maskinvaren og så mye tid, hva er den beste modellen du kan produsere? Alt annet er en fri variabel. Agenten kan utforske hele Pareto-overflaten av modellstørrelse, gjennomstrømning og konvergenshastighet uten at noen av disse avveiningene blir forstyrret av evalueringsprotokollen. Målingen er også nøye valgt. Det er bits per byte, ikke kryssentropi-tap. Kryssentropi avhenger av vokabularstørrelsen din. En modell med 32 000 tokens og en modell med 8 000 tokens vil ha svært forskjellige tapsverdier, selv om de komprimerer dataene like godt. BPB normaliserer dette bort ved å summere kryssentropien per token i nats, summere UTF-8 byte-lengdene til måltokenene, og konvertere NATS-per-byte til bits-per-byte. Så selv om agenten endrer noe som påvirker den effektive tokenfordelingen, forblir sammenligningen rettferdig. Disse to valgene, fast veggtid og en vokabular-invariant metrikk, gjør det som ville vært et rotete, uforlignelig søk til et rent optimaliseringsproblem. Nå selve modellen. det er en GPT, men med en rekke moderne triks som er verdt å forstå. Først, RMSnorm overalt. på blokkinputene (pre-norm), og også på spørringer og nøkler rett før attention dot-produktet. denne QK-norm-greia er viktig fordi uten den kan normene for q og k vokse grenseløst under trening, noe som gjør at attention logits skjerpes og softmax blir mettet. Normalisering av Q og K holder DOT-produktene i et stabilt område uansett hvor dypt nettverket er eller hvordan treningsdynamikken utvikler seg. oppmerksomheten i seg selv er FA 3, lastet gjennom kjernebiblioteket. Det bruker Varunneals implementering på Hopper (sm_90) og faller tilbake på en fellesskapsbygging på eldre GPU-er. oppmerksomhetsmønsteret er "SSSL", som betyr tre lag med glidende vindusoppmerksomhet (vindu = halvparten av sekvenslengden) etterfulgt av ett lag med full kausal oppmerksomhet, som gjentas. Dette er det tynne til tette mønsteret du ser i Mistral og Gemma2. De lokale oppmerksomhetslagene er beregningsmessig billige fordi oppmerksomhetsmatrisen er båndet, og det periodiske globale laget lar informasjon flyte gjennom hele konteksten. Med 8 lag og et 4-tegns mønster får du lag 0,1,2 lokalt, lag 3 globalt, lag 4,5,6 lokalt, lag 7 globalt. Det siste laget tvinges globalt uansett mønster. Value embedding-greia er subtil og jeg tror den blir undervurdert. hvert annet lag får sin egen innbeddingstabell, helt adskilt fra hovedtoken-innbeddingen, som mapper token-ID-er direkte til verdidimensjonsvektorer. Disse blandes inn i oppmerksomhetsverdiene gjennom en lært port: v = v + 2 * sigmoid(W_gate @ x:32) * ve. portvekten er null-initialisert, så sigmoid(0) = 0,5 ganger 2 gir 1,0, som er et nøytralt utgangspunkt. Gjennom trening kan modellen lære å forsterke eller undertrykke verdiembedding per hode basert på de første 32 dimensjonene av den skjulte tilstanden. dette kommer fra ResFormer-linjen, og intuisjonen er at det gir oppmerksomhet en direkte snarvei til tokenidentitet. Verdivektorene kan bære informasjon om «hvilket token som befinner seg på denne posisjonen» uten at denne informasjonen må overleve de resterende strømtransformasjonene fra tidligere lag. Det er i bunn og grunn en hopp-forbindelse fra inputen direkte til oppmerksomhetsverdiene, låst slik at modellen kan bestemme når det er nyttig. Det finnes også per-lags lærbare skalarer på reststrømmen: x = lambda_residi * x + lambda_x0i * x0, hvor x0 er den normaliserte innleiringen fra lag 0. Hvert lag kan uavhengig kontrollere hvor mye det lytter til den løpende residualen kontra den opprinnelige inngangen. De resterende lambdas starter på 1,0, x0 lambdas starter på 0,1. Dette er en myk versjon av ideen om «usammenfiltret residual». I en standard transformator er reststrømmen summen av alle utganger fra forrige lag, og den blir stadig mer forurenset jo dypere du kommer. Å gi hvert lag tilgang til den rene opprinnelige embeddingen betyr at det ikke trenger å lære å "angre" tidligere lag for å hente ut lavnivåinformasjon. Loggene er softcaped til 15 via tanh(logits/15)*15, noe som forhindrer at modellen blir overmodig tidlig i treningen når representasjonene fortsatt er støyende. Men ærlig talt er den mest interessante delen av hele filen optimalisatoren. MuonAdamW er en kombinert optimalisator som sender ut ulike oppdateringsregler basert på parametergruppe. Embeddings (token-embeddings, verdiembeddinger, unembbedding head) og per-lag-skalarer får standard AdamW med ulike læringsrater for hver gruppe. Spredningen er vill. Å legge inn LR er 0,6, å fjerne LR er 0,004, det er en forskjell på 150 ganger, og det er med vilje. Embedding-matrisen ser hver eneste token og må oppdateres aggressivt. Unembbedding-matrisen er en lineær probe på den endelige representasjonen og drar nytte av stabilitet. innbedding, verdiinnbedding og avinnbedding av læringsrater skaleres alle med (d_model / 768)^(-0,5), som er en MuP-inspirert korreksjon. Når modellbredden endres, justeres læringsratene for å holde funksjonslæringsdynamikken skalerbar. De skalare læringsratene for lambdaene per lag håndteres separat og får ikke denne skaleringen. 2D-vektmatrisene i transformatoren, oppmerksomhetsprojeksjoner og mlp-vekter, får Muon, og det er her det virkelig blir interessant. Muon tar gradienten, påfører Nesterov-momentum, og kjører deretter en Newton-Schulz-iterasjon for å tilnærme den polare dekomponeringen av gradientmatrisen. den polare dekomponeringen faktoriserer en matrise G inn i G = U * S hvor U er ortogonal og S er symmetrisk positivt semidefinit. muon beregner U, den nærmeste ortogonale matrisen til gradienten, og bruker den som oppdateringsretning. Newton-Schulz-iterasjonen består av 5 trinn. for høye matriser (flere rader enn kolonner), A = X^T @ X deretter X -> aX + X @ (bA + cA^2). for brede matriser, A = X @ X^T og deretter X -> aX + (bA + cA^2) @ X. Koeffisientene er hardkodet fra en forhåndsberegning. De kaller det "Polar Express." Hele greia kompileres til en enkelt fusjonert kjerne via torch.compile. Hvorfor er dette viktig? Fordi for vektmatriser er Frobenius-normgradienten (slik Adam og SGD bruker) geometrisk feil. Den "riktige" bratteste nedstigningsretningen for en vektmatrise er den som minimerer tapet, med forbehold om at oppdateringen har enhetsspektralnorm, ikke enhets Frobenius-norm. Den ortogonale polarfaktoren gir deg nettopp dette. I praksis betyr det at Muon lager mye større, effektive oppdateringer fordi det ikke kaster bort steglengden på å skalere enkeltverdier. Den roterer dem bare. Dette er grunnen til at myon konvergerer betydelig raskere enn Adam på transformatorvektmatriser. Muon opprettholder momentumbuffere per element (samme form som parametrene, stablet over hver formgruppe), men i motsetning til Adam sporer den ikke sekundmomenter per element. de andre momentestimatene er per rad eller per kolonne etter ortogonalisering, ikke per element. Det er her NorMuon kommer inn. På toppen av basemyonen er det NorMuon, et variansreduksjonssystem. Etter ortogonalisering beregner den per rad (eller per kolonne avhengig av aspektforhold) estimater for andre moment, opprettholder et eksponentielt glidende gjennomsnitt av disse, og skalerer oppdateringen på nytt slik at hver utdatadimensjon får sin egen adaptive steglengde. Det er i bunn og grunn Adam-adaptivitetsideen, men anvendt i det ortogonaliserte koordinatsystemet i stedet for det rå parameterrommet. Vektnedgangen er også ikke standard. Den er «forsiktig», noe som betyr at den kun avtar parametere der myonens oppdateringsretning stemmer overens med parametertegnet: maske = (g * parametere) >= 0. Dette unngår den kjente feilmodusen hvor vektavvik skyver parametere mot null mot oppdateringens ønsker, noe som kan destabilisere treningen. En liten detalj jeg satte pris på: etter det aller første treningssteget kaller koden gc.collect(), gc.freeze(), gc.disable() for å slå helt av Pythons søppelsamler. Pythons GC kjører periodisk og forårsaker ~500 ms stopp. når totalbudsjettet ditt er 300 sekunder og hvert steg kanskje er 300 ms, koster en tilfeldig GC-pause deg nesten 2 treningssteg. De utløser manuelt gc.collect() hvert 5000. steg som et kompromiss. Dette er noe du bare lærer ved å profilere ekte treningsløp og legge merke til mystiske gjennomstrømningsfall. De første 11 stegene (0 til 10) regnes heller ikke med i tidsbudsjettet. det er oppvarmingen hvor torch.compile gjør jobben sin, og CUDA-kjerner blir JIT-et. Uten denne eksklusjonen ville ulike eksperimenter få ulik mengde «ekte» trening avhengig av hvor lang tid kompileringen tar for den aktuelle modellkonfigurasjonen. Igjen, et designvalg som virker lite, men som er avgjørende for å gjøre eksperimentene sammenlignbare. Nå zoom ut. Den faktiske autoresearch-løkken er: agenten leser program.md (en markdown-fil som beskriver jobben), modifiserer train .py, commiter, kjører i 5 minutter, sjekker om val_bpb er forbedret, beholder eller reverserer, gjentar. program.md sier eksplisitt «ALDRI STOPP.» agenten løper uendelig til mennesket dreper den. ~12 eksperimenter i timen, ~100 over natten mens du sover. ...