Files
mlflow-practice/CODE_WALKTHROUGH.md
T
second_constantine d5c5bf3767 Recall везде + фиксы окружения
- recall_macro/recall_weighted во всех скриптах с метриками
  (train_simple, train_gpu, autolog_demo, hyperparam_sweep,
   grid_search_cv, load_and_predict, compare_runs)
- grid_search_cv: multi-metric scoring (accuracy + recall_macro)
- register_model: выбор лучшей модели по recall (--metric)
- MLflow warnings: name вместо artifact_path, сигнатура модели,
  numpy input_example для pytorch, подавление env-var INFO
- start_ui.sh: порт 5555 + file-store backend (фикс: UI не показывал
  эксперименты, т.к. скрипты писали в mlruns/, а сервер читал sqlite)
- setup_server.sh: развилка macOS/Linux + проверка MPS
- train_gpu: get_device CUDA→MPS→CPU, num_workers=0 на macOS
- AGENTS.md: гид для агента + саморегламент обновления
- README/CODE_WALKTHROUGH: выровнена нумерация уроков 8/9
- .gitignore: +.DS_Store
- удалён scripts/setup_git.sh
2026-07-20 22:28:23 +03:00

20 KiB
Raw Blame History

🔍 Экскурс по коду — разбор каждого файла

Полный разбор всех файлов проекта с пояснениями, что и почему.


Структура проекта

mlflow-practice/
├── MLproject              # Формат MLflow Projects (воспроизводимость)
├── python_env.yaml        # Окружение для MLproject
├── requirements.txt        # Зависимости
├── setup_server.sh        # Установка на GPU-сервере
├── start_ui.sh            # Запуск MLflow Tracking Server
├── scripts/
│   └── setup_git.sh       # Настройка Git (HTTPS + токен)
└── src/
    ├── train_simple.py     # Урок 1: sklearn + Tracking (ручное логирование)
    ├── train_gpu.py        # Урок 2: PyTorch CNN на GPU + логирование по эпохам
    ├── register_model.py   # Урок 3: Model Registry (версии, стадии)
    ├── load_and_predict.py # Урок 4: загрузка модели + предсказание
    ├── compare_runs.py     # Урок 5: сравнение запусков через API
    ├── autolog_demo.py     # Урок 6: autolog (автоматическое логирование)
    ├── hyperparam_sweep.py # Урок 7: перебор гиперпараметров (вручную)
    ├── serve_model.py      # Урок 8: REST API сервер модели
    └── grid_search_cv.py   # Урок 9: GridSearchCV + autolog

📄 train_simple.py — основы MLflow Tracking

Это главный файл для понимания. Здесь показаны все базовые операции MLflow.

Импорты

import mlflow
import mlflow.sklearn

mlflow — ядро (start_run, log_param, log_metric, log_artifact). mlflow.sklearn — flavor для scikit-learn моделей (save_model, load_model).

Задание эксперимента

mlflow.set_experiment("digits_classification")

Эксперимент — это контейнер для связанных запусков (runs). Если эксперимент не существует — MLflow создаст его автоматически. Все runs с одинаковым именем эксперимента группируются вместе в UI.

Контекстный менеджер run

with mlflow.start_run(run_name="rf_100_8") as run:

start_run() начинает новый запуск. Контекстный менеджер (with) гарантирует, что run закроется даже при ошибке — всё сохранится. run.info.run_id — уникальный ID запуска, через него можно к нему вернуться.

Логирование параметров

mlflow.log_param("n_estimators", args.n_estimators)
mlflow.log_param("max_depth", args.max_depth)

Параметры логируются ДО или ВО ВРЕМЯ обучения. Это то, что вы выбрали — гиперпараметры модели. Каждый параметр — строка/число. В UI параметры показываются в таблице запусков как колонки.

Логирование метрик

mlflow.log_metric("accuracy", acc)

Метрики — числа, которые оценивают качество. Главное отличие от параметров: метрики могут логироваться несколько раз с разными шагами (step=N), образуя график:

for i, tree in enumerate(model.estimators_):
    tree_acc = accuracy_score(y_test, tree.predict(X_test))
    mlflow.log_metric("per_tree_accuracy", tree_acc, step=i)

В UI это покажется как линейный график — как accuracy растёт по мере добавления деревьев в RandomForest.

Логирование артефактов

mlflow.log_artifact("artifacts/confusion_matrix.png")

Артефакт — любой файл: картинка, текст, JSON, модель. MLflow копирует файл в хранилище артефактов (папка ./artifacts/ или S3). В UI артефакты можно просматривать прямо в браузере.

Логирование модели

mlflow.sklearn.log_model(model, artifact_path="model")

Модель — это особый артефакт. MLflow сохраняет её в стандартном формате с файлом MLmodel (метаданные: flavor, версия, сигнатура). Позже её можно загрузить по run_id или зарегистрировать в Registry.

Теги

mlflow.set_tag("model_type", "RandomForest")
mlflow.set_tag("author", "practice")

Теги — произвольные метки для фильтрации и поиска. В отличие от параметров, теги можно менять после завершения run.


📄 train_gpu.py — PyTorch + GPU + логирование по эпохам

Определение устройства

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

Код GPU-aware: если есть CUDA — обучение на 3090, иначе на CPU. Всё переносится на устройство: model.to(device), data.to(device).

Логирование параметров окружения

mlflow.log_param("device", str(device))
mlflow.log_param("gpu_name", gpu_name)

Полезно: в UI видно, на каком GPU обучалась модель. Если запускаете на разных машинах — сразу видно различия.

Логирование метрик по эпохам

mlflow.log_metric("train_loss", avg_loss, step=epoch)
mlflow.log_metric("test_accuracy", acc, step=epoch)

step=epoch — ключевой момент. Каждая метрика логируется с номером шага. В UI это строит график learning curve — как loss падает, а accuracy растёт от эпохи к эпохе.

Сохранение графика как артефакта

fig.savefig("artifacts/training_curve.png")
mlflow.log_artifact(plot_path)

Matplotlib-график сохраняется в файл и логируется. В UI его можно открыть прямо в браузере — артефакты отображаются превью.

Логирование PyTorch-модели

mlflow.pytorch.log_model(
    model,
    artifact_path="model",
    serialization_format="pickle",
    input_example=torch.randn(1, 1, 28, 28).to(device),
)

serialization_format="pickle" — формат сохранения (см. урок 2). input_example — пример входа, нужен для построения сигнатуры модели. MLflow сохраняет модель в формате, совместимом с mlflow.pytorch.load_model().


📄 register_model.py — Model Registry

Поиск лучшего запуска

runs = client.search_runs(
    experiment_ids=[exp.experiment_id],
    order_by=["metrics.accuracy DESC"],
    max_results=1,
)

MlflowClient — программный доступ к API Tracking Server. search_runs с order_by — сортировка по метрике. Так можно найти лучший run без UI — полностью программно.

Регистрация модели

model_uri = f"runs:/{run_id}/model"
result = mlflow.register_model(model_uri=model_uri, name="digits_rf_model")

model_uri — указывает на модель внутри конкретного run. register_model создаёт новую версию в Model Registry. result.version — номер версии (1, 2, 3...).

Управление стадиями

client.transition_model_version_stage(
    name="digits_rf_model",
    version=result.version,
    stage="Production",
    archive_existing_versions=True,
)

archive_existing_versions=True — автоматически переводит все предыдущие Production-версии в Archived. Так в Production всегда только одна версия.

Жизненный цикл модели

None → Staging → Production → Archived
              ↑                  ↓
              └──────────────────┘
              (можно вернуть обратно)
  • Staging — тестирование, ещё не готова
  • Production — используется в приложении
  • Archived — устарела, но сохранена для истории

📄 load_and_predict.py — загрузка модели

Загрузка по имени и стадии

model = mlflow.sklearn.load_model("models:/digits_rf_model/Production")

models:/ — URI для Model Registry. Формат: models:/{name}/{stage} или models:/{name}/{version}. Загружает последнюю версию в стадии Production. Если версию перевели в Archived — эта загрузка сломается, что правильно — так вы узнаёте о проблеме сразу.

Отличие от загрузки по run_id

# По run_id (привязка к конкретному запуску):
model = mlflow.sklearn.load_model("runs:/abc123/model")

# По registry (привязка к имени + стадии):
model = mlflow.sklearn.load_model("models:/digits_rf_model/Production")

Registry — уровень абстракции выше. Код не меняется при смене версии модели, только стадия в UI.


📄 compare_runs.py — сравнение через API

runs = client.search_runs(
    experiment_ids=[exp.experiment_id],
    order_by=["metrics.accuracy DESC"],
    max_results=10,
)

Программный аналог таблицы в UI. Каждый run содержит:

  • run.info.run_id — ID
  • run.data.params — словарь параметров
  • run.data.metrics — словарь метрик
  • run.data.tags — теги

Это позволяет автоматизировать: найти лучший, сравнить, отсеять.


📄 autolog_demo.py — автоматическое логирование

Ключевая строка

mlflow.sklearn.autolog(
    log_models=True,
    log_datasets=True,
    max_tuning_runs=10,
)

После вызова autolog()больше не нужно писать log_param, log_metric, log_model вручную. MLflow перехватывает вызовы sklearn и логирует всё автоматически:

Что autolog перехватывает Что логируется
model.fit() все параметры конструктора модели
Обучение training_accuracy, training_log_loss
Модель сохраняется в artifacts/model/
Датасет профиль (размер, типы признаков)
GridSearchCV каждый под-вариант как отдельный run

Сравнение: ручное vs autolog

# Ручное (train_simple.py):
mlflow.log_param("n_estimators", 100)
mlflow.log_param("max_depth", 8)
mlflow.log_metric("accuracy", acc)
mlflow.sklearn.log_model(model, "model")

# Autolog (autolog_demo.py):
mlflow.sklearn.autolog()  # одна строка!
model.fit(X_train, y_train)  # всё залогируется само

Когда использовать autolog: быстрые эксперименты, прототипы. Когда ручное логирование: нужен контроль над тем, что попадает в MLflow, кастомные метрики, специфичные артефакты.


📄 hyperparam_sweep.py — перебор гиперпараметров

Генерация комбинаций

all_combos = list(itertools.product(
    param_grid["n_estimators"],
    param_grid["max_depth"],
    ...
))

itertools.product — декартово произведение всех параметров. 5 × 6 × 3 × 3 × 3 = 810 возможных комбинаций. Берём случайные max_combos из них — для скорости.

Почему случайные, а не все 810?

Полный перебор (Grid Search) = 810 × 5 (CV) = 4050 обучений. На 3090 это реально, но:

  • Random Search часто находит хорошие параметры быстрее
  • MLflow хранит каждый run — 810 runs засоряют UI
  • 20-50 случайных обычно достаточно для практики

Кросс-валидация

cv_scores = cross_val_score(model, X_train, y_train, cv=5, scoring="accuracy")
mlflow.log_metric("cv_mean_accuracy", cv_scores.mean())
mlflow.log_metric("cv_std_accuracy", cv_scores.std())

CV делит train на 5 фолдов, обучает 5 раз, усредняет. cv_stdразброс между фолдами. Если std высокий — модель нестабильна (зависит от разбиения данных).

Что искать в UI

  1. Отсортировать по test_accuracy DESC
  2. Сравнить cv_mean_accuracy и test_accuracy — если CV сильно выше теста → переобучение (overfitting)
  3. Высокий cv_std_accuracy → нестабильная модель
  4. Scatter plot: n_estimators vs accuracy — видна ли зависимость

📄 serve_model.py — REST API

Загрузка модели при старте

model = mlflow.sklearn.load_model(f"models:/{model_name}/{stage}")

Загружается один раз при создании Flask-приложения, не на каждый запрос. Это важно для производительности.

Эндпоинты

Эндпоинт Метод Что делает
/health GET Проверка состояния ("жив" ли сервер)
/info GET Информация о модели (признаки, классы)
/predict POST Предсказание для переданных данных
/predict_random GET Случайный образец из digits + предсказание

Формат запроса /predict

{
  "instances": [
    [0, 0, 0, ..., 16, 8, 0, ...],  // 64 числа — 8×8 пикселей
    [0, 0, 12, ..., 0, 0, 0, ...]   // можно несколько образцов
  ]
}

predict_proba

probabilities = model.predict_proba(instances)

RandomForest поддерживает predict_proba — вероятности для каждого класса. В ответе: предсказание + вероятности + confidence.

Альтернатива — встроенный MLflow server

mlflow models serve -m models:/digits_rf_model/Production -p 5001

MLflow сам поднимает сервер с стандартным API. Кастомный скрипт (serve_model.py) даёт больше контроля — свои эндпоинты, логика, демо-эндпоинт с случайным образом.


📄 grid_search_cv.py — GridSearchCV + autolog

mlflow.sklearn.autolog(max_tuning_runs=20)
...
grid = GridSearchCV(estimator=..., param_grid=..., cv=5)
grid.fit(X_train, y_train)

MLflow autolog + GridSearchCV = дерево запусков:

  • Parent run (gridsearch_rf) — главный, содержит лучшие параметры
  • Child runs — каждая комбинация гиперпараметров отдельный run

В UI это видно как иерархия. Parent показывает итог, children — детали.


📄 MLproject — воспроизводимость

name: mlflow-practice
entry_points:
  main:
    parameters:
      n_estimators: {type: int, default: 100}
    command: "python src/train_simple.py --n-estimators {n_estimators}"

MLproject — декларативное описание проекта. MLflow может запустить его одной командой:

mlflow run . -P n_estimators=200 -P max_depth=12

При этом MLflow:

  1. Читает python_env.yaml → создаёт виртуальное окружение
  2. Устанавливает зависимости
  3. Запускает команду из entry_point с параметрами
  4. Логирует всё в tracking server

Это гарантирует воспроизводимость: любой человек на любой машине может запустить ваш эксперимент с тем же результатом.


🗂️ Как файлы связаны между собой

train_simple.py ──→ логирует run ──→ register_model.py ──→ load_and_predict.py
      │                                      │
      │                                      ↓
      │                              Model Registry
      │                                      │
      ↓                                      ↓
compare_runs.py                        serve_model.py
(ищет лучший run)                    (загружает из Registry)

autolog_demo.py ──→ autolog (альтернатива ручному логированию)

hyperparam_sweep.py ──→ много runs (перебор)
grid_search_cv.py ──→ parent + child runs (autolog + GridSearch)

MLproject ──→ оборачивает всё в воспроизводимый формат

💡 Ключевые концепции MLflow — шпаргалка

Концепция Что это Где используется
Experiment Группа связанных runs set_experiment()
Run Один запуск обучения start_run()
Param Гиперпараметр (вход) log_param()
Metric Метрика (результат), может быть графиком log_metric(step=N)
Artifact Любой файл (картинка, модель) log_artifact()
Tag Метка для поиска/фильтрации set_tag()
Flavor Формат модели (sklearn, pytorch) mlflow.sklearn, mlflow.pytorch
Model Registry Реестр версий моделей register_model(), transition_stage()
Autolog Автоматическое логирование mlflow.sklearn.autolog()
MLproject Воспроизводимый запуск mlflow run .
Model URI Ссылка на модель models:/name/Production, runs:/id/model