pimenov.ai

База знаний

OpenMed — локальные медицинские ИИ-модели для клинического NER и обезличивания

OpenMed — открытый набор медицинских ИИ-моделей для клинического NER и обезличивания данных. Работают локально на устройстве или сервере, без облака.

Опубликовано

OpenMed — открытый набор медицинских ИИ-моделей для распознавания клинических сущностей и обезличивания персональных данных. Работают локально: на ноутбуке, телефоне или вашем сервере, и данные пациента никуда не уходят.

Что такое OpenMed

OpenMed — семейство специализированных моделей для обработки медицинского текста. Групп две: клинический NER (извлечение сущностей вроде болезней, препаратов, генов, анатомии) и обезличивание персональных данных (PII). Проект открыт по лицензии Apache-2.0, модели небольшие и рассчитаны на локальный запуск. Автор — Maziyar Panahi (CNRS, Париж), к проекту приложена научная статья с замерами на 12 биомедицинских датасетах.

💡
NER (Named Entity Recognition) — распознавание именованных сущностей. Модель читает текст и размечает в нём конкретные объекты: где болезнь, где лекарство, где ген. PII (Personally Identifiable Information) — персональные данные: имя, дата, телефон, адрес, номер документа.

По цифрам проекта: свыше 1500 моделей на Hugging Face, 15 языков для обезличивания, 247 PII-чекпойнтов, десятки миллионов загрузок. Всё бесплатно и с открытыми весами.

Что умеет: основные возможности

ВозможностьЧто делает
Клинический NERИзвлекает болезни, препараты, гены, белки, анатомию, химические вещества и онкологические сущности из текста
ОбезличиваниеНаходит и маскирует персональные данные по всем 18 идентификаторам HIPAA Safe Harbor
Локальный запускCPU, CUDA, Apple Silicon (MLX), iPhone/iPad/Mac через OpenMedKit, браузер через Transformers.js/WebGPU
Форматы и интеграцияКлинический текст на входе; для медицинских систем есть интеграция с HL7 v2 и FHIR
Языки обезличивания15 языков (см. раздел про приватность). Русского в списке нет

Как это работает

Одна функция принимает текст и возвращает размеченные сущности. По сети при этом ничего не отправляется.

flowchart LR
    A["Клинический текст"] --> B["OpenMed (100% локально)"]
    B --> C["Медицинские сущности"]
    B --> D["Найденные PII"]
    B --> E["Обезличенный текст"]

Подключение и интеграция

Python

Базовая установка и первый вызов:

# Ядро + рантайм Hugging Face (Linux, macOS, Windows; CPU или CUDA)
pip install "openmed[hf]"
from openmed import analyze_text

result = analyze_text(
    "Patient started on imatinib for chronic myeloid leukemia.",
    model_name="disease_detection_superclinical",
)

for entity in result.entities:
    print(entity.label, entity.text, entity.confidence)
# DISEASE  chronic myeloid leukemia  0.98
# DRUG     imatinib                  0.95

REST-сервис (Docker)

Для интеграции с бэкендом поднимается FastAPI-сервис:

pip install "openmed[hf,service]"
uvicorn openmed.service.app:app --host 0.0.0.0 --port 8080
# либо через Docker
docker run --rm -p 8080:8080 -e OPENMED_PROFILE=prod openmed:1.7.0

Эндпоинты: GET /health, POST /analyze, POST /pii/extract, POST /pii/deidentify.

Apple Silicon и iOS

На Mac и iPhone инструмент работает через Swift-пакет OpenMedKit с ускорением MLX. По замерам проекта MLX на Apple Silicon даёт до 24–33× ускорения относительно CPU. Установка Python-варианта с MLX:

pip install "openmed[mlx]"

Браузер

Токен-классификацию можно упаковать в ONNX и запускать прямо в браузере через Transformers.js на WebGPU, без сервера.

Модели: с чего начать

МодельСпециализацияСущностиРазмер
disease_detection_superclinicalБолезни и состоянияDISEASE, CONDITION, DIAGNOSIS434M
pharma_detection_superclinicalПрепаратыDRUG, MEDICATION, TREATMENT434M
pii_superclinical_largeОбезличиваниеNAME, DATE, SSN, PHONE, EMAIL, ADDRESS434M
anatomy_detection_electramedАнатомияANATOMY, ORGAN, BODY_PART109M
gene_detection_genecorpusГены и белкиGENE, PROTEIN109M

Полный каталог — на сайте проекта в разделе Model Registry.

Приватность: обезличивание и PII

PII-модели находят все 18 идентификаторов HIPAA Safe Harbor и маскируют их. Есть «умное склеивание» токенов (чтобы дата 01/15/1970 не разбивалась на куски), политики HIPAA/GDPR, подписанные отчёты аудита и предпросмотр редактуры.

Поддерживаемые языки обезличивания: ar, de, en, es, fr, he, hi, id, it, ja, nl, pt, te, th, tr.

⚠️
Русского языка (ru) в списке нет. Для русского клинического текста готовые PII-модели OpenMed из коробки не работают. Подробнее — в разделе про Россию.

Тарифы и лимиты

Основное бесплатно и открыто. Веса, код и рецепты обучения под Apache-2.0, можно использовать в исследованиях и в продакшене без оплаты. Для тех, кому нужен управляемый вариант, часть моделей доступна как пакеты на AWS Marketplace и SageMaker с HIPAA-совместимыми эндпоинтами. Это уже платный облачный путь, и для российского контура он, как правило, не подходит (см. ниже).

Применение в России: нюансы, проблемы и как подступиться

Это самый важный раздел, если вы работаете с данными российских пациентов. Здесь три слоя: закон, язык и интеграция.

⚖️
Материал носит справочный характер и не заменяет консультацию юриста. Перед внедрением в клинике проверьте конфигурацию с вашим специалистом по защите данных и юристом.

Что говорит закон

  • 152-ФЗ «О персональных данных». Данные о здоровье — специальная категория (ст. 10). К их обработке предъявляются повышенные требования: отдельное согласие, усиленная защита.
  • Локализация (ст. 18, ч. 5). Запись, систематизация и хранение персональных данных россиян должны идти в базах на территории РФ. Локальный и on-prem-запуск OpenMed сюда вписывается: данные физически не покидают ваш сервер.
  • Врачебная тайна (ст. 13 ФЗ-323 «Об основах охраны здоровья граждан»). Передача сведений о пациенте третьим лицам ограничена. On-device-подход снимает риск утечки во внешний сервис.
  • Обезличивание. Требования к обезличиванию задаёт Роскомнадзор. Нюанс: обезличивание OpenMed калибровалось под HIPAA, а не под российские правила, поэтому набор полей и логику нужно проверять и настраивать под 152-ФЗ.

Здесь и кроется сильная сторона инструмента для РФ: локальная обработка закрывает сразу и локализацию, и врачебную тайну.

Главная техническая проблема: русский язык

Модели обучены на англоязычных биомедицинских корпусах (PubMed, arXiv, MIMIC-III) и на англоязычных датасетах NER (BC5CDR, NCBI-Disease и других). Русского в списке языков PII нет вовсе.

Что это значит на практике:

  • Готовые модели хорошо разбирают английский клинический текст и научную литературу.
  • На русской карте пациента они работать не будут: другой алфавит, другая терминология, другие форматы документов и идентификаторов (СНИЛС, полис ОМС, паспорт вместо SSN).
  • Российские справочники (МНН, ЖНВЛП, коды МКБ на русском) в модель не заложены.

ПО как медицинское изделие

Если инструмент влияет на постановку диагноза или выбор лечения, он может подпадать под регулирование программного обеспечения как медицинского изделия и требовать регистрации в Росздравнадзоре. Извлечение сущностей для исследований и внутренней аналитики обычно в эту категорию не попадает, но границу стоит оценивать под конкретный сценарий.

Интеграция с российским ИТ-контуром

  • Российские МИС и ЕГИСЗ не всегда говорят на FHIR/HL7, хотя движение в эту сторону идёт. Поддержка FHIR/HL7 в OpenMed поможет там, где стандарты уже внедрены.
  • Apple-стек (MLX, OpenMedKit) удобен, но не обязателен: сервис поднимается на обычном Linux-сервере с CPU или GPU, что проще для российской инфраструктуры.

Как всё-таки применить

  1. Обработка англоязычной литературы и исследований. Майнинг статей, извлечение препаратов и болезней из зарубежных источников работает из коробки.
  2. Обезличивание перед отправкой в облачную LLM. Схема «сначала локально обезличить, потом отправить» ценна сама по себе. Для русского текста PII-часть придётся заменить или дообучить.
  3. Дообучение под русский язык. Подход из статьи (лёгкий domain-adaptive pretraining плюс LoRA) описан и воспроизводим. На открытых весах модель можно дообучить на русском клиническом корпусе. Задача реальная, хотя и небыстрая, для ML-команды. Пошаговый рецепт — в отдельном разделе ниже.
  4. Связка с российскими решениями. Для русского текста разумно комбинировать OpenMed с локальными русскоязычными моделями и инфраструктурой (например, on-prem-платформами вроде NeuralDeep), оставляя за OpenMed англоязычную часть и саму архитектуру обезличивания.
  5. On-prem-пилот в клинике. Развернуть сервис в закрытом контуре, прогнать на обезличенных данных, оценить качество и только потом расширять.

Дообучение под русский корпус: пошаговая инструкция

Готовых русскоязычных моделей у OpenMed нет, но архитектура и рецепт обучения открыты — значит, модель можно адаптировать под русский клинический текст самостоятельно. Ниже воспроизводимый путь от сырых данных до модели, подключённой к сервису. Ориентир по срокам: пилотная модель — 2–4 недели работы ML-инженера, доведение до продакшена дольше.

🔒
Обучение тоже подпадает под 152-ФЗ. Весь пайплайн (разметка, тренировка, хранение чекпойнтов) держите в закрытом контуре на территории РФ. Для разметки берите обезличенные или синтетические данные, а согласие пациентов на обработку оформляйте отдельно.
  • Данные: русский клинический текст — выписки, протоколы, анамнезы. Для NER хватает нескольких тысяч размеченных предложений на старт, для устойчивого PII нужно больше и с примерами всех типов идентификаторов.
  • Железо: одна GPU на 24 ГБ (RTX 3090/4090, A10) закрывает LoRA-дообучение. Для полноценного domain-adaptive pretraining желательно больше памяти или несколько карт.
  • Стек: Python и библиотеки transformers, datasets, peft, accelerate, seqeval.
pip install transformers datasets peft accelerate seqeval

Англоязычные веса OpenMed для русского не подойдут как есть — нужна база, которая уже видела русский язык. Разумные варианты:

БазаКогда брать
DeepPavlov/rubert-base-casedРусскоязычная база общего домена, хорошая точка старта
xlm-roberta-base / largeМультиязычная, если корпус смешанный (русский + английский)
Русские биомед-энкодерыЕсли найдёте предобученную на медицинском русском — лучший старт

Архитектура OpenMed для NER — это токен-классификация поверх энкодера, поэтому рецепт переносится на любую из этих баз один в один.

Разметка ведётся в BIO-схеме: B- — начало сущности, I- — продолжение, O — вне сущности. Удобные инструменты — Label Studio или Doccano. Формат хранения — CoNLL (токен и метка через пробел, предложения разделены пустой строкой):

Пациент O
жалуется O
на O
головную B-SYMPTOM
боль I-SYMPTOM
,  O
назначен O
эналаприл B-DRUG

Сразу закладывайте в набор меток и клинические сущности (DISEASE, DRUG, SYMPTOM, ANATOMY), и российские идентификаторы для PII (SNILS, OMS, PASSPORT, MED_RECORD).

Если неразмеченного русского медтекста много, сначала дообучите базу на задаче masked language modeling — модель «привыкнет» к терминологии до разметки. Это и есть DAPT из статьи OpenMed. Шаг можно пропустить, если размеченных данных достаточно, и сразу идти к LoRA.

from transformers import AutoModelForMaskedLM, AutoTokenizer, DataCollatorForLanguageModeling
# Дообучение base на неразмеченном русском мед-корпусе через MLM,
# затем сохранённый чекпойнт используется как база для Шага 4.

LoRA обучает лишь малую долю весов — быстро, дёшево по памяти и без риска «сломать» базу.

from transformers import (
    AutoModelForTokenClassification, AutoTokenizer,
    TrainingArguments, Trainer, DataCollatorForTokenClassification,
)
from peft import LoraConfig, get_peft_model, TaskType

base = "DeepPavlov/rubert-base-cased"
tokenizer = AutoTokenizer.from_pretrained(base)
model = AutoModelForTokenClassification.from_pretrained(
    base, num_labels=len(label_list), id2label=id2label, label2id=label2id,
)

lora = LoraConfig(
    task_type=TaskType.TOKEN_CLS,
    r=16, lora_alpha=32, lora_dropout=0.1,
    target_modules=["query", "value"],
)
model = get_peft_model(model, lora)
model.print_trainable_parameters()  # обучается менее 1,5% весов (как в статье OpenMed)

args = TrainingArguments(
    output_dir="openmed-ru-ner",
    learning_rate=2e-4,
    per_device_train_batch_size=16,
    num_train_epochs=5,
    eval_strategy="epoch",
    save_strategy="epoch",
)
trainer = Trainer(
    model=model, args=args,
    train_dataset=train_ds, eval_dataset=val_ds,
    tokenizer=tokenizer,
    data_collator=DataCollatorForTokenClassification(tokenizer),
)
trainer.train()
model.save_pretrained("openmed-ru-ner")

Важная деталь: при токенизации метки нужно выравнивать по субтокенам (первому субтокену слова — реальная метка, остальным -100), иначе обучение поедет.

СНИЛС, полис ОМС и паспорт имеют жёсткий формат, поэтому гибрид «модель + регулярные выражения» работает надёжнее и дешевле, чем одна модель:

import re

PATTERNS = {
    "SNILS":    r"\b\d{3}-\d{3}-\d{3} \d{2}\b",
    "OMS":      r"\b\d{16}\b",
    "PASSPORT": r"\b\d{2} ?\d{2} \d{6}\b",
}

def mask_ru_pii(text: str) -> str:
    for label, pattern in PATTERNS.items():
        text = re.sub(pattern, f"[{label}]", text)
    return text

Регулярками закрываете структурированные идентификаторы, а обученной моделью — свободный текст (имена, адреса, даты).

Метрика для NER — F1 по сущностям (не по токенам), считается через seqeval. Держите отдельный held-out набор, который модель не видела при обучении:

from seqeval.metrics import classification_report
print(classification_report(true_labels, pred_labels))

Ориентир: сначала добейтесь приемлемого F1 на частых сущностях (DISEASE, DRUG), затем прицельно докидывайте примеры туда, где модель проседает.

Дообученная модель — это стандартный чекпойнт transformers, поэтому её можно отдавать через тот же REST-слой OpenMed (Шаг «Подключение и интеграция»), указав путь к своей модели вместо англоязычной. Так русский NER/PII встаёт в уже готовый пайплайн /analyze и /pii/deidentify.

📌
Короткий вывод для России: готового решения «поставил и работает по-русски» здесь нет. Но есть открытая, приватная, локальная основа, которую можно адаптировать под 152-ФЗ и русский язык. В этом её главная ценность.

Полезные ссылки

По теме

Если вы работаете с медицинскими данными и хотите обрабатывать их локально, без передачи в облако, OpenMed станет хорошей отправной точкой для приватного контура.

Если захотите обсудить, как это применить у себя или в команде — пишите в Telegram @pimenov