图谱构建流程:药物-基因-疾病三元组提取,实体对齐与标准化
好,咱们进入第四章。前几章聊完了参数调试的整体哲学,今天来点硬核的——怎么从海量文献和数据库里,把药物、基因、疾病这三个核心要素拽出来,拼成能用的知识图谱。
我经常看到有人拿着漂亮的图谱工具,结果建出来的图全是错边。药物和基因连反了,疾病名写一堆同义词。说白了,源头没弄干净,后面调参数全是白搭。
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”。这就是实体对齐要解决的问题。
我把它拆成三步来处理:
- 同义词映射:利用UMLS、DrugBank、HGNC这些标准库,建立别名到标准名的映射表。
- 模糊匹配:处理拼写错误、缩写不一致、大小写差异。比如“IL-2”和“interleukin-2”得认出来是同一个东西。
- 消歧:一词多义的问题。比如“TP53”既是基因名,也是蛋白质名。这得看上下文来判断。
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)
处理完这些,来一张整体流程的总结图,方便你对照检查自己的管道是不是都覆盖到了。
4.4 实用参数调试清单
给各位一个速查表,我在调这三个模块时,优先关注这几个参数:
- NER的实体边界偏移: 默认-2到+2,如果跨句实体被截断,扩大到-3到+3
- 关系抽取的置信度阈值: 我从0.8开始,根据验证集的假阳性率向下调,最低到0.6
- 对齐相似度阈值: 余弦相似度≥0.85才自动合并,0.75-0.85进人工审核队列
- 标准ID回退策略: 如果映射不到标准库,用UUID临时占位,标记为unresolved,等下一轮对齐再处理
嗯,说到这,我个人觉得最容易被忽视的是实体对齐这一步。好多团队花大量时间调三元组提取的模型,结果对齐做得糙,最后图谱里充满了同义节点。你想想看,一个“EGFR”和“表皮生长因子受体”在图谱里并排站着,怎么推理?“吉非替尼靶向EGFR”和“吉非替尼靶向表皮生长因子受体”就会变成两条记录,查询聚合的时候就出错了。
所以我的经验是:舍得在对齐这一步花时间调参。把规则的覆盖率和嵌入模型的距离阈值做成可配置参数,然后用一个标注好的黄金标准集去评估。每次改参数,跑一遍评估,迭代个十几轮,你就能找到最适合当前数据集的组合。