第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') 最稳妥。我曾经用逗号分割的数据,结果有些关系名里自带了逗号,直接翻车。嗯,血的教训。

数据预处理的步骤我总结为三步:

  1. 数据标准化——把所有实体和关系的名字转成小写,去掉特殊字符,避免编码问题。
  2. 三元组拆分——把完整的关系图拆成训练集、验证集、测试集。一般按 8:1:1 或 7:1.5:1.5。
  3. 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 整个流程的脑图

说了这么多,咱们用一张流程图把整体的数据准备脉络串起来。你跟着这张图走,基本不会犯方向性错误。

数据集准备流程图:从原始数据到ComplEx模型输入 数据集准备整体流程 原始数据 数据提取 数据清洗 三元组拆分 ID映射 质量检查 处理要点 • DrugBank解析XML • BioKG已提供三元组 • 过滤自环三元组 • 按实体切分数据 • 统一小写 + 标准化 • 检查缺失值 • 统计唯一实体/关系数

说白了,数据准备就是一场「把大象装进冰箱」的过程。原始数据推门而入,数据提取把它搬上台,数据清洗去毛刺,然后切成训练集和测试集,最后贴上 ID 标签。每一步都不能跳过。

最终交付物:你手里应该有四个文件——entities.txt(实体ID映射表)、relations.txt(关系ID映射表)、train.txt(训练集三元组)、valid.txt(验证集三元组)、test.txt(测试集三元组)。搞定了这些,下章咱们就能正式开始训练 ComplEx 模型了。