图谱构建流程:药物-基因-疾病三元组提取,实体对齐与标准化

好,咱们进入第四章。前几章聊完了参数调试的整体哲学,今天来点硬核的——怎么从海量文献和数据库里,把药物、基因、疾病这三个核心要素拽出来,拼成能用的知识图谱。

我经常看到有人拿着漂亮的图谱工具,结果建出来的图全是错边。药物和基因连反了,疾病名写一堆同义词。说白了,源头没弄干净,后面调参数全是白搭。

4.1 三元组提取:从非结构化文本到结构化事实

这一步的目标很单纯:从PubMed摘要、临床试验报告、药品说明书里,抽取出(药物, 关系, 基因)或(药物, 关系, 疾病)这样的三元组。

我个人习惯用流水线方式来做,每一步都能单独调参数,出了问题好排查。

  • 命名实体识别 (NER):先找出文本里哪些词是药物名、基因名、疾病名。我用过Scispacy和BERT-BiLSTM-CRF,各有千秋。
  • 关系抽取 (RE):判断实体之间有没有关系。比如“阿托伐他汀治疗高脂血症”,这里的关系是“治疗”。
  • 三元组组装:把实体和关系拼成结构化格式。
提示: 别一上来就上大模型。我在一个抗肿瘤药物项目中试过,效果确实好,但推理时间太长,迭代一次要三天。先拿规则+词典做快速原型,能省下大量调试时间。

这里有个常见的参数坑:NER模型的上下文窗口大小。窗口设小了,跨句的实体关系抓不住;设大了,噪声太多。我通常从32开始调,看验证集的F1分数变化。

来,看下典型的输出格式:

[
  {
    "drug": "阿托伐他汀",
    "relation": "TREATS",
    "disease": "高脂血症",
    "confidence": 0.97,
    "source": "PMID_12345678"
  },
  {
    "drug": "伊马替尼",
    "relation": "TARGETS",
    "gene": "BCR-ABL",
    "confidence": 0.99,
    "source": "PMID_23456789"
  },
  {
    "gene": "EGFR",
    "relation": "ASSOCIATED_WITH",
    "disease": "非小细胞肺癌",
    "confidence": 0.89,
    "source": "PMID_34567890"
  }
]

注意那个confidence字段。我一般设阈值为0.85,低于这个的扔进待审核队列,而不是直接丢掉。为什么呢?有些罕见病和孤儿药的文献少,模型置信度天然低,但恰恰是最需要保留的数据。

4.2 实体对齐与标准化:把同物不同名的妖怪收服

提取完三元组,你会发现同一个药有七八个名字。比如“对乙酰氨基酚”也叫“扑热息痛”、“泰诺”、“APAP”。这就是实体对齐要解决的问题。

我把它拆成三步来处理:

  1. 同义词映射:利用UMLS、DrugBank、HGNC这些标准库,建立别名到标准名的映射表。
  2. 模糊匹配:处理拼写错误、缩写不一致、大小写差异。比如“IL-2”和“interleukin-2”得认出来是同一个东西。
  3. 消歧:一词多义的问题。比如“TP53”既是基因名,也是蛋白质名。这得看上下文来判断。
警告: 我曾经吃过大亏,没做消歧就把“ACE”(血管紧张素转化酶)和“ACE”(美国体能协会)混一起了。一个基因库跑出运动俱乐部参数,幸亏发现得早。

4.2.1 基于规则的标准化

规则方法虽然老套,但稳定。我通常写一套正则表达式和查表逻辑来搞定大部分简单情况:

# 示例:药物名标准化规则片段
normalization_rules = {
    "pattern": [
        (r"(?i)\bativan\b", "劳拉西泮"),
        (r"(?i)\bvalium\b", "地西泮"),
        (r"(?i)\bpropranolol\b", "普萘洛尔"),
    ],
    "suffix_removal": [
        " hydrochloride", " sodium", " sulfate"
    ],
    "gene_hgnc_mapping": "hgnc_complete_set.json"
}

规则能覆盖大约60%的实体,剩下的交给机器学习来做。

4.2.2 基于嵌入的语义对齐

对于规则搞不定的情况,我用实体嵌入来做语义相似度计算。把每个实体名称转成向量,然后算余弦相似度。

调参要点在于嵌入模型的维度。256维还是512维?我在一个包含300万三元组的项目中试过,512维对罕见实体召回率提升明显,但内存开销翻倍。最终折中选了384维。

对齐策略 召回率(Top-1) 耗时/百万实体 适用场景
精确字符串匹配 22.4% 2秒 标准术语库预热
规则+同义词表 63.1% 15秒 常见药物与疾病
嵌入相似度 88.7% 4分12秒 变异名、拼写错误
混合流水线 94.8% 6分45秒 生产环境全量对齐

你看,混合流水线比单纯用嵌入只高了6个百分点,但耗时多了将近50%。值不值得?如果是离线构建图谱,我认为值得;如果是实时查询,你得做增量对齐。

4.3 标准化命名空间与ID系统

对齐之后,要给每个实体分配一个全局唯一的标准ID。没有标准ID,图谱就是一团乱麻。

我用的标准命名空间:

  • 药物:优先用DrugBank ID (DBxxxxx),其次是PubChem CID
  • 基因:HGNC ID (HGNC:xxxxx),兼容Ensembl ID
  • 疾病:OMIM ID,或者DOID (Disease Ontology ID)
关键参数: ID映射表的版本号。我见过最惨的bug,是混用了DrugBank 5.0和5.1的ID,导致阿司匹林和布洛芬在图谱里变成了同一个节点。务必在元数据里记录每个实体的ID版本。

处理完这些,来一张整体流程的总结图,方便你对照检查自己的管道是不是都覆盖到了。

三元组提取与标准化流程总览 三元组提取 → 实体对齐 → 标准化流程 步骤1:三元组提取 NER + RE → 结构化三元组 步骤2:基础对齐 规则 + 同义词表映射 步骤3:高级对齐 嵌入相似度 + 消歧 对齐后的三元组 含置信度与源信息 步骤4:标准化命名 分配标准ID(DrugBank/HGNC/OMIM) 标准化知识图谱 药物-基因-疾病三元组,版本化ID,可搜索 ⚠ 避坑:ID版本混用 → 图谱节点重复或关联错误 每一步结束后,输出带版本戳的中间快照,方便回溯

4.4 实用参数调试清单

给各位一个速查表,我在调这三个模块时,优先关注这几个参数:

  • NER的实体边界偏移: 默认-2到+2,如果跨句实体被截断,扩大到-3到+3
  • 关系抽取的置信度阈值: 我从0.8开始,根据验证集的假阳性率向下调,最低到0.6
  • 对齐相似度阈值: 余弦相似度≥0.85才自动合并,0.75-0.85进人工审核队列
  • 标准ID回退策略: 如果映射不到标准库,用UUID临时占位,标记为unresolved,等下一轮对齐再处理
小技巧: 你可以在标准化步骤之后,跑一个“拓扑一致性检查”。比如,如果A药物靶向B基因,B基因关联C疾病,但A和C之间没有治疗关系,那说明可能漏了三元组,或者对齐有误。这个启发式检查能帮你把漏网之鱼抓回来。

嗯,说到这,我个人觉得最容易被忽视的是实体对齐这一步。好多团队花大量时间调三元组提取的模型,结果对齐做得糙,最后图谱里充满了同义节点。你想想看,一个“EGFR”和“表皮生长因子受体”在图谱里并排站着,怎么推理?“吉非替尼靶向EGFR”和“吉非替尼靶向表皮生长因子受体”就会变成两条记录,查询聚合的时候就出错了。

所以我的经验是:舍得在对齐这一步花时间调参。把规则的覆盖率和嵌入模型的距离阈值做成可配置参数,然后用一个标注好的黄金标准集去评估。每次改参数,跑一遍评估,迭代个十几轮,你就能找到最适合当前数据集的组合。