diff --git a/AGENTS.md b/AGENTS.md index fc9dbea..299928a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -145,7 +145,7 @@ mlflow run . -P epochs=10 -P batch_size=256 -P lr=0.001 --entry-point gpu - **`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/` (скрипт распакует их сам). -- **`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/скаляр. +- **`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=: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://:5555` (например, при логировании на удалённый сервер). diff --git a/src/train_gpu.py b/src/train_gpu.py index 61cf45d..fe8ef77 100644 --- a/src/train_gpu.py +++ b/src/train_gpu.py @@ -269,10 +269,14 @@ def main(): # ─── Логируем модель ─── # input_example — numpy ndarray (MLflow 3.x не принимает torch.Tensor). # Форма как у входа модели: (batch, channels, H, W) = (1, 1, 28, 28). + # serialization_format="pickle": MLflow 3.x по умолчанию использует "pt2", + # который требует сигнатуру через TensorSpec (а у нас numpy-пример → + # numpy-сигнатура). "pickle" совместим с numpy-сигнатурой. mlflow.pytorch.log_model( model, name="model", # name вместо устаревшего artifact_path registered_model_name=None, + serialization_format="pickle", input_example=np.random.rand(1, 1, 28, 28).astype(np.float32), )