Sanjeever/OpenMed

基于 OpenMed 中文 PII ONNX 模型的本地 REST API。

★ 0Forks 0PythonGitHub ↗Compare

README

OpenMed Chinese PII API

基于 OpenMed 中文 PII ONNX 模型的本地 REST API。服务使用 FP32 模型,可通过 ONNX Runtime 的 CPU 或 NVIDIA CUDA Execution Provider 进行推理,也不会把病例材料发送到外部服务。

API 支持超过模型 512-token 上限的长病例:服务会按 480 token 分窗,相邻窗口重叠 64 token,随后恢复实体在完整原文中的字符偏移并过滤重叠结果。

环境要求

  • Git
  • Git LFS
  • uv
  • Python 3.12,由 uv 自动管理
  • CUDA 模式需要 NVIDIA 显卡及兼容的驱动

首次使用 Git LFS:

git lfs install

克隆项目

模型仓库通过 Git submodule 固定到根仓库记录的版本。完整克隆应用和模型:

git clone --recurse-submodules git@github.com:Sanjeever/OpenMed.git
cd OpenMed

如果已经克隆了根仓库:

git submodule update --init --recursive
git -C "models/OpenMed-PII-Chinese-NomicMed-Large-395M-v1-onnx-android" lfs pull

模型仓库还包含 INT8、FP16 和 ORT Mobile 文件,但本服务只加载 model.onnx。如果只想下载运行所需的 FP32 文件,可在首次克隆时跳过 Git LFS 自动下载:

$env:GIT_LFS_SKIP_SMUDGE = "1"
git clone --recurse-submodules git@github.com:Sanjeever/OpenMed.git
Remove-Item Env:GIT_LFS_SKIP_SMUDGE
cd OpenMed
git -C "models/OpenMed-PII-Chinese-NomicMed-Large-395M-v1-onnx-android" lfs pull --include="model.onnx"

安装依赖

uv sync

依赖锁定在 uv.lock 中。项目安装 onnxruntime-gpu 及其 CUDA、cuDNN 运行库;该包同时提供 CPUExecutionProvider,因此 CPU 和 GPU 模式使用同一套 ONNX Runtime。

启动服务

CPU:

uv run openmed-api --provider cpu --host 127.0.0.1 --port 8000

NVIDIA GPU:

uv run openmed-api --provider cuda --host 127.0.0.1 --port 8000

--provider 默认为 cpu。启动命令固定使用单 worker,模型在服务启动时只加载一次。CUDA 模式如果没有成功启用 CUDAExecutionProvider 会直接启动失败,不会静默退回 CPU。

原来的 Uvicorn 启动方式仍可使用,并默认选择 CPU:

uv run uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 1

健康检查:

curl.exe "http://127.0.0.1:8000/health"

CUDA 模式的健康检查应以 CUDAExecutionProvider 为首选 Provider:

{
  "status": "ok",
  "provider": "cuda",
  "variant": "fp32",
  "providers": [
    "CUDAExecutionProvider",
    "CPUExecutionProvider"
  ]
}

PII 检测:

$body = @{
    text = "患者联系电话13800138000,于2026年8月17日入院。"
    threshold = 0.5
} | ConvertTo-Json

Invoke-RestMethod `
    -Method Post `
    -Uri "http://127.0.0.1:8000/v1/pii/detect" `
    -ContentType "application/json" `
    -Body $body

响应示例:

{
  "model": "OpenMed-PII-Chinese-NomicMed-Large-395M-v1",
  "variant": "fp32",
  "text_length": 32,
  "chunk_count": 1,
  "truncated": false,
  "entities": [
    {
      "label": "PHONE",
      "score": 0.99,
      "start": 6,
      "end": 17,
      "text": "13800138000"
    }
  ]
}

start 和 end 是完整输入文本中的 Python 字符下标,满足:

request_text[entity["start"]:entity["end"]] == entity["text"]

长病例示例

examples/long-case-request.json 是一份完全合成的长住院病例请求体,包含分散在病例开头、中段和末尾的姓名、电话、日期、地址、邮箱及住院号等测试信息。文件长度超过单个模型窗口,可用于验证滑窗推理、全局字符偏移和 CPU/GPU 性能。

先启动服务,再读取整个 JSON 文件并发送请求:

$body = Get-Content `
    -Raw `
    -Encoding UTF8 `
    "examples/long-case-request.json"

$response = Invoke-RestMethod `
    -Method Post `
    -Uri "http://127.0.0.1:8000/v1/pii/detect" `
    -ContentType "application/json; charset=utf-8" `
    -Body $body

查看模型、窗口数量和是否发生截断:

$response | Select-Object `
    model, `
    variant, `
    text_length, `
    chunk_count, `
    truncated

按原文位置查看识别到的实体:

$response.entities |
    Sort-Object start |
    Format-Table label, score, start, end, text -AutoSize

长病例正常处理时,chunk_count 应大于 1,truncated 应为 false。所有实体的 start 和 end 都是相对于完整病例原文的字符位置。

对比 CPU 与 GPU

每种模式启动后先发送一次长病例请求进行预热,再执行多次计时。第一次请求不计入结果。

$body = Get-Content -Raw -Encoding UTF8 "examples/long-case-request.json"

1..5 | ForEach-Object {
    $elapsed = Measure-Command {
        $null = Invoke-RestMethod `
            -Method Post `
            -Uri "http://127.0.0.1:8000/v1/pii/detect" `
            -ContentType "application/json; charset=utf-8" `
            -Body $body
    }

    [PSCustomObject]@{
        run = $_
        milliseconds = [math]::Round($elapsed.TotalMilliseconds, 2)
    }
} | Format-Table

停止服务,分别使用 --provider cpu 和 --provider cuda 启动后执行同一段命令,即可得到可比的端到端延迟。

更新模型 submodule

根仓库记录的是模型仓库的确定 commit。更新模型时,先更新 submodule,再在根仓库记录新的 gitlink:

$modelPath = "models/OpenMed-PII-Chinese-NomicMed-Large-395M-v1-onnx-android"

git -C $modelPath fetch origin
git -C $modelPath switch main
git -C $modelPath pull --ff-only
git -C $modelPath lfs pull --include="model.onnx"

git add $modelPath
git status

提交根仓库后,其他环境运行以下命令即可切换到同一个模型版本:

git submodule update --init --recursive

不要在根仓库中直接添加或提交模型二进制文件;模型版本只通过 submodule gitlink 管理。

Contributors

Sanjeever

Issues