fix: ensure_mnist распаковывает gz (torchvision _check_exists ждёт файлы без .gz)
This commit is contained in:
@@ -144,7 +144,7 @@ mlflow run . -P epochs=10 -P batch_size=256 -P lr=0.001 --entry-point gpu
|
|||||||
- **INFO про переменные окружения** (`OPENAI_API_KEY` и т.п.) при логировании подавлено через `MLFLOW_RECORD_ENV_VARS_IN_MODEL_LOGGING=false` в скриптах, логирующих модели.
|
- **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`.
|
- **`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).
|
- **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/`.
|
- **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 '<class 'torch.Tensor'>'`. В `train_gpu.py` передаётся `np.random.rand(1,1,28,28).astype(np.float32)`. Аналогично для других flavor'ов — пример должен быть DataFrame/ndarray/dict/list/скаляр.
|
- **`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+) — стороннее шумовое предупреждение, к проекту отношения не имеет; не подавляем (чтобы не прятать реальные предупреждения).
|
- **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://-путям. Эти флаги были в исходном проекте и случайно потерялись при миграции — восстановлены.
|
- **Доступ к 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://-путям. Эти флаги были в исходном проекте и случайно потерялись при миграции — восстановлены.
|
||||||
|
|||||||
+36
-17
@@ -73,14 +73,21 @@ def get_device() -> torch.device:
|
|||||||
|
|
||||||
|
|
||||||
def ensure_mnist(data_dir: str) -> None:
|
def ensure_mnist(data_dir: str) -> None:
|
||||||
"""Гарантирует наличие MNIST.
|
"""Гарантирует наличие MNIST в распакованном виде.
|
||||||
|
|
||||||
torchvision.MNIST(download=True) тянет файлы с yann.lecun.com/exdb/mnist/,
|
torchvision.MNIST(download=True) тянет файлы с yann.lecun.com/exdb/mnist/,
|
||||||
который умер (HTTP 404). Кладём 4 gz-файла с рабочего зеркала
|
который умер (HTTP 404). При этом torchvision.MNIST._check_exists() ищет в
|
||||||
ossci-datasets.s3.amazonaws.com (официальный fallback PyTorch) в
|
data/MNIST/raw/ РАСПАКОВАННЫЕ файлы (без .gz), а не архивы. Поэтому:
|
||||||
data/MNIST/raw/ — после этого torchvision находит их и в сеть не лезет.
|
1. качаем 4 gz с рабочего зеркала ossci-datasets.s3.amazonaws.com;
|
||||||
|
2. распаковываем их (gunzip) в data/MNIST/raw/.
|
||||||
|
После этого torchvision находит распакованные файлы и в сеть не лезет.
|
||||||
|
Если и S3 недоступен — положите 4 MNIST gz-файла вручную в raw/ (скрипт
|
||||||
|
распакует их сам при следующем запуске).
|
||||||
"""
|
"""
|
||||||
|
import gzip
|
||||||
|
import shutil
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
raw = os.path.join(data_dir, "MNIST", "raw")
|
raw = os.path.join(data_dir, "MNIST", "raw")
|
||||||
os.makedirs(raw, exist_ok=True)
|
os.makedirs(raw, exist_ok=True)
|
||||||
base = "https://ossci-datasets.s3.amazonaws.com/mnist/"
|
base = "https://ossci-datasets.s3.amazonaws.com/mnist/"
|
||||||
@@ -90,20 +97,32 @@ def ensure_mnist(data_dir: str) -> None:
|
|||||||
"t10k-images-idx3-ubyte.gz",
|
"t10k-images-idx3-ubyte.gz",
|
||||||
"t10k-labels-idx1-ubyte.gz",
|
"t10k-labels-idx1-ubyte.gz",
|
||||||
]
|
]
|
||||||
needed = [f for f in files if not os.path.exists(os.path.join(raw, f))]
|
# torchvision проверяет РАСПАКОВАННЫЕ файлы (имя без .gz)
|
||||||
if not needed:
|
decomp = [f[:-3] for f in files]
|
||||||
|
missing = [f for f in decomp if not os.path.exists(os.path.join(raw, f))]
|
||||||
|
if not missing:
|
||||||
return
|
return
|
||||||
print(f"📥 MNIST: докачиваю {len(needed)} файл(ов) с ossci-datasets.s3.amazonaws.com ...")
|
|
||||||
for fname in needed:
|
print(f"📥 MNIST: готовлю {len(missing)} распакованных файл(ов)...")
|
||||||
dst = os.path.join(raw, fname)
|
for fname in files:
|
||||||
print(f" {fname}")
|
gz = os.path.join(raw, fname)
|
||||||
try:
|
out = os.path.join(raw, fname[:-3])
|
||||||
urllib.request.urlretrieve(base + fname, dst)
|
if os.path.exists(out):
|
||||||
except Exception as e:
|
continue
|
||||||
raise RuntimeError(
|
# 1) Ensure the .gz archive is present
|
||||||
f"Не удалось скачать {fname} с {base} ({e}). "
|
if not os.path.exists(gz):
|
||||||
f"Положите 4 MNIST gz-файла вручную в {raw}/"
|
print(f" download {fname} ← {base}")
|
||||||
) from e
|
try:
|
||||||
|
urllib.request.urlretrieve(base + fname, gz)
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Не удалось скачать {fname} с {base} ({e}). "
|
||||||
|
f"Положите 4 MNIST gz-файла вручную в {raw}/"
|
||||||
|
) from e
|
||||||
|
# 2) Decompress .gz → raw file (то, что ищет torchvision)
|
||||||
|
print(f" gunzip {fname} → {fname[:-3]}")
|
||||||
|
with gzip.open(gz, "rb") as src, open(out, "wb") as dst:
|
||||||
|
shutil.copyfileobj(src, dst)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
Reference in New Issue
Block a user