openmed 深度拆解:1000+ 本地医疗 NER 模型 + HIPAA 级 PII 反识别,把临床 NLP 装进 iPhone 的 Apache-2.0 全栈方案
posts posts 2026-06-09T21:04:14+08:00openmed(maziyarpanahi/openmed)是 2025-10 发布的本地优先医疗 AI 库,1,000+ 临床 NER 模型 + 247 个 PII checkpoint 覆盖 12 种语言;支持 CPU/CUDA/Apple MLX/iOS Swift 五种后端,全部 Apache-2.0 + arXiv:2508.01630 背书,是把临床 NLP “送进 iPhone” 的最完整开源方案。技术笔记openmed, 医疗 AI, NER, PII 反识别, 本地推理, Apple MLX, HIPAA, Swift, iOS, Apache-2.0, openmedopenmed 深度拆解:1000+ 本地医疗 NER 模型 + HIPAA 级 PII 反识别,把临床 NLP 装进 iPhone 的 Apache-2.0 全栈方案
目标读者:在医疗 / 保险 / 律所 / EHR 集成场景做 NLP 工程的 Python / iOS 工程师与解决方案架构师 核心问题:临床文本里的疾病 / 药物 / 解剖结构识别和 HIPAA 18 项标识符脱敏,能不能完全在本地完成、不向任何云端发请求?最好还能装进 iPhone 用 Swift 直接调? 难度:⭐⭐⭐(中级;需要懂 NER / 命名实体识别、Python 包结构、MLX 概念) 预计阅读时间:25 分钟
一、openmed 是什么
openmed(GitHub:maziyarpanahi/openmed,官网 openmed.life)是 Maziyar Panahi 在 2025-10 发布的本地优先医疗 AI 库,Apache-2.0,论文挂在 arXiv:2508.01630。它把医疗 NLP 里最常见的两件事——临床 NER(命名实体识别) 和 PII 反识别(de-identification)——封装成“一行 Python”的统一接口,并且完全在本地硬件上跑,从 CPU、CUDA 到 Apple Silicon 的 MLX,再到 iOS / macOS 里的 Swift OpenMedKit。
截至 2026-06-09 的仓库状态:
| 指标 | 数值 |
|---|---|
| 仓库 | maziyarpanahi/openmed |
| Stars | 1,601 |
| Forks | 196 |
| 主语言 | Python(94%),另有 Swift |
| 许可证 | Apache-2.0 |
| 创建时间 | 2025-10-04 |
| 最近推送 | 2026-06-08(持续活跃) |
| PyPI | pip install "openmed[hf]" / [hf,mlx] / [hf,service] |
| 模型规模 | 1,000+ 临床 NER 模型;247 个 PII checkpoint |
| 多语言 | 12 种(en/zh/es/fr/de/it/pt/nl/hi/te/ar/ja/tr) |
| 部署形态 | Python API / FastAPI / Docker / iOS Swift |
数据来源:GitHub 仓库页面、PyPI
openmed页面与 README(截至 2026-06-09)。
它在 README 里给出的 30 秒最小例子:
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(f"{entity.label:<12} {entity.text:<28} {entity.confidence:.2f}")
# DISEASE chronic myeloid leukemia 0.98
# DRUG imatinib 0.95零 API key、零网络请求、零云端依赖。患者姓名、诊断、用药全部在调用方的机器上识别。
二、核心判断
在展开细节之前,先给出我对这个项目的判断:
- 它不是"又一个医疗 LLM"。仓库主语言是 Python,但底层模型大多是 100M–500M 级别的专用 NER Transformer(多数是 BERT 系/RoBERTa 系/domain-adapted encoder),不是生成式 LLM。在医疗 NLP 任务上能跑出接近 GPT 级别的精度,推理成本低 10–100 倍。
- 它是“医疗 NLP 的 Hugging Face”。1000+ 模型 + 247 个 PII checkpoint + 12 种语言 + Python / FastAPI / Swift 多种接口形态,本质上把分散在 HuggingFace 上的医疗模型统一管理、统一接口、统一部署。
- 它的杀手锏是"端到端本地 + 多后端路由"。同样的
extract_pii(text, model_name=...)调用,在 MacBook(M 系列)上自动走 MLX 后端,在 Linux 服务器上自动走 PyTorch + CUDA,在 iPhone 上换成 Swift OpenMedKit——用户只关心业务逻辑,硬件后端自动切换。 - 它不是万能的。它专做结构化抽取 + 反识别,不做自由文本生成、问诊推理、影像识别。想做“AI 医生”问诊助手,它不适合;但给现有 EHR 文本流加一层“实体抽取 + 脱敏”管道,它几乎是最省心的开源方案。
三、系统地图:仓库的 8 大子模块
openmed/ 顶层目录结构(来自 2026-06-09 抓取):
openmed/
├── __init__.py # 20 KB 顶层公开 API
├── __about__.py # 版本号
├── core/ # 模型加载、配置、注册表
│ ├── model_registry.py
│ ├── models.py # ModelLoader / load_model
│ ├── config.py # OpenMedConfig(设备、后端、batch size)
│ ├── backends.py # PyTorch / MLX / CoreML 路由
│ ├── labels.py # 实体标签集
│ ├── pii.py # 顶层 PII 接口(extract_pii / deidentify)
│ ├── pii_entity_merger.py # 智能实体合并(防分词碎片化)
│ ├── pii_i18n.py # PII 多语言支持
│ ├── quality_gates.py
│ ├── anonymizer/ # 反识别策略(mask / replace / hash / shift_dates)
│ └── decoding/ # 解码策略
├── ner/ # NER 推理
│ ├── infer.py
│ ├── adapter.py # 与 HuggingFace transformers 对接
│ ├── indexing.py
│ ├── labels.py
│ ├── families/ # 模型族(按 backbone 分类)
│ └── exceptions.py
├── processing/ # 文本预处理 / 后处理
│ ├── text.py
│ ├── sentences.py
│ ├── tokenization.py
│ ├── outputs.py
│ ├── batch.py # BatchProcessor
│ └── advanced_ner.py
├── torch/ # PyTorch 后端
├── mlx/ # Apple MLX 后端
├── coreml/ # CoreML 导出与回退
├── service/ # FastAPI REST 服务
├── zero_shot/ # GLiNER 系零样本
└── utils/ # 日志 / 验证 / 性能分析仓库根目录还有 swift/ 子目录,装着 OpenMedKit(给 iOS/iPadOS/macOS 用的 Swift Package)和三个 SwiftUI 演示工程:
swift/
├── OpenMedKit # 真正的 Swift Package
├── OpenMedDemo # SwiftUI 演示 1
├── OpenMedScanDemo # 扫描式 PII 检测演示
└── OpenMedDemo # 综合演示各模块职责一句话总结
| 模块 | 职责 |
|---|---|
core | 模型加载、注册表查询、配置管理、PII 顶层接口、跨后端路由 |
ner | NER 推理主体,与 HuggingFace transformers 适配 |
processing | 文本预处理、句子切分、tokenization、批量编排、输出格式化 |
torch | PyTorch 后端(CPU + CUDA) |
mlx | Apple MLX 后端(Apple Silicon 专用加速) |
coreml | CoreML 导出与回退路径 |
service | FastAPI REST 服务、/analyze /pii/extract /pii/deidentify 等端点 |
zero_shot | GLiNER 家族零样本抽取实验性支持 |
utils | 日志、配置校验、性能分析(Profiler / Timer / profile decorator) |
swift/OpenMedKit | iOS/iPadOS/macOS 原生 Swift 入口 |
四、关键机制 1:1000+ 模型的统一注册表
openmed 的核心抽象是 model_name 字符串 + ModelLoader + model_registry。所有 1000+ 临床 NER 模型都按命名规范 *_detection_<dataset> 注册,调用方只需要写:
from openmed import analyze_text
analyze_text("...", model_name="disease_detection_superclinical")背后实际发生的事:
model_registry.get_model_info("disease_detection_superclinical")查到 HuggingFace 上的模型 ID;ModelLoader根据OpenMedConfig.device选择 PyTorch / MLX / CoreML 后端;- 第一次调用时下载模型权重并缓存到本地,之后完全离线;
- 走
ner/infer.py的推理管道,输出统一PredictionResult对象。
这种“按名字路由”的设计有两个好处:
- 业务代码完全后端无关。换成 Apple Silicon Mac 不用改一行代码,后端路由在
core/backends.py里处理; - 可以在线热切换模型。
analyze_text(text, model_name="...")每次调用都可以传不同的model_name,很适合 A/B 评测或不同病种路由。
README 列出的 5 个旗舰模型(截至 1.5.5):
| 模型名 | 任务 | 实体类型 | 参数量 |
|---|---|---|---|
disease_detection_superclinical | 疾病 / 病症 | DISEASE, CONDITION, DIAGNOSIS | 434M |
pharma_detection_superclinical | 药物 / 用药 | DRUG, MEDICATION, TREATMENT | 434M |
pii_superclinical_large | PII / 脱敏 | NAME, DATE, SSN, PHONE, EMAIL, ADDRESS | 434M |
anatomy_detection_electramed | 解剖 / 部位 | ANATOMY, ORGAN, BODY_PART | 109M |
gene_detection_genecorpus | 基因 / 蛋白 | GENE, PROTEIN | 109M |
五、关键机制 2:HIPAA 级 PII 反识别的 4 种策略
PII 反识别(de-identification)是 openmed 第二个杀手锏。core/pii.py 暴露两个核心函数:
from openmed import extract_pii, deidentify
text = "Patient: John Doe, DOB: 01/15/1970, SSN: 123-45-6789"
# 1. 抽取:返回实体列表
result = extract_pii(text, model_name="pii_superclinical_large", use_smart_merging=True)
# 2. 脱敏:4 种策略
deidentify(text, method="mask") # [NAME], [DATE]
deidentify(text, method="replace") # Faker 生成假数据(locale-aware, 格式保留)
deidentify(text, method="hash") # 加密哈希
deidentify(text, method="shift_dates", date_shift_days=180) # 日期整体平移method="replace" 的细节尤其值得展开:
- Faker-backed:用 Faker 库生成 locale 对应的伪造值(同一个 locale 下多次调用会生成不同假名,但格式一致——美式 SSN 永远是
###-##-####形式); - 临床 ID 专用 provider:内置 CPF(巴西)、CNPJ(巴西企业)、BSN(荷兰)、NIR(法国社保)、Codice Fiscale(意大利税号)、NIE(西班牙外国人)、Aadhaar(印度)、Steuer-ID(德国税号)、NPI(美国医师)等;
- 格式保留:
01/15/1970不会被替换成1992-08-03这种乱格式,依然是MM/DD/YYYY。
use_smart_merging=True 是另一个重要开关——它解决 NER 模型的经典痛点:token-level 识别会把“John Smith”切成两个 NAME,把“01/15/1970”切成三个 DATE 片段。智能合并在 pii_entity_merger.py 里实现,把同一 span 的相邻同类 token 合并回去,对下游正则匹配非常关键。
HIPAA 18 项标识符全部覆盖(README 明确声明),并提供 confidence_threshold 阈值调参,匹配强度可调。
PII 任务流案例
from openmed import extract_pii, deidentify
clinical_note = """
Patient: Sarah Connor (DOB: 03/15/1985) at MRN 4471882.
Admitted to St. Mary's Hospital for chest pain. Contact: sarah.connor@email.com, +1-555-0123.
"""
# 抽取
entities = extract_pii(clinical_note, model_name="OpenMed/privacy-filter-nemotron", lang="en")
for e in entities:
print(f"{e.label:<10s} {e.text:<35s} {e.confidence:.2f}")
# NAME Sarah Connor 0.99
# DATE 03/15/1985 0.99
# ID 4471882 0.94
# ORG St. Mary's Hospital 0.87
# EMAIL sarah.connor@email.com 0.99
# PHONE +1-555-0123 0.97
# 脱敏(用 replace 生成假数据,输出仍是临床叙事格式)
deidentified = deidentify(clinical_note, method="replace", lang="en")
print(deidentified)
# Patient: John Williams (DOB: 07/22/1991) at MRN 8847163.
# Admitted to St. Joseph's Hospital for chest pain. Contact: j.williams@example.net, +1-555-8847.整个流程 0 次云端调用。这是它与传统云端医疗 NLP API(AWS Comprehend Medical、Azure Health Insights、Google Healthcare NLP)的根本差异。
六、关键机制 3:跨后端自动路由(PyTorch / MLX / CoreML)
这是 openmed 跟普通“医疗 NER 包”最大的区别——同一份业务代码,CPU、CUDA、Apple MLX、iOS CoreML 都能跑。
6.1 路由逻辑
core/backends.py 里的核心思路是:
OpenMedConfig(device="auto")时,先检测硬件;- Apple Silicon Mac → 优先 MLX;
- NVIDIA GPU 存在 → PyTorch + CUDA;
- 其他情况 → PyTorch + CPU;
- iOS / iPadOS / macOS native Swift → CoreML(带 MLX fallback)。
6.2 模型命名带后端信息
OpenMed/privacy-filter-mlx 是 MLX 版本,OpenMed/privacy-filter-nemotron 是 PyTorch 版本。当你写 model_name="OpenMed/privacy-filter-mlx" 时,openmed 会优先在 MLX 后端加载;如果你在非 Apple 机器上跑,它会自动 fallback 到 PyTorch 同名 checkpoint 并打一次 warning:
MLX model names are automatically substituted with the matching PyTorch checkpoint on non-Apple-Silicon hosts — ship one model name, run anywhere.
这种**“模型名就是部署路由”**的设计,对想统一管理多平台部署的工程团队非常友好。
6.3 MLX 后端实战
# Apple Silicon Mac
pip install "openmed[mlx]"from openmed import extract_pii
# 这条调用在 M1/M2/M3 Mac 上走 MLX;在 Linux 服务器上自动 fallback 到 PyTorch
result = extract_pii(
"Patient Sarah Connor (DOB: 03/15/1985) at MRN 4471882.",
model_name="OpenMed/privacy-filter-mlx",
)MLX 后端比纯 PyTorch CPU 快 3–8 倍(取决于序列长度),比 CUDA 略慢但不需要独立显卡。
6.4 Swift 入口:把 PII 检测装进 iPhone
iOS 工程集成:
// Package.swift
dependencies: [
.package(url: "https://github.com/maziyarpanahi/openmed.git", from: "1.5.5"),
]Swift 端用 OpenMedKit 调用 MLX 推理,全部在设备本地完成。这意味着:
- 一款记录医患对话的 iOS App,可以在录音结束后立即在本地做 PII 脱敏,然后再决定哪些片段上传到云端;
- 一款 EHR 离线 App,可以在完全无网的诊所 / 飞机 / 战地医院里继续运行 NER 和 PII 检测;
- 隐私敏感的临床研究 App,可以让患者数据永远不离开 iPhone。
README 里强调的核心承诺:
Patient data leaves your network: Never.
这条承诺在医疗 AI 行业里是真正的护城河。
七、关键机制 4:FastAPI 服务 + Docker 一键部署
对于后端集成场景,openmed 提供了FastAPI 微服务 + Docker 镜像两种部署形态。
pip install "openmed[hf,service]"
uvicorn openmed.service.app:app --host 0.0.0.0 --port 8080主要端点:
| 端点 | 方法 | 用途 |
|---|---|---|
/health | GET | 健康检查 |
/analyze | POST | 通用 NER 抽取(指定 model_name) |
/pii/extract | POST | PII 抽取(支持 lang) |
/pii/deidentify | POST | 脱敏(支持 method) |
/models/loaded | GET | 当前加载的模型列表 |
/models/unload | POST | 主动释放模型显存({"all": true}) |
OPENMED_SERVICE_KEEP_ALIVE | env | 模型闲置多久后自动卸载(默认 10 分钟) |
Docker 部署:
docker build -t openmed:1.5.5 .
docker run --rm -p 8080:8080 -e OPENMED_PROFILE=prod openmed:1.5.5OPENMED_PROFILE=prod 启用了 production profile,关闭 debug 端点、限制 CORS、强制设置超时。这种“开箱即用 + 容器化”的设计对医院 IT 团队非常友好——他们可以在不连公网的环境下拉镜像,直接跑起来。
任务流案例:批量脱敏
from openmed import BatchProcessor
p = BatchProcessor(
model_name="pii_superclinical_large",
operation="deidentify",
method="replace",
batch_size=16,
use_smart_merging=True,
)
results = p.process_texts([note1, note2, note3, ...]) # 几千份临床记录一次性脱敏BatchProcessor 在 processing/batch.py 里实现,自动做 chunk、padding、显存复用,单卡 A100 跑 100 万行病历大概几个小时——比人工脱敏快几个数量级。
八、12 种语言 + 247 个 PII checkpoint:多语言怎么落地的
README 顶部用 12 个 flag 展示的多语言支持是另一大卖点。extract_pii(text, lang="pt") 的背后是:
- 每个语言有专属 PII checkpoint(共 247 个),按 locale 训练;
- 国家级 ID 提供商独立覆盖(CPF、BSN、NIR、Codice Fiscale、NIE、Aadhaar、Steuer-ID、NPI、マイナンバー 等);
anonymizer/replace走 locale 化的 Faker provider,假数据格式贴合目标国家。
示例(葡语):
portuguese = extract_pii(
"Paciente: Pedro Almeida, CPF: 123.456.789-09, telefone: +351 912 345 678",
lang="pt",
use_smart_merging=True,
)
# NAME Pedro Almeida 0.99
# CPF 123.456.789-09 0.99
# PHONE +351 912 345 678 0.99这种“按国家做实体识别 + 国家级 ID provider + locale 化假数据”三件套,是少有把多语言 PII 反识别做到生产级别的开源方案。
九、适用边界与不适用场景
读到这里,openmed 的能力边界已经比较清晰了。最后给一份明确判断:
9.1 适合用 openmed 的场景
- EHR 数据接入管道:从医院 HIS / EMR 系统抽取临床文本,做实体识别 + 脱敏,再入库到数据仓库;
- 保险理赔自动化:从理赔病历 / 化验单 / 出院小结抽取 ICD 编码、用药、诊断,辅助核赔;
- 临床研究数据准备:把多中心、多语言的临床文本做一致的反识别,让伦理委员会 (IRB / Ethics Committee) 审查更顺;
- 离线 / 边缘 / 隐私敏感场景:诊所 App、iOS App、飞机上的医生工作站、战地医疗——任何不允许数据出本机的场景;
- NLP 工具链中的 PII 脱敏前置:在做 LLM 推理、RAG、embedding 之前,先用 openmed 把所有 PII 抽掉、换掉、mask 掉。
9.2 不适合用 openmed 的场景
- 需要“AI 医生”做问诊 / 推理 / 自由对话——openmed 是 NER + 反识别专精,不是医疗 LLM;
- 医学影像识别(CT、MRI、病理切片)——这是另一个领域,需要专门的多模态模型;
- 超大模型 + 长上下文场景——openmed 的 434M 模型适合短文本(病历、出院小结、化验单),不适合 10K+ token 的长文档摘要;
- 实时语音流——它是离线 batch 推理,不是流式 ASR;
- 完全无 ML 背景的医疗团队——openmed 需要懂一点 Python / NER / batch 推理概念,纯临床背景的人会觉得陡。
十、采用建议
如果你正在评估是否把 openmed 引入生产栈,下面是 3 条经验法则:
- 先从小任务试点。用 100–1000 份真实病历跑
extract_pii+deidentify,看精度是否够用(经验上 434M 的*_superclinical系列在英文临床 NER 上 F1 ≥ 0.92,多语言 PII 上 F1 ≥ 0.88)。如果不够,再考虑换更重的 LLM。 - 永远在 PyTorch 路线上做评测。MLX 跑得快但只支持 Apple 硬件;PyTorch + CUDA 是兼容性最广的部署路径。Swift 端在生产前要做充分的真机测试。
- 把 PII 抽离到独立微服务。即使你主系统是 Java / Go / Node,用 FastAPI 单独跑一个 openmed 微服务也比硬塞进主进程更安全——可以独立扩缩容、独立更新模型、独立审计。
决策树
你要处理的是 临床文本 中的 实体识别 / 脱敏 吗?
├── 是 → openmed 是首选
│ ├── 部署在云端?→ PyTorch + FastAPI + Docker
│ ├── 部署在 Mac 工作站?→ MLX 后端
│ └── 部署在 iOS App?→ OpenMedKit + Swift
└── 否(图像 / 语音 / 长文本生成)→ 看其他方案十一、结语
openmed 的意义不在 star 数——它把**“医疗 NLP 必须云端 + 必须付费 API”这个十几年的行业默认假设打破了。在 2026 年这个数据隐私法规越来越严、本地硬件越来越强、开源模型越来越准的交汇点上,它提供了一个“Apache-2.0 + 1000+ 模型 + 12 种语言 + 5 种后端 + iOS Swift”**的完整栈。
如果你正在做医疗 / 保险 / 律所 / EHR 集成相关项目,建议花一个下午跑一遍它的 README 30-second example 和 Swift OpenMedKit 演示工程。很可能你会发现——原来 80% 的工作早就有人替你做好了。