develop - hack_2026

This commit is contained in:
denis 2026-09-27 03:43:07 +03:00
parent 5bc6df6e85
commit 157b7b89a6
7 changed files with 221 additions and 94 deletions

View File

@ -1,8 +1,9 @@
# .dockerignore
# Цель: контекст сборки должен быть маленьким. Образ собирается из src,
# requirements.txt и run.sh (см. Dockerfile); чекпоинт в образ не копируется, а
# монтируется в /app/models при запуске. Данные, тесты и служебные файлы
# исключаются — DICOM-датасет не должен попадать в образ с медицинскими данными.
# requirements.txt, run.sh и чекпоинта models/dxa_model.pth — он закоммичен в
# репозиторий и копируется внутрь образа (см. Dockerfile), поэтому модели не
# нужно монтировать при запуске. Данные, тесты и служебные файлы исключаются —
# DICOM-датасет не должен попадать в образ с медицинскими данными.
__pycache__
**/__pycache__
*.pyc

View File

@ -1,12 +1,13 @@
# DXA Quality Assessment — контейнер для инференса (CPU)
#
# Сборка: docker build -t rell.ru:5000/rell-bone-2026:latest .
# Запуск: docker run -v /data/bone_2026/models:/app/models:ro -p 8000:8000 dxa-quality
# Сборка: docker build -t dxa-quality .
# Запуск: docker run -p 8000:8000 dxa-quality
# или: docker compose up -d (сервис dxa-cpu)
#
# Веса в образ НЕ копируются: swarm монтирует каталог с чекпоинтом в
# /app/models только для чтения, а models/ не попадает в git (см. .gitignore),
# поэтому COPY models ломал бы сборку в CI. Путь к чекпоинту берётся из
# DXA_MODEL_PATH и по умолчанию указывает на смонтированный каталог.
# Чекпоинт лежит ВНУТРИ образа: models/dxa_model.pth отслеживается git (каталог
# models/ не исключён в .gitignore) и копируется на этапе сборки. Монтировать
# пути не нужно — условие приёмки в том, что модели уже в образе. Если всё-таки
# смонтировать каталог в /app/models, он перекроет встроенный чекпоинт.
#
# Требования методики: зафиксированные версии зависимостей и работа без
# внешних сервисов. Inference не скачивает веса из сети — они внутри
@ -40,8 +41,7 @@ COPY run.sh ./run.sh
RUN chmod +x ./run.sh
# Smoke-проверка импорта: падает на сборке, если расходятся зависимости или
# модуль не найден, вместо тихой 500-й на каждом запросе в рантайме. Модель
# здесь не загружается — чекпоинта в контексте сборки нет.
# модуль не найден, вместо тихой 500-й на каждом запросе в рантайме.
RUN python -c "import src.main; print('app import ok')"
# --- Проверка статики для офлайн-работы ---
@ -54,6 +54,21 @@ RUN python -c "import os; \
assert not missing, f'missing frontend assets: {missing}'; \
print('frontend assets ok')"
# --- Чекпоинт внутри образа ---
# Условие приёмки: модель уже в образе, чтобы проверяющему не приходилось
# указывать путь к каталогу с весами. models/dxa_model.pth отслеживается git,
# поэтому копирование работает и в CI.
COPY models/dxa_model.pth /app/models/dxa_model.pth
# Проверка, что чекпоинт читается и содержит всё нужное для инференса: без этого
# битый или забытый файл проявился бы только в рантайме как model_loaded: false
# и HTTP 500 на каждый запрос анализа.
RUN python -c "import torch; \
ckpt=torch.load('/app/models/dxa_model.pth', map_location='cpu', weights_only=False); \
missing=[k for k in ('model_state_dict','backbone','head','threshold') if k not in ckpt]; \
assert not missing, f'checkpoint missing keys: {missing}'; \
print('checkpoint ok:', ckpt['backbone'], ckpt['head'], 'threshold', ckpt['threshold'])"
# Каталоги монтирований swarm: создаём заранее, чтобы образ вёл себя одинаково
# и при обычном `docker run` без volume.
RUN mkdir -p /app/data /app/logs

View File

@ -1,71 +1,106 @@
# DXA Quality Assessment - Docker Container (GPU / CUDA)
# Build: docker build -f Dockerfile_cuda -t dxa-quality-cuda .
# Run: docker run --gpus all -v /path/to/data:/data -v /path/to/models:/app/models \
# -p 8000:8000 dxa-quality-cuda
# DXA Quality Assessment — контейнер для инференса (GPU / CUDA)
#
# Сборка: docker build -f Dockerfile_cuda -t dxa-quality:cuda .
# Запуск: docker run --gpus all -p 8000:8000 dxa-quality:cuda
# или: docker compose --profile cuda up -d dxa-cuda
#
# База — тот же python:3.11-slim, что и у CPU-варианта, а CUDA и cuDNN приходят
# внутри колёс torch с индексом cu126. Отдельный nvidia/cuda-образ не нужен:
# контейнеру достаточно драйвера хоста, который подставляет
# nvidia-container-toolkit по флагу --gpus all (или секции deploy в compose).
# Требуется драйвер NVIDIA >= 525; для H200 (sm_90) в кластере он заведомо новее.
#
# Индекс cu118 в прежней версии файла не работал: torch 2.8.0 под CUDA 11.8 не
# публикуется (последняя версия там — 2.7.1), поэтому сборка падала на pip.
# Для 2.8.0 доступны только cu126 и cu128; выбран cu126 как наиболее
# распространённый. Под H200 нужен torch>=2.5, sm_90 поддерживается.
#
# Чекпоинт лежит ВНУТРИ образа (см. Dockerfile): монтировать пути не нужно.
FROM python:3.11-slim
# Base image with GPU support
FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04
# Неинтерактивный режим для apt
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
python3.10 \
python3-pip \
python3.10-dev \
python3.10-venv \
build-essential \
ninja-build \
libgl1-mesa-glx \
# libgl1/libglib2.0-0 нужны opencv (импортируется через зависимости проекта),
# libgomp1 — для параллельных циклов torch. Список совпадает с CPU-образом.
RUN apt-get update && apt-get install -y --no-install-recommends \
libgl1 \
libglib2.0-0 \
libsm6 \
libxext6 \
libxrender1 \
libgomp1 \
&& rm -rf /var/lib/apt/lists/*
# Обновляем pip и ставим свежий meson (0.64.0+) — ДО всех Python-пакетов
RUN pip3 install --no-cache-dir --upgrade pip "meson>=0.64.0"
WORKDIR /app
# --- PyTorch под CUDA 11.8 (ставим отдельно, чтобы не тянулся CUDA 12.6) ---
RUN pip3 install --no-cache-dir \
torch==2.8.0 torchvision==0.23.0 \
--index-url https://download.pytorch.org/whl/cu118
# --- PyTorch под CUDA 12.6 ---
# CUDA-колёса ставим первыми и с явным индексом: иначе pip взял бы с PyPI сборку
# под CUDA 12.6/12.8 произвольной ревизии. torchvision 0.23.0 — парная к torch
# 2.8.0 версия (другие комбинации несовместимы).
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir \
--index-url https://download.pytorch.org/whl/cu126 \
torch==2.8.0 torchvision==0.23.0
# --- Остальные зависимости ---
# Список версий один на оба образа: requirements.txt. Уже установленный torch
# pip не переустановит — версия из cu118 удовлетворяет требованию.
# Отдельным слоем и после torch: список версий один на оба образа
# (requirements.txt), а уже установленный torch==2.8.0+cu126 pip повторно не
# поставит — требование torch==2.8.0 считается выполненным, потому что локальная
# метка сборки при сравнении версий не учитывается.
COPY requirements.txt ./
RUN pip3 install --no-cache-dir -r requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# --- Код приложения ---
# --- Код ---
COPY src ./src
# Скрипт пакетной обработки (оценка берёт его из корня образа).
COPY run.sh ./run.sh
RUN chmod +x ./run.sh
RUN mkdir -p models
# Smoke-проверка импорта: падает на сборке, если зависимость недоступна или
# модуль не найден, вместо тихой 500-й в рантайме. Чекпоинт здесь не читается.
RUN python3 -c "import src.main; print('app import ok')"
# Smoke-проверка импорта: падает на сборке, если расходятся зависимости или
# модуль не найден, вместо тихой 500-й на каждом запросе в рантайме.
RUN python -c "import src.main; print('app import ok')"
# --- Проверка статики для офлайн-работы ---
# Tailwind и FontAwesome лежат в src/api/static, обращений к CDN быть не должно.
RUN python3 -c "import os; \
# Веб-интерфейс не должен зависеть от CDN: Tailwind и FontAwesome лежат в
# src/api/static/vendor и src/api/static/webfonts (см. index.html).
RUN python -c "import os; \
files=['src/api/static/vendor/tailwind.js','src/api/static/vendor/fontawesome.css', \
'src/api/static/webfonts/fa-solid-900.woff2']; \
missing=[f for f in files if not os.path.exists(f)]; \
assert not missing, f'missing frontend assets: {missing}'; \
print('frontend assets ok')"
# Expose API port
EXPOSE 8000
# --- Чекпоинт внутри образа ---
# Условие приёмки: модель уже в образе, чтобы проверяющему не приходилось
# указывать путь к каталогу с весами. models/dxa_model.pth отслеживается git,
# поэтому копирование работает и в CI.
COPY models/dxa_model.pth /app/models/dxa_model.pth
# Environment variables
# Проверка, что чекпоинт читается и содержит всё нужное для инференса, а также
# что сборка torch видит CUDA: без этого проблемы всплыли бы только в рантайме.
RUN python -c "import torch; \
ckpt=torch.load('/app/models/dxa_model.pth', map_location='cpu', weights_only=False); \
missing=[k for k in ('model_state_dict','backbone','head','threshold') if k not in ckpt]; \
assert not missing, f'checkpoint missing keys: {missing}'; \
print('checkpoint ok:', ckpt['backbone'], ckpt['head'], 'threshold', ckpt['threshold']); \
print('torch', torch.__version__, 'cuda built:', torch.version.cuda)"
# --- Переменные окружения ---
# Читается кодом только DXA_MODEL_PATH (src/main.py). MODEL_PATH, DATA_PATH,
# ANNOTATION_DIR swarm передаёт по инерции от прежней версии — код их не
# использует, и они ни на что не влияют.
ENV PYTHONUNBUFFERED=1 \
PYTHONPATH=/app \
DXA_MODEL_PATH=/app/models/dxa_model.pth
DXA_MODEL_PATH=/app/models/dxa_model.pth \
MODEL_PATH=/app/models \
DATA_PATH=/app/data \
ANNOTATION_DIR=/app/data/annotations
# Default command
CMD ["python3", "-m", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
RUN mkdir -p /app/data /app/logs
EXPOSE 8000
# HEALTHCHECK опирается на /api/v1/health, который отдаёт model_loaded.
# На GPU-устройстве старт дольше: даём запас по start-period.
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD python -c "import urllib.request,sys; \
r=urllib.request.urlopen('http://127.0.0.1:8000/api/v1/health', timeout=5); \
sys.exit(0 if r.status==200 else 1)"
CMD ["python", "-m", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

19
QWEN.md
View File

@ -297,14 +297,19 @@ position_validator,universal_scorer,medical_quality}.py`) удалены 2026-09
## Docker
```bash
docker build -t dxa-quality .
docker run -v /path/to/data:/data -p 8000:8000 dxa-quality
docker compose up -d # CPU, http://localhost:8000
docker compose --profile cuda up -d dxa-cuda # GPU (нужен nvidia-container-toolkit)
```
Dockerfile ставит зафиксированные версии, копирует только `src/` и `run.sh`,
проверяет импорт приложения и наличие офлайн-ассетов фронтенда на этапе сборки и
имеет HEALTHCHECK. Чекпоинт в образ не копируется — он монтируется в `/app/models`
при запуске (`DXA_MODEL_PATH`). Данные, тесты и `labels/` в образ не попадают
Образы: `Dockerfile` (python:3.11-slim, torch из CPU-индекса) и `Dockerfile_cuda`
(та же база, torch и torchvision из индекса `cu126` — под CUDA 11.8 колёс torch
2.8.0 нет, индекс заканчивается на 2.7.1). Оба собираются из `src/`,
`requirements.txt`, `run.sh` и **чекпоинта**: `models/dxa_model.pth` отслеживается
git и копируется в `/app/models` на этапе сборки, поэтому монтировать пути при
запуске не нужно (условие приёмки — модели уже внутри образа). Сборка проверяет
импорт приложения, наличие офлайн-ассетов фронтенда и читаемость чекпоинта,
образ имеет HEALTHCHECK. Данные, тесты и `labels/` в образ не попадают
(`.dockerignore`), поэтому `./run.sh train` внутри контейнера возьмёт метки из
имён файлов (с предупреждением); для обучения в контейнере смонтируйте `labels/`
или передайте свой `--labels-csv`.
или передайте свой `--labels-csv`. Если внешний `docker-swarm.yml` монтирует
каталог с моделями в `/app/models`, монтирование перекроет встроенный чекпоинт.

52
docker-compose.yml Normal file
View File

@ -0,0 +1,52 @@
# DXA Quality Assessment — запуск сервиса в контейнере.
#
# Чекпоинт лежит внутри образа (models/dxa_model.pth копируется на этапе сборки),
# поэтому монтировать пути не нужно: ни каталог с моделями, ни данные.
# Данные для анализа загружаются через веб-интерфейс (http://localhost:8000).
#
# CPU — работает на любой машине, ничего кроме docker не требуется:
# docker compose up -d # сервис dxa-cpu, порт 8000
#
# GPU — H200 и другие ускорители; нужен драйвер NVIDIA и nvidia-container-toolkit:
# docker compose --profile cuda up -d dxa-cuda
# имя сервиса указано явно: без него поднялись бы оба и разошлись по одному порту
#
# Пересобрать после правок кода: docker compose build [dxa-cpu|dxa-cuda]
# Логи: docker compose logs -f dxa-cpu
# Состояние: curl -s localhost:8000/api/v1/health
# Остановить: docker compose down
name: dxa-quality
services:
# Обычный инференс на CPU: torch ставится из CPU-индекса, GPU не используется.
dxa-cpu:
image: dxa-quality:cpu
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
restart: unless-stopped
# Инференс на GPU: базовый образ тот же, но torch собран под CUDA 12.6.
# Профиль cuda — чтобы на машинах без NVIDIA сервис не мешал запуску по умолчанию.
dxa-cuda:
profiles: ["cuda"]
image: dxa-quality:cuda
build:
context: .
dockerfile: Dockerfile_cuda
ports:
- "8000:8000"
restart: unless-stopped
environment:
NVIDIA_VISIBLE_DEVICES: all
NVIDIA_DRIVER_CAPABILITIES: compute,utility
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]

View File

@ -158,10 +158,10 @@
<td>FastAPI: восемь маршрутов, включая <code>/api/v1/batch</code> и <code>/api/v1/export</code></td>
<td>Разд. 10</td></tr>
<tr><td>Обязательная контейнеризация, скрипт сборки и запуска в Linux</td>
<td><code>Dockerfile</code> (python:3.11-slim), <code>Dockerfile_cuda</code> для GPU, <code>run.sh</code></td>
<td>Разд. 14; сборка проверена, см. 14</td></tr>
<td><code>Dockerfile</code> (python:3.11-slim, чекпоинт внутри образа), <code>Dockerfile_cuda</code> для GPU, <code>docker-compose.yml</code> на оба случая, <code>run.sh</code></td>
<td>Разд. 14; сборка и запуск проверены, см. 14</td></tr>
<tr><td>Фиксация зависимостей, включая базовый контейнер</td>
<td><code>requirements.txt</code> с точными версиями, базовый образ по тегу, torch с CPU-индексом</td>
<td><code>requirements.txt</code> с точными версиями, базовый образ по тегу, torch с CPU-индексом (CPU-вариант) и cu126 (GPU-вариант)</td>
<td>Разд. 14</td></tr>
<tr><td>Работа без обращения к внешним сервисам</td>
<td>Офлайн-ассеты фронтенда, отсутствие сетевых вызовов в коде предсказания</td>
@ -185,7 +185,7 @@
<td>Загрузка файлов, таблица результатов, панель деталей, панель «О модели»; все подписи и метрики получает с сервера</td></tr>
<tr><td>Эвристики качества</td><td><code>src/quality/</code></td>
<td>Числовые измерения снимка (резкость, плотные включения, полнота и поворот области) для панели деталей</td></tr>
<tr><td>Инфраструктура</td><td><code>run.sh</code>, <code>Dockerfile</code>, <code>Dockerfile_cuda</code>, <code>Jenkinsfile</code></td>
<tr><td>Инфраструктура</td><td><code>run.sh</code>, <code>docker-compose.yml</code>, <code>Dockerfile</code>, <code>Dockerfile_cuda</code>, <code>Jenkinsfile</code></td>
<td>Единая точка входа для сборки, обучения, инференса и тестов; сборка образа и выкладка</td></tr>
</table>
@ -729,16 +729,20 @@ seed'ах. Принято правило «только экспертная т
<tr><th style="width:26%">Ресурс</th><th style="width:37%">Минимальная конфигурация</th><th>Рекомендуемая</th></tr>
<tr><td>Процессор</td><td>2 ядра x86-64 или ARM64</td><td>4 ядра и более; инференс векторизован и масштабируется по кадрам</td></tr>
<tr><td>Оперативная память</td><td>1.5 ГБ (измеренный пик ≈ 640 МБ + запас на ОС и веб-слой)</td><td>4 ГБ</td></tr>
<tr><td>Графический ускоритель</td><td><b>Не требуется</b>: torch устанавливается из CPU-индекса, инференс на CPU укладывается в требование по времени</td><td>Любой GPU с поддержкой CUDA 11.8 — вариант сборки <code>Dockerfile_cuda</code></td></tr>
<tr><td>Дисковое пространство</td><td>базовый образ python:3.11-slim (150 МБ) + зависимости и код; чекпоинт 43 МБ монтируется отдельно</td><td>плюс место под входные архивы DICOM и результаты</td></tr>
<tr><td>Графический ускоритель</td><td><b>Не требуется</b>: torch устанавливается из CPU-индекса, инференс на CPU укладывается в требование по времени</td><td>Любой GPU с поддержкой CUDA 12.6, включая H200 (compute capability 9.0) — вариант сборки <code>Dockerfile_cuda</code></td></tr>
<tr><td>Дисковое пространство</td><td>базовый образ python:3.11-slim (150 МБ) + зависимости, код и чекпоинт (43 МБ) — всё внутри образа</td><td>плюс место под входные архивы DICOM и результаты</td></tr>
<tr><td>Сеть</td><td>Не требуется при работе: образ и веса фиксированы, обращений во внешние сервисы нет</td><td>Нужна только для сборки образа и загрузки базовых зависимостей</td></tr>
</table>
<p><b>Размер образа (измерено):</b> 1.41 ГБ (1 412 385 143 байт) для сборки под
<code>linux/arm64</code>. Базовый образ <code>python:3.11-slim</code> занимает 150 МБ, остальное —
зависимости из <code>requirements.txt</code>, включая torch и torchvision из CPU-индекса; CUDA-колёса
в образ не попадают. Данные, тесты, <code>labels/</code> и <code>assets/</code> в образ не входят,
чекпоинт (43 МБ) монтируется отдельно.</p>
<p><b>Размер образа (измерено).</b> CPU-вариант: 1.46 ГБ под <code>linux/arm64</code> и 1.8 ГБ под
<code>linux/amd64</code>. GPU-вариант: 7.0 ГБ под <code>linux/amd64</code> — почти всё сверх базы
занимают CUDA-библиотеки внутри колёс torch (<code>nvidia-cublas-cu12</code>,
<code>nvidia-cudnn-cu12</code>, <code>nvidia-nccl-cu12</code> и другие). Базовый образ
<code>python:3.11-slim</code> — 150 МБ; torch и torchvision берутся из CPU-индекса в CPU-варианте и из
индекса <code>cu126</code> в GPU-варианте, поэтому CUDA-колёса в CPU-образ не попадают (там
<code>torch 2.8.0+cpu</code> и ноль nvidia-пакетов). Данные, тесты, <code>labels/</code>,
<code>assets/</code> и <code>docs/</code> в образ не входят; чекпоинт (43 МБ), наоборот, лежит внутри —
монтировать каталог с моделями при запуске не нужно.</p>
<h2 id="s14">14. Сборка, запуск и развёртывание</h2>
@ -746,30 +750,40 @@ seed'ах. Принято правило «только экспертная т
<table>
<tr><th style="width:30%">Элемент</th><th>Значение</th></tr>
<tr><td>Базовый образ</td><td><code>python:3.11-slim</code> (фиксированный тег)</td></tr>
<tr><td>Зависимости</td><td><code>requirements.txt</code> с точными версиями; torch и torchvision — из CPU-индекса PyTorch, чтобы в образ не попали CUDA-колёса</td></tr>
<tr><td>Что копируется в образ</td><td>только <code>src/</code>, <code>requirements.txt</code> и <code>run.sh</code></td></tr>
<tr><td>Что не копируется</td><td>данные, тесты, <code>labels/</code>, <code>assets/</code>, <code>docs/</code> (см. <code>.dockerignore</code>), а также чекпоинт</td></tr>
<tr><td>Чекпоинт</td><td>монтируется при запуске в <code>/app/models</code>; путь задаётся переменной <code>DXA_MODEL_PATH</code> (по умолчанию <code>/app/models/dxa_model.pth</code>)</td></tr>
<tr><td>Проверки на этапе сборки</td><td>импорт приложения (<code>import src.main</code>) и наличие офлайн-ассетов фронтенда; сборка падает при их отсутствии</td></tr>
<tr><td>Базовый образ</td><td><code>python:3.11-slim</code> (фиксированный тег); у GPU-варианта он тот же — CUDA и cuDNN приходят внутри колёс torch, отдельный <code>nvidia/cuda</code>-образ не нужен</td></tr>
<tr><td>Зависимости</td><td><code>requirements.txt</code> с точными версиями; CPU-вариант берёт torch и torchvision из CPU-индекса PyTorch, GPU-вариант — из индекса <code>cu126</code></td></tr>
<tr><td>Что копируется в образ</td><td><code>src/</code>, <code>requirements.txt</code>, <code>run.sh</code> и чекпоинт <code>models/dxa_model.pth</code></td></tr>
<tr><td>Что не копируется</td><td>данные, тесты, <code>labels/</code>, <code>assets/</code>, <code>docs/</code> (см. <code>.dockerignore</code>)</td></tr>
<tr><td>Чекпоинт</td><td>копируется на этапе сборки и лежит в образе как <code>/app/models/dxa_model.pth</code>; путь задаётся переменной <code>DXA_MODEL_PATH</code>. Монтировать каталог с моделями не нужно</td></tr>
<tr><td>Проверки на этапе сборки</td><td>импорт приложения (<code>import src.main</code>), наличие офлайн-ассетов фронтенда и читаемость чекпоинта (ключи <code>backbone</code>, <code>head</code>, <code>threshold</code>); сборка падает при их отсутствии</td></tr>
<tr><td>Контроль состояния</td><td><code>HEALTHCHECK</code> обращается к <code>/api/v1/health</code> каждые 30 с</td></tr>
<tr><td>Запуск</td><td><code>docker-compose.yml</code>: сервис <code>dxa-cpu</code> поднимается по умолчанию, GPU-сервис <code>dxa-cuda</code> — под профилем <code>cuda</code></td></tr>
<tr><td>Точка входа</td><td><code>uvicorn src.main:app</code> на порту 8000</td></tr>
</table>
<p>Если чекпоинт не смонтирован, сервис всё равно поднимается: <code>/api/v1/health</code> сообщает
<p>Модель встроена в образ, поэтому штатно чекпоинт всегда на месте. Если файл всё же недоступен
(например, образ повреждён), сервис всё равно поднимается: <code>/api/v1/health</code> сообщает
<code>model_loaded: false</code>, а запросы анализа возвращают ошибку вместо тихой неверной оценки.
Это сознательный выбор: отсутствие модели должно быть заметно сразу, а не проявляться как «странные»
результаты.</p>
<p><b>Что проверено на самом образе.</b> Сборка прошла обе внутренние проверки
(<code>app import ok</code>, <code>frontend assets ok</code>). Образ запущен и проверен в двух режимах:
без смонтированного чекпоинта <code>/api/v1/health</code> отдаёт <code>model_loaded: false</code> и
<code>exists: false</code>, а запрос анализа — HTTP 500 с телом <code>{"error": "Model not loaded"}</code>;
с примонтированным каталогом моделей <code>/api/v1/health</code> сообщает <code>model_loaded: true</code>,
<code>device: cpu</code>, эпоху 39 и файл разметки <code>labels/labels_images.csv</code>, запрос анализа
отвечает корректной строкой результата, а <code>/api/v1/export</code> возвращает XLSX с ожидаемым
набором столбцов. Сквозная проверка выполнялась в контейнере на той же машине, где снимались
измерения производительности.</p>
<p><b>Что проверено на самом образе.</b> Сборка проходит три внутренние проверки:
<code>app import ok</code>, <code>frontend assets ok</code> и
<code>checkpoint ok: resnet18 linear threshold -0.4930129051208496</code>. Контейнер запущен без единого
монтирования (<code>docker run -d -p 8000:8000 dxa-quality:cpu</code>): чекпоинт присутствует внутри
образа (<code>/app/models/dxa_model.pth</code>), <code>/api/v1/health</code> сообщает
<code>model_loaded: true</code>, <code>device: cpu</code>, эпоху 39 и файл разметки
<code>labels/labels_images.csv</code>, запрос <code>/api/v1/analyze</code> отвечает корректной строкой
результата, а <code>/api/v1/export</code> возвращает XLSX с ожидаемым набором столбцов. Сквозная
проверка выполнена в контейнере на той же машине, где снимались измерения производительности. Оба
варианта дополнительно собраны под целевой <code>linux/amd64</code> (эмуляция на той же машине):
CPU-образ содержит <code>torch 2.8.0+cpu</code> и ни одного nvidia-пакета, GPU-образ —
<code>torch 2.8.0+cu126</code> с cuDNN 9.10.2 и библиотекой <code>libtorch_cuda.so</code>; в обоих
сервис стартует, и <code>/api/v1/health</code>, <code>/api/v1/analyze</code> и
<code>/api/v1/export</code> отвечают корректно (<code>device: cpu</code>, поскольку ускорителя в машине
нет). Работа на самом GPU не проверялась: для неё нужна машина с драйвером NVIDIA, где критерий
приёмки — <code>torch.cuda.is_available() == True</code> и <code>device: cuda</code> в
<code>/api/v1/health</code>.</p>
<h3>14.2. Воспроизводимость</h3>
@ -788,7 +802,10 @@ seed'ах. Принято правило «только экспертная т
<p><code>Jenkinsfile</code> выполняет три шага: сборка образа, публикация в реестр и выкладка в
кластер. Скрипт <code>run.sh</code> — единая точка входа для разработки и эксплуатации: обучение,
разметка, приведение имён файлов, инференс, сервис, сравнение вариантов разметки и тесты
(перечень команд — приложение Б).</p>
(перечень команд — приложение Б). Для запуска образа есть <code>docker-compose.yml</code> с двумя
сервисами (14.1). Файл выкладки swarm (<code>/data/deploy/rell-bone-2026/docker-swarm.yml</code>) лежит
вне репозитория: если он монтирует каталог с моделями в <code>/app/models</code>, монтирование
перекроет встроенный чекпоинт, поэтому его нужно убрать при обновлении выкладки.</p>
<h2 id="s15">15. Тесты и проверки</h2>
@ -968,7 +985,7 @@ seed'ах. Принято правило «только экспертная т
├── models/dxa_model.pth рабочий чекпоинт и отчёт обучения
├── tests/ pytest + браузерные проверки интерфейса
├── docs/ настоящий документ (HTML и PDF)
├── run.sh, Dockerfile, Dockerfile_cuda, Jenkinsfile, requirements.txt</pre>
├── run.sh, docker-compose.yml, Dockerfile, Dockerfile_cuda, Jenkinsfile, requirements.txt</pre>
<h2 id="a2">Приложение Б. Команды</h2>
@ -987,9 +1004,11 @@ python -m src.dxa.discriminator проверка вклада содер
./run.sh test pytest (209 тестов)
node tests/browser/ui_check.js проверки интерфейса (нужен сервер на 127.0.0.1:8123)
# контейнер
docker build -t dxa-quality .
docker run -v /path/to/data:/data -v /path/to/models:/app/models -p 8000:8000 dxa-quality</pre>
# контейнер (чекпоинт лежит внутри образа, монтировать пути не нужно)
docker compose up -d CPU-сервис в фоне, http://localhost:8000
docker compose --profile cuda up -d dxa-cuda GPU-сервис (нужен nvidia-container-toolkit)
docker build -t dxa-quality . сборка CPU-образа напрямую
docker run -p 8000:8000 dxa-quality запуск без compose</pre>
<p class="sign">Документ подготовлен по фактическому состоянию репозитория; машинные отчёты обучения —
<code>models/train_report.json</code>, обоснование разметки — <code>assets/labeling.md</code>.</p>

Binary file not shown.