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

517 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🔍 Экскурс по коду — разбор каждого файла
Полный разбор всех файлов проекта с пояснениями, что и почему.
---
## Структура проекта
```
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.
### Импорты
```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` — видна ли зависимость
---
## 📄 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) даёт больше контроля — свои эндпоинты, логика,
демо-эндпоинт с случайным образом.
---
## 📄 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 — детали.
---
## 📄 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` |