Зазначимо: коли патологу потрібно переглянути 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-інженера: ми допоможемо підібрати архітектуру під ваші завдання.







