OpenMed Transformers.js 导出:将 ONNX 临床 NER 模型打包为浏览器端 Token-Classification 推理包
2026/9/18 22:22:08 网站建设 项目流程

OpenMed Transformers.js 导出:将 ONNX 临床 NER 模型打包为浏览器端 Token-Classification 推理包

【免费下载链接】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

导读

本文讲解 OpenMed 如何将 ONNX token-classification 产物打包为 Transformers.js 可直接加载的浏览器推理包(transformersjs/目录),覆盖"从零转换"与"基于既有 ONNX 导出增量打包"两条路径、图契约校验、Python/Node 双重验证,以及浏览器端pipeline("token-classification", ...)的加载方式。读完本文,你将掌握如何把临床 NER / HIPAA PII 脱敏模型导出为纯前端可运行的本地优先推理资产,让 PHI 始终停留在用户浏览器会话内。

背景:浏览器端本地优先推理的打包需求

OpenMed 的 ONNX 转换子系统 负责把 Hugging Face token-classification 检查点导出为多种运行时格式(fp32 ONNX、WebGPU fp16、Android ONNX Runtime、OpenVINO IR 等)。其中,面向浏览器场景的格式由 openmed/onnx/transformersjs.py 完成:它从 ONNX 导出目录生成一份 Transformers.js 兼容的 bundle,包含浏览器 pipeline 期望的完整文件布局,并在写入前对 ONNX 图的张量契约进行严格校验。

从源码结构看,openmed.onnx.convertopenmed.onnx.transformersjs是两个正交的入口:convert面向"导出 ONNX + 附带打包",transformersjs面向"仅基于已有 ONNX 产物增量打包",二者最终都会调用export_transformersjs_bundle(见 convert.py#L548-L563 与 transformersjs.py#L74-L178)。

Bundle 文件布局

打包产物是transformersjs/目录,文件布局如下:

transformersjs/ config.json tokenizer.json tokenizer_config.json quantize_config.json transformersjs-contract.json onnx/ model.onnx model_quantized.onnx

其中model_quantized.onnx默认由 onnxruntime 对model.onnx做动态 INT8 权重量化生成(quantize_dynamic(..., weight_type=QuantType.QInt8),见 transformersjs.py#L427-L442)。源码中REQUIRED_BUNDLE_FILES(transformersjs.py#L31-L38)把上述 6 个必需文件定义为契约的一部分:

REQUIRED_BUNDLE_FILES = ( "onnx/model.onnx", "onnx/model_quantized.onnx", "tokenizer.json", "tokenizer_config.json", "config.json", "quantize_config.json", )

tokenizer.json是硬性要求——它是 Transformers.js 在浏览器端执行快速分词所必需的 fast tokenizer 资产。打包逻辑会尝试从源导出目录复制 9 类分词器资产(tokenizer.jsontokenizer_config.jsonspecial_tokens_map.jsonadded_tokens.jsonvocab.txtvocab.jsonmerges.txtspiece.modelsentencepiece.bpe.model);若tokenizer.json缺失,则退回到用AutoTokenizer.from_pretrained(..., use_fast=True)从模型 ID 重新保存,且明确要求 fast tokenizer(transformersjs.py#L356-L405)。

图契约:浏览器 pipeline 校验什么

转换器会校验 ONNX 图是否暴露 token-classification 契约(transformersjs.py#L218-L290):

  • 输入input_idsattention_mask(必需),token_type_ids(可选)
  • 输出:仅logits(不允许存在其他输出)
  • 动态轴:输入为[batch, sequence](两轴都必须动态);logits[batch, sequence, labels],其中batchsequence动态,labels必须是静态轴
  • opset 下限:要求 ONNX 模型 opset >= 18(MINIMUM_TOKEN_CLASSIFICATION_OPSET = 18,transformersjs.py#L26),opset 17 及更旧的图会被直接拒绝

校验通过后,契约数据写入transformersjs-contract.json,结构形如:

{ "task": "token-classification", "format": "transformersjs", "minimum_opset": 18, "model_opset": 18, "inputs": [ {"name": "input_ids", "axes": ["batch", "sequence"], "shape": [{"kind": "dynamic", "name": "batch"}, {"kind": "dynamic", "name": "sequence"}]}, {"name": "attention_mask", "axes": ["batch", "sequence"], "shape": [{"kind": "dynamic", "name": "batch"}, {"kind": "dynamic", "name": "sequence"}]} ], "outputs": [ {"name": "logits", "axes": ["batch", "sequence", "labels"], "shape": [{"kind": "dynamic", "name": "batch"}, {"kind": "dynamic", "name": "sequence"}, {"kind": "static", "value": 3}]} ] }

说明:labels轴静态是因为它由config.json中的id2label标签表唯一决定(如{"0": "O", "1": "B-NAME", "2": "I-NAME"}),浏览器端解码 logits 需要确定性的类别数。

此外,config.json会被规范化:id2label必须是非空映射,同时补写label2id反查表与task字段(默认token-classification),见 transformersjs.py#L408-L424。

路径一:从全新 ONNX 转换打包

在创建 ONNX 产物时直接加--include-transformersjs

.venv/bin/python -m openmed.onnx.convert \ --model dslim/bert-base-NER \ --output dist/example-onnx \ --include-transformersjs

该参数在 convert.py#L1569-L1573 注册,转换流程内部调用export_transformersjs_bundle生成dist/example-onnx/transformersjs,并把transformersjs作为一条 artifact 记录进openmed-onnx.json清单(convert.py#L548-L563)。清单中的 formats 列表(如["onnx", "webgpu", "transformersjs"])随后可以被发布流程整体携带,供publish_to_hub等后续步骤使用(convert.py#L620-L637)。

测试 tests/unit/onnx/test_transformersjs_export.py#L133-L202 验证了这条路径:include_transformersjs=True时,最终 formats 为["onnx", "webgpu", "transformersjs"],清单中transformersjsartifact 的记录为{"format": "transformersjs", "path": "transformersjs", "precision": "int8"},且该格式列表被完整透传给发布调用。

路径二:基于既有 ONNX 导出增量打包

如果导出目录已存在model.onnxconfig.jsontokenizer.jsontokenizer_config.json,可以只构建浏览器 bundle:

.venv/bin/python -m openmed.onnx.transformersjs \ --onnx-export-dir dist/example-onnx

默认输出到dist/example-onnx/transformersjs,并更新源目录下的openmed-onnx.json(若存在);传--no-manifest-update则保持源清单不变。CLI 的全部参数见 transformersjs.py#L293-L353:

参数默认值说明
--onnx-export-dir必填model.onnxconfig.json与分词器资产的目录
--output<onnx-export-dir>/transformersjs目标 bundle 目录
--tokenizer-sourceONNX 导出目录已保存分词器目录或 HF 模型 ID
--config<onnx-export-dir>/config.jsonconfig.json路径
--no-quantize关闭不量化,直接把model.onnx复制为model_quantized.onnx
--no-manifest-update关闭不修改源目录的openmed-onnx.json
--minimum-opset18bundle 契约要求的最低 opset
--segmenter打包紧凑的 Han / Indic 分词器资源集(见下节)

底层函数export_transformersjs_bundle(transformersjs.py#L74-L178)还支持onnx_filename(源 ONNX 文件名,默认model.onnx)、quantizeupdate_manifestsegmenter_id等关键字参数,供程序化调用。

清单更新行为

更新openmed-onnx.json时,若formats列表尚未包含transformersjs则追加;若artifacts中没有transformersjs条目则新增,precision字段记录实际精度(量化时为int8--no-quantize时为float32),见 transformersjs.py#L518-L557。quantize_config.json则固定记录量化元数据(algorithm: "dynamic"weight_type: "qint8""copied"),见 transformersjs.py#L505-L515。

可选:打包紧凑分词器资源集

对于中英文混排等场景,--segmenter可以把紧凑的 Han / Indic 分词器资源一并装入 bundle,可选值来自 openmed/processing/tokenization.py#L70-L74:

  • openmed-han-v1:Han 脚本分词词表(MIT)
  • openmed-indic-v1:Indic(天城文)断词规则(ICU,含许可声明文件)
  • openmed-cjk-indic-v1:Han + Devanagari 组合(默认值,MIT AND ICU)

打包后 bundle 内会多出一个segmenter/资源目录,并在openmed-onnx.json中写入segmenter描述符(含资源文件路径与许可信息)。测试 test_transformersjs_export.py#L93-L117 验证了该行为:segmenter_id="openmed-cjk-indic-v1"时 bundle 清单记录license: "MIT AND ICU",且segmenter/ICU.txt文件确实落盘。

验证:Python 校验器与 Node smoke fixture

打包完成后可用 Python 校验器做全面检查,覆盖必需文件、config.json标签元数据与 ONNX 图契约:

from openmed.onnx import validate_transformersjs_bundle validate_transformersjs_bundle("dist/example-onnx/transformersjs")

validate_transformersjs_bundle(transformersjs.py#L181-L208)会依次执行:文件完整性检查(find_missing_bundle_files)、id2label非空校验、可选segmenter资源校验,以及 ONNX 图契约校验。单测覆盖了关键失败路径:静态sequence轴被拒绝(test_transformersjs_export.py#L80-L90)、opset 17 被拒绝(test_transformersjs_export.py#L120-L130)。

仓库还自带一个无头 Node smoke fixture tests/fixtures/onnx/transformersjs_smoke.mjs,用法:

node tests/fixtures/onnx/transformersjs_smoke.mjs dist/example-onnx/transformersjs

它独立复述同一套契约:校验 7 个必需文件、config.json含非空id2labelquantize_config.json标识transformersjs格式、输入含input_ids/attention_mask且前两维动态、输出唯一且为logits[batch, sequence, labels],labels 静态)。全部通过后打印Transformers.js token-classification contract ok。该 fixture 由测试 test_transformersjs_export.py#L241-L263 驱动,在 Node 可用时对真实打包结果执行端到端校验。

浏览器端使用

transformersjs/目录通过静态服务器暴露(或放入模型资产路径)后,即可用 Transformers.js 加载:

import { pipeline } from "@huggingface/transformers"; const detector = await pipeline( "token-classification", "/models/openmed-pii/transformersjs", { device: "webgpu" }, ); const entities = await detector("Patient Casey Example called 212-555-0198.");
  • 加载路径指向transformersjs目录本身(即含config.json的那一层),浏览器端会依据config.json/tokenizer.json/onnx/完成 pipeline 装配,并自动选择model_quantized.onnx(INT8)以减小下载体积。
  • 设备选择device: "webgpu"让推理走 WebGPU 后端;也可省略该选项由 Transformers.js 按运行时能力回退到 WebAssembly 等执行后端。
  • 数据边界:该 bundle 专为本地优先(local-first)token 分类设计——PHI 在用户浏览器会话内完成检测与脱敏,不外发到任何服务器。
  • 注意事项:导出步骤只负责打包模型资产,不改变 Hugging Face 仓库的可见性设置;浏览器端可用的 token 分类能力同时受模型本身(如多语言覆盖)与 WebGPU/WASM 运行时支持范围约束,实际精度以所导出检查点为准。

离线演练:无模型下载的合成走查

无需下载模型、不发起网络请求即可查看期望的 bundle 文件清单与浏览器加载代码片段:

uv run python examples/v17_multimodal_browser_interop.py

该示例(examples/v17_multimodal_browser_interop.py)使用合成数据与内置假引擎执行公开 API;其run_transformersjs_example(L205-L213)通过find_missing_bundle_files打印期望的 7 个必需文件,并输出BROWSER_PIPELINE_SNIPPET(即上文浏览器加载代码),输出 JSON 中transformersjs一节即为此演示结果,适合作为 CI 或本地快速验证的参照。

小结

OpenMed 的 Transformers.js 导出把 ONNX 图契约、分词器资产、INT8 量化与清单管理收敛为一个自校验的transformersjs/目录:

  • 两种入口convert --include-transformersjs(转换时同步打包)或transformersjs --onnx-export-dir(既有产物增量打包);
  • 一道契约input_ids/attention_mask(+可选token_type_ids)→logits,batch/sequence 动态、labels 静态、opset ≥ 18,由 Python 校验器与 Node smoke fixture 双重把关;
  • 一种消费方式:静态目录 +pipeline("token-classification", ...),配合device: "webgpu"实现纯浏览器端的本地优先 PHI 脱敏推理。

相关实现与测试可继续在仓库中深挖:openmed/onnx/transformersjs.py、openmed/onnx/convert.py、tests/unit/onnx/test_transformersjs_export.py、tests/fixtures/onnx/transformersjs_smoke.mjs。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询