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
This commit is contained in:
2026-07-20 22:28:23 +03:00
parent d6a4b46db0
commit d5c5bf3767
15 changed files with 547 additions and 257 deletions
+206
View File
@@ -0,0 +1,206 @@
# AGENTS.md — гид для агента по проекту mlflow-practice
> Этот файл — ориентир для любого AI-агента (и человека), который будет
> работать с проектом. Читай его **первым** при входе в репозиторий.
---
## 🎯 Назначение проекта
Учебный проект для изучения **MLflow** на практике: от логирования
экспериментов до Model Registry и serving моделей. Девять последовательных
уроков, каждый — отдельный скрипт в `src/`. Поддерживает **GPU (RTX 3090, CUDA)**
и **macOS (Intel CPU / Apple Silicon MPS)**.
Не production-система. Цель — показать полный цикл MLOps на маленьких
встроенных датасетах (digits, wine, MNIST).
---
## 📁 Карта проекта
```
mlflow-practice/
├── AGENTS.md # ← этот файл (поддерживай актуальным!)
├── README.md # пользовательская документация (как запускать)
├── CODE_WALKTHROUGH.md # подробный разбор каждого файла с пояснениями
├── MLproject # MLflow Projects: entry points main/gpu/sweep
├── python_env.yaml # окружение для MLproject (python 3.10)
├── requirements.txt # pip-зависимости
├── setup_server.sh # установка окружения (РАЗВИЛКА macOS/Linux)
├── start_ui.sh # запуск MLflow Tracking Server (UI на :5555, file-store)
├── .gitignore
├── scripts/
│ └── setup_git.sh # настройка Git (HTTPS + токен)
├── src/ # ← весь код уроков здесь
│ ├── train_simple.py # Урок 1: sklearn + Tracking (ручное логирование)
│ ├── train_gpu.py # Урок 2: PyTorch CNN, CUDA→MPS→CPU
│ ├── register_model.py # Урок 3: Model Registry (версии, стадии)
│ ├── load_and_predict.py # Урок 4: загрузка модели + предсказание
│ ├── compare_runs.py # Урок 5: сравнение запусков через API
│ ├── autolog_demo.py # Урок 6: mlflow.autolog()
│ ├── hyperparam_sweep.py # Урок 7: перебор гиперпараметров вручную
│ ├── serve_model.py # Урок 8: REST API сервер модели (Flask)
│ └── grid_search_cv.py # Урок 9: GridSearchCV + autolog
├── data/ # датасеты (MNIST скачается автоматически)
├── artifacts/ # графики, отчёты (логируются в MLflow)
└── mlruns/ # локальный file store MLflow (в .gitignore)
```
---
## 🧩 Роль каждого файла (кратко)
| Файл | Что делает | Датасет | Фреймворк |
|---|---|---|---|
| `train_simple.py` | RandomForest, ручное логирование params/metrics/model/artifacts | digits | sklearn |
| `train_gpu.py` | CNN, логирование по эпохам, выбор устройства `get_device()` | MNIST | PyTorch |
| `register_model.py` | ищет лучший run по метрике, регистрирует модель, стадия Production | — | MLflow API |
| `load_and_predict.py` | грузит `models:/digits_rf_model/Production`, предсказывает | digits | MLflow |
| `compare_runs.py` | `MlflowClient.search_runs`, топ-N по метрике | — | MLflow API |
| `autolog_demo.py` | `mlflow.autolog()` для GradientBoosting | wine | sklearn |
| `hyperparam_sweep.py` | ручной перебор комбинаций, `--max-combos` | digits | sklearn |
| `serve_model.py` | Flask-сервер `/predict`, грузит модель из реестра | digits | Flask + MLflow |
| `grid_search_cv.py` | `GridSearchCV`, autolog залогит каждую попытку | digits | sklearn |
---
## 🖥️ Платформенная специфика
### Выбор устройства в `train_gpu.py`
Функция `get_device()` определяет устройство в порядке:
**CUDA → MPS (Apple Silicon) → CPU**. Тег MLflow `device_type` хранит выбор
(`cuda` / `mps` / `cpu`) — по нему удобно фильтровать запуски в UI.
### `num_workers` в DataLoader
На **macOS с MPS** многопроцессная загрузка нестабильна (fork + MPS),
поэтому `num_workers=0` на Darwin и `4` на Linux. `pin_memory=True`
только для CUDA. Логика — в `train_gpu.py` рядом с `DataLoader`.
### `setup_server.sh` — развивка по ОС
- `Darwin` → macOS: проверка Homebrew, `python@3.12` через brew, PyTorch
из PyPI (без CUDA), проверка `torch.backends.mps.is_available()`.
- `Linux` → GPU-сервер: `nvidia-smi`, PyTorch из
`--index-url https://download.pytorch.org/whl/cu121`, проверка VRAM.
При добавлении новой платформы — расширяй `case "$OS"` в начале скрипта.
---
## 🚀 Стандартные команды
```bash
# Окружение
bash setup_server.sh # установка (macOS или Linux)
source .venv/bin/activate
./start_ui.sh # MLflow UI → http://localhost:5555
# Уроки (в отдельном терминале, с активированным venv)
python src/train_simple.py --n-estimators 100 --max-depth 8
python src/train_gpu.py --epochs 5 --batch-size 128 --lr 0.001 # macOS/MPS
python src/register_model.py --experiment digits_classification
python src/load_and_predict.py
python src/compare_runs.py --experiment digits_classification --top 10
python src/autolog_demo.py
python src/hyperparam_sweep.py --max-combos 20
python src/serve_model.py --port 5001
python src/grid_search_cv.py
# Через MLflow Projects (изолированное окружение по MLproject)
mlflow run . -P n_estimators=100 -P max_depth=8
mlflow run . -P epochs=10 -P batch_size=256 -P lr=0.001 --entry-point gpu
```
---
## 🔧 Конвенции кода
- **Датасеты** — только встроенные (`load_digits`, `load_wine`, MNIST через
`torchvision`). Никаких внешних файлов в `data/`, кроме скачанного MNIST.
- **Логирование** — параметры/метрики/модель/артефакты. Графики сохраняются
в `artifacts/` и логируются через `mlflow.log_artifact`.
- **Имена экспериментов**: `digits_classification`, `mnist_cnn_gpu`,
`wine_autolog` и т.п. Модель в реестре — `digits_rf_model`.
- **`matplotlib.use("Agg")`** обязателен перед импортом pyplot — скрипты
работают без GUI (на сервере).
- **Python ≥ 3.10** (см. `python_env.yaml`). type hints опциональны.
- **CLI через `argparse`** — все скрипты с параметрами запускаются из CLI.
---
## ⚠️ Известные шероховатости / TODO
- ~~Нумерация уроков 8/9 расходилась между докстрингами и README/CODE_WALKTHROUGH~~ —
**исправлено**: канон `serve_model.py`=Урок 8 / `grid_search_cv.py`=Урок 9,
README.md и CODE_WALKTHROUGH.md выровнены под докстринги (порядок секций тоже).
- `requirements.txt` включает `torch`/`torchvision` из PyPI (CPU/MPS).
Для CUDA их нужно ставить отдельно через `--index-url .../whl/cu121`
(см. `setup_server.sh`, ветка Linux).
- **Порт MLflow UI = 5555** (не 5000): на macOS порт 5000 занят системным сервисом AirPlay Receiver (процесс `ControlCenter`), поэтому `start_ui.sh` использует 5555. На Linux можно вернуть 5000, но 5555 работает везде.
- **MLflow 3.x: `artifact_path` устарел** — во всех `log_model` используется `name=...` (был `artifact_path=...`). При обновлении/добавлении log_model не используй `artifact_path`.
- **Сигнатура модели**: `train_simple.py` логирует модель с явной `signature=` и `input_example=` (через `mlflow.models.signature.infer_signature`). Без этого MLflow пишет warning «Model logged without a signature and input example».
- `train_gpu.py` передаёт `input_example` → сигнатура авто-infer'ится.
- `autolog_demo.py` намеренно отключает сигнатуры (`log_model_signatures=False`) как демо опций autolog — там warning ожидаем.
- **INFO про переменные окружения** (`OPENAI_API_KEY` и т.п.) при логировании подавлено через `MLFLOW_RECORD_ENV_VARS_IN_MODEL_LOGGING=false` в скриптах, логирующих модели.
- **`input_example` для `mlflow.pytorch.log_model` — только numpy**, не `torch.Tensor`: MLflow 3.x валидирует пример входа и падает с `MlflowException ... but got '<class 'torch.Tensor'>'`. В `train_gpu.py` передаётся `np.random.rand(1,1,28,28).astype(np.float32)`. Аналогично для других flavor'ов — пример должен быть DataFrame/ndarray/dict/list/скаляр.
- **urllib3 `NotOpenSSLWarning`** на macOS (системный `ssl` собран с LibreSSL 2.8.3, urllib3 v2 просит OpenSSL 1.1.1+) — стороннее шумовое предупреждение, к проекту отношения не имеет; не подавляем (чтобы не прятать реальные предупреждения).
- **«Эксперимент не виден в UI» (главная причина)**: скрипты по умолчанию пишут в локальный file store `./mlruns`, а `start_ui.sh` раньше поднимал сервер на **SQLite** (`mlflow.db`) — два разных хранилища, UI ничего не показывал. Теперь `start_ui.sh` использует `--backend-store-uri file://${PWD}/mlruns`**то же хранилище, что и скрипты**, поэтому всё видно без настройки tracking URI. Альтернатива (SQLite-бэкенд) оставлена закомментированной в `start_ui.sh`; при её включении в скриптах нужно задать `mlflow.set_tracking_uri('http://localhost:5555')` (или `MLFLOW_TRACKING_URI`).
- `mlruns/`, `artifacts/*`, `data/MNIST/`, `mlflow.db` — в `.gitignore`,
в репозиторий не попадают.
---
## 🔄 Когда обновлять этот файл
**AGENTS.md — живой документ. Обновляй его при:**
1. **Добавлении/удалении файла в `src/`** — обнови карту проекта и таблицу ролей.
2. **Изменении платформенной логики** (новое устройство, новая ОС в
`setup_server.sh`, изменение `get_device()` или `num_workers`) — обнови
раздел «Платформенная специфика».
3. **Изменении имён экспериментов/моделей** в реестре — обнови «Конвенции».
4. **Изменении зависимостей или версии Python** (`requirements.txt`,
`python_env.yaml`) — проверь раздел команд и конвенции.
5. **Появлении новых entry points в `MLproject`** — добавь в карту и команды.
6. **Обнаружении gotcha/шероховатости** — занеси в «Известные шероховатости»,
чтобы следующий агент не наступал повторно.
Правило простое: **если ты что-то изменил в структуре/поведении проекта,
отрази это в AGENTS.md в том же коммите.** Если файл устарел относительно
кода — это баг, который нужно исправить.
---
## 📌 Шпаргалка по MLflow API (часто используемое в проекте)
```python
mlflow.set_experiment("name")
with mlflow.start_run(run_name="...") as run:
mlflow.log_param("k", v)
mlflow.log_metric("acc", 0.95, step=epoch)
mlflow.log_artifact("artifacts/curve.png")
mlflow.sklearn.log_model(model, "model") # sklearn flavor
mlflow.pytorch.log_model(model, "model") # pytorch flavor
mlflow.set_tag("device_type", "mps")
mlflow.autolog() # авто-логирование
# Model Registry
client = MlflowClient()
client.search_runs([experiment_id], order_by=["metrics.accuracy DESC"])
mlflow.register_model("runs:/<run_id>/model", "digits_rf_model")
client.transition_model_version_stage("digits_rf_model", version=1, stage="Production")
mlflow.pyfunc.load_model("models:/digits_rf_model/Production")
```
---
## 🧭 С чего начать новому агенту
1. Прочитай этот файл целиком.
2. Прочитай `README.md` (пользовательский взгляд) и `CODE_WALKTHROUGH.md`
(детальный разбор кода).
3. Прогони `bash setup_server.sh` на целевой платформе, чтобы понять
состояние окружения.
4. Перед правками проверь `git status` — в этом окружении `.git` может быть
read-only для агента (тогда коммит делает пользователь вручную).
5. Любое структурное изменение → коммит + правка AGENTS.md.