bone_2026/assets/labeling.md

215 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Разметка датасета и выбор правила метки
Документ описывает, откуда берутся метки снимков, как проверялось правило
разметки и какие результаты из этого следуют. Числа проверяемы: ссылки на
источники приведены рядом.
## 1. Задача разметки
Экспертная оценка в наборе сделана на уровне **исследования**: в таблице
`разметка.xlsx` по каждому исследованию отмечены критерии качества, а не по
отдельному снимку. Модель же работает со снимками, поэтому вердикт исследования
нужно было перенести на каждый снимок — без догадок и без потери информации о
том, откуда метка взялась.
## 2. Экспертная таблица
Столбцы (две строки заголовков, данные с третьей):
| Столбец | Смысл | Тип нарушения |
|---|---|---|
| 2 | Позвоночник: укладка | `positioning` |
| 3 | Позвоночник: ось | `axis_deviation` |
| 4 | Позвоночник: артефакты, наложения | `artifact` |
| 5, 6 | Бедро R: позиционирование/ротация, область интереса | `rotation`, `roi_incorrect` |
| 7, 8 | Бедро L: то же | `rotation`, `roi_incorrect` |
| 9, 10, 11 | Итог по области | — |
| 12 | Комментарий эксперта | — |
**Семантика значений** установлена по данным, а не по формулировкам заголовков:
- `1` в столбце критерия означает **нарушение**, хотя часть заголовков
сформулирована положительно («корректная укладка»);
- итог области равен логическому ИЛИ критериев: бёдра 72/72 и 78/78,
позвоночник 96/99;
- три расхождения позвоночника (два случая «итог без критериев», один «критерий
без итога») трактуются как нарушение — по правилу «хотя бы один существенный
пункт нарушен»;
- заполненность: позвоночник 99/100, бедро R 72/100, бедро L 78/100; у 23
исследований есть комментарий.
## 3. Перенос вердикта на снимок
| Шаг | Что делается | Почему так |
|---|---|---|
| Ключ склейки | имя каталога исследования | таблица ссылается на каталог (`2.25…`), а в DICOM лежит другой идентификатор (`1.2.643…`); соответствие каталог → тег 100/100 |
| Склейка дублей | по хешу пиксельных данных | 544 файла — это 252 уникальных снимка |
| Область снимка | голосование по именам файлов группы | один снимок назван и как позвоночник, и как бедро; при равенстве голосов область остаётся неопределённой (такой снимок один) |
| Вердикт | оценка области переносится на её снимок | **каждая область встречается в исследовании ровно один раз**, поэтому не нужно решать, какой из нескольких снимков «плохой» |
| Сторона бедра | при единственном снимке бедра берётся единственный заполненный столбец | в 71 из 72 исследований с двумя бёдрами столбцы совпадают с именами файлов; в 7 исследованиях с одним снимком заполнена противоположная сторона. Факт переноса фиксируется флагом `laterality_mirrored`; теги `Laterality` в DICOM пусты |
| Тип нарушения | только из структурированных критериев | комментарии («сколиоз», «эндопротезирование ТБС») сохранены дословно: перекладывать свободный текст в код — догадка |
## 4. Правило метки и почему оно такое
Метка могла строиться двумя способами: только из таблицы или с добавлением
пометок, которые вручную проставлялись в именах файлов (суффикс `_bad`). Пометки
в именах оказались ненадёжными: они расходились с оценкой эксперта в **15 случаях
из 252**. Выбор сделан измерением, а не по вкусу.
**Постановка.** Разбиение по исследованиям зафиксировано один раз, оба варианта
обучены пятью seed'ами на нём, оценены по одному эталону — вердикту эксперта на
снимках валидации. Снимки валидации не участвуют в обучении ни в одном варианте.
Воспроизведение: `python -m src.dxa.excel_labels --label-rule {table,union,expert}`
и `python -m src.dxa.compare_labels --split-file labels/split_expert_seed42.json`.
**Результат** (53 снимка валидации, 16 нарушений по эталону, 5 seed'ов):
| Метрика (эталон) | только таблица | таблица или суффикс `_bad` |
|---|---|---|
| ROC-AUC | **0.6764** [0.6309, 0.7218] | 0.6199 [0.5840, 0.6559] |
| PR-AUC | **0.4759** [0.4141, 0.5377] | 0.4046 [0.3702, 0.4391] |
| F1 | **0.5676** [0.5270, 0.6082] | 0.5426 [0.5073, 0.5778] |
| Recall | 0.7000 | 0.8625 |
| Precision | 0.4857 | 0.4043 |
Парная разница («только таблица» − «с суффиксами»):
| Метрика | Δ, среднее [95 % ДИ] | Знаки по seed'ам |
|---|---|---|
| ROC-AUC | **+0.0564** [+0.0403, +0.0725] | `+++++` |
| PR-AUC | **+0.0713** [+0.0398, +0.1028] | `+++++` |
| F1 | +0.0251 [−0.0056, +0.0558] | `+++-+` |
| Precision | +0.0815 [+0.0590, +0.1039] | `+++++` |
| Recall | −0.1625 [−0.2715, −0.0535] | `--0--` |
**Решение: принято правило «только экспертная таблица».** Дополнительные пометки
из имён файлов ухудшали согласие модели с экспертом на невиданных
исследованиях: вариант с ними чаще срабатывал (recall 0.86), но за счёт
точности, а по беспороговым метрикам проигрывал на всех пяти seed'ах.
Оговорка: эталон — та же экспертная таблица, поэтому вариант, обучавшийся
непосредственно на ней, находится в выигрышном положении. Значимо здесь другое:
добавление ненадёжных пометок **снижает** согласие с экспертом, что и служит
аргументом против них.
## 5. Словарь типов нарушений
| Код | Подпись | Область | Источник |
|---|---|---|---|
| `positioning` | Некорректная укладка | позвоночник | таблица |
| `axis_deviation` | Отклонение оси | позвоночник | таблица |
| `artifact` | Артефакты и импланты | любая | таблица |
| `rotation` | Ротация, позиционирование | бедро | таблица |
| `roi_incorrect` | Некорректная область интереса | любая | таблица |
| `motion` | Движение, размытие | любая | критерии методики |
| `incomplete_anatomy` | Анатомия видна не полностью | любая | критерии методики |
| `labeling_error` | Ошибка разметки | позвоночник | критерии методики |
| `unspecified` | Нарушение без уточнения | любая | служебный |
Распределение в разметке: `rotation` 36, `artifact` 17, `axis_deviation` 10,
`roi_incorrect` 7, `positioning` 6, `unspecified` 5.
Словарь один на всё решение (`src/dxa/violations.py`): коды используют инференс,
отчёт DICOM SR и веб-интерфейс, подписи отдаёт сервер, копий в JavaScript нет.
## 6. Результат разметки
`labels/labels_images.csv` (и XLSX) — по одной строке на уникальный снимок:
| | позвоночник | бедро R | бедро L | неопред. | всего |
|---|---|---|---|---|---|
| качественных | 66 | 58 | 51 | 0 | 175 |
| с нарушением | 33 | 21 | 22 | 1 | 77 |
| **итого** | **99** | **79** | **73** | **1** | **252** |
Помимо метки в CSV сохранено происхождение: `quality_from_excel` (вердикт
эксперта), `quality_from_filename` (пометка из имени файла), `sources_conflict`,
`label_rule`, `filename_fallback`, `laterality_mirrored`, `region_ambiguous`,
`expert_comment`. Поэтому правило можно переиграть без повторного разбора.
Рядом лежат варианты для воспроизведения сравнения:
`labels_images_table.csv`, `labels_images_union.csv`, `labels_images_expert.csv`
(эталон: только снимки с экспертной оценкой, 249 строк) и
`split_expert_seed42.json` (зафиксированное разбиение).
## 7. Гигиена данных: имена файлов
Имена проставлялись вручную и разошлись: `spine_1` и `spine_01`, `Spine_01`,
`r_spine_03`, `r_hip03`, `r_hop_02` (опечатка), `spine-1`. Они приведены к виду
`<область>_<NN>[_good|_bad].dcm` инструментом `src/dxa/rename_files.py`;
переименовано 344 файла из 548, карта отката — `labels/rename_map.csv`.
Переименование сделано **после** того, как разметка и метрики были посчитаны, и
проверено, что оно на них не влияет: набор из 252 пиксельных групп идентичен до и
после, разметка не изменилась ни в одной строке. Суффиксы `_good`/`_bad`
сохранены как были и в метках не участвуют — только как диагностический столбец.
## 8. Оценка качества модели
Разбиение по исследованиям (по умолчанию seed 42): обучение 199 снимков /
81 исследование, валидация 53 снимка / 19 исследований, 16 нарушений.
| Метрика | Значение | Как получено |
|---|---|---|
| ROC-AUC | **0.6764** [0.6309, 0.7218] | 5 seed'ов на фиксированном разбиении, эталон — вердикт эксперта |
| PR-AUC | 0.4759 [0.4141, 0.5377] | там же |
| F1 | 0.5676 [0.5270, 0.6082] | там же; порог подобран по F1 на валидации, поэтому смещён вверх |
| ROC-AUC по областям | позвоночник 0.943, бедро R 0.576, бедро L 0.550 | рабочий чекпоинт, его собственная валидация |
Рабочий чекпоинт — обычный прогон с seed по умолчанию (эпоха 39, порог логита
−0.4930 → вероятность 0.379), его собственная валидационная ROC-AUC 0.6706
близка к среднему по seed'ам, то есть результат не отобран по удачности.
Проверка, что модель смотрит на снимок, а не угадывает анатомию: внутри областей
она даёт AUC 0.85–0.93, правило «позвоночник значит нарушение» — ровно 0.50.
Числа считаются на всём наборе, включая обучающие снимки, поэтому смещены вверх и
отвечают на вопрос «есть ли вклад содержимого», а не «каково качество на новых
данных».
## 9. Ограничения
1. **Разметка унаследована от исследования.** Таблица оценивает исследование, а
не снимок; перенос однозначен, потому что область встречается один раз, но
исходная оценка всё равно не поштучная.
2. **Мало данных:** 252 снимка, 77 нарушений. Интервалы широкие.
3. **Эталон — та же таблица.** Независимой истины нет; вариант, обучавшийся на
таблице, в сравнении в выигрышном положении.
4. **Тип нарушения — эвристика**, а не вывод модели: 5 снимков имеют только
`unspecified`, у остальных тип приходит из критериев таблицы.
5. **Три снимка без экспертной оценки** размечены по пометке в имени файла и
помечены `filename_fallback`.
6. **Сторона бедра в 7 исследованиях не проверяема:** теги латеральности пусты.
7. **Корректность областей интереса наследуется из таблицы:** разметки ROI в
DICOM нет, сравнить её напрямую не с чем.
## 10. Отрицательный результат: локальная vision-модель
Планировалась визуальная разметка локальной vision-моделью (9 млрд параметров,
офлайн), чтобы не зависеть от таблицы. На калибровке по 14 снимкам, из которых 8
заведомо с нарушениями, модель вынесла «непригоден» всем 14, включая все
качественные, с шаблонными формулировками и выдуманными имплантами. Разделяющая
способность — на уровне случайной, поэтому как разметчик модель непригодна.
Вывод, который стоит зафиксировать: разделяющую способность инструмента нужно
проверять **до** того, как строить на нём пайплайн. Инструменты рендера снимков и
контактных листов остались в `src/dxa/render.py` — они полезны для выборочной
ручной проверки.
## 11. Воспроизведение
```bash
./run.sh label # разметка по экспертной таблице
./run.sh rename # имена файлов (план; --apply)
./run.sh split && ./run.sh compare # выбор правила метки, 5 seed'ов
python -m src.dxa.excel_labels --label-rule union --out-name labels_images_union
python -m src.dxa.render --by-region --out dataset_hack/_preview
```
## 12. Что дальше
- Поштучная разметка снимков специалистом — снимет ограничение №1.
- Мультилейбл по типам нарушений вместо эвристики.
- Больше исследований (500+), чтобы сузить интервалы.
- Калибровка порога определения области под конкретное оборудование.