- BrainTools - https://www.braintools.ru -

«Ты не пройдёшь!»: как мы учили нейросеть искать патологии на КТ, обучая её только на норме. Наш опыт на ЛЦТ-2025

Привет, Habr!

Знаете, как обычно проходят будни исследователя в AI? Сидишь, читаешь статьи, пьёшь восьмую кружку кофе и пытаешься уговорить модель наконец‑то сойтись. А потом кто‑то из коллег кидает в чат ссылку: «Ребята, тут хакатон. „Лидеры цифровой трансформации 2025“. По медицине. Пойдём?».

Ну, а мы что? Мы пошли.

Мы — это три исследователя из группы Foundation Models лаборатории «Сильный ИИ в медицине» Института AIRI. Базируемся в Москве, любим большие модели и сложные задачи. Нам достался, возможно, один из самых интересных треков: «Сервис для выявления компьютерных томографий органов грудной клетки без патологий».

Казалось бы, что сложного? Но тут дьявол в деталях. О них и хотелось бы рассказать подробнее.

ChatGPT

ChatGPT

Проблема: найти то, не знаю, что

Обычно в медицинском AI как? Вот вам 100 500 снимков с пневмонией, вот 100 500 здоровых. Обучите классификатор.

Наша задача была поставлена хитрее. Мы должны были создать сервис, который бы отсеивал абсолютно здоровых пациентов. Зачем? Чтобы врачи‑рентгенологи — люди, чьё время бесценно, — не тратили его на просмотр «чистых» снимков, а концентрировались на тех, где действительно что‑то есть.

Проблема в том, что «что‑то есть» — это бесконечное множество вариантов. Новые болезни, редкие патологии, артефакты. Обучить модель на всех возможных отклонениях нереально.

Наша идея: режим «вышибалы»

Тут мы и решили: а что, если пойти от обратного? Вместо того чтобы учить модель находить патологии, мы научим её тому, как выглядит идеальная норма.

Представьте, что наша модель — это строгий охранник на входе в элитный клуб «Здоровая грудная клетка». Он досконально изучил, как выглядит «свой» (здоровый снимок). А если к нему подходят КТ с чем‑то непонятным — будь то опухоль, ателектаз или просто странный артефакт, который он видит впервые, — он бьёт тревогу и кричит: «Ты не пройдёшь!».

Именно эта идея и легла в основу нашего проекта. Фишка была в том, чтобы обучать модель только на нормальных снимках, но при этом заставить её находить любые аномалии.

Наша тех-кухня: как это (почти) работало

Тут нам, честно говоря, повезло. Не пришлось неделями копать arXiv в поисках «той самой» архитектуры. Оказалось, что у нашего коллеги Михаила Гончарова как раз недавно вышла статья (вот она [1]), которая описывала ровно тот подход, который был нам нужен. Грех было не воспользоваться!

Если в двух словах, то наш «Screener» — это не одна модель, а целая система из пары компонентов. Мы взяли за основу фреймворк для поиска аномалий Unsupervised Visual Anomaly Segmentation (UVAS) и немного его докрутили.

1. Модель-Дескриптор (Descriptor model)

Первым делом нам нужен был кто‑то, кто умеет «смотреть» на 3D‑снимок и создавать детальный «цифровой отпечаток» (дескриптор) для каждого вокселя (пикселя в 3D). Мы взяли UNet‑подобную архитектуру и обучили её с помощью плотного (dense) self‑supervised learning (SSL). По сути, мы заставили её научиться отличать один кусочек грудной клетки от другого, просто на большом объёме неразмеченных данных.

2. Модель-Оценщик (Density model) — наш «вышибала»

Мы немного упростили подход, описанный в статье. Наш «вышибала» (Оценщик) работал без «кондишн‑модели», то есть ему не нужно было сверяться с «ожиданиями». Он принимал решение, глядя всего на один отчет для каждой точки снимка (обозначим её как p):

Отчет от Дескриптора (назовем его «Факт»). Это «цифровой отпечаток» (в статье — вектор y[p]), который модель создает для каждой точки p на снимке. Этот набор чисел описывает то, как выглядит ткань в этом месте. Это максимально сухой ответ-факт на вопрос: «Что я здесь вижу?».

Как же тогда работал «вышибала»?

Во время обучения [2] Оценщик просто насмотрелся на миллионы этих «фактов» (y[p]) и выучил, как выглядит статистически нормальный цифровой отпечаток здоровой ткани. Он построил в своей «голове» эталонный портрет «нормы».

А дальше всё просто. Когда ему на вход поступал «Факт» (y[p]) с нового снимка, Оценщик смотрел на него и думал: «Так, этот отпечаток похож на то, что я видел в клубе „Здоровая грудная клетка“?».

Если «Факт» (y[p]) сильно не совпадал с его представлением о «норме» (например, имел статистически редкие, нетипичные значения), «вышибала» бил тревогу и присваивал этой точке высокий балл аномальности. Готово, патология поймана!

Адаптация? Нет. Боль с данными? ДА!

Нам не пришлось страдать, пытаясь адаптировать архитектуру — в статье «Screener», которую мы взяли за основу, уже была 3D‑модель, так что эту часть боли [3] мы счастливо пропустили.

Но зато мы во всей красе вкусили вторую главную боль любого AI‑соревнования — данные.

Помните, нашему «вышибале» нужна была только чистая норма? Ему нужен был «золотой стандарт» абсолютно здоровых органов грудной клетки. Где его взять?

Мы взяли открытый датасет CT‑RATE. Там была куча КТ‑снимков, и вот в чем его фишка: у каждого снимка была подробная разметка. В ней был целый чек‑лист из 18 (восемнадцати, Карл!) возможных патологий — от кардиомегалии до эмфиземы. Мы пожали плечами и приняли волевое решение: «Если у снимка в чек‑листе ни одна из этих 18 патологий не отмечена — бинго! Считаем это „золотой“ нормой!».

Данных, очевидно, всё равно не хватало. И тут мы придумали наш главный хак.

В CT‑RATE был класс «узелки в легких». Это 100% патология. Но в чём проблема: узелок — это, условно, 3–5 срезов из всего 3D‑объёма, в котором их 100+. Просто выкинуть весь «больной» скан? Расточительство!

Нам нужно было точно вырезать эти узелки, а всё остальное — забрать. Чтобы сделать это чисто, мы прогнали эти «больные» снимки через TotalSegmentator [4]. Он помог нам получить точную 3D‑сегментацию этих узелков. Имея на руках эту маску «зла», мы написали скрипт, который находил все срезы, пересекающиеся с этой маской, и аккуратно вычищал их из 3D‑тома.

А оставшиеся 90+ «чистых» срезов из того же снимка мы с радостным гиканьем кинули в нашу копилку «нормы». Профит! Так мы быстро и почти бесплатно нехило пополнили наш «чистый» датасет.

Конечно, вы понимаете, что «нет лейбла» — не значит «нет патологии». В этой нашей «норме», собранной «из того, что было», всё ещё оставался мусор.

«Ты не пройдёшь!»: как мы учили нейросеть искать патологии на КТ, обучая её только на норме. Наш опыт на ЛЦТ-2025 - 2

А оно вообще работает? Стресс-тест на «диких» данных

Вопреки всем канонам жанра, с обучением самой ML‑модели у нас почти не было проблем (да, мы сами в шоке). Loss красиво падал, GPU не взрывалась, модель честно училась отличать «норму» от «всего остального». Но оставался главный вопрос: а не «заточилась» ли наша модель конкретно под датасет CT‑RATE?

Знаете, как это бывает: на своём тестовом сплите у тебя 99% accuracy, ты несёшь её в прод, а там её встречает реальный мир, и всё разваливается.

Чтобы не сесть в лужу, мы устроили нашему Screener’у настоящую проверку на прочность. Для этого мы взяли два совершенно незнакомых ему открытых датасета из серии MosMedData:

  1. MosMedData‑LDCT‑LUNGCR‑type I‑v 1 (снимки с раком лёгких)

  2. MosMedData‑CT‑COVID19-type I‑v 4 (снимки с COVID-19)

Мы прогнали их через наш сервис, зажмурились и… получили очень приличные метрики.

Но самое смешное было даже не в этом. Метрики на этих абсолютно новых для модели данных оказались выше, чем на нашем собственном тестовом сплите из CT‑RATE!

Мы сначала не поверили, а потом поняли: это значит, что та «норма», которую мы с таким трудом вычищали из CT‑RATE, всё равно была, скажем так, грязноватой. А эти новые датасеты оказались чище, и наша модель смогла показать себя во всей красе.

Это был, пожалуй, самый приятный инсайт: наш «вышибала» действительно научился отличать «норму» от «патологии» в целом, а не просто выучил особенности одного датасета.

Инференс модели

Инференс модели

Модель сошлась. А что с инженерией?

Задачей соревнования было сделать сервис. А сервис — это не просто model.h5 в Jupyter‑ноутбуке, это в первую очередь инженерия. И первой инженерной задачей по условиям хакатона стал «фейс‑контроль» на входе: нам нужен был «умный» сервис, который не просто ищет аномалии, а делает это только на КТ грудной клетки. Любые другие модальности (МРТ, рентген), «битые» файлы или КТ других частей тела он должен был вежливо «отшивать».

И это не просто прихоть организаторов. Что будет, если врач (или тестировщик) по ошибке [5] загрузит ему КТ брюшной полости? Или коленки? Или вообще МРТ мозга [6]? Наша модель, которая за всю свою жизнь не видела ничего, кроме грудной клетки, просто сойдёт с ума. Она начнёт находить жуткие аномалии в мениске или сером веществе, потому что, с её точки зрения [7] (то есть, статистически), это действительно аномалия — ведь это ни разу не похоже на здоровую ткань грудной клетки.

Нам был нужен строгий «фейс‑контроль» на входе.

Тут мы и прикрутили TotalSegmentator. Это мощнейшая готовая модель, которая умеет находить и сегментировать на КТ‑снимках кучу разных органов.

Наш пайплайн на входе стал выглядеть так:

  1. Пользователь загружает какой‑то DICOM‑файл.

  2. Сначала — быстрая проверка «по паспорту». Мы лезем прямо в DICOM‑теги файла и проверяем: это вообще КТ (тег Modality == CT)? Это грудная клетка (тег BodyPartExamined == CHEST)? Если это МРТ, рентген или «битый» файл (где теги не читаются) — он сразу получает отказ.

  3. Если «паспорт» в порядке, мы не спешим отдавать файл нашему Screener’у. Сначала он уходит «на допрос» к TotalSegmentator.

  4. Мы задаём ему простой вопрос: «Парень, ты видишь на этом снимке лёгкие и сердце?».

  5. Если TotalSegmentator отвечает: «Да, вот они!» (то есть успешно их сегментирует), — мы говорим: «Отлично, это наш клиент!» — и только тогда передаём снимок нашему Screener’у для поиска патологий.

  6. А если TotalSegmentator разводит руками (не находит легкие или сердце), сервис вежливо отвечает пользователю: «Извините, это не похоже на КТ грудной клетки. Обработка невозможна».

От «что-то не так» до «патология в левом легком»

Итак, наш «вышибала» (Оценщик) отработал. На выходе мы получили не просто «да/нет», а 3D‑тепловую карту аномальности. На ней были ярко подсвечены все воксели, которые модель сочла подозрительными.

Но врачу нужна конкретика. Как превратить эту подсветку в диагноз? Мы сделали следующий конвейер:

  1. Отсекаем шум. Сначала мы превратили тепловую карту в чёткую бинарную маску предсказаний. Просто взяли и отсекли все воксели, у которых балл аномальности был ниже, чем 0.7.

  2. Принимаем решение. Теперь у нас есть маска. Но это уже патология или просто какой‑то артефакт? Мы ввели свой «критерий паники»: просто просуммировали значения всех вокселей, которые попали в маску. Если итоговая сумма была больше 1800 — мы считали, что на снимке точно есть что‑то клинически значимое.

  3. Локализуем! А где именно? Вот тут‑то нам снова пригодился — вы не поверите! — TotalSegmentator, который мы уже использовали на входе! Мы брали нашу готовую маску аномалии и накладывали её на маску органов, которую нам давал сегментатор. Пересечение давало нам точный ответ: «Аномалия находится в таком‑то органе / такой‑то части грудной клетки».

Кстати, эти «магические числа» (0.7 и 1800) мы не из головы взяли. Мы долго подбирали их на нашем тестовом сплите CT‑RATE, а потом успешно валидировали на «чужих» данных из MosMedData.

Именно поэтому в нашем сервисе можно было не просто получить вердикт, а вживую посмотреть на КТ‑снимок с наложенной маской патологии. А в скачиваемом отчёте четко было написано, в какой части грудной клетки находится проблема.

Момент истины: Docker-контейнер и живое демо

И вот, за несколько дней до дедлайна, всё было готово. Модель работала, пайплайн был отлажен. Но notebook.ipynb жюри не покажешь. Нам нужно было «завернуть» это в готовый продукт.

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

  1. Для инженеров (Docker‑контейнер): Мы аккуратно «завернули» всё наше приложение — и TotalSegmentator, и Screener, и весь API — в один Docker‑образ. К нему приложили подробнейшую техническую документацию: как поднять сервис у себя, какие ресурсы ему нужны и как в него стучаться. Бери и пользуйся.

  2. Для людей (Интерактивный сервис): Это была наша гордость. Мы развернули весь бэкенд у себя на сервере и сделали к нему красивый, интуитивно понятный фронтенд.

Это был не просто «интерфейс на скорую руку». Врач мог зайти по ссылке, перетащить свой DICOM, и сервис не ограничивался выдачей вердикта. В нем можно было полноценно работать со снимком: врач мог в один клик переключаться между стандартными режимами просмотра (например, легочным окном для паренхимы или мягкотканным окном для окружающих грудную клетку тканей), просматривать 3D‑КТ послойно и — самое главное — видеть в режиме реального времени те самые аномалии, которые наша модель аккуратно обводила (помните про порог 0.7?).

Сервис выдавал и подробный скачиваемый отчет: не просто бинарное «Норма / Патология», а конкретные названия патологичных органов (спасибо TotalSegmentator’у) и точные координаты боксов, которые выделяли аномалию.

Ну и вишенка на торте — тот самый «фейс‑контроль» на входе. Наш Screener сразу проверял загруженный файл. Если это был не DICOM, не КТ или КТ коленки — сервис вежливо отказывался обрабатывать снимок. Только ОГК, только хардкор!

Что в итоге? Боль, радость и второе место!

Мы не будем утверждать, что создали «замену рентгенологов». Мы сделали рабочий прототип, который показал жизнеспособность подхода.

И, как оказалось, не зря: жюри оценило нашего «вышибалу» и крутой интерфейс, и мы взяли второе место!

Наша команда на вручении: Евгения Пржездзецкая, Дмитрий Умеренков и Николай Нечаев

Наша команда на вручении: Евгения Пржездзецкая, Дмитрий Умеренков и Николай Нечаев

Главный инсайт: аномальный детектор в медицине — это не фантастика. Это реально работающий инструмент, который может сэкономить тонну времени. Особенно когда у тебя нет возможности собрать датасет всех болезней мира.

Второй инсайт: 90% работы Data Scientist’а — это подготовка данных. Остальные 10% — это ожидание, пока данные подготовятся.

Соревнования вроде ЛЦТ — это квинтэссенция боли и радости. Ты три недели не спишь, питаешься пиццей, ненавидишь свой код, коллег и весь мир. А потом твоя модель выдаёт правильный результат, и ты стоишь на сцене, держа диплом, и готов обнять серверную стойку.

Оно того стоило.

Если вы айтишник и когда‑нибудь думали: «А не пойти ли мне на хакатон?» — идите.

Особенно на те, что связаны с реальными задачами, как эта. Нет ничего круче, чем понимать, что твой код, твои бессонные ночи и твои сошедшиеся графики могут в будущем реально помочь врачу спасти чью‑то жизнь.

Это, пожалуй, лучший способ проверить и себя, и свои идеи на прочность.


P. S. Спасибо организаторам ЛЦТ-2025 за интересный трек и отдельное спасибо нашей команде за выдержку!

Автор: jehb4ik

Источник [8]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/22641

URLs in this post:

[1] вот она: https://arxiv.org/abs/2502.08321

[2] обучения: http://www.braintools.ru/article/5125

[3] боли: http://www.braintools.ru/article/9901

[4] TotalSegmentator: https://github.com/wasserth/TotalSegmentator

[5] ошибке: http://www.braintools.ru/article/4192

[6] мозга: http://www.braintools.ru/parts-of-the-brain

[7] зрения: http://www.braintools.ru/article/6238

[8] Источник: https://habr.com/ru/companies/airi/articles/972104/?utm_campaign=972104&utm_source=habrahabr&utm_medium=rss

www.BrainTools.ru

Rambler's Top100