Seize jours de calcul sur un seul GPU
Une seule longue exécution
ANDREA-120M prend ~23 jours sur un RTX 4090 (FP16, 6 pas/min, 200K pas). Consommation électrique, paniques du noyau, plantages du proxy, & changements de configuration délibérés se produisent tous dans cette fenêtre. Sans points de contrôle, un seul hoquet jette toute l'exécution.
v1 a perdu 27K pas à cause d'une erreur (lr=0.001 trop agressif) car aucun point de contrôle n'était plus proche que le point de lancement. v2 a intégré cette leçon : cadence des points de contrôle réduite à tous les 100 pas, & un gestionnaire de signaux CUDA garantit l'écriture d'un point de contrôle sur SIGTERM.
Trois rôles
Un checkpoint remplit trois fonctions à la fois :
1. Point de récupération. Le processus meurt, la machine redémarre, panic du noyau : reprise à partir du dernier step_NNNNNN.bin.
2. Pivot poli. Étape 112 619 : changer le curriculum sans réentraînement. SIGUSR1 force un checkpoint propre, le proxy s’arrête, de nouveaux caps & floors sont soumis, CUDA reprend à partir du point sauvegardé sous une nouvelle politique.
3. Fork d’audit. Comparer deux configs aux mêmes poids de départ : copier un checkpoint, exécuter deux branches divergentes vers l’avant, observer laquelle converge.
Chaque 100 étapes donne ~17 minutes d'entraînement entre les écritures à 6 étapes/min. 100 étapes correspondent aussi à sample_every : chaque point de contrôle correspond à un audit d'échantillon frais, & chaque audit d'échantillon correspond à un point de récupération.
Trois Rôles pour Un Fichier
Cinq Régions dans Un Fichier
Le Format
Chaque step_NNNNNN.bin contient cinq régions contiguës :
[int32 step] [int32 n_params] [n_params x float32 weights] [n_params x float32 m] [n_params x float32 v]
Région par région
En-tête (8 octets au total). Un nombre d'étape de 32 bits nous indique où se trouve ce snapshot dans l'entraînement ; un compteur de paramètres de 32 bits nous indique la taille de chacun des trois tableaux suivants.
Poids (n_params x 4 octets). Chaque paramètre appris, aplati. L'ordre correspond à l'itérateur de paramètres du modèle : embeddings de tokens & positions, puis poids d'attention & MLP par couche, puis la tête de sortie.
Premier moment Adam m (n_params x 4 octets). Moyenne exponentielle mobile des gradients passés (beta1 = 0.9). Même forme que les poids. Requis pour la reprise d'AdamW.
Deuxième moment Adam v (n_params x 4 bytes). EMA des gradients carrés passés (beta2 = 0.999). Même forme que les poids. Requis pour la reprise AdamW.
Taille Totale
Octets totaux = 8 + 12 x n_params. ANDREA-12M (12,8M params) : 154 MB sur disque (147 MB arrondi). ANDREA-120M (~120M params) FP32 : ~1,44 GB. Trois tableaux de forme identique, empilés l'un après l'autre, avec un petit en-tête.
Pourquoi Sauvegarder m & v
Adam vanilla suit les taux d'apprentissage par paramètre via m & v. Les supprimer à l'écriture du checkpoint & une reprise commence avec zéro momentum & zéro estimation de variance, équivalent à un taux d'apprentissage 0 pour un pas puis une rampe soudaine. Les pertes explosent ; le modèle peut sortir de son bassin actuel. Sauvegarder m & v rend la reprise bit-équivalente (modulo aléatoire du dataloader) à la base never-stopped.
Dimensionnement d'un Checkpoint
SIGTERM & SIGUSR1
Pourquoi CUDA Gère les Signaux
L'entraînement s'exécute en tant que processus foreground de longue durée. Lorsque le proxy ou l'opérateur veut arrêter le GPU, un signal est envoyé au moteur CUDA. Sans gestionnaire, un SIGTERM par défaut tue le processus immédiatement : calcul de gradient en cours abandonné, derniers poids depuis le dernier point de contrôle perdus. Avec un gestionnaire, le moteur écrit d'abord un point de contrôle puis sort proprement.
SIGTERM : écriture & sortie
Envoyé par un bouton d'arrêt, un systemctl stop, ou un kill depuis un proxy parent. CUDA termine l'étape en cours, écrit step_NNNNNN.bin sur disque, puis sort. La récupération depuis cet état ne nécessite que le dernier .bin : zéro travail perdu au-delà de l'étape partielle en cours.
SIGUSR1 : écriture & continuation
Envoyé à la demande par un opérateur ou un script proxy. CUDA termine l'étape en cours, écrit step_NNNNNN.bin, puis continue l'entraînement comme si de rien n'était. Utile pour : déclencher un point d'audit juste avant un changement de configuration ; capturer les poids à un moment connu comme bon ; aligner un point de contrôle avec une exécution de notation de qualité d'échantillons externe.
La Séquence de Pivot Polonais (étape 112,619)
1. L'opérateur envoie SIGUSR1 à CUDA. Le checkpoint s'écrit à la prochaine frontière de 100 étapes (étape 112,700).
2. L'opérateur arrête le proxy.
3. .samples.json & .state.json sont archivés (journal d'échantillons & état du bandit préservés comme enregistrement historique).
4. .loss.json reste en place. Historique d'entraînement cumulé ; jamais archivé.
5. Le proxy redémarre avec de nouveaux plafonds & planchers.
6. CUDA reprend à partir de step_112700.bin avec un bandit frais mais des poids complets, m, & v.
L'historique des pertes se poursuit sans interruption à travers le pivot. La journalisation d'échantillon se réinitialise proprement. Bandit obtient un nouveau départ sous la nouvelle politique.
Choisir le Signal
Historique Cumulatif de l'Entraînement
Trois fichiers satellites
Aux côtés de chaque point de contrôle, le proxy maintient trois fichiers satellites JSON dans le répertoire d'exécution :
- .loss.json -- une entrée par étape, toujours. ~200 000 entrées à la fin de l'exécution. Historique cumulatif de l'entraînement.
- .samples.json -- échantillons générés récents pour audit. Réinitialisé lors des pivots de polissage.
- .state.json -- tirages de bras bandit, récompenses EMA, compteurs de phases. Réinitialisé lors des pivots de polissage.
Qu'est-ce qui se réinitialise, qu'est-ce qui persiste
Les pivots polonais sont des changements de politique, pas des réinitialisations de run. Les poids du modèle, m, v, & l'historique des pertes continuent tous sans interruption. Les récompenses accumulées du bandit NE continuent PAS : les nouveaux plafonds & planchers définissent une politique différente, & le bandit doit réapprendre sous la nouvelle politique à partir d'un état vierge.
Pourquoi .loss.json Reste
L'historique des pertes sert de piste d'audit pour le run. Toute affirmation publiée sur ANDREA-120M (EMA de perte à l'étape 110K, récupération après pivot polonais, convergence à l'étape 112K) remonte aux entrées de ce fichier. Archiver .loss.json entre les phases forcerait les lecteurs à assembler des fragments pour reconstruire le run ; le garder en mode append-only & intact préserve la provenance.
La Leçon du Bras Zombie
L'étape 112,619 a trouvé un bras repo-docstrings dans .state.json portant un poids de 1.546 d'un run précédent. L'état du bandit avait été préservé lors d'un redémarrage antérieur mais la source de données n'était plus disponible, produisant des tirages zombies qui déformaient la comptabilité d'exploration. Leçon : l'état du bandit EST autorisé à dériver de manière surprenante lors des redémarrages. L'historique des pertes est le seul fichier qui doit rester intact pour toute la durée de vie du run.
Une Règle pour les Gouverner Toutes
Archivez .samples.json & .state.json librement entre les phases. N'archivez jamais .loss.json. Le dernier .loss.json est toujours l'historique d'entraînement canonique.
Appliquer la Règle
Ce qui a été construit et pourquoi
Cinq décisions
1. Cadence : toutes les 100 étapes. Granularité du point de récupération ~17 minutes. Aligné avec sample_every pour que chaque point de contrôle corresponde à un audit d'échantillon frais.
2. Format : en-tête + 3 tableaux. Minimal : en-tête de 8 octets indique la taille de chaque tableau suivant. Pas de gonflement de métadonnées. Reprise bit à bit quand m & v sont sauvegardés.
3. Signaux : SIGTERM & SIGUSR1. Deux rôles, deux signaux. L'arrêt par défaut de systemd obtient un point de contrôle propre via SIGTERM ; les points d'audit à la demande obtiennent un point de contrôle propre via SIGUSR1 sans s'arrêter.
4. Continuité de la perte : jamais archivée. L'historique d'entraînement cumulé persiste à travers les pivots de polissage, les redémarrages, & les changements de politique. Une seule piste d'audit pour toute l'exécution.
5. État bandit : réinitialisations autorisées. La politique bandit vit sous une config à la fois. Les pivots de polissage réinitialisent ; l'historique de perte continue. Deux durées de vie différentes partagent le même répertoire d'exécution.
À quoi cette leçon se connecte
- Activité 23 (grow_a_language_model_sample_audit). La cadence sample_every correspond à la cadence des checkpoints ; les deux se déclenchent tous les 100 pas.
- Activité 24 (grow_a_language_model_microgpt_to_andrea). L'effondrement v1, le correctif v2.5, le pivot de polissage v3 nécessitent tous des checkpoints propres pour fonctionner.
- Activité 10 (grow_a_language_model_adamw). Sauvegarder m & v dans le checkpoint est important car la règle de mise à jour d'AdamW dépend des deux. Les supprimer fait diverger la reprise.
Une dernière vérité d'ingénierie
Le code survit aux auteurs. L'infrastructure survit aux constructeurs. Un format de checkpoint simple survit à tous les schémas de reprise sophistiqués qui promettaient de sauter la sauvegarde de l'état de l'optimiseur. Économisez les octets ; économisez l'exécution.