Уроки 6-9: autolog, hyperparam sweep, grid search, serving + MLproject, walkthrough

This commit is contained in:
2026-07-20 15:01:31 +03:00
parent 2c07fd63c0
commit d6a4b46db0
14 changed files with 1248 additions and 95 deletions
+516
View File
@@ -0,0 +1,516 @@
# 🔍 Экскурс по коду — разбор каждого файла
Полный разбор всех файлов проекта с пояснениями, что и почему.
---
## Структура проекта
```
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: перебор гиперпараметров (вручную)
├── grid_search_cv.py # Урок 8: GridSearchCV + autolog
└── serve_model.py # Урок 9: REST API сервер модели
```
---
## 📄 train_simple.py — основы MLflow Tracking
Это **главный файл для понимания**. Здесь показаны все базовые операции MLflow.
### Импорты
```python
import mlflow
import mlflow.sklearn
```
`mlflow` — ядро (start_run, log_param, log_metric, log_artifact).
`mlflow.sklearn` — flavor для scikit-learn моделей (save_model, load_model).
### Задание эксперимента
```python
mlflow.set_experiment("digits_classification")
```
Эксперимент — это **контейнер для связанных запусков (runs)**.
Если эксперимент не существует — MLflow создаст его автоматически.
Все runs с одинаковым именем эксперимента группируются вместе в UI.
### Контекстный менеджер run
```python
with mlflow.start_run(run_name="rf_100_8") as run:
```
`start_run()` начинает новый запуск. Контекстный менеджер (`with`)
гарантирует, что run закроется даже при ошибке — всё сохранится.
`run.info.run_id` — уникальный ID запуска, через него можно к нему вернуться.
### Логирование параметров
```python
mlflow.log_param("n_estimators", args.n_estimators)
mlflow.log_param("max_depth", args.max_depth)
```
Параметры логируются **ДО или ВО ВРЕМЯ обучения**. Это то, что вы
выбрали — гиперпараметры модели. Каждый параметр — строка/число.
В UI параметры показываются в таблице запусков как колонки.
### Логирование метрик
```python
mlflow.log_metric("accuracy", acc)
```
Метрики — числа, которые оценивают качество. Главное отличие от
параметров: метрики могут логироваться **несколько раз** с разными
шагами (`step=N`), образуя график:
```python
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.
### Логирование артефактов
```python
mlflow.log_artifact("artifacts/confusion_matrix.png")
```
Артефакт — **любой файл**: картинка, текст, JSON, модель. MLflow
копирует файл в хранилище артефактов (папка `./artifacts/` или S3).
В UI артефакты можно просматривать прямо в браузере.
### Логирование модели
```python
mlflow.sklearn.log_model(model, artifact_path="model")
```
Модель — это особый артефакт. MLflow сохраняет её в стандартном
формате с файлом `MLmodel` (метаданные: flavor, версия, сигнатура).
Позже её можно загрузить по `run_id` или зарегистрировать в Registry.
### Теги
```python
mlflow.set_tag("model_type", "RandomForest")
mlflow.set_tag("author", "practice")
```
Теги — произвольные метки для фильтрации и поиска. В отличие от
параметров, теги можно менять после завершения run.
---
## 📄 train_gpu.py — PyTorch + GPU + логирование по эпохам
### Определение устройства
```python
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
```
Код **GPU-aware**: если есть CUDA — обучение на 3090, иначе на CPU.
Всё переносится на устройство: `model.to(device)`, `data.to(device)`.
### Логирование параметров окружения
```python
mlflow.log_param("device", str(device))
mlflow.log_param("gpu_name", gpu_name)
```
Полезно: в UI видно, на каком GPU обучалась модель. Если запускаете
на разных машинах — сразу видно различия.
### Логирование метрик по эпохам
```python
mlflow.log_metric("train_loss", avg_loss, step=epoch)
mlflow.log_metric("test_accuracy", acc, step=epoch)
```
`step=epoch` — ключевой момент. Каждая метрика логируется с номером
шага. В UI это строит **график learning curve** — как loss падает, а
accuracy растёт от эпохи к эпохе.
### Сохранение графика как артефакта
```python
fig.savefig("artifacts/training_curve.png")
mlflow.log_artifact(plot_path)
```
Matplotlib-график сохраняется в файл и логируется. В UI его можно
открыть прямо в браузере — артефакты отображаются превью.
### Логирование PyTorch-модели
```python
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
### Поиск лучшего запуска
```python
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 — полностью программно.
### Регистрация модели
```python
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...).
### Управление стадиями
```python
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 — загрузка модели
### Загрузка по имени и стадии
```python
model = mlflow.sklearn.load_model("models:/digits_rf_model/Production")
```
`models:/` — URI для Model Registry. Формат: `models:/{name}/{stage}`
или `models:/{name}/{version}`. Загружает последнюю версию в стадии
Production. Если версию перевели в Archived — эта загрузка сломается,
что правильно — так вы узнаёте о проблеме сразу.
### Отличие от загрузки по run_id
```python
# По 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
```python
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 — автоматическое логирование
### Ключевая строка
```python
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
```python
# Ручное (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 — перебор гиперпараметров
### Генерация комбинаций
```python
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 случайных обычно достаточно для практики
### Кросс-валидация
```python
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` — видна ли зависимость
---
## 📄 grid_search_cv.py — GridSearchCV + autolog
```python
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 — детали.
---
## 📄 serve_model.py — REST API
### Загрузка модели при старте
```python
model = mlflow.sklearn.load_model(f"models:/{model_name}/{stage}")
```
Загружается **один раз** при создании Flask-приложения, не на каждый
запрос. Это важно для производительности.
### Эндпоинты
| Эндпоинт | Метод | Что делает |
|---|---|---|
| `/health` | GET | Проверка состояния ("жив" ли сервер) |
| `/info` | GET | Информация о модели (признаки, классы) |
| `/predict` | POST | Предсказание для переданных данных |
| `/predict_random` | GET | Случайный образец из digits + предсказание |
### Формат запроса /predict
```json
{
"instances": [
[0, 0, 0, ..., 16, 8, 0, ...], // 64 числа — 8×8 пикселей
[0, 0, 12, ..., 0, 0, 0, ...] // можно несколько образцов
]
}
```
### predict_proba
```python
probabilities = model.predict_proba(instances)
```
RandomForest поддерживает `predict_proba` — вероятности для каждого
класса. В ответе: предсказание + вероятности + confidence.
### Альтернатива — встроенный MLflow server
```bash
mlflow models serve -m models:/digits_rf_model/Production -p 5001
```
MLflow сам поднимает сервер с стандартным API. Кастомный скрипт
(serve_model.py) даёт больше контроля — свои эндпоинты, логика,
демо-эндпоинт с случайным образом.
---
## 📄 MLproject — воспроизводимость
```yaml
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 может запустить
его одной командой:
```bash
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` |