Сервис автоматизированного контроля качества денситометрических исследований (DXA): принимает DICOM, определяет анатомическую область, оценивает, пригодно ли изображение для клинической интерпретации, и формирует структурированный отчёт. https://bone.rell.ru/
Go to file
denis 9354f5f304 develop - hack_2026 2026-09-29 23:57:44 +03:00
assets develop - hack_2026 2026-09-27 22:04:50 +03:00
docs develop - hack_2026 2026-09-29 23:57:44 +03:00
labels develop - hack_2026 2026-09-29 23:57:44 +03:00
models develop - hack_2026 2026-09-27 22:04:50 +03:00
src develop - hack_2026 2026-09-27 22:04:50 +03:00
tests develop - hack_2026 2026-09-27 22:04:50 +03:00
.dockerignore develop - hack_2026 2026-09-27 03:43:07 +03:00
.gitignore develop - hack_2026 2026-09-27 22:04:50 +03:00
Dockerfile develop - hack_2026 2026-09-27 22:04:50 +03:00
Dockerfile_cuda develop - hack_2026 2026-09-27 23:17:28 +03:00
Jenkinsfile develop 2026-08-28 03:41:41 +03:00
QWEN.md develop - hack_2026 2026-09-27 23:17:28 +03:00
README.md develop - hack_2026 2026-09-27 22:04:50 +03:00
condition.txt develop - hack_2026 2026-09-22 22:21:43 +03:00
condition_doctor.txt develop - hack_2026 2026-09-22 22:21:43 +03:00
docker-compose.yml develop - hack_2026 2026-09-27 23:17:28 +03:00
requirements.txt develop - hack_2026 2026-09-27 02:20:47 +03:00
run.sh develop - hack_2026 2026-09-27 22:04:50 +03:00

README.md

🦴 DXA Quality Assessment

Сервис автоматизированного контроля качества денситометрических исследований (DXA): принимает DICOM, определяет анатомическую область, оценивает, пригодно ли изображение для клинической интерпретации, и формирует структурированный отчёт.

Результаты обработки в веб-интерфейсе: полоса состояния, статистика и таблица снимков

Что делает решение

Шаг Реализация
Определение анатомической области Ширина кадра (позвоночник / бедро) + голова области + геометрия яркой зоны
Бинарная оценка качества ResNet18 (ImageNet) → линейная голова; порог подобран по F1 на валидации
Тип нарушения Общая категория для снимков с нарушением; детальный тип требует разметки типов на уровне снимка
Отчёт XLSX/CSV со столбцами из требований; опционально zip с визуализацией зоны интереса
API FastAPI: анализ, детальный анализ, пакетная обработка, экспорт, DICOM SR (текст)
Веб-интерфейс Загрузка DICOM, таблица результатов, панель деталей с визуализацией, панель «О модели»

Установка и запуск

python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt

./run.sh label                                        # разметить датасет по Excel
./run.sh rename                                       # план приведения имён файлов
./run.sh train                                        # обучить модель качества
./run.sh infer "dataset_hack/Для теста" results.xlsx  # пакетная обработка
./run.sh serve                                        # API и веб-интерфейс на :8000
./run.sh split && ./run.sh compare                    # сравнить варианты разметки
./run.sh test                                         # тесты

Для инференса только на CPU (образ меньше, без CUDA-колёс):

pip install -r requirements.txt --extra-index-url https://download.pytorch.org/whl/cpu

Обучение и инференс можно вызывать напрямую:

python -m src.dxa.train --epochs 100 --output-dir models
python -m src.dxa.inference --input-path dataset_hack --output-path results.xlsx --zip-out masks.zip

После запуска сервера:

Docker

docker build -t dxa-quality .
docker run -v /path/to/data:/data -v /path/to/models:/app/models -p 8000:8000 dxa-quality

Чекпоинт в образ не копируется: он монтируется в /app/models, путь задаётся переменной DXA_MODEL_PATH (по умолчанию /app/models/dxa_model.pth). Без чекпоинта сервис всё равно поднимается — /api/v1/health сообщает model_loaded: false, а анализ отвечает ошибкой Model not loaded вместо тихой неверной оценки. Из сети ничего не скачивается. Сборка проверяет, что приложение импортируется и что офлайн-ассеты фронтенда на месте.


Архитектура

Пайплайн обработки DXA: загрузка DICOM, разбор метаданных, предобработка, инференс, определение области, метрики качества, отчёт

Схема работающего пути в терминах решения:

DICOM ──▶ предобработка ──▶ ResNet18 (заморожен) ──▶ линейная голова ──▶ логит
              │                                            │
              │                                            └──▶ голова области (вспомогательная)
              │
              ├──▶ геометрия яркой зоны: область, ROI, геометрия кадра
              └──▶ эвристики: резкость, «плотные» включения

Ключевые решения и почему они такие:

  1. Линейный зонд вместо полного fine-tune. Уникальных снимков в наборе ~250. Полный fine-tune ResNet18 переобучается за несколько эпох (train F1 → 1.0 при val AUC ≈ 0.5). Замороженный backbone + линейная голова удерживает val AUC ≈ 0.7–0.85. Режим --head mlp --freeze-epochs 0 оставлен для экспериментов на большем объёме данных.

  2. Метки на уровне снимка. Источник — labels/labels_images.csv, построенный из экспертной таблицы командой ./run.sh label (разбор — в assets/labeling.md): в таблице отмечены критерии качества по каждому исследованию, а каждая область встречается в нём ровно один раз, поэтому вердикт переносится на снимок однозначно. Такой разметки — 76 нарушений из 251 (30.3 %). Правило выбрано измерением: учёт ручных пометок из имён файлов дал худший результат на held-out наборе, поэтому в метках они не участвуют. Резервный режим --labels-csv "" берёт метку из суффикса _good/_bad и оставлен для совместимости.

  3. Склейка побайтных дублей. В датасете 482 файла, но 251 уникальный снимок: один и тот же кадр сохранён многократно под разными именами (часть — с меткой, часть — без). Без склейки одно изображение попадало бы в оба класса. Лишние байт-идентичные копии удалены.

  4. Разбиение по исследованиям. Снимки одного исследования не попадают одновременно в train и val — иначе метрики завышаются за счёт утечки.

  5. Аугментация отключена. Проверено экспериментально: яркостный разброс и сдвиг кадра снижают AUC с 0.87 до 0.56, потому что распределение яркости и положение области сами являются признаками качества. Флаг --augment включает её для экспериментов.

  6. Порог по логиту. При доле нарушений ~15 % порог 0.5 даёт нулевой recall. Порог подбирается по F1 на валидации и сохраняется в чекпоинт; решение принимается по логиту (численно устойчиво при насыщении вероятностей).

Схемы системы

Компоненты Поток данных
Диаграмма компонентов: веб-клиент, FastAPI, модели DXA, оценка качества, файловая система Поток данных: вход, предобработка, модели, анализ качества, выход
Последовательность детального анализа Развёртывание
Диаграмма последовательности: запрос /analyze/detailed, предобработка, инференс, регион, сегментация, отчёт Диаграмма развёртывания: клиент, приложение, ML-пайплайн, инфраструктура
Определение анатомической области Основные сущности
Алгоритм определения области: порог по перцентилю яркости, bounding box, соотношение сторон, сторона бедра Диаграмма классов: классификатор, модель, детальная оценка, определение области

Схемы взяты из проектного документа и показывают целевую архитектуру: блоки Orchestrator, UNet-сегментации и готовых вердиктов качества в решении не реализованы. Работающий путь описан выше.


Формат выходных данных

Основные столбцы соответствуют требованиям задания:

Столбец Описание
path_to_study Путь к исследованию (для HTTP-загрузки — upload://<имя>)
study_uid StudyInstanceUID
image_uid SOPInstanceUID
anatomical_region spine / hip_left / hip_right / hip
quality_class 0 — качественное, 1 — есть нарушение
violation_type Тип нарушения или пустая строка
processing_status Success или Failure: <причина>
time_of_processing Время обработки, секунды

Дополнительно добавляются confidence, violation_reason, region_confidence — они не мешают автоматическому разбору обязательных столбцов.

API

Метод Путь Назначение
GET / Веб-интерфейс
GET /label Интерфейс ручной разметки: подтверждение и правка вердиктов специалистом
GET /api/v1/health Статус, признак загрузки модели и её происхождение
GET /api/v1/model Карточка решения: разметка, данные, метрики с интервалами, словарь нарушений
POST /api/v1/analyze Базовый анализ одного файла
POST /api/v1/analyze/detailed Расширенный отчёт, опционально маска
POST /api/v1/analyze/sr Текстовое представление отчёта DICOM SR
POST /api/v1/batch Пакетный анализ
POST /api/v1/export Пакетный анализ и выгрузка в XLSX
GET /api/v1/labeling/items Снимки датасета для разбора: текущая метка, её источник, расхождения
GET /api/v1/labeling/image PNG снимка для просмотра
POST /api/v1/labeling/verdict Сохранить вердикт специалиста
DELETE /api/v1/labeling/verdict Снять вердикт, вернуть снимок к построенной разметке
GET /api/v1/labeling/export Выгрузка разметки: scope=all принимается обучением как --labels-csv
curl -X POST http://localhost:8000/api/v1/analyze -F "file=@study/spine.dcm"
{
  "study_uid": "1.2.643...",
  "image_uid": "1.2.643...",
  "anatomical_region": "spine",
  "quality_class": 1,
  "quality_label": "Есть нарушение качества",
  "violation_type": "artifact",
  "violation_type_label": "Артефакты и импланты",
  "violation_type_is_heuristic": true,
  "violation_type_note": "Тип нарушения определён эвристикой по метрикам снимка, а не моделью...",
  "reason": "Посторонние включения или артефакты в зоне интереса",
  "confidence": 0.72,
  "threshold_probability": 0.5103,
  "processing_status": "Success"
}

Коды violation_type берутся из единого словаря src/dxa/violations.py (positioning, axis_deviation, artifact, rotation, roi_incorrect, motion, incomplete_anatomy, labeling_error, unspecified) — первые пять кодирует экспертная таблица, остальные приходят из критериев пригодности. Поле violation_type_is_heuristic показывает, что тип определён эвристикой, а не моделью: модель решает только бинарную задачу. Подписи для интерфейса отдаёт сервер, своей копии словаря фронтенд не держит.


Веб-интерфейс

Полоса состояния в шапке показывает, какая модель сейчас работает: имя чекпоинта, источник разметки, эпоху и порог. По интерфейсу сразу видно, какая версия решения отвечает, — без обращения к логам.

Полоса состояния: загруженная модель, разметка, эпоха, порог

Строки таблицы результатов кликабельны, но об этом надо сказать прямо — иначе не догадываются. Над таблицей висит подсказка «Нажмите на любую строку», в конце каждой строки есть кнопка «Открыть», а клик по строке прокручивает страницу к панели деталей. Подсказка скрывается, когда фильтр не оставил ни одной строки. Кнопка «Открыть» — настоящая кнопка, поэтому панель доступна и с клавиатуры (Tab + Enter), а не только мышью. Сама таблица со статистикой и подсказкой — на первом скриншоте.

Панель деталей открывается по клику на строку: заключение, измерения и визуализация снимка. Специально показаны обе ветки — «нарушение» и «норма»:

Панель деталей для снимка с нарушением: красный бейдж, уровень HIGH, заключение с вероятностью и порогом, визуализация Панель деталей для качественного снимка: зелёный бейдж, справочные измерения
Нарушение. Бейдж «Нарушение», уровень HIGH, заключение с вероятностью и порогом, тип нарушения с пометкой «эвристика». Норма. Зелёный бейдж. Измерения (резкость, границы области) подписаны как справочные — их пороги не калиброваны.

Панель «О модели» (кнопка в шапке): что за чекпоинт работает, на какой разметке он обучен, с каким порогом решает, метрики сравнения с 95 % интервалами, состав данных, словарь типов нарушений и список ограничений.

Панель «О модели»: сведения о чекпоинте, метрики с интервалами, состав данных, словарь нарушений

Источник всех этих значений — сервер (/api/v1/health, /api/v1/model). Фронтенд намеренно не держит собственных копий: раньше подписи типов нарушений были продублированы в dxa-app.js и разошлись с серверными, из-за чего коды экспертной таблицы показывались как есть.

Что интерфейс теперь не утверждает:

  • тип нарушения помечен как «эвристика» с пояснением — модель решает только бинарную задачу и тип не предсказывает;
  • метрика рабочего чекпоинта помечена как завышенная прямо в баннере состояния и в карточке: чекпоинт выбран лучшим из пяти seed'ов, честная оценка варианта — среднее по seed'ам;
  • плитка «Ср. уверенность модели» больше не называется точностью: точность требует эталонных меток, которых для произвольного файла нет;
  • числовые метрики в панели деталей по-прежнему идут под дисклеймером о некалиброванности порогов.

Ручная разметка (/label)

Поштучной экспертной оценки снимков в наборе нет: таблица оценивает исследование, и снимок наследует вердикт исследования. Закрыть это автоматически нечем — локальная vision-модель оказалась непригодна (assets/labeling.md §11). Поэтому в сервисе есть отдельный интерфейс ручной разметки: раздел 2.6 задания просит «автоматическую коррекцию разметки с возможностью подтверждения специалистом».

Откройте http://localhost:8000/label. Интерфейс показывает снимок целиком и его текущую метку с указанием источника (экспертная таблица, имя файла, ручной вердикт). Снимки, где метка разметки расходится с пометкой в имени файла, идут первыми: там ошибка возможна в любом из источников. Специалист подтверждает вердикт или ставит свой — область, «годное / нарушение», тип нарушения, комментарий.

Вердикты сохраняются в labels/manual_labels.csv в том же формате, что и построенная разметка, поэтому файл можно сразу передать обучению:

curl -o manual_labels.csv "http://localhost:8000/api/v1/labeling/export?scope=all"
python -m src.dxa.train --labels-csv manual_labels.csv

scope=all накладывает ручные вердикты на построенную разметку (весь набор), scope=reviewed отдаёт только разобранные снимки — для сверки с построенной разметкой. Оценка модели в интерфейсе намеренно не показывается: подсказка смещала бы разметчика, а ценность здесь именно в независимом суждении.

Передать разметку врачу: автономный HTML-пакет

Интерфейс /label требует запущенного сервиса, а размечать должен специалист — на своей машине, без Python и без сети. Поэтому тот же сценарий упаковывается в один файл: снимки встроены внутрь, вердикты хранятся в браузере, выгрузка — CSV.

./run.sh review --out review/doctor_review.html          # весь набор, ~7 МБ
./run.sh review --limit 20 --out review/pilot.html       # пилот на выборке из 20 снимков

Файл врач открывает двойным щелчком: снимок, построенная метка с указанием источника, кнопки «годное / нарушение», тип нарушения, комментарий, горячие клавиши. Работает полностью офлайн — страница не делает ни одного сетевого запроса, и это проверяется автоматически (tests/browser/review_pack.js). Прогресс сохраняется в браузере, а кнопка «Выгрузить CSV» отдаёт файл, который принимается обучением как есть; загрузить его обратно можно на другой машине — кнопкой «Загрузить CSV».

Вердикты врача накладываются на построенную разметку одной командой, с проверкой, что все пути есть в датасете:

./run.sh review --merge review/doctor.csv --out labels/labels_images_reviewed.csv
python -m src.dxa.train --labels-csv labels/labels_images_reviewed.csv

Каталог review/ в git не хранится: пакет генерируется, а внутри — медицинские снимки.


Метрики

Метрики зависят от выбранного разбиения по исследованиям, поэтому приводятся с разбросом. Основная оценка — на фиксированном разбиении по исследованиям (обучение 198 снимков / 81 исследование, валидация 53 снимка / 19 исследований, 16 нарушений), пять seed'ов обучения, эталон — вердикт эксперта:

Что измерено Значение Как измерено
ROC-AUC 0.6726 [0.6367, 0.7086] 5 seed'ов на фиксированном разбиении
PR-AUC 0.4753 [0.4188, 0.5318] там же; базовый уровень при 30 % нарушений — 0.30
F1 0.5559 [0.5212, 0.5906] там же; порог подобран на той же валидации — смещено вверх
ROC-AUC по областям позвоночник 0.929, бедро R 0.606, бедро L 0.567 рабочий чекпоинт, собственная валидация
Контрольная задача «позвоночник / бедро» AUC 1.00 проверка работоспособности пайплайна
Модель использует снимок, а не область AUC 0.885 против 0.534 у правила области discriminator на всём наборе, включая обучающие снимки

Метрики по областям — в models/train_report.md, он создаётся при обучении. Разбивка важна, потому что нарушения распределены неравномерно: в позвоночнике 33 из 99, у бёдер 20–22 из 73–78, а область почти однозначно определяется по ширине кадра. Поэтому общий AUC частично отражает различение области, а не только распознавание дефекта.

Выбор правила разметки

Метку можно было строить только из экспертной таблицы или дополнительно учитывать пометки, проставленные вручную в именах файлов (суффикс _bad). Пометки расходились с оценкой эксперта в 62 случаях из 251, поэтому правило выбиралось измерением: одно разбиение, пять seed'ов, один эталон.

Метрика (эталон) только таблица с суффиксами имён
ROC-AUC 0.6726 [0.6367, 0.7086] 0.6003 [0.5592, 0.6415]
PR-AUC 0.4753 [0.4188, 0.5318] 0.3864 [0.3487, 0.4242]
F1 0.5559 [0.5212, 0.5906] 0.5354 [0.4978, 0.5729]
Recall / Precision 0.713 / 0.472 0.788 / 0.409

Парная разница (только таблица − с суффиксами): ROC-AUC +0.0723 [+0.0541, +0.0905], PR-AUC +0.0889 [+0.0500, +0.1277] — знаки +++++, то есть преимущество на всех пяти seed'ах.

Принято правило «только экспертная таблица». Пометки в именах файлов проставлялись вручную и оказались ненадёжными: они ухудшали согласие модели с экспертом на невиданных исследованиях. Оговорка: эталон — та же таблица, поэтому вариант, обучавшийся на ней, в выигрышном положении; значимо то, что добавление ненадёжных пометок согласие снижает.

Отчёт с полными числами и разбором ограничений — assets/labeling.md; отчёт обучения рабочего чекпоинта — models/train_report.json. Само сравнение правил хранится только в виде чисел в этих документах: чекпоинты прогонов удалены, а пересобрать их можно командой ./run.sh split && ./run.sh compare.

Время обработки одного снимка — порядка 0.02–0.05 с на CPU (ResNet18 с замороженным backbone), то есть требование «не более 3 минут на исследование» выполняется с большим запасом.


Ограничения (важно для интерпретации)

  1. Разметка выведена из оценки исследования. Экспертная таблица описывает исследование, а не снимок; перенос однозначен (область встречается один раз), но поштучной экспертной оценки снимков в наборе нет. Происхождение каждой строки зафиксировано в labels/labels_images.csv. Для поштучной разметки есть интерфейс /label (см. «Веб-интерфейс»), им ещё не пользовались. Разбор — в assets/labeling.md.

  2. Мало данных. 251 уникальный снимок, 76 нарушений. Доверительные интервалы широкие; оценка на закрытом наборе может отличаться.

  3. Тип нарушения определяется эвристиками, а не обученной моделью. Для честного мультикласса нужна разметка типов на уровне снимка — её даёт /label.

  4. Область определяется по размеру кадра. Признак безошибочно работает на этом оборудовании (99/99 для позвоночника), но при смене аппарата порог SPINE_MIN_WIDTH потребует калибровки.

  5. В DICOM нет разметки ROI. Ни overlay, ни graphic annotation в файлах нет, поэтому корректность нанесённых областей измерения нельзя проверить прямым сравнением — оценивается только геометрия видимой зоны.

  6. Эвристики из src/quality/detailed_assessment.py не калиброваны. Пороги для «движения», «артефактов» и отступов ROI рассчитаны на другой масштаб интенсивностей: на этом наборе они срабатывают почти для любого снимка, а признак резкости ведёт себя противоположно в позвоночнике и бёдрах. Поэтому в панели деталей показываются числовые измерения с пометкой «справ.», а не вердикты «Да/Нет». Классификацию выполняет только модель. Проверить её вклад можно командой:

    python -m src.dxa.discriminator --model-path models/dxa_model.pth
    

Что проверено и как

Проверка Команда Результат
Модель использует снимок, а не только область python -m src.dxa.discriminator AUC 0.885 против 0.534 у правила «позвоночник = нарушение»; внутри областей у модели 0.88–0.95, у правила 0.50
Контракт API для веб-интерфейса python -m pytest tests/test_api_contract.py поля панели деталей, различимость метрик, PNG-визуализации, отсутствие некалиброванных вердиктов, канонические коды типов нарушений, карточка модели
Ручная разметка python -m pytest tests/test_manual_labels.py tests/test_labeling_api.py проверка вердикта (область, метка, совместимость типа нарушения с областью), хранение и наложение на построенную разметку, отказ отдавать файлы вне датасета, выгрузка, читаемая обучением
Пакет разметки для врача python -m pytest tests/test_review_pack.py отпечаток набора, порядок «расхождения первыми», встроенные данные, отсутствие сетевых ссылок в странице, слияние вердиктов с разметкой
Единый словарь нарушений python -m pytest tests/test_violations.py коды, подписи, коды SR, приведение устаревших значений, согласованность с таблицей
Разметка и разбиение данных python -m pytest tests/test_labels.py склейка дублей, разбор имён, фиксация разбиения, отсутствие утечки между train/val
Имена DICOM-файлов python -m pytest tests/test_rename_files.py разбор имён, поиск свободного номера при конфликте, отказ от угадывания области, цикл «применить → откатить»
Разметка по экспертной таблице python -m pytest tests/test_excel_labels.py чтение критериев, голосование по области, перенос на единственное бедро, правила table/union/expert, подключение к обучению
Выбор правила метки ./run.sh split && ./run.sh compare ROC-AUC 0.6726 против 0.6003 по эталону, парная Δ +0.0723 [+0.0541, +0.0905], 5/5 seed'ов в пользу экспертной таблицы
Метрики и порог python -m pytest tests/test_preprocess_and_model.py подбор порога при дисбалансе, roundtrip чекпоинта, BatchNorm
Веб-интерфейс в браузере node tests/browser/ui_check.js подсказка о кликабельности строк видна и скрывается на пустом фильтре; кнопка «Открыть» открывает панель; значения панели меняются при переключении строк; панель «О модели» наполняется метриками и словарём
Интерфейс ручной разметки в браузере node tests/browser/ui_labeling.js расхождения идут первыми, снимок отрисовывается, вердикт сохраняется и переживает перезагрузку страницы, фильтр «только расхождения» работает, внешних запросов нет
Работа без сети node tests/browser/ui_offline.js ноль внешних запросов, стили и иконки на месте

Браузерные проверки требуют запущенного сервера:

python -m uvicorn src.main:app --port 8123
node tests/browser/ui_check.js      # панель деталей обновляется по клику
node tests/browser/ui_offline.js    # работа без доступа к внешним сервисам
# проверка ручной разметки пишет вердикты, поэтому файл стоит отвести в /tmp:
DXA_MANUAL_LABELS=/tmp/manual_ui.csv python -m uvicorn src.main:app --port 8123
node tests/browser/ui_labeling.js

Офлайн-режим обеспечен локальными копиями Tailwind и FontAwesome (src/api/static/vendor, src/api/static/webfonts); страница не обращается к CDN.

План доработки

  • Разметить типы нарушений на уровне снимка и обучить мультилейбл-классификатор.
  • Собрать 500+ исследований для устойчивых метрик и честной валидации.
  • Заменить порог по ширине кадра на калибровку по метаданным аппарата.

Структура проекта

bone_2026/
├── src/
│   ├── main.py                     # FastAPI: маршруты и загрузка модели
│   ├── dxa/                        # действующий модуль оценки качества
│   │   ├── labels.py               # разбор имён, метки, склейка дублей, сплит
│   │   ├── excel_labels.py         # разметка снимков по экспертной таблице
│   │   ├── manual_labels.py        # ручная разметка: хранение вердиктов (/label)
│   │   ├── review_pack.py          # автономный HTML-пакет разметки для врача
│   │   ├── rename_files.py         # приведение имён DICOM к единому виду
│   │   ├── violations.py           # единый словарь типов нарушений (коды, подписи)
│   │   ├── model_card.py           # карточка решения для /api/v1/model и интерфейса
│   │   ├── compare_labels.py       # сравнение источников разметки на одном наборе
│   │   ├── discriminator.py        # проверка вклада содержимого снимка
│   │   ├── render.py               # рендер снимков и контактных листов
│   │   ├── preprocess.py           # DICOM -> тензор (общий для обучения и API)
│   │   ├── dataset.py              # Dataset и DataLoader
│   │   ├── model.py                # сеть, метрики, подбор порога
│   │   ├── train.py                # обучение и отчёт
│   │   └── inference.py            # пакетный инференс, определение области
│   ├── quality/                    # эвристики качества, используются API
│   └── api/static/                 # веб-интерфейс (index.html — анализ, label.html — разметка)
├── labels/labels_images.csv        # разметка снимков: официальная (+ .xlsx)
├── labels/manual_labels.csv        # вердикты специалиста из /label (появляется после правок)
├── labels/labels_images_table.csv  # вариант «только таблица» (то же, что выше)
├── labels/labels_images_union.csv  # вариант «таблица или суффикс имени»
├── labels/labels_images_expert.csv # эталон для оценки: только снимки с оценкой
├── labels/split_expert_seed42.json # зафиксированное разбиение (19 исследований)
├── labels/rename_map.csv           # карта переименований файлов (для отката)
├── assets/img/                     # схемы архитектуры и скриншоты интерфейса
├── assets/labeling.md              # как построена разметка и как сравнивались варианты
├── models/dxa_model.pth            # рабочий чекпоинт (+ train_report.md/.json)
├── tests/                          # pytest: метки, сплит, метрики, модель, API
├── dataset_hack/                   # данные (в git не хранятся)
├── Dockerfile
├── requirements.txt
└── run.sh

Запуск обучения

python -m src.dxa.train --epochs 100 --output-dir models
Параметр По умолчанию Описание
--data-root dataset_hack Каталог датасета
--annotation-path dataset_hack/НД_для_обучения/разметка.xlsx Excel с разметкой (только отчёт о расхождениях)
--labels-csv labels/labels_images.csv Разметка снимков из ./run.sh label; пустая строка — метки из имён файлов; отсутствующий файл — откат к именам с предупреждением
--split-file — Зафиксированное разбиение из ./run.sh split; одинаковый held-out набор для сравнения вариантов разметки
--backbone resnet18 resnet18 / resnet34
--head linear linear (линейный зонд) / mlp
--freeze-epochs -1 -1 — backbone заморожен всегда; 0 — обучать всю сеть
--epochs, --batch-size, --learning-rate, --weight-decay 100 / 16 / 3e-4 / 5e-2 Оптимизация
--balance none loss / sampler для компенсации дисбаланса
--val-fraction, --seed 0.2 / 42 Разбиение по исследованиям
--augment выключено Включает яркостную аугментацию (ухудшает метрики, см. п. 5)
--output-dir models Куда сохранять чекпоинт и отчёты
--dry-run — Проверить разбор данных и разбиение без обучения

После обучения в --output-dir появляются dxa_model.pth, train_report.md и train_report.json; отчёт удобно приложить к презентации.

Команда

  • Грачев Денис — разработка
  • Грачев Татьяна — капитан
Built for Bone Quality Assessment Hackathon 2026