OpenMed 多语言临床概念接地资源:CC0 映射表、load_crosswalk 加载机制与纯离线 Grounding 实践
【免费下载链接】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/clinical/grounding/data/目录的官方说明文档为骨架,完整讲解 OpenMed 随包分发的 CC0-1.0 多语言临床映射表(crosswalk)与别名表的文件清单、JSON Schema 字段与取值约束,并结合 crosswalk.py 中load_crosswalk()/load_default_crosswalks()的校验实现、multilingual.py 的MultilingualGrounder路由机制以及 ICD10CNBridge 桥接器,给出从"自定义映射表 → 加载 → 本地离线接地"的完整可复现实操路径。读完本篇,你将能够:独立编写并通过校验的更大规模 crosswalk JSON 文件;理解 schema 版本、许可证与redistributable声明为何是硬性门槛;掌握中文/印地语/孟加拉语等源语言概念到 ICD-10 与 HPO 国际代码的确定性映射流程及其溯源(provenance)字段。
一、这批资源是什么:CC0 起步映射表而非受限词表
原始说明文档(data/README.md)对目录内容给出了三条关键定性,这也是理解整个设计的前提:
- 许可与内容边界:该目录下的 JSON 文件是 OpenMed 维护的小型 CC0-1.0 起步级 crosswalk 与临床别名表。其中不包含患者记录、凭据、模型权重,也不包含任何受许可限制的术语发布数据(如 UMLS、SNOMED CT 内容)。
- 自描述元数据:每个文件都声明 schema 版本(
schema_version)、资源版本(version)、许可证(license)、再分发标志(redistributable)以及精确的"源系统 → 国际代码"映射条目。 - 可扩展加载:更大的资源不随包分发,而是通过
load_crosswalk()从调用方自管的本地存储加载——加载路径全程无网络访问。
目录中当前实际随包分发的文件有三个数据文件(外加说明文档本身):
| 文件 | 资源名(name字段) | 源系统 → 目标系统 | 覆盖 locale |
|---|---|---|---|
| icd10cn_icd10.json | openmed-icd10cn-icd10-starter | ICD-10-CN→ICD10 | zh-CN |
| chpo_hpo.json | openmed-chpo-hpo-starter | CHPO→HPO | zh-CN |
| indic_hpo_aliases.json | openmed-indic-hpo-starter-aliases | OPENMED-INDIC-ALIAS→HPO | hi-IN/bn-IN/ta-IN/te-IN |
三者均声明"schema_version": 1、"version": "2026.08.0"、"license": "CC0-1.0"、"redistributable": true。
一个典型的chpo_hpo.json条目长这样:源系统CHPO的代码CHPO:0001945,在zh-CNlocale 下挂中文别名["发热", "发烧"],精确映射到 HPO 的HP:0001945(Fever)。icd10cn_icd10.json 则展示了中文扩展代码到国际 ICD-10 的三级映射,如E11.900(2型糖尿病 / Ⅱ型糖尿病)→E11.9、I10.x00(原发性高血压 / 高血压病)→I10、J18.900(肺炎 / 未特指病原体的肺炎)→J18.9。indic_hpo_aliases.json 用OM-HI-xxxx、OM-BN-xxxx等内部代号组织印地语、孟加拉语、泰米尔语、泰卢固语四种语言的 HPO 症状别名( Fever / Headache / Muscle weakness 三个症状 × 四语言)。
注意区分两个"crosswalk"概念:同一模块 crosswalk.py 的前半部分实现了面向受限词表(UMLSMRCONSO/MRMAP)的UMLSCrosswalk,它刻意没有任何默认数据源、下载客户端或环境变量回退,必须指向调用方持有权的本地文件;而本文档讨论的data/目录资源属于许可宽松的多语言 crosswalk,走CrosswalkResource/load_crosswalk()这条 JSON 路径。模块 docstring 明确了两条路径都不进行网络访问。
二、JSON Schema 逐字段拆解:从三个随包文件反推的完整约束
结合三个 JSON 文件的实际结构与 crosswalk.py 中_resource_from_payload/_entry_from_payload/CrosswalkEntry的校验逻辑,一份合法 crosswalk 文件的完整契约如下。
2.1 顶层字段
| 字段 | 必填 | 约束与校验来源 |
|---|---|---|
schema_version | 是 | 必须等于CROSSWALK_SCHEMA_VERSION(当前为1),否则抛CrosswalkFormatError,见 crosswalk.py |
name | 是 | 非空文本;用于resource_version标识与默认资源查找 |
version | 是 | 非空文本;随包文件当前为2026.08.0 |
license | 是 | 非空文本;随包文件声明CC0-1.0 |
redistributable | 是 | 必须为布尔true。CrosswalkResource.__post_init__中为false时直接抛CrosswalkLicenseError(crosswalk.py)——这是"许可声明"作为硬性门槛的落点 |
entries | 是 | 必须是 JSON 数组,逐条解析为CrosswalkEntry;空表同样报错 |
此外,load_crosswalk()在解析前对文件本身有两道前置检查:扩展名必须是.json(大小写不敏感),文件必须真实存在且可读(crosswalk.py)。整个文件字节会被用于计算content_hash = sha256:<hexdigest>,即内容哈希由文件原始字节而非序列化结果计算,保证同一文件在任何加载路径下哈希一致。
2.2 条目(entry)字段
| 字段 | 必填 | 校验行为 |
|---|---|---|
source_system | 是 | 非空文本,如ICD-10-CN、CHPO、OPENMED-INDIC-ALIAS |
source_code | 是 | 非空文本;匹配时会经过normalize_alias()归一化后比较 |
locale | 是 | 归一化为语言-区域标签:下划线转连字符、语言小写、区域段大写(hi_IN→hi-IN),见_normalize_locale(crosswalk.py) |
aliases | 是 | 必须是列表且至少一个非空元素;去重按归一化后的文本进行(_unique_text),避免同形别名重复计数 |
target_system | 是 | 非空文本,统一转大写;只允许ICD10或HPO两个目标系统(_SUPPORTED_TARGET_SYSTEMS,crosswalk.py)——宽松 crosswalk 目前只承载这两类国际代码 |
target_code | 是 | 非空文本,如E11.9、HP:0001945 |
target_display | 是 | 非空文本,国际术语的展示名(如Fever) |
CrosswalkEntry还提供了两个派生属性:language(取 locale 的主语言,用于按语言路由条目)与surfaces(source_code与各别名按确定性顺序去重拼接,是字符串匹配时遍历的完整表面集合)。
资源级还有一层唯一性不变式:(source_system, source_code, locale, target_system, target_code)五元组在表内必须唯一,重复映射会抛CrosswalkFormatError(crosswalk.py)。CrosswalkResource.resource_version拼为name@version+sha256:<hash>的稳定标识,这个值会原样进入每次接地结果的provenance.mapping_resource_version字段,是复现与审计的关键锚点。
三、加载 API:load_crosswalk() 与 load_default_crosswalks()
两个入口函数都定义在 crosswalk.py 并通过 grounding 包init导出:
from openmed.clinical.grounding import ( load_crosswalk, # 加载调用方自管的本地 JSON 文件 load_default_crosswalks, # 加载随包捆绑的三份 CC0 起步表 ) # 1) 捆绑资源:返回 (icd10cn_icd10, chpo_hpo, indic_hpo_aliases) 三个 # CrosswalkResource,顺序固定为 DEFAULT_CROSSWALK_RESOURCES 声明顺序 defaults = load_default_crosswalks() # 2) 调用方资源:更大的表放在自己控制的本地存储中,加载即校验 my_resource = load_crosswalk("/data/terminology/my_region_icd10cn.json")load_crosswalk()的完整行为链:路径解析(expanduser)→ 存在性与.json后缀检查 →read_bytes()读取原始字节 →json.loads解析 →_resource_from_payload逐字段校验并计算 SHA-256 内容哈希 → 构造不可变的CrosswalkResource。任何一步失败都归约为两类异常:格式问题抛CrosswalkFormatError(继承ValueError),许可声明不通过抛其子类CrosswalkLicenseError。这个异常分层意味着你可以用except CrosswalkLicenseError单独捕获"文件合法但不可再分发"的场景。
CrosswalkResource随后提供三类查询方法,供上层精确匹配使用(crosswalk.py):
entries_for_locale(locale):按主语言路由,返回某区域标签下的全部条目——这是多语言路由的第一道闸;entries_for_source_code(source_code, source_system=None):源代码精确匹配(normalize_alias归一化后比较),可附带源系统过滤;entries_for_target_code(target_code, target_system=None):反向查询,由国际代码找回全部源扩展。
3.1 端到端实操示例(可直接复制)
以下示例对应 test_multilingual.py 中已验证的行为:
from openmed.clinical.grounding import ground_multilingual # 中文源文本 → ICD-10(走 icd10cn_icd10 表) chinese = ground_multilingual("2型糖尿病", "zh-CN") # chinese.system == "ICD10" # chinese.code == "E11.9" # chinese.display == "Type 2 diabetes mellitus without complications" # chinese.score == 1.0(别名精确命中) # chinese.source_language == "zh" # 印地语源文本 → HPO(locale 下划线会自动归一化为 hi-IN) hindi = ground_multilingual("बुखार", "hi_IN") # (hindi.system, hindi.code) == ("HPO", "HP:0001945") # hindi.provenance["offline"] is True # 溯源字段:资源标识精确到内容哈希 # chinese.provenance["mapping_resource_version"] 形如 # "openmed-icd10cn-icd10-starter@2026.08.0+sha256:..."测试结果断言(test_multilingual.py)同时验证了:source_locale归一化为zh-CN、cross_lingual_match_score == 1.0、以及mapping_resource_version以openmed-icd10cn-icd10-starter@2026.08.0+sha256:开头——这证明每次接地结果的溯源都绑定到具体的资源版本与内容哈希,而非模糊的"内置表"。
四、资源如何被消费:MultilingualGrounder 的三级匹配路由
说明文档最后一句"更大的资源可通过load_crosswalk()从调用方本地存储加载",其消费方是 multilingual.py 中的MultilingualGrounder。构造参数resources的三种取值直接对应资源来源策略(multilingual.py):
resources取值 | 行为 |
|---|---|
None(默认) | 调用load_default_crosswalks(),即本data/目录的三份捆绑表 |
()(空序列) | 禁用宽松映射,仅保留编码器与 UMLS 门控路径 |
(load_crosswalk(path), ...) | 使用调用方自管的更大规模表,与捆绑表可混合 |
ground(mention, locale, top_k=5)的匹配流水线(multilingual.py):
- locale 路由:归一化 locale 后,只对
entries_for_locale(resolved_locale)命中的条目做匹配——中文查询绝不会与印地语别名比较; - 确定性字符串匹配(
string_score_cutoff默认0.72):对每个条目的全部surfaces(源码 + 别名)做normalize_alias归一化后比较,完全一致得 1.0 分(match_kind="exact-crosswalk"),否则用SequenceMatcher.ratio()计算相似度(match_kind="string-crosswalk")。无编码器时这是唯一自由词表路径; - 可选密集向量匹配(
dense_score_cutoff默认0.50):提供本地 MLX SapBERT 类编码器时,对 mention 与各别名做嵌入并计算余弦相似度,产生dense-cross-lingual候选。encoder_path指向本地权重文件,缺失时静默降级到纯字符串匹配——test_multilingual.py 中"缺失权重路径 →encoder_enabled is False→ 模糊查询मांसपेशियों मे कमजोरी仍能以string-crosswalk命中HP:0001324"正是对这一降级路径的测试; - 门控 UMLS 路径:仅当调用方显式传入以用户密钥构造的
UserKeyVocabularyLoader时才启用,受限词表数据永不随包分发; - 排序与截断:候选按
(system, code)去重保留最高分,再按分数降序(并列按系统名、代码)取前top_k。第一个候选即选中结果,其余保留用于 Acc@k 评估与人工复核。
结果的provenance字典携带source_language、source_locale、mapping_resource_version、参与该 locale 的全部资源的name/version/content_hash/license摘要、encoder_id、match_method以及恒为true的offline标志——从源码结构看,这套字段就是为审计"这次接地用了哪一版哪一份数据"而设计的。
五、代码级精确映射:ICD10CNBridge 的正反向查询
对于不需要模糊匹配、只做精确代码桥接的场景,openmed/interop/bridges/icd10cn.py 提供了ICD10CNBridge,它是CrosswalkResource上"精确码视图":
from openmed.interop.bridges.icd10cn import ( ICD10CNBridge, load_icd10cn_crosswalk, map_icd10cn_code, map_icd10_to_icd10cn, ) bridge = ICD10CNBridge(load_icd10cn_crosswalk()) # 不给路径时回落到捆绑的 # openmed-icd10cn-icd10-starter m = bridge.to_icd10("E11.900") # m.target_code == "E11.9" # m.resource_version == "openmed-icd10cn-icd10-starter@2026.08.0+sha256:..." bridge.from_icd10("I10") # → (ICD10CNMapping(source_code="I10.x00", ...),) 反向找回中文扩展 map_icd10cn_code("J18.900") # 单方向快捷函数 map_icd10_to_icd10cn("E11.9") # 反向快捷函数构造ICD10CNBridge时会对资源做结构断言:所有条目必须是ICD-10-CN → ICD10(源系统比较不区分大小写,目标系统必须是ICD10),否则抛ValueError——这防止把 HPO 表误用进 ICD-10 桥接链路。单元测试(test_multilingual.py)遍历了捆绑表的每一条条目验证正反向映射一致,并断言未知扩展码E11.9000返回None而非报错——未知码的语义是"查无"而非异常。
六、扩展你自己的映射表:编写规范与常见失败模式
把上面的校验规则汇总为编写 checklist,并给出最小合法模板(字段命名与 indic_hpo_aliases.json 完全一致):
{ "schema_version": 1, "name": "my-regional-hpo-aliases", "version": "1.0.0", "license": "CC0-1.0", "redistributable": true, "entries": [ { "source_system": "MY-CLINIC", "source_code": "MY-0001", "locale": "zh-CN", "aliases": ["咳嗽", "干咳"], "target_system": "HPO", "target_code": "HP:0000478", "target_display": "Cough" } ] }从 crosswalk.py 的校验代码可以列出最常见的失败模式及对应异常:
| 失败模式 | 抛出异常 | 触发点 |
|---|---|---|
schema_version不是1 | CrosswalkFormatError | _resource_from_payload |
redistributable为false或缺省 | CrosswalkLicenseError | CrosswalkResource.__post_init__ |
aliases写成字符串/空列表 | CrosswalkFormatError | CrosswalkEntry.__post_init__ |
target_system写成SNOMEDCT、LOINC等 | CrosswalkFormatError("free crosswalk target_system must be ICD10 or HPO") | CrosswalkEntry.__post_init__ |
文件扩展名不是.json、路径不存在、JSON 解析失败 | CrosswalkFormatError | load_crosswalk |
| 五元组重复映射 | CrosswalkFormatError | CrosswalkResource.__post_init__ |
加载后即可按第三节的模式接入ground_multilingual(把load_crosswalk()的返回值经resources=传入),或在需要精确码桥接时包一层自定义 Bridge。测试文件(test_multilingual.py 的_write_crosswalk辅助函数)演示了"临时写一份合成表 → 加载 → 断言接地结果"的完整回路,可作为自建表的验收参考;其中还包含redistributable=false触发CrosswalkLicenseError的负向用例。
七、设计要点小结:为什么这样切分"宽松"与"受限"两条路径
从源码结构看,这套目录的设计意图可以归纳为四点,均能在仓库中找到直接证据:
- 许可隔离:
data/README.md声明"不含许可受限的术语发布数据";crosswalk.py 模块 docstring 进一步说明 UMLS/SNOMED 是受限词表,包内刻意没有默认源、下载客户端或捆绑内容,加载方必须自备本地授权文件。两条数据路径(JSON 宽松表 vs MRCONSO/MRMAP 受限表)在 API 层面完全分离,避免"看起来能离线跑、实际违反许可证"的灰色地带。 - 全程离线:
load_crosswalk只读本地字节;MultilingualGrounder的模块 docstring 声明"不存在翻译服务、模型下载或其他网络路径";每个结果的provenance都带offline: true。 - 可审计性:
name@version+sha256:<内容哈希>的资源版本标识、条目级vocab_version、逐资源 license 摘要,使得任何一次接地都能追溯到具体文件的具体字节。 - 确定性降级:无编码器时字符串匹配仍可用;模糊匹配阈值(
0.72/0.50)可构造时显式调节;未知代码返回空结果而非抛错。
需要说明的适用前提:捆绑三份起步表目前只覆盖zh-CN与印地/孟加拉/泰米尔/泰卢固四种 locale,且自由 crosswalk 的目标系统仅限ICD10与HPO;其他语言或其他目标词表(如 LOINC、RXNorm)应通过load_crosswalk()加载自建表,或经由 vocab.py 的自由词表加载器与受限词表门控流程处理,而不是塞进这批 JSON 资源。
【免费下载链接】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),仅供参考