第4章 数据集准备:DrugBank与BioKG
做药物关系预测,数据就是食材。没有好的数据,模型再先进也白搭。今天咱们就来聊聊两个最主流的药物知识图谱数据集——DrugBank 和 BioKG。我当年刚接触生物医药数据时,第一个打交道的数据集就是 DrugBank,至今印象深刻。
4.1 两个数据集,到底选哪个?
你可能会问:两个数据集有什么区别?我用一个表格说清楚:
| 对比维度 | DrugBank | BioKG |
|---|---|---|
| 数据规模 | 约 1.5 万种药物,中等规模 | 包含 15 万 + 实体,覆盖面广 |
| 关系类型 | 以药物-靶点、药物-药物相互作用为主 | 药物-疾病、药物-基因、基因-疾病等跨领域关系 |
| 数据来源 | FDA 批准的药物,人工审核 | 整合 DrugBank、CTD、SIDER 等多个数据库 |
| 适用场景 | 药物相互作用预测、靶点发现 | 多模态关系推理、药物重定位 |
| 预处理难度 | 较低,已结构化 | 中等,需要做格式统一 |
我的建议:初学者先拿 DrugBank 练手。数据干净,关系清晰,跑 ComplEx 模型时不容易踩坑。BioKG 更适合做多跳推理,或者你后续想做药物重定位的场景。
4.2 数据长什么样?
咱们先看看 DrugBank 的数据结构。DrugBank 的标准导出格式是 XML,解压后大概这个样:
<drugbank xmlns="http://www.drugbank.ca">
<drug type="biotech">
<drugbank-id primary="true">DB00001</drugbank-id>
<name>Leptrudin</name>
<targets>
<target>
<id>BE0000001</id>
<name>Thrombin</name>
<action>inhibitor</action>
</target>
</targets>
</drug>
</drugbank>
BioKG 就友好多了,它直接提供三元组文件,格式类似这样:
DB00001 interacts_with DB00002
DB00001 targets BE0000001
DB00002 treats DOID:1234
DB00003 has_side_effect SIDER:C00001
看到没?头实体、关系、尾实体,对齐了知识图谱的经典三元组结构。这点对跑 ComplEx 模型特别重要,因为 ComplEx 吃的就是这种格式的数据。
核心要点:ComplEx 模型要求输入格式为 (头实体, 关系, 尾实体)。每条样本就是一个三元组。DrugBank 需要自己从 XML 里提取,BioKG 已经做好这一步了。
4.3 数据预处理实战
我个人习惯先把数据洗成统一格式。不管你是用 DrugBank 还是 BioKG,最后都要转成这种样子:
# 标准三元组格式 tab 分隔
db00001 interacts_with db00002
db00001 targets be0000001
db00002 treats doid_1234
db00003 has_side_effect sider_c00001
为什么强调用 tab 分割?因为后面读取数据时,用 split('\t') 最稳妥。我曾经用逗号分割的数据,结果有些关系名里自带了逗号,直接翻车。嗯,血的教训。
数据预处理的步骤我总结为三步:
- 数据标准化——把所有实体和关系的名字转成小写,去掉特殊字符,避免编码问题。
- 三元组拆分——把完整的关系图拆成训练集、验证集、测试集。一般按 8:1:1 或 7:1.5:1.5。
- ID 映射——把实体名和关系名都映射成整数 ID。ComplEx 模型内部不认字符串,只认数字。
一个小技巧:拆三元组时,千万别打乱顺序直接随机切分。要按实体来分,确保训练集里的实体在测试集里也能看到。我刚开始不懂,直接随机切,结果模型学了一堆过拟合的规律,测试指标虚高。
4.4 数据质量检查
数据洗完后,一定要做个质量检查。我习惯写个简单的统计脚本:
import pandas as pd
# 读取清洗后的数据
df = pd.read_csv('kg_triples.txt', sep='\t',
names=['head', 'relation', 'tail'])
print(f"总三元组数: {len(df)}")
print(f"唯一实体数: {len(set(df['head'].unique()) | set(df['tail'].unique()))}")
print(f"唯一关系数: {df['relation'].nunique()}")
print(f"缺失值检查:\n{df.isnull().sum()}")
输出应该类似这样:
总三元组数: 1048576
唯一实体数: 15678
唯一关系数: 36
缺失值检查:
head 0
relation 0
tail 0
dtype: int64
注意,缺失值必须为 0。如果有缺失,赶紧回去查数据源,大概率是解析 XML 时漏字段了。
避坑指南:我曾经在处理 BioKG 时,发现数据集中有自环三元组(头实体 == 尾实体),比如某些药物与自身的相互作用。这种三元组对于关系预测模型来说是噪声,一定要过滤掉!
4.5 整个流程的脑图
说了这么多,咱们用一张流程图把整体的数据准备脉络串起来。你跟着这张图走,基本不会犯方向性错误。
说白了,数据准备就是一场「把大象装进冰箱」的过程。原始数据推门而入,数据提取把它搬上台,数据清洗去毛刺,然后切成训练集和测试集,最后贴上 ID 标签。每一步都不能跳过。
最终交付物:你手里应该有四个文件——entities.txt(实体ID映射表)、relations.txt(关系ID映射表)、train.txt(训练集三元组)、valid.txt(验证集三元组)、test.txt(测试集三元组)。搞定了这些,下章咱们就能正式开始训练 ComplEx 模型了。