Files
mlflow-practice/AGENTS.md
T

214 lines
18 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.
# 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, SQLite backend)
├── .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 (в .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 (нужны MLFLOW_TRACKING_URI + активный venv)
source .venv/bin/activate
export MLFLOW_TRACKING_URI=http://localhost:5555
mlflow run . --env-manager local --experiment-name digits_classification -P n_estimators=100 -P max_depth=8
mlflow run . --env-manager local -e gpu --experiment-name mnist_cnn_gpu -P epochs=10 -P batch_size=256 -P lr=0.001
```
---
## 🔧 Конвенции кода
- **Датасеты** — только встроенные (`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` в скриптах, логирующих модели.
- **`mlflow run` (MLflow Projects) — три условия**, иначе падает: (1) нужно `export MLFLOW_TRACKING_URI=http://localhost:5555` (сервер работает) — иначе `mlflow run` создаёт run в дефолтном file-store → maintenance mode; (2) venv активирован (`source .venv/bin/activate`) — иначе `python: command not found` в подпроцессе entry-point; (3) `--env-manager local` — иначе MLflow создаёт новый venv из `python_env.yaml` (python 3.10, медленно, может не быть нужного интерпретатора). Скрипты `train_simple/train_gpu/hyperparam_sweep` не вызывают `set_experiment` под `mlflow run` (детект `MLFLOW_RUN_ID`), поэтому `--experiment-name` опционален.
- **`python -m venv` падает на Linux (ensurepip)**: на Debian/Ubuntu без пакета `python3.13-venv` (или `python3-venv`) создание venv падает на шаге `ensurepip` (`non-zero exit status 1`). `setup_server.sh` ловит это и пробует fallback: `venv --without-pip` + bootstrap pip через `get-pip.py`. Альтернатива руками — `sudo apt-get install -y python3.13-venv python3.13-dev`.
- **File-store как tracking backend запрещён в MLflow 3.x** (maintenance mode): падает и `mlflow server --backend-store-uri file://...`, и клиент-скрипт с дефолтным `./mlruns` — оба с `MlflowException ... in maintenance mode`. Поэтому проект использует **SQLite** (`sqlite:///mlflow.db`) как tracking backend, а скрипты логируют на сервер по HTTP. Опциональный opt-out `MLFLOW_ALLOW_FILE_STORE=true` НЕ используем (он может быть убран в будущем). Артефакты при этом лежат на локальном диске (`./artifacts`, file artifact repository — он НЕ в maintenance mode).
- **MNIST: `yann.lecun.com/exdb/mnist/` умер (HTTP 404)** — `torchvision.MNIST (download=True)` падает при скачивании. В `train_gpu.py` есть `ensure_mnist()`: качает 4 gz-файла с рабочего зеркала `https://ossci-datasets.s3.amazonaws.com/mnist/` и **распаковывает их** (gunzip) в `data/MNIST/raw/` в файлы без `.gz`. Важно: torchvision.MNIST._check_exists() ищет именно РАСПАКОВАННЫЕ файлы (`train-images-idx3-ubyte` и т.п.), а не архивы — поэтому просто положить gz недостаточно, нужен gunzip. Если и S3 недоступен — положите 4 MNIST gz-файла вручную в `data/MNIST/raw/` (скрипт распакует их сам).
- **`mlflow.pytorch.log_model` в MLflow 3.x — два нюанса**: (1) `input_example` — только numpy, не `torch.Tensor` (иначе `MlflowException ... but got torch.Tensor`); (2) по умолчанию `serialization_format='pt2'`, который требует сигнатуру через `TensorSpec` — а numpy-пример даёт numpy-сигнатуру → `Unsupported signature type`. Поэтому в `train_gpu.py` явно `serialization_format='pickle'` + `input_example=np.random.rand(1,1,28,28).astype(np.float32)`. (MLflow при этом печатает WARNING о безопасности pickle — это нормально для учебного проекта.)
- **urllib3 `NotOpenSSLWarning`** на macOS (системный `ssl` собран с LibreSSL 2.8.3, urllib3 v2 просит OpenSSL 1.1.1+) — стороннее шумовое предупреждение, к проекту отношения не имеет; не подавляем (чтобы не прятать реальные предупреждения).
- **Доступ к UI с других машин (браузером по IP сервера)** — НЕ удалять флаги в `start_ui.sh`: `--host 0.0.0.0`, `--allowed-hosts "*"`, `--cors-allowed-origins "*"`, `export MLFLOW_ALLOW_ORIGIN="*"`, `--serve-artifacts` + `--artifacts-destination`, `--dev`. Без `--allowed-hosts "*"` MLflow режет запросы с `Host=<server-ip>:5555` (UI не открывается удалённо); без `--serve-artifacts` UI на удалённой машине не скачает артефакты (model/графики) по file://-путям. Эти флаги были в исходном проекте и случайно потерялись при миграции — восстановлены.
- **Архитектура хранения (SQLite + HTTP-клиенты)**: `start_ui.sh` поднимает сервер с `--backend-store-uri sqlite:///${PWD}/mlflow.db`. Все скрипты в `src/` содержат `mlflow.set_tracking_uri(os.environ.get("MLFLOW_TRACKING_URI", "http://localhost:5555"))` — пишут на сервер. UI и скрипты работают с **одной** sqlite-базой → эксперименты сразу видны. **Сервер должен быть запущен до запуска скриптов** (иначе `ConnectionError` к localhost:5555). Переопределить адрес: `export MLFLOW_TRACKING_URI=http://<host>:5555` (например, при логировании на удалённый сервер).
- `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.