From c8043ad7c486aa811c8b9a5e5e1cfd8c8d77e9bd Mon Sep 17 00:00:00 2001 From: second_constantine Date: Tue, 21 Jul 2026 01:19:40 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20mlflow=20run=20=E2=80=94=20guard=20set?= =?UTF-8?q?=5Fexperiment=20=D0=BF=D0=BE=D0=B4=20MLFLOW=5FRUN=5FID=20+=20?= =?UTF-8?q?=D1=80=D0=B5=D1=86=D0=B5=D0=BF=D1=82=20(tracking=20URI,=20venv,?= =?UTF-8?q?=20--env-manager=20local)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 9 ++++++--- README.md | 20 ++++++++++++++++---- src/hyperparam_sweep.py | 5 ++++- src/train_gpu.py | 5 ++++- src/train_simple.py | 5 ++++- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 299928a..b4ff5b1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -106,9 +106,11 @@ 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 +# Через 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 ``` --- @@ -142,6 +144,7 @@ mlflow run . -P epochs=10 -P batch_size=256 -P lr=0.001 --entry-point gpu - `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/` (скрипт распакует их сам). diff --git a/README.md b/README.md index b88992c..afc09bc 100644 --- a/README.md +++ b/README.md @@ -97,13 +97,25 @@ python src/grid_search_cv.py ### 4. MLflow Projects (воспроизводимый запуск) +`mlflow run` нужен установленный `MLFLOW_TRACKING_URI` и активированный venv +(иначе: file-store maintenance mode и `python: command not found`). + ```bash -# Запуск через MLproject — MLflow сам создаст окружение -mlflow run . -P n_estimators=200 -P max_depth=12 -mlflow run . -e gpu -P epochs=10 -P batch_size=256 -P lr=0.001 -mlflow run . -e sweep -P max_combos=30 +source .venv/bin/activate +export MLFLOW_TRACKING_URI=http://localhost:5555 # сервер MLflow должен работать + +# --env-manager local — использовать текущий venv (не создавать новый из python_env.yaml) +mlflow run . --env-manager local --experiment-name digits_classification \ + -P n_estimators=200 -P max_depth=12 +mlflow run . --env-manager local -e gpu --experiment-name mnist_cnn_gpu \ + -P epochs=10 -P batch_size=256 -P lr=0.001 +mlflow run . --env-manager local -e sweep --experiment-name sweep_digits \ + -P max_combos=30 ``` +> `--experiment-name` теперь опционален (скрипты не конфликтуют с run'ом от +> `mlflow run`), но полезен, чтобы запуск попал в нужный эксперимент, а не в Default. + --- ## 📚 Что изучает каждый урок diff --git a/src/hyperparam_sweep.py b/src/hyperparam_sweep.py index 619b5cc..13279e3 100644 --- a/src/hyperparam_sweep.py +++ b/src/hyperparam_sweep.py @@ -69,7 +69,10 @@ def main(): X, y, test_size=0.2, random_state=42 ) - mlflow.set_experiment(args.experiment_name) + # под `mlflow run` эксперимент уже задан Projects (MLFLOW_RUN_ID) — + # не переопределяем, иначе конфликт "active experiment ID does not match". + if not os.environ.get("MLFLOW_RUN_ID"): + mlflow.set_experiment(args.experiment_name) best_acc = 0.0 best_run_id = None diff --git a/src/train_gpu.py b/src/train_gpu.py index fe8ef77..6657b18 100644 --- a/src/train_gpu.py +++ b/src/train_gpu.py @@ -145,7 +145,10 @@ def main(): print(" Apple Silicon GPU (MPS)") # ─── MLflow эксперимент ─── - mlflow.set_experiment(args.experiment_name) + # под `mlflow run` эксперимент уже задан Projects (MLFLOW_RUN_ID) — + # не переопределяем, иначе конфликт "active experiment ID does not match". + if not os.environ.get("MLFLOW_RUN_ID"): + mlflow.set_experiment(args.experiment_name) # ─── Данные ─── transform = transforms.Compose([ diff --git a/src/train_simple.py b/src/train_simple.py index 4554d00..9978868 100644 --- a/src/train_simple.py +++ b/src/train_simple.py @@ -36,7 +36,10 @@ def main(): args = parser.parse_args() # --- MLflow: задаём эксперимент --- - mlflow.set_experiment(args.experiment_name) + # под `mlflow run` эксперимент уже задан Projects (MLFLOW_RUN_ID) — + # не переопределяем, иначе конфликт "active experiment ID does not match". + if not os.environ.get("MLFLOW_RUN_ID"): + mlflow.set_experiment(args.experiment_name) # --- Данные --- digits = load_digits()