OpenMed 零样本 NER 实战:GLiNER 模型索引、领域感知标签与本地推理全流程
2026/9/18 23:36:57 网站建设 项目流程

OpenMed 零样本 NER 实战:GLiNER 模型索引、领域感知标签与本地推理全流程

【免费下载链接】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 的零样本 NER 工具链基于 GLiNER 系列模型构建,提供从模型目录扫描索引、领域感知标签默认值,到统一推理入口和 token 级标注转换的完整能力,全部可在本地离线运行。本文覆盖.[gliner]依赖安装、build_index/write_index索引构建、领域标签解析优先级、infer推理调用、BIO/BILOU 适配器,以及冒烟测试与单元测试的运行方式,读完即可在自己的环境中跑通端到端的零样本命名实体识别。若只需要一份任务导向的速查流程,可参考 Zero-shot NER How-to。

安装与运行前提

零样本工具链依赖 GLiNER 可选依赖组,使用 uv 安装:

uv pip install ".[gliner]"

该 extra 在 pyproject.toml 中定义为gliner[tokenizers]>=0.2.0,底层还要求torchtransformers可导入。运行冒烟测试或推理前,需要保证 GLiNER 检查点已存在于本地——工具链默认在仓库根目录的models/index.json位置查找索引文件,这一默认路径在 indexing.py 中由DEFAULT_INDEX_PATH = PACKAGE_ROOT / "models" / "index.json"确定。

依赖缺失时不会静默失败:families/gliner.py 中的ensure_gliner_available()会依次检查glinertorchtransformers三个模块,缺失时抛出MissingDependencyError并附带pip install openmed[gliner]的修复提示(对应 exceptions.py)。

模型索引:扫描模型目录并生成 index.json

索引器会遍历一个存放模型工件(GLiNER 检查点、Hugging Face 导出目录等)的目录树,为每个候选模型目录生成一条结构化元数据记录,并持久化为统一的index.json。Python API 用法:

from pathlib import Path from openmed.ner import build_index, write_index models_dir = Path("/path/to/models") index = build_index(models_dir) write_index(index, Path("models/index.json"), pretty=True)

生成的索引包含metagenerated_atsource_dirmodel_countdomain_count)与models列表两部分。结合 indexing.py 源码,可以明确以下几点实现细节:

  • 模型目录识别启发式:目录内包含config.jsontokenizer.jsonmodel.safetensorspytorch_model.bin等核心文件(MODEL_CORE_FILES集合),或存在分片.safetensors,或带metadata.json/config.yaml的目录会被视为模型目录。
  • 家族识别:路径 token 匹配FAMILY_HINTS表——glinerzero-shotzeroshotbiomed归入 GLiNER 家族;gliner2fastinofastglinerv2归入 GLiNER2 家族;未命中则标记为OTHER。这个家族字段后续直接决定推理走哪条执行路径。
  • 领域与语言推断DOMAIN_TOKENS表把路径中的关键词(如biomedbiomedicalgenomicsgenomiccybercybersecurity)归一到规范领域名,识别不到时落入genericLANGUAGE_HINTS表类似地推断enesmulti等语言标签,推断不到时默认en
  • 去重与稳定性_deduplicate按模型标识符去重并按 id 排序;模型 id 由相对路径各段以连字符拼接并转小写生成,保证跨次运行稳定,便于下游组件确定性引用。
  • 原子写入write_index先写.tmpos.replace覆盖,避免半截 JSON 损坏索引。

build_index对输入有严格校验:目录不存在抛FileNotFoundError,不是目录抛NotADirectoryError。生成的索引可由load_index(path)重新加载,索引文件中的notes字段来自模型目录内metadata.jsonnotesdescription字段。

领域感知标签默认值

不同医学子领域需要不同的实体类型集合。工具链内置一份精选的"领域 → 标签列表"映射,默认数据打包在 defaults.json(资源路径openmed.zero_shot.data.label_maps),加载逻辑位于 labels.py。

查看可用领域与默认标签:

from openmed.ner import available_domains, get_default_labels print(available_domains()) print(get_default_labels("biomedical")) print(get_default_labels("endocrinology"))

打包的defaults.json覆盖了大量领域,例如:

领域默认标签
biomedicalDisease, Drug, Gene, Organism
clinicalProblem, Treatment, Test, BodyPart
endocrinologyGlycemicMeasure, ThyroidFunctionMeasure, HormoneLevel, InsulinRegimen, MetabolicFinding, EndocrineGland
oncologySimpleChemical, Cancer, GeneOrGeneProduct, Organ, Tissue, PathologicalFormation 等 18 个
radiologyFinding, ImagingModality, Anatomy, Laterality, Measurement, Impression
genericPerson, Organization, Location, Date

完整的 40 余个领域(含麻醉、营养、儿保、接种、过敏、肺科、护理观察等临床子领域)可直接阅读上述 JSON 文件。从 labels.py 的实现看,加载过程有几个值得注意的机制:

  • load_default_label_map(overrides_path=None)支持传入自定义路径覆盖打包默认值——文档中"测试或部署中可通过向高层 API 提供自定义路径来覆盖"即指此参数;
  • 领域名会经过strip().lower().replace(" ", "_")归一化,标签按小写去重,因此get_default_labels("Endocrinology ")"endocrinology"等价;
  • 资源读取使用@lru_cache缓存,reload_default_label_map()可清缓存强制重载;
  • 领域缺失时get_default_labels默认回退到generic集合(inherit_generic=True),保证推理总能拿到一组标签。

推理 API:NerRequest 与 infer

统一推理入口是 infer.py 中的infer()

from openmed.ner import NerRequest, infer req = NerRequest( model_id="gliner-biomed-tiny", text="Imatinib inhibits BCR-ABL in chronic myeloid leukaemia.", threshold=0.55, domain="biomedical", ) resp = infer(req) for entity in resp.entities: print(entity.label, entity.text, entity.score)

NerRequest字段:model_id(必填,须匹配索引中的模型标识)、text(必填)、threshold(默认0.5)、labels(显式标签列表,可选)、domain(可选)。

标签解析优先级(与_resolve_labels实现逐条对应):

  1. 显式传入的request.labels(去除空字符串后直接使用,此时domain字段仅作为元数据记录);
  2. request.domain对应的默认标签;
  3. 请求未指定领域时,取索引条目record.domains[0]
  4. 仍解析不出时回退generic标签集。

执行路径按索引中的家族字段分派:

  • GLiNER 家族:经load_gliner_handle加载模型(@lru_cache(maxsize=4)缓存实例,hf_tokencache_dirdevice均取自全局OpenMedConfig),调用predict_entities(text, labels, threshold, flat_ner=True)
  • GLiNER2 家族:走load_gliner2_handle,调用方式相同;
  • 其他家族:降级到 Hugging Face 风格 pipeline(task="token-classification"aggregation_strategy="simple")。

无论走哪条路径,_apply_threshold会再按request.threshold做一遍score >= threshold过滤,最终实体以Entitytextstartendlabelscoregroupextras)统一表示。NerResponse.meta会记录实际使用的labels_useddomain_usedthreshold,便于审计"这次推理到底用了哪组标签"。注意model_id未命中索引时抛ValueError,因此先建索引再推理是必要前置步骤。

Token 分类适配器:span 实体转 BIO/BILOU 标签

当结果需要喂给 token 级分类器或做训练数据准备时,可用 adapter.py 中的to_token_classification把 span 实体投影到 token 级标注:

from openmed.ner import to_token_classification tokens = to_token_classification(resp.entities, req.text, scheme="BILOU") print(tokens.labels())

从源码实现看,该适配器有三个关键行为:

  • 方案校验:仅支持BIOBILOU(大小写不敏感),非法 scheme 抛ValueError
  • 重叠 span 按分数优先_assign_entities_to_tokens先把实体按score降序排列再逐一定位 token,同一 token 被多个实体覆盖时保留分数更高者;
  • 分词回退策略:优先使用传入 tokenizer 的offset_mapping(支持TokenClassifier风格的get_tokenizer()协议对象);若 tokenizer 缺失、调用失败或不返回 offset,回退到基于正则\S+的空白分词(_simple_tokenize),保证无 tokenizer 环境也能出标注。

BILOU 模式下,单 token 实体标记为U-Label,多 token 实体首尾为B-/L-、中间为I-;返回的TokenClassificationResult通过labels()返回标签序列,metadata.groups记录实体group对应的 token 索引,方便下游按组聚合。

冒烟测试:端到端快速验证

仓库内置 smoke_gliner.py 用于轻量端到端验证:

python scripts/smoke_gliner.py --limit 2 --threshold 0.4 --adapter

脚本参数与默认值(对照源码_build_parser):

参数默认值说明
--index打包的默认索引指定models/index.json路径
--limit3最多测试前 N 个 GLiNER 家族模型
--threshold0.4置信度阈值
--adapter关闭推理后追加打印 token 级标签

脚本逻辑:先检查 GLiNER 依赖可用性(缺失时parser.error直接中止并提示安装命令)→ 从索引中筛出 GLiNER 家族模型 → 按模型第一个领域选取内置示例文本(DEFAULT_SAMPLE_TEXTS覆盖 biomedical、clinical、genomic、finance 等 15 个领域的短句)→ 调用infer并打印label: 'span' [start-end] score=xxx格式的结果,--adapter时额外打印逐 token 标注。该脚本是"索引 → 标签解析 → 推理 → 适配"整条链路的最小验证工具。

单元测试

在 tests/unit/ner 目录下运行单元测试(排除慢速冒烟检查):

python3 -m pytest tests/unit/ner -m "not slow"

需要完整覆盖时包含慢速测试:

python3 -m pytest -m slow

测试套件对 GLiNER API 使用 mock,单元测试过程中不会触发任何模型下载。套件按模块拆分,分别覆盖本文涉及的各个能力:test_indexing.py 验证索引构建与去重,test_labels.py 与 test_domain_label_maps.py 验证领域标签解析与覆盖路径,test_infer.py 验证标签优先级与阈值过滤,test_adapter.py 验证 BIO/BILOU 投影与重叠处理,test_gliner_loader.py 验证依赖检查与句柄缓存,test_smoke.py 覆盖冒烟脚本参数解析。

小结

OpenMed 零样本 NER 工具链的设计核心是"索引驱动 + 领域感知":先由build_index把本地模型目录扫描成稳定的元数据索引,再由infer依据索引中的家族与领域字段完成标签解析和推理分派,最后由 token 分类适配器衔接 token 级下游任务。所有环节均围绕本地检查点与打包的领域标签映射工作,依赖缺失会显式报错而非静默降级,配合冒烟脚本与 mock 化的测试套件,可以在无网络下载的前提下完成能力验证。

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

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

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

立即咨询