Привет, Habr!

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

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

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

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

ChatGPT
ChatGPT

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Вопреки всем канонам жанра, с обучением самой 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‑ноутбуке, это в первую очередь инженерия. И первой инженерной задачей по условиям хакатона стал «фейс‑контроль» на входе: нам нужен был «умный» сервис, который не просто ищет аномалии, а делает это только на КТ грудной клетки. Любые другие модальности (МРТ, рентген), «битые» файлы или КТ других частей тела он должен был вежливо «отшивать».

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

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

Тут мы и прикрутили 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 за интересный трек и отдельное спасибо нашей команде за выдержку!

Комментарии (4)


  1. Mihij
    02.12.2025 08:11

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


    1. jehb4ik Автор
      02.12.2025 08:11


    1. jehb4ik Автор
      02.12.2025 08:11


  1. R0bur
    02.12.2025 08:11

    Учитывая то, насколько возросли возможности анализа изображений с развитием средств машинного обучения, вызывает недоумение, что эти технологии ещё не получили широкого распространения в сфере медицины. Или всё-таки получили? Интересно было бы прочитать мнение практикующих врачей-диагностов.