Воспроизводимость: seed, окружение, lineage

«Запустил тот же код — получил другую модель» — нормально для ML и решается дисциплиной воспроизводимости.

Воспроизводимость — свойство ML-системы давать тот же результат при повторном запуске за счёт фиксации случайности, окружения, данных и кода.

Почему ML невоспроизводим по умолчанию

Обычный код детерминирован. ML — нет: инициализация весов, перемешивание данных, dropout, сэмплирование — всё опирается на генератор случайных чисел. Запустите обучение дважды без фиксации — получите две разные модели. А ещё результат зависит от версии библиотеки, версии данных и даже железа. Воспроизводимость нужна, чтобы отлаживать, сравнивать честно и доказывать происхождение модели (особенно в регулируемых отраслях).

Четыре оси воспроизводимости

  1. Случайность (seed). Фиксируем seed всех генераторов: random, numpy, фреймворк.
  2. Окружение. Фиксируем версии пакетов (lock-файл) и системные зависимости (Docker-образ).
  3. Данные. Фиксируем версию датасета (DVC-хеш).
  4. Код. Фиксируем git-коммит.

Фиксация seed

Один seed детерминирует всю цепочку случайных решений. Проверим на чистом Python, что без seed результат пляшет, а с seed — повторяется.

import random

def sample_run(seed=None):
    if seed is not None:
        random.seed(seed)
    return [random.randint(0, 99) for _ in range(5)]

print("Без seed (два запуска подряд могут отличаться):")
print(" ", sample_run())
print(" ", sample_run())

print("С seed=42 (всегда одно и то же):")
print(" ", sample_run(42))
print(" ", sample_run(42))

Вывод:

Без seed (два запуска подряд могут отличаться):
  [49, 97, 53, 5, 33]
  [65, 62, 51, 100, 38]
С seed=42 (всегда одно и то же):
  [81, 14, 3, 94, 35]
  [81, 14, 3, 94, 35]

Первые две строки на вашем запуске будут другими (случайны), но две последние с seed=42 — всегда одинаковы. В реальном обучении так же фиксируют seed numpy и фреймворка; иногда дополнительно включают детерминированные операции GPU (ценой скорости).

Фиксация окружения

Версии библиотек влияют на результат: новый minor может изменить дефолт алгоритма. Поэтому фиксируют точные версии в lock-файле и собирают Docker-образ.

scikit-learn==1.3.2
numpy==1.26.4
pandas==2.1.4

Lineage: происхождение артефактов

Lineage отвечает на вопрос «как именно получился этот артефакт». Для прод-модели lineage — это цепочка: git-коммит кода → версия данных (DVC) → run эксперимента (параметры, seed) → окружение (образ) → версия в реестре. Если каждое звено зафиксировано, модель воспроизводима байт-в-байт, а инцидент можно расследовать до корня.

git sha + DVC hash + seed + образ  -->  run (MLflow)  -->  модель v7  -->  Production
        (что и из чего)                (как обучали)        (артефакт)     (где живёт)

Как работает под капотом

Воспроизводимость — это сведение всей случайности и неявных зависимостей к явным, зафиксированным входам. Seed убирает стохастику алгоритма; lock-файл и образ убирают «дрейф окружения»; DVC-хеш убирает изменчивость данных; git-коммит фиксирует код. Трекинг-система записывает все эти входы в run, образуя проверяемую цепочку lineage. Повторный прогон с теми же входами обязан дать тот же выход.

Частые ошибки

  • Фиксировать seed только одного генератора. Несколько источников случайности — фиксировать нужно все (random, numpy, фреймворк).
  • Не пинить версии пакетов. «Работало вчера» ломается после тихого обновления зависимости.
  • Хранить lineage в голове. Если связь код↔данные↔run не записана, воспроизвести модель спустя месяцы невозможно.

Итог

  • ML невоспроизводим по умолчанию из-за случайности, окружения, данных и кода — все четыре оси нужно фиксировать.
  • Seed детерминирует случайные решения; lock-файл и Docker фиксируют окружение; DVC и git — данные и код.
  • Lineage связывает прод-модель со всеми входами, делая её воспроизводимой и расследуемой.
Проверьте себя
1. Почему ML-обучение невоспроизводимо по умолчанию?
AИз-за багов в Python
BОно опирается на случайность: инициализацию весов, перемешивание, dropout
CПотому что данные всегда меняются на лету
DИз-за медленного железа
2. Что фиксирует lock-файл версий пакетов?
ASeed генератора
BТочные версии библиотек, чтобы окружение не дрейфовало
CВерсию данных
DСтадию модели
3. Что описывает lineage модели?
AТолько её точность
BЦепочку происхождения: код, данные, run, окружение и версию в реестре
CРазмер артефакта
DИмя автора