📦 Артефакты моделей: хранение, бэкап и деплой
О чём это
Обученные модели — приватная половина open-core, поэтому они не лежат ни в одном публичном репозитории и не вшиты в публичный образ. Здесь: инвентарь (где что реально находится), разбор вариантов бэкапа с ценой каждого, и runbook деплоя модели в analytics.
Связанные: ml-lab-journal · roadmap-value-model · dicechess-analytics-on-aurora-deploy-and-sizing · northflank-onnx-bots-runbook · dexus-kcp-one-ply-bot-runbook · win-probability-for-sparse-positions
1. Инвентарь на 09.08.2026 — честная картина
| Артефакт | Где физически | Продублировано? |
|---|---|---|
models/*.onnx, *.txt в dicechess-ev | только MacBook (+ iCloud с 09.08) | было нигде |
| веса задеплоенных ботов | внутри приватного GHCR-образа house-bots и приватного GAR-образа Northflank ONNX-ботов | да, образы в реестрах |
production opening_book.tsv для Northflank | внутри приватного GAR-образа, 147 строк | да, образ в реестре; в публичном repo только 5-строчный sample |
датасеты training_data_*.csv.gz (0,7 ГБ) | только MacBook | нет |
| исходная БД (из которой всё выводится) | aurora + ночные бэкапы | да |
Обнаружено при подготовке деплоя
models/стоит в.gitignoreрепозитория ev (строка 10), поэтому в git не лежит ни одна модель — включая июльскуюdf_rich.onnx, от которой зависит задеплоенный oracle-3. До 09.08 весь набор существовал в единственном экземпляре на ноутбуке. Копия в iCloud закрывает самый острый риск (потеря железа), но не решает две другие проблемы — см. ниже.
2. Почему копии файла недостаточно
Файл — наименее ценная часть артефакта. Модель без происхождения нельзя ни проверить, ни воспроизвести, ни осмысленно сравнить. Реальный пример из этой же работы: чтобы понять, чем является df_rich.onnx, пришлось восстанавливать это по датам файлов и старым логам — какой корпус, какие флаги, какой learning rate. Ответ «дистиллированная rich-9 на ~1,3 млн строк» был выведен, а не прочитан.
Переобучить «то же самое» нельзя. Кажется, что модель всегда можно получить заново: БД бэкапится, команды записаны. Но экспорт зависит от момента — БД растёт непрерывно, и завтрашний экспорт даст другой датасет, а значит другую модель. Рецепт воспроизводит похожую модель, а не ту же. Поэтому артефакт нужно хранить, а не только рецепт.
Отсюда требование: рядом с каждым файлом должен лежать манифест — датасет (имя + число строк), команда обучения целиком, метрики на холдауте, дата, SHA коммита ev. Тогда через полгода вопрос «а это что за модель и можно ли ей верить» имеет ответ, а не расследование.
3. Варианты хранения
| Вариант | Версионирование | Провенанс | Достать на сервер | Цена/минусы |
|---|---|---|---|---|
| iCloud / Dropbox (сейчас) | нет | нет | руками через ноутбук | нулевая цена, но это backstop, а не система учёта |
Коммит в приватный ev (снять models/ из .gitignore) | git-история | коммит + манифест рядом | git clone / gh | бинарники в истории; при 1–2 МБ и нескольких моделях в месяц — годы до проблемы |
| GitHub Releases приватного ev | теги релизов | тело релиза | gh release download | не раздувает историю; чуть больше ручных шагов |
| Приватный GHCR-пакет | теги образа | лейблы образа | docker pull | прецедент есть (house-bots); тяжеловесно для итераций |
| Объектное хранилище (R2/S3) | вручную | вручную | curl с ключом | ещё одни креды и ещё один сервис |
4. Рекомендация
Система учёта — приватный ev; iCloud остаётся дополнительным слоем.
- Снять
models/из.gitignoreв ev (репозиторий приватный, файлы 1–2 МБ). Модель попадает в git вместе с манифестом — версионирование, провенанс и бэкап получаются одним действием. - Рядом с каждой моделью —
<model>.json: датасет и число строк, полная команда обучения, метрики на общем холдауте, дата, SHA ev. Заполняется в момент обучения, а не потом по памяти. - Датасеты не хранить — они выводятся из БД, а БД бэкапится. Хранить только команду экспорта и число строк, чтобы отличать корпуса друг от друга.
- Для доставки на сервер, где
git cloneнеуместен, —gh release downloadиз того же приватного репо.
Чего сознательно не делаем: не кладём веса в публичный analytics и не вшиваем их в публичный образ — это прямо нарушило бы open-core (см. win-probability-for-sparse-positions).
5. Runbook: деплой модели в analytics
Порядок обязателен
Модель кладётся после релиза образа: до него в образе просто нет кода, который читает
EV_MODEL_PATH. Релиз и прод-деплой — операции владельца.
Предусловия: analytics#273 смёржен и выпущен релизом (образ с нужным тегом опубликован).
Шаг 1 — положить модель на хост. API живёт на aurora в ~/dicechess-analytics (compose + .env, порт 8020):
mkdir -p ~/dicechess-analytics/models
scp models/kcp_nodice_serving_20260809.onnx aurora:~/dicechess-analytics/models/Шаг 2 — смонтировать в контейнер. В docker-compose.yaml, сервис api:
volumes:
- ./models:/models:roШаг 3 — указать путь. В .env:
EV_MODEL_PATH=/models/kcp_nodice_serving_20260809.onnxШаг 4 — поднять и проверить:
docker compose up -d api
curl -s 'http://localhost:8020/api/positions/win-probability?fen=rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR%20w%20KQkq%20-'Ожидаемо: win_probability около 0.5375 для стартовой позиции. Ориентир для сверки — эмпирика по 1,6 млн партий даёт 53.41%, то есть расхождение около 0,3 пп. Если пришло 503 — переменная не подхватилась; если контейнер не стартует — см. следующий пункт.
Брать модель на 13 фич, а не на 19
kcp_nodice_serving_*.onnx— 13 входов, это pre-roll модель, которую обслуживает эндпоинт.kcpdice_serving_*.onnx— 19 входов, roll-aware, для будущего per-roll эндпоинта. Перепутать не страшно: загрузчик сверяет ширину входа и отказывается стартовать, объясняя, какая раскладка ожидалась. Сделано намеренно — раскладки различаются только шириной, и скоринг «не своей» моделью не падает, а тихо возвращает правдоподобные неверные числа.
Откат: убрать EV_MODEL_PATH из .env и перезапустить. Эндпоинт вернётся к 503, остальное API не затронуто — это штатное состояние публичного образа без приватных весов.
6. Что осталось решить
- Снять
models/из.gitignoreв ev и завести манифесты (решение владельца — игнор стоял осознанно). - Добавить монтирование
./models:/models:roвdocker-compose.yamlanalytics отдельным PR. Безопасно для всех: безEV_MODEL_PATHэндпоинт отвечает 503, пустая папка ничему не мешает. - Задним числом восстановить провенанс июльских моделей (
df_rich,kcp_nodice) — пока он выведен из дат и логов, а не записан.