# 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` в скриптах, логирующих модели. - **File-store как СЕРВЕРНЫЙ бэкенд в maintenance mode (MLflow 3.x)**: `mlflow server --backend-store-uri file://...` по умолчанию падает с `MlflowException ... filesystem tracking backend is in maintenance mode`. Официальный opt-out — `export MLFLOW_ALLOW_FILE_STORE=true` (уже выставлено в `start_ui.sh` перед file-блоком). Это касается ТОЛЬКО сервера; клиентский file store (как пишут скрипты в `./mlruns`) работает без opt-out. Альтернатива без opt-out — SQLite-бэкенд (закомментированный блок в `start_ui.sh`), но тогда скрипты должны логировать на сервер через `mlflow.set_tracking_uri('http://localhost:5555')`. - **`input_example` для `mlflow.pytorch.log_model` — только numpy**, не `torch.Tensor`: MLflow 3.x валидирует пример входа и падает с `MlflowException ... but got ''`. В `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://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.