第3章 药物知识图谱构建:从数据到图的那些坑

3.1 数据来源:三大核心源头

构建药物知识图谱,第一步就是吃透数据源。我做过十几个药图项目,每次都会先问自己:数据准不准?全不全?更新频率够不够?说白了,源头脏了,后面所有工作都是白费。

目前业界公认的三个主力来源:DrugBank、PubMed 和 临床试验记录。它们各有侧重,我习惯把它们组合成一个“数据三角”。

数据源内容特点我的用法
DrugBank结构化药物‑靶点‑疾病三元组,含化学性质、药物互作作为核心骨架,通常直接RDF解析
PubMed2000万+生物医学文献摘要,半结构化用NER挖出隐含关系,但要处理噪声
临床试验记录CT.gov、WHO ICTRP,含剂量、适应症、不良事件补充真实世界证据,对齐编码体系

举个例子,DrugBank里的“阿司匹林→COX1”是明确的关系,但PubMed摘要里可能写“阿司匹林通过抑制COX1减少前列腺素”,这是自由文本。说白了,前一个是数据库,后一个是语料库。

⚡ 数据质量优先级

  • DrugBank:稳定、结构化,我标为可信级
  • 临床试验记录:标准规范,但需要解析ANSI编码
  • PubMed:信息最广泛,但实体歧义最多

3.2 实体抽取与关系抽取

拿到原始数据后,第一件事就是抽实体。我遇到过一个项目,用通用BERT抽药物名,结果把“血压”也当成了药……嗯,踩坑之后我改用BioBERT或PubMedBERT了。

实体抽取主要用序列标注思路。关系抽取目前主流是远程监督+注意力。下面给一段我常用的Python片段(用spaCy + 自定义规则做粗筛,再用模型精调):

import spacy
from spacy.pipeline import EntityRecognizer

nlp = spacy.load("en_core_sci_sm")
text = "Aspirin binds to COX1 and reduces prostaglandin synthesis."
doc = nlp(text)
for ent in doc.ents:
    print(f"{ent.text} -> {ent.label_}")
# 输出:Aspirin -> CHEMICAL
#       COX1 -> GENE
#       prostaglandin -> CHEMICAL

关系抽取我习惯用远程监督对齐:先用已有知识库(DrugBank的三元组)回标语料,再用BiLSTM+ATT训练。这里贴一个mini配置示例:

// relation_extraction_config.yaml
model: "bert-bilstm-att"
max_seq_length: 128
relation_types: ["interact", "inhibit", "activate", "treat"]
positive_ratio: 0.3
pretrain_model: "dmis-lab/biobert-base-cased-v1.1"

注意:远程监督会引入假正例。我曾在CT.gov里见过一条记录说“格列卫治疗CML”,但另一篇文献说格列卫也用于GIST,远程监督直接标成两个关系——这就需要后面融合阶段做消歧。

💡 小技巧

别一上来就上大模型。先用词典规则抽出肯定出现的实体(比如DrugBank中所有的药物名),跑通流水线再说。我曾经在初期就直接上BERT,结果找错花了三天。

3.3 知识融合与对齐

抽出来的实体和关系,名字不一样但指代同一事物,怎么办?这就是融合对齐的活。

我总结过三种典型对齐场景:

  • 模式层对齐:不同数据源的同种属性映射到同一个Schema(比如DrugBank的“target”和PubMed的“binds to”都算“互作”)
  • 实体对齐:“Aspirin”“Acetylsalicylic acid”“ASA”指向同一个药物
  • 关系实例对齐:从不同来源抽出的同样三元组,合并置信度

下面是我用过的一个简单对齐规则片段(JSON格式的映射表):

{
  "entity_alias": {
    "Aspirin": ["ASA", "Acetylsalicylic acid", "2-acetoxybenzoic acid"],
    "Ibuprofen": ["Advil", "Motrin", "2-(4-isobutylphenyl)propanoic acid"]
  },
  "relation_mapping": {
    "interact_with": ["binds", "targets", "has_target"],
    "treats": ["indicated_for", "therapy_of", "used_for"]
  }
}

实战中我遇到过最头疼的问题:PubMed里“TNF-alpha inhibition”和DrugBank里“TNF inhibitor”其实是一个意思,但一个用动词一个用名词。我曾经用编辑距离+同义词库做过一次粗对齐,再人工校验3000条,把准确率从72%拉到91%。

⚠️ 对齐陷阱

千万别直接用字符串包含判断。“雷帕霉素”和“西罗莫司”是同一种药,但字符串完全不像。必须引入别名库或嵌入相似度。我一开始偷懒用contains,结果把“雷帕霉素衍生物”也误对齐了,数据直接污染。

整体流程一图说清

我不喜欢光讲理论。下面这张图是我在项目中反复给团队画的骨架——三个数据源如何汇聚成知识图谱:

药物知识图谱构建流程 DrugBank PubMed 文献 临床试验记录 实体与关系抽取(NER + RE) 知识融合与对齐(别名映射、消歧、schema整合) 药物知识图谱 (DrugKG) 数据源各用不同解析策略 对齐阶段需人工review锚定

你看,从三条数据管道往下走,实体抽取之后必须过一道对齐关口。我自己的经验是:融合阶段至少留20%的时间做人工校验,特别是药物别名和多义词。这个步骤没人能替你写代码搞定。

📌 本章核心要点

  1. 数据源选型:DrugBank做骨架,PubMed做广度,临床试验做验证
  2. 实体抽取不要迷信全模型,先用词典兜底
  3. 关系抽取用远程监督速度快,但必须加假正例过滤
  4. 融合对齐是整个构建流程中最费人的一环,别名库和嵌入相似度必须组合使用

好了,这一章就聊这些。数据到手、实体抽出来了、关系对好了,下一步就是把这些三元组装成图结构——那时候你会遇到很多特征提取的坑,后面章节咱们再慢慢拆解。