ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

OpenMed for Web 实战指南:在浏览器与 Node.js 中运行本地医疗 NER 与 PII 去标识化

OpenMed for Web 实战指南:在浏览器与 Node.js 中运行本地医疗 NER 与 PII 去标识化 OpenMed for Web 实战指南在浏览器与 Node.js 中运行本地医疗 NER 与 PII 去标识化【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmedOpenMed for Webnpm 包openmed将 OpenMed 的医疗命名实体识别NER与 PII 去标识化能力带进浏览器和 Node.js推理全部在本地完成临床文本不会发送到任何托管 API。本指南以 js/openmedkit-web/README.md 为核心结合 js/openmedkit-web/src 源码与 tests/web 测试完整讲解安装、deidentify()/extractPii()用法、ONNX 模型加载与变体选择、token 偏移对齐原理、本地浏览器运行时WebGPU/WASM以及隐私安全边界读完后你可以在自己的 Web 应用中直接落地一套数据不出本地的临床文本脱敏能力。一、OpenMed for Web 是什么OpenMed for Web 是 OpenMed 多语言运行时家族Python、Swift、Android、Web中的 Web/Node 实现包名为openmed当前仓库版本 2.3.0见 js/openmedkit-web/package.json。它与 Python、Swift、Android 端使用同一套 OpenMed 模型因此一次训练的模型可以在全平台获得一致的识别结果。核心特性本地推理文本在浏览器或 Node.js 进程内处理不依赖云 API从 Hugging Face 加载模型仅发生一次下载之后推理不再产生任何网络请求。两条运行路径通过 Transformers.js 加载 OpenMed ONNX 模型高层 API一行deidentify()即可用通过 ONNX Runtime Web 直接创建 session低层 API可完全控制模型与运行时资源路径、执行后端。双端兼容同一套 API 既可用于浏览器本地路径 本地资源也可用于 Node.js 服务端脱敏。二、安装根据你选择的加载路径安装依赖。两种方式的openmed包本体一致只是推理后端不同# 通过 Transformers.js 加载 OpenMed 模型推荐最省事 npm install openmed huggingface/transformers # 直接使用 ONNX Runtime Web session npm install openmed onnxruntime-web两个运行时均为可选 peer 依赖见 js/openmedkit-web/package.jsonhuggingface/transformers 3.0.0与onnxruntime-web 1.20.0peerDependenciesMeta中两者都可选意味着你只需安装实际使用的那个。包要求node 20package.json并在engines中声明。源码层面resolveRuntime()会先尝试动态import(huggingface/transformers)失败时抛出 Install huggingface/transformers or pass a token-classification pipeline. 的明确错误见 src/model-loader.tsONNX Runtime Web 路径同理。三、快速开始一行代码完成临床文本脱敏3.1 最基本的 deidentifyimport { deidentify } from openmed; const result await deidentify( Patient Alice Nguyen was seen in cardiology., ); console.log(result.deidentifiedText); console.log(result.spans);返回的OpenMedDeidentifyResult包含三个字段见 src/index.ts字段说明text原始输入文本deidentifiedText已替换敏感实体的脱敏文本默认将每个 span 替换为[规范标签]形式spans每个敏感实体的完整记录OpenMedSpan[]deidentify()的底层实现是extractPii()拿到 span 后再经spansToRedactedText()按start从大到小排序、用replacement默认为[${span.canonical_label}]切片替换原文而成见 src/index.ts。3.2 extractPii只取实体不脱敏如果只想获得实体列表而不修改原文使用extractPii()import { extractPii } from openmed; const spans await extractPii( Patient Alice Nguyen was seen in cardiology., { docId: note-001 }, );每条OpenMedSpan见 src/types.ts携带位置与内容start/endJavaScript UTF-16 索引、text_hashHMAC-SHA256 哈希而非原文标签体系entity_type模型原始标签、canonical_label50 个规范标签之一如PERSON、PHONE、SSN、policy_labelDIRECT_IDENTIFIER/QUASI_IDENTIFIER/CLINICAL_CONCEPT之一元数据score、doc_id、detector、section、regulatory_tags、replacement、reversible_id等且所有 span 都带schema_version: 1OPENMED_SPAN_SCHEMA_VERSION。规范标签的完整清单见 src/types.ts含ACCOUNT_NUMBER、CREDIT_CARD、EMAIL、IBAN、IP_ADDRESS、GPS_COORDINATES、STREET_ADDRESS、ZIPCODE等每种规范标签到策略标签的映射表定义在 src/decoder.ts例如SSN → DIRECT_IDENTIFIER、AGE → QUASI_IDENTIFIER、DATE → QUASI_IDENTIFIER。normalizeLabel()还内置了一张跨语言/跨模型别名表ALIAS_MAPsrc/decoder.ts例如aadhaar → ID_NUM、cpf → ID_NUM、medicalrecordnumber → ID_NUM、nhsnumber → ID_NUM、teudatzehut → ID_NUM让不同来源的模型标签归一到同一套规范体系。3.3 常用选项ExtractPiiOptionssrc/types.ts支持以下常用参数参数类型作用modelstring指定 Hugging Face 模型仓库默认DEFAULT_MODEL_IDpipelineRawTokenClassificationPipeline传入自定义 token 分类 pipeline优先级高于modelmodelLoader/loaderOptionsModelLoader/LoadModelOptions自定义模型加载器及其选项thresholdnumber实体平均置信度阈值低于阈值的 span 被丢弃默认0docIdstring文档标识写入每条 span默认documenthashSecretstring \| Uint8Array计算text_hash的 HMAC 密钥默认openmedkit-webdetectorstring \| null记录检测后端标识默认transformersjssectionstring \| null章节信息regulatoryTagsstring[]监管标签pipelineOptionsTokenClassificationCallOptions透传给底层 pipeline 的调用选项DeidentifyOptions在ExtractPiiOptions之上增加replacement?: (span) string可自定义每个实体的替换策略src/types.ts。四、默认模型与 ONNX 模型加载4.1 DEFAULT_MODEL_ID开箱即用的临床 PII 模型不传model或pipeline时deidentify()与extractPii()默认加载见 src/index.tsexport const DEFAULT_MODEL_ID OpenMed/OpenMed-PII-ClinicalE5-Small-33M-v1-onnx-android;即33M 参数的临床 PII 模型根目录 INT8 工件约 70 MB通过 Transformers.js 提供token-classification能力。extractPii()内部会判断模型名是否匹配/-onnx-android$/iONNX_ANDROID_REPO_PATTERN匹配则走loadOnnxModel()按 ONNX 工件文件名加载否则走通用loadTokenClassificationPipeline()见 src/index.ts。4.2 指定其他模型或自建 pipeline可以传入任意公开的OpenMed/model-onnx-android仓库或自行加载后通过pipeline注入import { deidentify, loadOnnxModel } from openmed; const model await loadOnnxModel(OpenMed/model-onnx-android); const result await deidentify( Patient Alice Nguyen was seen in cardiology., { pipeline: model }, );4.3 变体选择int8 / fp32 / fp16loadOnnxModel()默认选择根目录INT8模型。需要其他已发布的精度变体时传入variantconst fp32 await loadOnnxModel(OpenMed/model-onnx-android, { variant: fp32, }); const fp16 await loadOnnxModel(OpenMed/model-onnx-android, { variant: fp16, });OpenMedOnnxVariant int8 | fp32 | fp16src/types.ts三种变体分别映射到 ONNX 仓库内文件名model_int8/model/model_fp16ONNX_MODEL_FILENAMESsrc/model-loader.ts。加载时强制quantized: false以避免 Transformers.js 二次量化并设置subfolder: 指向仓库根目录src/model-loader.ts。4.4 本地文件与离线模型isLocalModelReference()src/model-loader.ts识别file://前缀、/、./、../、~开头以及 Windows 盘符路径/^[A-Za-z]:[\\/]/作为本地模型引用。识别为本地引用时自动启用local_files_only并在加载期间临时把 Transformers.js 的allowRemoteModels设为与本地引用一致的布尔值加载完成后在finally中恢复原状态src/model-loader.ts确保远程加载开关不影响其他调用方。五、Token 偏移对齐从 token 到原文字符的精确定位5.1 为什么需要对齐Transformers.js 的 token-classification 输出不携带字符偏移只有entity、score、index、word。OpenMed 在解码 span 前会先把每个 token 对齐回源文本保证result.spans总是携带指向原始字符串的start/end。对齐实现位于 src/offsets.ts 的alignTokenOffsets()大小写与重音不敏感先做小写化 NFD 分解 去除组合变音标记normalizeStringsrc/offsets.ts因此 BERT 风格的小写化 tokenizer 仍能对齐处理多种子词标记WordPiece##、SentencePiece▁、byte-level BPEĠ前缀stripTokenMarkerssrc/offsets.ts跳过特殊 token[CLS]、[SEP]、[PAD]、s、/s、pad等SPECIAL_TOKEN_WORDSsrc/offsets.ts顺序游标 连续搜索窗口已带有效偏移的 token 直接保留并推进游标无偏移 token 在剩余文本中按顺序查找contiguous 模式在 16 字符窗口内搜索CONTIGUOUS_SEARCH_WINDOWsrc/offsets.ts并检查词边界对齐失败即报错未知或无法对齐的 token 抛出无内容的错误Token offset alignment failed; provide source offsets.而不是静默返回不完整的脱敏结果src/offsets.ts。5.2 偏移的坐标约定偏移使用JavaScript UTF-16 索引for (const character of text)按码点迭代、按 UTF-16 code unit 返回偏移见 src/offsets.ts分解的重音归属其源字符的区间NFD 分解出的组合变音标记被并入前一个字符的toOriginalEnd自定义 tokenizer 或过滤后的输出应提供精确的源偏移遇到对齐错误时应把该次扫描视为失败而非无 PII文档。5.3 面向无偏移运行时的类型契约既有TokenClassificationEntity与TokenClassificationPipeline输出保留必需的数值偏移对于不带偏移的运行时使用加性类型RawTokenClassificationEntity、RawTokenClassificationPipeline与RawTransformersRuntimesrc/types.ts。模型加载器与alignTokenOffsets()返回已对齐的实体从而保持 v2.2 以来的 typed-consumer 契约——该契约有测试锁定tests/web/test_npm_deidentify.spec.ts中通过alignTokenOffsets(...).map(numericOffsets)断言输出均为数值偏移并以RawTokenClassificationPipeline/RawTransformersRuntime注入无偏移运行时验证extractPii()仍可正常工作。5.4 从 logits 到 span 的完整解码链路extractPii()的解码管线src/index.ts以aggregation_strategy: none、ignore_labels: []调用 pipeline保留Otoken使偏移对齐能看到完整 token 序列decodeBioTokenSpans()src/decoder.ts先对齐偏移、解析 BIO/BIES 边界标签B-/I-/E-/S-/O再聚合相邻同标签 token 为实体最后mergeAdjacentSpans()合并仅以空白分隔的同类型相邻 spanrefinePrivacyFilterSpan()src/decoder.ts对 email/URL/phone 等结构化实体用正则精修边界并去掉 and / or 尾缀最终按threshold过滤低置信度 span。六、本地浏览器运行时WebGPU → WASM 三级后端6.1 后端选择优先级loadOrtWebSession()与loadOrtWebTokenClassificationPipeline()是低层 ONNX Runtime Web 加载器src/runtime/ort-web-loader.ts模型与运行时资源全部使用本地路径并按以下优先级选择最强的执行路径WebGPUWebAssembly SIMD threads单线程 WebAssembly能力探测由 src/runtime/capability.ts 的detectOrtWebCapabilities()完成检查navigator.gpuWebGPU、WebAssembly、SIMD通过内置的 WASM SIMD 探针字节码WASM_SIMD_PROBE验证、SharedArrayBuffer、crossOriginIsolated与hardwareConcurrency产出一个OrtWebCapabilityProfileprobeOrtWebCapabilities()还会调用navigator.gpu.requestAdapter()验证适配器真实可用。selectOrtWebBackend()依据该画像决策多线程时线程数上限为MAX_WASM_THREADS 4src/runtime/capability.ts。6.2 加载本地 token 分类 pipelineimport { deidentify, loadOrtWebTokenClassificationPipeline, } from openmed; const pipeline await loadOrtWebTokenClassificationPipeline({ modelPath: /models/openmed/model.onnx, assetPath: /models/openmed/onnxruntime/, tokenize: tokenizeClinicalNote, // (text) OrtFeeds decode: decodeTokenClassificationOutputs, // ({text, inputs, outputs, session, backend}) entities }); const result await deidentify(clinicalNote, { pipeline, detector: ort-web, });tokenize与decode是必填回调OrtWebTokenClassificationPipelineOptionssrc/runtime/ort-web-loader.tsdecode收到完整上下文OrtTokenClassificationDecodeContext文本、输入 feeds、输出张量、session、后端与调用选项把 logits 转成 token 分类输出后由deidentify()完成后续对齐与脱敏。底层createOrtWebSession()会先configureOrtWebRuntime()设置wasmPaths、simd、numThreads、proxy: false再以graphOptimizationLevel: all创建 session[src/runtime/ort-web-loader.ts](https://link.gitcode.com/i/420950490c0bb257e807f2e2314acfcf#L167-L171, L228-L239)。session 默认按模型路径、资源路径、后端、session 选项稳定序列化出的 key 缓存于DEFAULT_ORT_WEB_SESSION_CACHE失败时自动从缓存剔除可用clearOrtWebSessionCache()清空。6.3 多线程 WASM 的前置条件跨源隔离多线程 WebAssembly 需要跨源隔离cross-origin isolation。服务端必须下发响应头Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp否则 OpenMed 自动回退到单线程 WebAssembly。这是因为SharedArrayBuffer仅在crossOriginIsolated true时可用而线程共享依赖它见OrtWebCapabilityProfile.crossOriginIsolatedsrc/runtime/capability.ts。6.4 类型化的 WebGPU 会话与 fail-closed 校验loadWebGpuTokenClassificationSession()提供类型化的run(tokens) - logits契约src/runtime/webgpu-session.ts并支持 WebGPU / WASM双工件import { loadWebGpuTokenClassificationSession } from openmed; const session await loadWebGpuTokenClassificationSession({ modelPath: { webgpu: /models/openmed/model.webgpu.onnx, wasm: /models/openmed/model.onnx, }, assetPath: /models/openmed/onnxruntime/, }); const logits await session.run({ inputIds, attentionMask, batchSize: 1, sequenceLength: inputIds.length, }); await session.dispose();该 session 还提供两套工程化保障源码见 src/runtime/webgpu-session.ts本地基准记录通过benchmarkSink回调输出仅保存在本地的 warm/cold 每设备基准报告WebGpuBenchmarkReport基准套件名webgpu-token-classification-runtime可用于对比不同设备的推理性能fail-closed 校验门Python 参考一致性门certifyWebGpuReference()将 WebGPU 输出的 logits 与参考实现对比默认 logit 容差DEFAULT_WEBGPU_LOGIT_TOLERANCE 1e-3并给出 span 级一致性结论WebGpuReferenceCertification关键标签召回门evaluateWebGpuRecallGate()计算候选 span 对参考 span 的召回率与recall_delta默认最大差DEFAULT_WEBGPU_MAX_RECALL_DELTA 0即不允许召回率退化并逐标签统计关键标签遗漏数WebGpuRecallGate任一指标不达标即passed: false杜绝 WebGPU 实现悄悄产生错误脱敏。七、隐私与安全设计OpenMed for Web 从设计上把隐私与安全内建为硬约束与仓库 docs/security 与 docs/operations/no-phi-telemetry.md 的定位一致默认无遥测不启用任何 telemetry本地路径拒绝远程 URLassertOfflineAssetPath()拒绝//、\\前缀、普通 URL scheme仅放行file://且仅限 localhost/空 hostname等远程资源路径src/runtime/ort-web-loader.tsspan 只存哈希与偏移text_hash使用 Web CryptoHMAC-SHA256Node 环境回退node:crypto对实体原文计算span 记录不含原始标识符文本src/index.ts配合可配置的hashSecret支持证据审计风险边界OpenMed不是医疗器械不得自主做出临床决策脱敏结果是辅助处理输出需结合 docs/compliance 中的 HIPAA 安全港、21 CFR Part 11 审计追踪等规范评估使用方式。八、与仓库其他部分的协作模型一致性Web 端默认模型与 Android/Swift 共用同一模型族Swift 端OpenMedKitRN.swift中也引用同名的OpenMed-PII-ClinicalE5-Small-33M-v1tokenizer见 js/openmedkit-react-native跨端可以共享模型工件与标签体系测试覆盖Node 侧测试位于 tests/web/test_npm_deidentify.spec.ts对齐与类型契约、tests/web/test_ort_web_loader.spec.ts加载器与离线路径校验、tests/web/test_webgpu_session.spec.tsWebGPU 会话与能力探测公共 API 快照见 tests/web/snapshots/openmedkit-web-public-api.json构建命令为npm run buildtsup 输出 ESM/CJS 双格式测试为npm test构建 类型检查 tsx 运行 Node 测试文档入口更完整的跨端定位可参考 docs/android-integration.md、docs/export-onnx-webgpu.md 与 docs/onnxruntime-web.md 对应主题文档。九、许可与适用边界OpenMed for Web 采用Apache-2.0许可js/openmedkit-web/LICENSE。模型与 Python、Swift、Android 运行时的完整文档与发布物可在本仓库根目录 README.md 与 models.jsonl 中查阅。适用前提浏览器端请确保模型/运行时资源为本地路径并正确配置跨源隔离响应头以启用多线程Node.js 端需node 20并按需安装huggingface/transformers或onnxruntime-web之一。从一行deidentify()到可校验的 WebGPU 会话OpenMed for Web 让医疗级 PII 去标识化真正跑在了终端用户的设备上——推理不出网、原文不进日志、span 只留哈希与偏移是构建隐私优先医疗应用的可靠起点。【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进