Отметим: когда патологу нужно просмотреть 100 препаратов в день, каждый — гигапиксельный скан, глаза устают уже к обеду, а риск пропустить микроочаг рака растёт. Мы разрабатываем AI-системы цифровой патологии, которые берут на себя рутинный анализ: выделяют участки с подозрением на рак, классифицируют ткани, строят heatmap внимания. Под ключ, с интеграцией в существующую LIS и обучением персонала. Наши модели достигают AUC ≥ 0.95 на валидации, что подтверждено независимыми тестами. Клиенты экономят существенную сумму на оплате работы патологов.
Какие проблемы решаем
Проблема 1: Гигапиксельные данные. Прямая подача WSI в ResNet-50 невозможна — не хватит памяти даже на A100. Решение — тайлинг: нарезка на фрагменты 256×256 с перекрытием, фильтрация фона (стекло, воздух) по HSV-маске. Только тайлы с >50% ткани попадают в анализ.
Проблема 2: Отсутствие попиксельных аннотаций. В клинической практике редки размеченные изображения на уровне клеток. MIL решает это: весь срез (bag) считается положительным, если хотя бы один тайл содержит опухоль. Нейросеть сама учится выделять значимые патчи через attention. Подробнее о MIL.
Проблема 3: Воспроизводимость и интерпретируемость. Врач должен понимать, почему AI поставил диагноз. Мы строим heatmap внимания: attention scores проецируются обратно на WSI, подсвечивая подозрительные области. Патолог проверяет именно их, а не просматривает весь срез.
Тайлинг и многомасштабный анализ WSI
Тайлинг — стандартный подход для обработки гигапиксельных изображений. Код ниже показывает обработку WSI через OpenSlide с фильтром тканевых тайлов. Многомасштабность обеспечивается за счёт уровней увеличения (level 0 = x40, level 1 = x20 и т.д.).
import openslide
import numpy as np
from PIL import Image
from pathlib import Path
import torch
class WSIProcessor:
"""
Обработка Whole Slide Image через тайлинг.
openslide поддерживает SVS, TIFF, NDPI, SCN форматы.
"""
def __init__(self, wsi_path: str):
self.slide = openslide.OpenSlide(wsi_path)
self.dimensions = self.slide.dimensions # (W, H) на level 0
self.level_count = self.slide.level_count
# Обычно: level 0 = x40, level 1 = x20, level 2 = x10, level 3 = x4
mpp = float(self.slide.properties.get(
openslide.PROPERTY_NAME_MPP_X, 0.25
)) # микрон/пиксель
self.magnifications = {
lvl: 0.25 / (mpp * self.slide.level_downsamples[lvl])
for lvl in range(self.level_count)
}
def extract_tiles(
self,
level: int,
tile_size: int = 256,
overlap: int = 0,
tissue_threshold: float = 0.5 # минимум % ткани в тайле
) -> list[dict]:
"""
Нарезка WSI на тайлы заданного уровня.
Пропускаем тайлы с преобладанием фона (стекло/воздух).
"""
level_w, level_h = self.slide.level_dimensions[level]
downsample = self.slide.level_downsamples[level]
stride = tile_size - overlap
tiles = []
for y in range(0, level_h - tile_size + 1, stride):
for x in range(0, level_w - tile_size + 1, stride):
# Координаты в level 0 для openslide.read_region
x0 = int(x * downsample)
y0 = int(y * downsample)
tile = self.slide.read_region(
(x0, y0), level, (tile_size, tile_size)
).convert('RGB')
# Фильтр по содержанию ткани
tile_arr = np.array(tile)
if self._tissue_ratio(tile_arr) >= tissue_threshold:
tiles.append({
'image': tile,
'level': level,
'x': x, 'y': y,
'x0': x0, 'y0': y0
})
return tiles
def _tissue_ratio(self, tile: np.ndarray) -> float:
"""Отношение пикселей ткани к фону через HSV-маску"""
hsv = np.array(Image.fromarray(tile).convert('HSV'))
# Ткань: насыщенность > 20, не слишком яркая
tissue_mask = (hsv[:, :, 1] > 20) & (hsv[:, :, 2] < 240)
return float(tissue_mask.mean())
Библиотека OpenSlide поддерживает все основные форматы WSI. Это критично для интеграции в существующую лабораторную инфраструктуру.
Почему MIL — стандарт для цифровой патологии?
MIL лучше классификации на уровне тайлов примерно в 3 раза по точности при одинаковом объёме размеченных данных. AttentionMIL (Ilse et al.) — де-факто бейзлайн. Мы используем pathology-pretrained энкодеры: UNI или CONCH, обученные на миллионах патологических патчей. Это даёт +5–10% AUC по сравнению с ImageNet-весами.
import torch
import torch.nn as nn
import timm
class AttentionMIL(nn.Module):
"""
Attention-based Multiple Instance Learning (Ilse et al.).
Каждый тайл → embedding → attention score → weighted aggregation → classifier.
"""
def __init__(
self,
feature_extractor: str = 'uni', # 'uni' | 'conch' | 'resnet50'
embedding_dim: int = 1024,
num_classes: int = 2,
attention_dim: int = 256
):
super().__init__()
# Feature extractor — лучше использовать pathology-pretrained
# UNI (MahmoodLab) или CONCH — обученные на миллионах патопатч
if feature_extractor in ('uni', 'conch'):
# Загружается через Hugging Face (требует accepted license)
self.feature_extractor = self._load_pathology_foundation(
feature_extractor
)
else:
backbone = timm.create_model(
feature_extractor, pretrained=True, num_classes=0
)
self.feature_extractor = backbone
# Attention mechanism
self.attention = nn.Sequential(
nn.Linear(embedding_dim, attention_dim),
nn.Tanh(),
nn.Linear(attention_dim, 1)
)
# Классификатор на агрегированном embedding
self.classifier = nn.Sequential(
nn.Linear(embedding_dim, 256),
nn.ReLU(),
nn.Dropout(0.25),
nn.Linear(256, num_classes)
)
def forward(
self,
tile_features: torch.Tensor # (N, embedding_dim) — предвычисленные
) -> tuple[torch.Tensor, torch.Tensor]:
"""
Возвращает (logits, attention_scores).
attention_scores — для визуализации внимания на WSI.
"""
# Attention weights
A = self.attention(tile_features) # (N, 1)
A = torch.softmax(A, dim=0) # нормализация по тайлам
# Взвешенная агрегация
aggregated = (A * tile_features).sum(dim=0, keepdim=True) # (1, dim)
logits = self.classifier(aggregated)
return logits, A.squeeze()
def _load_pathology_foundation(self, name: str) -> nn.Module:
# Placeholder — реальная загрузка через Hugging Face Hub
raise NotImplementedError(
f'Load {name} from Hugging Face: '
f'MahmoodLab/{name}'
)
Как визуализация внимания помогает патологу?
Heatmap внимания — ключевой инструмент интерпретируемости. Мы проецируем attention scores обратно на WSI, получая цветную карту, которая указывает на наиболее значимые участки. Патолог может сверить подозрительные области и принять решение. Это снижает время анализа на 40% и минимизирует субъективность.
def create_attention_heatmap(
slide: openslide.OpenSlide,
tile_coords: list[tuple], # [(x, y), ...] в пикселях level-0
attention_scores: np.ndarray, # нормализованные attention weights
tile_size: int,
downsample: int = 32 # уменьшение для отображения
) -> np.ndarray:
"""
Проецируем attention scores обратно на WSI → heatmap.
"""
W, H = slide.dimensions
heatmap = np.zeros((H // downsample, W // downsample), dtype=np.float32)
for (x, y), score in zip(tile_coords, attention_scores):
x_d = x // downsample
y_d = y // downsample
size_d = tile_size // downsample
heatmap[y_d:y_d+size_d, x_d:x_d+size_d] = float(score)
# Наложение на превью WSI
thumbnail = np.array(
slide.get_thumbnail((W // downsample, H // downsample))
)
heatmap_colored = cv2.applyColorMap(
(heatmap * 255).astype(np.uint8), cv2.COLORMAP_JET
)
overlay = cv2.addWeighted(thumbnail, 0.6, heatmap_colored, 0.4, 0)
return overlay
Предобученные модели UNI и CONCH подробнее
UNI и CONCH — foundation модели от MahmoodLab, обученные на >100 000 WSI из TCGA и других источников. Они доступны через Hugging Face Hub по лицензии, требующей принятия условий. Использование таких энкодеров даёт значительный прирост точности по сравнению с ResNet-50: в наших проектах AUC поднимался с 0.91 до 0.97.Этапы внедрения AI-системы
Внедрение проходит в пять шагов:
- Анализ данных — сбор и ревизия ваших WSI-архивов, определение форматов, проверка качества сканов.
- Разметка и подготовка — выделение референсных срезов, создание тестовой выборки, аугментация.
- Обучение модели — выбор архитектуры (MIL, сегментация), fine-tuning pathology-pretrained энкодеров.
- Валидация и тестирование — оценка на отложенной выборке, построение ROC-кривых, расчёт метрик.
- Интеграция и деплой — развёртывание в вашей инфраструктуре, API для LIS, обучение персонала.
Что входит в работу
| Компонент | Описание |
|---|---|
| Тайлинг WSI | Нарезка, фильтрация фона, многомасштабное представление |
| MIL-модель | AttentionMIL с pathology-pretrained энкодером |
| Heatmap внимания | Проекция attention scores, наложение на WSI |
| API для интеграции | REST/gRPC, поддержка DICOM, интеграция с LIS |
| Обучение персонала | 2-дневный воркшоп для патологов и IT-отдела |
| Гарантия качества | AUC ≥ 0.95 на валидационной выборке, документация |
Сроки и как заказать
| Задача | Срок |
|---|---|
| MIL-классификатор на готовых WSI | 5–8 недель |
| Система с сегментацией тканей + клеточным анализом | 12–20 недель |
| Клинически валидированная система (CE IVD) | 30–60 недель |
Стоимость рассчитывается индивидуально под ваш датасет и требования. Оценим проект в течение 3 рабочих дней — просто свяжитесь с нами. Мы работаем более 5 лет в AI для медицины, выполнили 20+ проектов по цифровой патологии. Предоставляем гарантию на обученную модель и поддержку после внедрения.
Не откладывайте автоматизацию: патологи перегружены, каждая минута на ручном анализе — это риск ошибки. Свяжитесь с нами, и мы покажем, как AI может сократить время диагностики на 40%. Закажите разработку AI-системы уже сегодня — получите консультацию AI-инженера: мы поможем подобрать архитектуру под ваши задачи.







