Шестнадцать дней вычислений на одной GPU
Один длинный запуск
ANDREA-120M занимает ~23 дня на RTX 4090 (FP16, 6 шагов/мин, 200K шагов). Потребление энергии, паники ядра, сбои прокси & преднамеренные изменения конфигурации происходят в этом окне. Без контрольных точек один сбой выбрасывает весь запуск.
v1 потеряла 27K шагов из-за одной ошибки (lr=0.001 слишком агрессивный), потому что контрольной точки ближе точки запуска не было. v2 усвоила урок: частота контрольных точек снижена до каждые 100 шагов, & обработчик сигнала CUDA гарантирует запись контрольной точки при SIGTERM.
Три роли
Контрольная точка выполняет три задачи одновременно:
1. Точка восстановления. Процесс умирает, машина перезагружается, паника ядра: возобновить с последней step_NNNNNN.bin.
2. Отшлифованный поворот. Шаг 112,619: изменить учебный план без переобучения. SIGUSR1 принудительно создаёт чистую контрольную точку, прокси останавливается, новые пределы отправляются, CUDA возобновляет с сохранённой точки под новой политикой.
3. Ветвление для аудита. Сравнить две конфигурации с одних и тех же стартовых весов: скопировать контрольную точку, запустить две расходящиеся ветви вперёд, наблюдать, какая сходится.
Каждые 100 шагов дают ~17 минут обучения между записями при 6 шагах/мин. 100 шагов также соответствуют sample_every: каждый чекпоинт соответствует одному новому аудиту сэмпла, & каждый аудит сэмпла соответствует точке восстановления.
Три роли для одного файла
Пять регионов в одном файле
Формат
Каждый step_NNNNNN.bin содержит пять непрерывных регионов:
[int32 step] [int32 n_params] [n_params x float32 weights] [n_params x float32 m] [n_params x float32 v]
По регионам
Заголовок (всего 8 байт). 32-битный номер шага указывает, где в обучении находится этот снимок; 32-битное количество параметров указывает, насколько велики три последующих массива.
Веса (n_params x 4 байта). Каждый обучаемый параметр, выровненный. Порядок соответствует итератору параметров модели: вложения токенов и позиций, затем веса внимания и MLP для каждого слоя, затем голова вывода.
Первое момент Адама m (n_params x 4 байта). EMA прошлых градиентов (beta1 = 0.9). Такая же форма, как у весов. Требуется для возобновления AdamW.
Второй момент Adam v (n_params x 4 байта). EMA прошлых квадратов градиентов (beta2 = 0.999). Такая же форма, как у весов. Требуется для возобновления AdamW.
Общий размер
Общее количество байт = 8 + 12 x n_params. ANDREA-12M (12.8M параметров): 154 МБ на диске (147 МБ округлено). ANDREA-120M (~120M параметров) FP32: ~1.44 ГБ. Три массива одинаковой формы, расположенные подряд, с маленьким заголовком.
Почему сохранять m & v
Обычный Adam отслеживает скорость обучения для каждого параметра через m & v. Если их сбросить при записи чекпоинта, возобновленный запуск начнется с нулевого импульса и нулевой оценки дисперсии, что эквивалентно скорости обучения 0 на один шаг, а затем резкому нарастанию. Функция потерь резко возрастает; модель может выпасть из текущей впадины. Сохранение m & v делает возобновление битово-эквивалентным (за исключением случайности даталоадера) непрерывному базовому сценарию.
Размер одного чекпоинта
SIGTERM и SIGUSR1
Почему CUDA обрабатывает сигналы
Обучение работает как долгоживущий процесс в переднем плане. Когда прокси или оператор хочет остановить GPU, сигнал отправляется в CUDA-движок. Без обработчика стандартный SIGTERM немедленно убивает процесс: вычисления градиента в полёте отбрасываются, последние веса с момента последнего чекпоинта потеряны. С обработчиком движок сначала записывает чекпоинт, а затем выходит чисто.
SIGTERM: запись и выход
Отправляется кнопкой остановки, systemctl stop или kill от родительского прокси. CUDA завершает текущий шаг, записывает step_NNNNNN.bin на диск, затем выходит. Восстановление из этого состояния требует только последний .bin: потеряна только частичная работа шага в полёте.
SIGUSR1: запись и продолжение
Отправляется по требованию оператором или скриптом прокси. CUDA завершает текущий шаг, записывает step_NNNNNN.bin, затем продолжает обучение, как будто ничего не произошло. Полезно для: запуска точки аудита прямо перед изменением конфигурации; захвата весов в известный хороший момент; выравнивания чекпоинта с внешним запуском оценки качества образцов.
Польская последовательность поворота (шаг 112,619)
1. Оператор отправляет SIGUSR1 в CUDA. Чекпоинт записывается на следующей границе 100 шагов (шаг 112,700).
2. Оператор останавливает прокси.
3. .samples.json & .state.json архивируются (лог образцов и состояние бандита сохраняются как историческая запись).
4. .loss.json остается на месте. Кумулятивная история обучения; никогда не архивируется.
5. Прокси перезапускается с новыми пределами caps & floors.
6. CUDA возобновляет с step_112700.bin с новым бандитом, но полными весами, m и v.
История потерь продолжается непрерывно через поворот. Пример лога сбрасывается чисто. Бандит получает свежий старт под новой политикой.
Выбор сигнала
Кумулятивная история обучения
Три боковых файла
Рядом с каждым чекпоинтом прокси поддерживает три JSON-боковых файла в директории запуска:
- .loss.json — одна запись на шаг, всегда. ~200 000 записей к концу запуска. Кумулятивная история обучения.
- .samples.json — недавние сгенерированные образцы для аудита. Сброс при поворотах полировки.
- .state.json — вытягивания рычагов бандита, EMA-награды, счетчики фаз. Сброс при поворотах полировки.
Что сбрасывается, что сохраняется
Польские повороты — это изменения политики, а не сбросы запуска. Веса модели, m, v и история потерь продолжают оставаться непрерывными. Накопленные награды бандита НЕ продолжаются: новые потолки и полы определяют другую политику, и бандит должен заново обучаться под новой политикой с чистого состояния.
Почему .loss.json остаётся
История потерь служит аудиторской тропой запуска. Каждое опубликованное утверждение об ANDREA-120M (EMA потерь на шаге 110K, восстановление после польского поворота, сходимость на шаге 112K) восходит к записям в этом файле. Архивирование .loss.json между фазами заставило бы читателей сшивать фрагменты для реконструкции запуска; хранение его только для добавления и без изменений сохраняет происхождение.
Урок о зомби-руке
Шаг 112,619 обнаружил руку repo-docstrings в .state.json с весом 1.546 из предыдущего запуска. Состояние бандита было сохранено через более ранний перезапуск, но источник данных больше не был доступен, что приводило к зомби-вытягиваниям, искажающим учёт исследования. Урок: состояние бандита МОЖЕТ дрейфовать через перезапуски неожиданными способами. История потерь — единственный файл, который должен оставаться нетронутым на протяжении всей жизни запуска.
Одно Правило, Чтобы Править Ими Всеми
Архивируйте .samples.json и .state.json свободно между фазами. Никогда не архивируйте .loss.json. Последний .loss.json всегда является канонической историей обучения.
Применение Правила
Что было создано и почему
Пять решений
1. Частота: каждые 100 шагов. Гранулярность точки восстановления ~17 минут. Согласована с sample_every, так что каждый чекпоинт соответствует одному свежему аудиту выборки.
2. Формат: заголовок + 3 массива. Минимальный: 8-байтовый заголовок указывает размер каждого последующего массива. Без избыточных метаданных. Возобновление бит-в-бит при сохранении m и v.
3. Сигналы: SIGTERM и SIGUSR1. Две роли, два сигнала. Стандартное выключение systemd получает чистый чекпоинт через SIGTERM; точки аудита по требованию получают чистый чекпоинт через SIGUSR1 без остановки.
4. Непрерывность лосса: никогда не архивируется. Кумулятивная история обучения сохраняется при поворотах полировки, перезапусках и изменениях политики. Один след аудита для всего запуска.
5. Состояние бандита: сбросы разрешены. Политика бандита существует под одной конфигурацией за раз. Повороты полировки сбрасывают; история лосса продолжается. Две разные жизненные фазы делят одну директорию запуска.
Что связывает этот урок
- Activity 23 (grow_a_language_model_sample_audit). sample_every совпадает с частотой чекпоинтов; оба срабатывают каждые 100 шагов.
- Activity 24 (grow_a_language_model_microgpt_to_andrea). v1 collapse, v2.5 patch, v3 polish pivot все требуют чистых чекпоинтов для работы.
- Activity 10 (grow_a_language_model_adamw). Сохранение m & v в чекпоинте важно, потому что правило обновления AdamW зависит от обоих. Удалите их & возобновление разойдется.
Последняя инженерная истина
Код переживает авторов. Инфраструктура переживает строителей. Простой формат чекпоинта переживает все хитрые схемы возобновления, которые обещали пропустить сохранение состояния оптимизатора. Сохраняйте байты; сохраняйте запуск.