211 lines
17 KiB
Markdown
211 lines
17 KiB
Markdown
# 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 (изолированное окружение по 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` в скриптах, логирующих модели.
|
||
- **`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/` в `data/MNIST/raw/`, после чего torchvision берёт локальные и в сеть не лезет. Если и S3 недоступен — положите 4 MNIST gz-файла вручную в `data/MNIST/raw/`.
|
||
- **`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 с других машин (браузером по 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.
|