Контроль качества денситометрииЦифровой помощник по оценке качества DXA

Принимает снимок DICOM и решает, пригоден ли он для дальнейшего анализа, с указанием причины

2анатомические области: поясничный отдел, проксимальный отдел бедра
252уникальных снимка в 100 исследованиях
≈0.05 сна исследование при бюджете 3 минуты
офлайнв контейнере, без передачи снимков наружу
Главная работа — не архитектура, а разметка. Экспертная оценка в наборе сделана на уровне исследования; мы перенесли её на отдельные снимки и измерили, какое правило разметки даёт лучшее качество. Правило выбиралось экспериментом, а не по вкусу.
Формат результата: одна строка на снимок — path_to_study, study_uid, image_uid, anatomical_region, quality_class, violation_type, processing_status, time_of_processing

ПостановкаЧто подаём на вход и что получаем на выходе

DICOMдо трёх изображений в исследовании, разметки ROI внутри нет
→
Решениеобласть + бинарный класс + тип нарушения
→
ТаблицаXLSX или CSV, строка на снимок
Колонка результатаСодержание
study_uid, image_uidидентификаторы исследования и снимка из DICOM
anatomical_regionпозвоночник, правое или левое бедро
quality_class0 — качественное, 1 — есть нарушение
violation_typeкод нарушения из единого словаря
processing_statusSuccess или Failure, ошибки не выбрасываются исключением
time_of_processingвремя обработки, секунды

Требования, которые определили решения

  • Офлайн. Медицинские изображения не покидают контур — это исключило внешние модели и облачные API на всех этапах, включая разметку.
  • Контейнер и скрипт запуска. Зафиксированные версии зависимостей, образ без данных; веса монтируются при запуске.
  • До трёх минут на исследование. Мы укладываемся с запасом более тысячи раз.
  • Воспроизводимость. Фиксированные генераторы, состав разбиения в файле, происхождение модели внутри чекпоинта.
  • Приоритетные метрики. F1 и ROC-AUC с 95 % доверительными интервалами — отсюда вся логика сравнения на слайдах 8–9.

Данные544 файла — это 252 снимка: половина набора оказалась дублями

544файла на диске
252уникальных снимка по пикселям
100исследований
99 / 79 / 73позвоночник / бедро R / бедро L

Что пришлось учесть

  • Дубли. Склейка по хешу пиксельных данных: иначе один снимок попадал в обучение и в валидацию одновременно.
  • Конфликт имён. Два дубля одного снимка названы как разные области — область решается голосованием по именам.
  • Каталог ≠ StudyInstanceUID. Экспертная таблица ссылается на имя каталога, в DICOM лежит другой идентификатор.
  • Латеральность пуста. Теги стороны бедра не заполнены — сторону нельзя проверить по метаданным.
  • Разметки ROI нет. Ни оверлеев, ни графических аннотаций — сравнить нанесённые области напрямую не с чем.

Почему дубли — это не мелочь

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

Распределение

Позвоночник 33 нарушения из 99, правое бедро 21 из 79, левое 22 из 73. Неравномерность учитываем отдельно: область почти однозначно определяется шириной кадра, поэтому общая метрика частично отражает различение области, а не качества.

РазметкаОт оценки исследования — к метке отдельного снимка

Экспертиза в наборе сделана по исследованиям; модель работает со снимками, поэтому вердикт нужно было перенести

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

Что получилось

252 снимка размечены

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

Три снимка таблица область не оценивала. Для них метка взята из служебной пометки и помечена флагом — иначе они остались бы без метки вообще.
Правило разметки выбиралось измерением — об этом следующий слайд: служебные пометки при подготовке набора расходились с оценкой эксперта в 15 случаях из 252.

Выбор правилаКакое правило разметки лучше — решил эксперимент

Служебные пометки расходились с оценкой эксперта в 15 случаях из 252: верить на слово нельзя ни одному варианту

Только экспертная таблицапринято по результату измерения
175 качественных
77 нарушений
Таблица и служебные пометкипометки при подготовке набора
160 качественных
92
нарушениекачественноевсего 252 снимка
15снимков, где пометка противоречит оценке эксперта
5 × 2прогонов: пять seed'ов на два варианта
одно разбиениеи один эталон для обоих вариантов
Учитывать ли служебные пометки — вопрос не вкуса, а измеримого качества. Постановка и результат — на слайдах 8–9.

ТаксономияДевять кодов нарушений — из двух документов задания

Пять кодирует экспертная таблица, три задаёт методика оценки пригодности, один служебный

Некорректная укладкаpositioning · позвоночник
Отклонение осиaxis_deviation · позвоночник
Артефакты и имплантыartifact · любая область
Ротация, позиционированиеrotation · бедро
Некорректная область интересаroi_incorrect · любая область
Движение, размытиеmotion · любая область
Анатомия видна не полностьюincomplete_anatomy · любая область
Ошибка разметкиlabeling_error · позвоночник
Нарушение без уточненияunspecified · служебный код
экспертная таблица критерии методики служебный
Раньше словарь существовал в трёх копиях — в модуле инференса, в API и в JavaScript интерфейса, — и ни одна не знала кодов экспертной таблицы: в интерфейсе они показывались как есть. Теперь словарь один, подписи отдаёт сервер.

Распределение в разметке: rotation 36, artifact 17, axis_deviation 10, roi_incorrect 7, positioning 6, unspecified 5.

МодельЗамороженный ResNet18 и линейная голова

DICOMперцентильная нормализация, 224×224
→
ResNet18веса ImageNet, заморожен
→
Головалинейный слой, 2 класса
ПараметрЗначение
Обучаемые параметрытысячи вместо 11.2 млн
Вспомогательная голова областивес в потерях 0.3
Пороглогит −0.493 (вероятность 0.379)
Чекпоинт42.8 МБ
Отбор эпохисглаженный ROC-AUC, окно 5

Почему не полное дообучение

Полный fine-tune на двухстах снимках переобучается: train-метрика уходит в единицу, качество на валидации — к случайному. Замороженный backbone даёт стабильные признаки.

Почему порог не 0.5

Вероятности насыщаются у краёв распределения, поэтому фиксированный порог при доле нарушений около 30 % давал почти нулевой recall. Порог подбирается по F1 на валидации и хранится в чекпоинте вместе с весами.

Почему при заморозке отключён и train-режим

Слои нормализации остаются в eval: иначе бегущие статистики продолжают обновляться на обучающих батчах, и признаки уезжают от тех, на которых калибровалась входная нормализация.

Проверено и отвергнутоТри решения приняты по результатам экспериментов

отвергнуто

Аугментация

Яркость и положение снимка сами являются признаками качества. Случайные искажения уничтожают ровно ту информацию, которую надо распознать.

AUC 0.87 → 0.56

при включении аугментации

отвергнуто

Служебные пометки в данных

Служебные пометки расходились с оценкой эксперта в 15 случаях из 252. Обучение с ними дало худшее качество на невиданных исследованиях.

ROC-AUC 0.620

против 0.676 у правила «только таблица»

отвергнуто

Локальная vision-модель 9B

Планировали размечать снимки визуально, офлайн. На калибровке из 14 снимков модель вынесла «непригоден» всем 14 — включая заведомо качественные — с выдуманными имплантами.

14 из 14

разделяющая способность на уровне случайной

Общее правило: инструмент проверяется на разделяющую способность до того, как строить на нём пайплайн. Каждая из трёх проверок заняла один прогон, а не дни разработки.

ЭкспериментКак сравнивать варианты, не обманывая себя

Ловушка первая. Обучать и оценивать на одних и тех же метках — круговое сравнение: выигрывает тот вариант, который проще запомнить.

Ловушка вторая. Разбиение стратифицируется по нарушениям, а они зависят от меток. Без фиксации у вариантов были бы разные валидационные наборы, и метрики оказались бы несравнимы.

Что сделали

  1. Зафиксировали разбиение по исследованиям — стратификация по вердикту эксперта.
  2. Обучили оба варианта пятью seed'ами на одном и том же наборе исследований; отличаются только метки.
  3. Оценили оба по одному эталону — вердикту эксперта на снимках валидации.

Схема

Валидация: 53 снимка, 16 нарушений по эталону. Снимки валидации не участвуют в обучении ни в одном варианте.

Вариант A — только экспертная таблица77 нарушений в наборе
Вариант B — таблица и служебные пометки92 нарушения в наборе
Один эталон для обоихвердикт эксперта, а не собственные метки варианта

Порог для F1 подбирается на эталоне отдельно для каждой модели, поэтому F1 сравнивается как рабочая точка, а ROC-AUC и PR-AUC — как беспороговые метрики.

РезультатПравило «только экспертная таблица» выигрывает устойчиво

Среднее по пяти seed'ам, 95 % доверительный интервал, оценка по одному эталону

0.40 0.50 0.60 0.70 0.5 — случайное угадывание ROC-AUC PR-AUC F1 0.6764 0.6199 0.4759 0.4046 0.5676 0.5426
только экспертная таблица со служебными пометками отрезок — 95 % доверительный интервал
Парная разница (таблица − с пометками)

ROC-AUC +0.0564

интервал +0.0403 … +0.0725

PR-AUC +0.0713

интервал +0.0398 … +0.1028

F1 +0.0251

интервал −0.0056 … +0.0558

ROC-AUC и PR-AUC выше на всех пяти seed'ах, доверительный интервал не включает ноль.

Оговорка: эталон — та же таблица, поэтому сравнение частично благоприятствует ей. Значимо то, что добавление служебных пометок согласие с экспертом снижает.

Проверка вклада моделиМодель смотрит на снимок, а не угадывает анатомию

Нарушения распределены почти равномерно, но область почти однозначно определяется шириной кадра — поэтому нужна отдельная проверка

0.5 0.7 0.9 1.0 порог случайного угадывания Общий Позвоночник Бедро R Бедро L 0.529 0.854 0.500 0.928 0.500 0.861 0.500 0.853
модель правило «позвоночник = нарушение»
Внутри области правило не имеет подсказки и даёт ровно 0.500. Модель на тех же снимках — 0.85–0.93. Значит, она использует содержимое изображения.

Как читать числа

  • Общий — по всему набору, включая обучающие снимки, поэтому значения смещены вверх.
  • Проверка отвечает на вопрос «есть ли вклад содержимого», а не «каково качество на новых данных».
  • Общий AUC у правила 0.529 — тоже выше 0.5, потому что в позвоночнике нарушений чуть больше: это и есть та подсказка, которую проверка исключает.
Без этой проверки можно было бы принять за распознавание качества обычное определение анатомической области.

Рабочая модельМетрики чекпоинта и одна честная оговорка

Собственная валидацияЗначение
Объём: 53 снимка / 19 исследований16 нарушений
ROC-AUC0.6706
PR-AUC0.4585
F1 / recall / precision0.5600 / 0.875 / 0.412
Пороглогит −0.493 (вероятность 0.379)
ROC-AUC по областямn / нарушенийAUC
Позвоночник19 / 50.943
Бедро R17 / 60.576
Бедро L17 / 50.550
Это обычный прогон с seed по умолчанию, а не лучший из выборки. Его собственная оценка 0.6706 близка к среднему по пяти seed'ам 0.6764 — результат не отобран по удачности.
Чего нельзя утверждать. Порог подобран на той же валидации по F1, поэтому F1 смещён вверх. Эталон — экспертная таблица, независимой истины нет. В валидации всего 16 нарушений, интервалы широкие.

Что это означает для внедрения

Ориентир для планирования — ROC-AUC около 0.68. Модель стоит использовать как фильтр для приоритизации ручного просмотра, а не как единственное заключение: пропуск нарушения дороже ложной тревоги.

ИнтерфейсРезультаты обработки — и как открыть детали

Скриншоты настоящего сервиса: загружены три снимка, обработаны за доли секунды

Полоса состояния: загруженная модель, разметка, эпоха, порог Таблица результатов: статистика по файлам, подсказка о кликабельности строк, строки с качеством и уверенностью

Сверху — что за модель работает; ниже — статистика и таблица. Подсказка над таблицей объясняет, что строки открываются кликом.

ИнтерфейсПанель деталей: нарушение и норма

Открывается кликом по строке: заключение, измерения и визуализация снимка

Панель деталей для снимка с нарушением: красный бейдж, заключение, визуализация

Нарушение. Бейдж «Нарушение», уровень HIGH, заключение с вероятностью и порогом, тип нарушения с пометкой «эвристика».

Панель деталей для качественного снимка: визуализация и измерения

Норма. Зелёный бейдж. Измерения (резкость, границы области) подписаны как справочные — их пороги не калиброваны.

ИнтерфейсПанель «О модели»: числа без завышения

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

Что здесь важно

  • Названо происхождение чекпоинта: разметка, эпоха, порог решения.
  • Метрики сравнения правил с 95 % интервалами — те же числа, что в отчёте, а не отдельная «красивая» сводка.
  • Состав данных: 252 снимка, 100 исследований, 77 нарушений в разметке, 3 снимка без экспертной оценки — они не спрятаны.
  • Словарь типов нарушений с указанием источника каждого кода.
  • Список ограничений — прямо в интерфейсе, а не только в документации.
Панель собирается из ответа сервера: подписи и числа не дублируются в JavaScript, поэтому интерфейс не может разойтись с моделью.

ЭксплуатацияСкорость, требования и упаковка

15 мсна снимок на ускорителе, медиана
20 мсна снимок на CPU, 8 потоков
≈0.05 сна исследование из трёх снимков
>3000×запас к бюджету три минуты

Что показали замеры

  • Разница между ускорителем и процессором — в пределах 1.4×. Время уходит на декодирование DICOM и препроцессинг, а не на сеть.
  • Значит, ускоритель для этой задачи не обязателен: минимальная конфигурация — CPU.
  • Чекпоинт 42.8 МБ, память ограничена средой исполнения, а не моделью.

Упаковка и API

  • Контейнер с зафиксированными версиями; образ без данных и без весов — чекпоинт монтируется при запуске.
  • Пакетная обработка, выгрузка XLSX, текстовый отчёт DICOM SR, веб-интерфейс с панелью «О модели».
  • processing_status = Failure вместо исключения — отчёт формируется всегда.
  • 208 автотестов, включая три браузерных сценария на живом сервере.
Интерфейс не утверждает больше, чем известно решению: тип нарушения помечен как эвристика, метрика названа без завышения, средняя уверенность не называется точностью.

Честные границыЧто решение не умеет и почему это сказано прямо

ОграничениеСледствиеЧто планируется
Разметка выведена из оценки исследования поштучной экспертной оценки снимков в наборе нет разметка отдельных снимков специалистом
Мало данных: 252 снимка, 77 нарушений широкие интервалы, закрытый набор может дать другие цифры 500+ исследований
Эталон — та же экспертная таблица сравнение правил частично благоприятствует таблице независимая экспертная оценка снимков
Тип нарушения — эвристика, не модель 5 снимков имеют только «нарушение без уточнения» мультилейбл-модель по типам
Область определяется по ширине кадра порог привязан к текущему оборудованию калибровка по метаданным аппарата
Сторона бедра в 7 исследованиях не проверяема теги латеральности в DICOM пусты запрос тега у источника данных
Разметки ROI нет в DICOM корректность областей наследуется из таблицы проверка на данных с экспортной разметкой
Позиция: сомнительный снимок лучше отправить специалисту, чем пропустить. Поэтому решение проектировалось так, чтобы ошибаться в сторону ручной проверки, а не автоматического одобрения.

ИтогТри тезиса

1. Правило разметки выбрано измерением, а не по вкусу

Одно разбиение, пять seed'ов, один эталон: учёт служебных пометок дал ROC-AUC 0.6199 против 0.6764 у разметки только по экспертной таблице — хуже на всех пяти seed'ах.

2. Модель проверена на то, что она смотрит на снимок

Внутри областей 0.85–0.93 против 0.50 у правила «позвоночник значит нарушение». Это исключает подмену распознавания качества определением анатомии.

3. Сказано и то, чего решение не умеет

Тип нарушения — эвристика, эталон — та же таблица, данных мало. Ориентир для внедрения — ROC-AUC около 0.68, использование как фильтра для приоритизации ручного просмотра.

Материалы: docs/pitch.html — спич, определения и техническая выкладка; docs/labeling.md — разметка и выбор правила; README.md — сборка и запуск
1 / 15
→ далее · ← назад · N заметки · F полный экран