- 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
20 KiB
🔍 Экскурс по коду — разбор каждого файла
Полный разбор всех файлов проекта с пояснениями, что и почему.
Структура проекта
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— IDrun.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
- Отсортировать по
test_accuracyDESC - Сравнить
cv_mean_accuracyиtest_accuracy— если CV сильно выше теста → переобучение (overfitting) - Высокий
cv_std_accuracy→ нестабильная модель - Scatter plot:
n_estimatorsvsaccuracy— видна ли зависимость
📄 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:
- Читает
python_env.yaml→ создаёт виртуальное окружение - Устанавливает зависимости
- Запускает команду из entry_point с параметрами
- Логирует всё в 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 |