Sexton dagars beräkning på en GPU
En enda lång körning
ANDREA-120M tar ~23 dagar på en RTX 4090 (FP16, 6 steg/min, 200K steg). Väggström, kernelpanik, proxykrascher & avsiktliga konfigurationsändringar händer alla under det fönstret. Utan kontrollpunkter kastar en enda hickup bort hela körningen.
v1 förlorade 27K steg på ett misstag (lr=0.001 för aggressiv) eftersom ingen kontrollpunkt satt närmare än startpunkten. v2 absorberade den lektionen: kontrollpunktstakt drogs ner till var 100:e steg, & en CUDA-signalhanterare garanterar en kontrollpunktsskrivning vid SIGTERM.
Tre roller
En checkpoint fyller tre jobb samtidigt:
1. Återställningspunkt. Processen dör, maskinen startar om, kernel panic: återuppta från senaste step_NNNNNN.bin.
2. Finjusteringspivot. Steg 112,619: ändra läroplanen utan omträning. SIGUSR1 tvingar fram en ren checkpoint, proxy stoppas, nya caps & floors skickas in, CUDA återupptar från den sparade punkten under en ny policy.
3. Granskningsgren. Jämför två konfigurationer vid samma startvikt: kopiera en checkpoint, kör två divergerande grenar framåt, observera vilken som konvergerar.
Varje 100 steg ger ~17 minuter träning mellan skrivningar vid 6 steg/min. 100 steg matchar också sample_every: varje kontrollpunkt motsvarar en färsk provgranskning, & varje provgranskning motsvarar en återställbar punkt.
Tre roller för en fil
Fem regioner i en fil
Formatet
Varje step_NNNNNN.bin packar fem sammanhängande regioner:
[int32 step] [int32 n_params] [n_params x float32 weights] [n_params x float32 m] [n_params x float32 v]
Region för region
Header (8 byte totalt). Ett 32-bitars stegnummer berättar var i träningen denna snapshot ligger; ett 32-bitars parameterantal berättar hur stora de tre efterföljande arrayerna är.
Vikter (n_params x 4 byte). Varje inlärd parameter, platt. Ordningsföljden matchar modellens parameteriterator: token- & positionsinbäddningar, sedan per-lager attention- & MLP-vikter, sedan utgångshuvudet.
Adam första moment m (n_params x 4 byte). EMA av tidigare gradienter (beta1 = 0.9). Samma form som vikterna. Krävs för AdamW-upphämtning.
Adams andra moment v (n_params x 4 bytes). EMA av tidigare kvadrerade gradienter (beta2 = 0.999). Samma form som vikterna. Krävs för AdamW-upphämtning.
Total storlek
Totala byte = 8 + 12 x n_params. ANDREA-12M (12.8M params): 154 MB på disk (147 MB avrundat). ANDREA-120M (~120M params) FP32: ~1.44 GB. Tre arrayer med identisk form, stapade efter varandra, med en liten header.
Varför spara m & v
Vanilla Adam spårar per-parameter inlärningshastigheter via m & v. Släpp dem vid kontrollpunktskrivning & en upphämtad körning startar med noll momentum & noll variansuppskattning, motsvarande inlärningshastighet 0 för ett steg sedan en plötslig ramp. Förlustspikar; modellen kan falla ur sin nuvarande bassäng. Att spara m & v gör upphämtning bit-ekvivalent (modulo dataladdar-slumpmässighet) till den aldrig-stoppad baslinjen.
Storlek på en kontrollpunkt
SIGTERM & SIGUSR1
Varför CUDA hanterar signaler
Träningen körs som en långlivad förgrundsprocess. När proxyn eller operatören vill att GPU:n ska stanna skickas en signal till CUDA-motorn. Utan en hanterare dödar standard-SIGTERM processen omedelbart: pågående gradientberäkning kastas bort, senaste vikterna sedan senaste kontrollpunkten förloras. Med en hanterare skriver motorn först en kontrollpunkt och avslutar sedan rent.
SIGTERM: skriv & avsluta
Skickas av en stoppknapp, en systemctl stop, eller en kill från en överordnad proxy. CUDA slutför det aktuella steget, skriver step_NNNNNN.bin till disk, och avslutar sedan. Återställning från detta tillstånd kräver endast den senaste .bin: noll arbete förlorat utöver det partiella steget som pågår.
SIGUSR1: skriv & fortsätt
Skickas på begäran av en operatör eller proxy-skript. CUDA slutför det aktuella steget, skriver step_NNNNNN.bin, och fortsätter sedan träningen som om inget hänt. Användbart för: att utlösa en revisionspunkt precis före en konfigurationsändring; att fånga vikter vid ett känt gott ögonblick; att synkronisera en kontrollpunkt med en extern körning för provkvalitetsbedömning.
Den polska pivot-sekvensen (steg 112,619)
1. Operatören skickar SIGUSR1 till CUDA. Kontrollpunkten skrivs vid nästa 100-stegsgräns (steg 112,700).
2. Operatören stoppar proxyn.
3. .samples.json & .state.json arkiveras (provlägg & bandit-tillstånd bevaras som historiskt register).
4. .loss.json stannar på platsen. Kumulativ träningshistorik; arkiveras aldrig.
5. Proxy startar om under nya caps & floors.
6. CUDA återupptar från step_112700.bin med en ny bandit men fulla vikter, m, & v.
Förlusthistoriken fortsätter obruten över pivot-punkten. Sampleloggen återställs rent. Banditen får en ny start under ny policy.
Välja signalen
Kumulativ träningshistorik
Tre sidovagns-filer
Tillsammans med varje kontrollpunkt upprätthåller proxyn tre JSON-sidovagnar i körningskatalogen:
- .loss.json -- en post per steg, alltid. ~200 000 poster vid körningens slut. Kumulativ träningshistorik.
- .samples.json -- nyligen genererade prover för granskning. Återställs vid poleringsvändningar.
- .state.json -- banditarmdrag, EMA-belöningar, fasräknare. Återställs vid poleringsvändningar.
Vad som återställs, vad som kvarstår
Polska pivots är policyändringar, inte köråterställningar. Modellens vikter, m, v och förlusthistorik fortsätter obrutna. Banditens ackumulerade belöningar FORTSÄTTER INTE: de nya taken och golven definierar en annan policy, och banditen måste lära om under den nya policyn från ett rent tillstånd.
Varför .loss.json Förblir
Förlusthistoriken fungerar som körens revisionsspår. Varje publicerat påstående om ANDREA-120M (förlust EMA vid steg 110K, polish-pivot-återhämtning, konvergens vid steg 112K) kan spåras tillbaka till poster i denna fil. Att arkivera .loss.json mellan faser skulle tvinga läsare att sy ihop fragment för att rekonstruera körningen; att hålla den append-only och orörd bevarar proveniens.
Zombiarm-lektionen
Steg 112,619 hittade en repo-docstrings-arm i .state.json som bar vikt 1.546 från en tidigare körning. Banditens tillstånd hade bevarats över en tidigare omstart, men datakällan var inte längre tillgänglig, vilket producerade zombi-drag som förvrängde utforskningsredovisningen. Lektion: banditens tillstånd FÅR driva över omstarter på överraskande sätt. Förlusthistoriken är den enda filen som måste förbli orörd under körens hela livstid.
En Regel som Here Alla
Arkivera .samples.json & .state.json fritt mellan faser. Arkivera aldrig .loss.json. Den senaste .loss.json är alltid den kanoniska träningshistoriken.
Tillämpa Regeln
Vad som byggdes & varför
Fem beslut
1. Takten: var 100:e steg. Granularitet för återställningspunkt ~17 minuter. Synkroniserat med sample_every så varje kontrollpunkt motsvarar en färsk provgranskning.
2. Format: rubrik + 3 arrayer. Minimalt: 8-bytes rubrik berättar hur stor varje efterföljande array är. Ingen metadatabloat. Bit-ekvivalent repris när m & v sparas.
3. Signaler: SIGTERM & SIGUSR1. Två roller, två signaler. Standard systemd-avstängning får en ren kontrollpunkt via SIGTERM; efterfrågan-granskningspunkter får en ren kontrollpunkt via SIGUSR1 utan att stoppa.
4. Förlustkontinuitet: aldrig arkiverad. Kumulativ träningshistorik kvarstår över poleringsvändningar, omstarter & policysändringar. En granskningsspår för hela körningen.
5. Bandit-tillstånd: nollställningar tillåtna. Bandit-policy lever under en konfig åt gången. Poleringsvändningar nollställer; förlusthistorik fortsätter. Två olika livstider delar samma kört katalog.
Vad denna lektion kopplar till
- Aktivitet 23 (grow_a_language_model_sample_audit). sample_every-takt matchar checkpoint-takt; båda utlöses var 100:e steg.
- Aktivitet 24 (grow_a_language_model_microgpt_to_andrea). v1 kollaps, v2.5 patch, v3 polish pivot kräver alla rena checkpoints för att fungera.
- Aktivitet 10 (grow_a_language_model_adamw). Att spara m & v i checkpointen är viktigt eftersom AdamW:s uppdateringsregel beror på båda. Utelämna dem & återuppta divergerar.
En sista ingenjörsmässig sanning
Kod överlever författare. Infrastruktur överlever byggare. Ett enkelt checkpoint-format överlever varje fancy återupptagningsmetod som lovade att skippa att spara optimizer-tillstånd. Spara bytarna; spara körningen.