Files

18 KiB
Raw Permalink Blame History

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 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 (часто используемое в проекте)

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.