简介本资源是一套基于知识图谱与推荐系统的药物靶点相互作用预测Python项目源码面向计算机相关专业学生适用于课程设计、期末大作业或项目实战练习也可作为生物信息学交叉方向的入门参考。压缩包共40个文件约56KB以9个py脚本为核心涵盖deepdti、kge_rf、kge_nfm等模型实现与train_all.py训练入口另含Pipfile、requirements.txt等依赖配置、README.md操作说明及LICENSE等辅助文件目录按data、output、logs、eg_model等模块划分结构清晰。项目完整呈现数据预处理、知识图谱构建、推荐算法实现与模型评估流程涉及Pandas、NetworkX、Scikit-learn等常用库并附操作指南帮助快速跑通。目前已有94人学习下载适合希望将理论落地、积累完整项目经验的学生参考借鉴。1. 药物靶点交互预测从知识图谱到推荐排序的工程化落地药物靶点交互Drug-Target Interaction, DTI预测这件事本质上是在回答一个排序问题给定一个药物它最可能作用在哪些靶点上以及这个相互作用的置信度有多高。传统做法靠湿实验筛选周期长、成本高一个靶点验证动辄数月。而把知识图谱和推荐系统拼在一起做思路就变了——药物和靶点都是图里的节点已知的交互是边预测任务就变成了在图上做链接预测再套一层推荐排序的框架把候选靶点按概率排出来。这套方案适合有 Python 基础、手头有 DTI 数据集、想快速跑通一个可复现 baseline 的从业者。我下面讲的路径核心是用知识图谱构建药物-靶点-疾病的关系网络再用推荐系统里的矩阵分解和神经协同过滤做打分排序全程 Python 实现不依赖任何闭源工具。2. 知识图谱构建药物、靶点、疾病三元组怎么落成可计算的图2.1 为什么 DTI 预测需要知识图谱而不是一张交互表如果只把药物-靶点交互存成一张二维表你丢掉了大量上下文信息。同一个药物可能通过不同通路作用于多个靶点靶点又关联到疾病、通路、基因本体这些关系恰恰是提升预测精度的关键。知识图谱把这些实体和关系显式建模成三元组(头实体, 关系, 尾实体)比如(阿司匹林, 靶向, COX-1)、(COX-1, 关联疾病, 炎症)。有了这层结构模型不仅能学到药物和靶点的直接交互还能通过路径推理捕捉间接信号。常见做法是从 DrugBank、STRING、DisGeNET 这类公开库导出数据清洗后统一成三元组格式。我一般会先把实体类型固定下来药物、靶点、疾病、通路四类关系类型控制在十种以内避免图太稀疏导致 embedding 学不出来。2.2 用 Python 把原始数据转成三元组并构建图结构数据源拿到手通常是 CSV 或 TSV字段命名五花八门。第一步是统一实体 ID药物用 DrugBank ID靶点用 UniProt ID疾病用 UMLS CUI这样跨库关联时不会因为名称拼写差异断链。下面这段代码做的是读取原始交互文件、生成三元组列表、并用 NetworkX 建图。import pandas as pd import networkx as nx # 读取药物-靶点交互数据假设列名为 drug_id, target_id, label dt pd.read_csv(drug_target.csv) # 读取药物-疾病关联 dd pd.read_csv(drug_disease.csv) # 读取靶点-疾病关联 td pd.read_csv(target_disease.csv) triples [] for _, row in dt.iterrows(): triples.append((row[drug_id], targets, row[target_id])) for _, row in dd.iterrows(): triples.append((row[drug_id], treats, row[disease_id])) for _, row in td.iterrows(): triples.append((row[target_id], associated_with, row[disease_id])) G nx.DiGraph() for h, r, t in triples: G.add_edge(h, t, relationr) print(f节点数: {G.number_of_nodes()}, 边数: {G.number_of_edges()})这段逻辑的关键点在于三元组的关系类型用边属性relation存而不是拆成多张图。这样后续做图神经网络采样时可以按关系类型过滤邻居。参数上drug_target.csv至少要有三列label 列暂时不用因为知识图谱构建阶段只关心结构不关心正负样本。如果数据量超过百万级边NetworkX 内存会吃紧这时候换 DGL 或 PyG 的异构图接口更合适但中小规模用 NetworkX 足够跑通。2.3 图结构存进 Neo4j 做可视化验证与路径查询建完图别急着喂模型先存进 Neo4j 看一眼。很多数据问题——比如某个药物孤立、某个靶点连接了上千个药物——在可视化阶段一眼就能发现。用py2neo或官方neo4jdriver 批量写入注意用MERGE而不是CREATE避免重复节点。from neo4j import GraphDatabase driver GraphDatabase.driver(bolt://localhost:7687, auth(neo4j, password)) def insert_triples(tx, triples): for h, r, t in triples: tx.run(fMERGE (a {{id: $h}}) MERGE (b {{id: $t}}) MERGE (a)-[:{r}]-(b), hh, tt) with driver.session() as session: session.execute_write(insert_triples, triples)这里关系类型直接拼进 Cypher 语句是因为 Neo4j 不支持参数化关系类型。实际用的时候要确保r来自白名单别把用户输入拼进去。写入完成后跑一句MATCH (n) RETURN count(n)确认节点数对得上。如果发现节点数远大于预期多半是 ID 大小写没统一比如P12345和p12345被当成两个节点。3. 推荐系统建模把 DTI 预测转成排序问题3.1 矩阵分解做 baseline为什么它仍然是第一选择知识图谱 embedding 方法比如 TransE、RotatE能学到实体向量但直接拿来做 DTI 预测有个问题它们优化的是链接预测的全局损失对「某个药物最可能交互的 top-k 靶点」这个排序目标不够敏感。推荐系统里的矩阵分解天然就是为排序设计的——把药物-靶点交互矩阵分解成两个低秩矩阵药物向量和靶点向量的内积就是交互得分。这个 baseline 训练快、可解释、调参直观我一般先用它把 pipeline 跑通再上更复杂的模型做对比。3.2 用 PyTorch 实现带负采样的矩阵分解DTI 数据有个特点正样本已知交互少负样本未验证交互多而且负样本里混着大量「尚未发现但可能为真」的交互。直接随机采样负样本会引入噪声常见做法是按药物-靶点度分布做加权采样降低热门靶点被采成负样本的概率。import torch import torch.nn as nn import numpy as np class MF(nn.Module): def __init__(self, n_drugs, n_targets, dim64): super().__init__() self.drug_emb nn.Embedding(n_drugs, dim) self.target_emb nn.Embedding(n_targets, dim) nn.init.normal_(self.drug_emb.weight, std0.01) nn.init.normal_(self.target_emb.weight, std0.01) def forward(self, drug_ids, target_ids): d self.drug_emb(drug_ids) t self.target_emb(target_ids) return (d * t).sum(dim1) # 负采样对每个正样本随机替换靶点生成负样本 def negative_sampling(pos_pairs, n_targets, neg_ratio4): neg_pairs [] for d, t in pos_pairs: for _ in range(neg_ratio): neg_t np.random.randint(0, n_targets) while neg_t t: neg_t np.random.randint(0, n_targets) neg_pairs.append((d, neg_t)) return neg_pairs模型维度dim64是常用起点数据量大可以加到 128 或 256。负采样比例neg_ratio4是经验值太高会让训练偏向负样本太低则学不到区分边界。损失函数用 BPRBayesian Personalized Ranking比交叉熵更合适因为它直接优化正样本得分高于负样本的概率。训练时注意把药物和靶点 ID 重新映射成连续整数别直接用原始字符串 ID。3.3 神经协同过滤用 MLP 替代内积捕捉非线性交互矩阵分解的内积假设药物和靶点向量在同一空间里线性交互但实际 DTI 关系往往是非线性的。Neural Collaborative FilteringNCF的思路是把药物和靶点 embedding 拼接后过几层 MLP让网络自己学交互函数。class NCF(nn.Module): def __init__(self, n_drugs, n_targets, dim64, hidden[128, 64]): super().__init__() self.drug_emb nn.Embedding(n_drugs, dim) self.target_emb nn.Embedding(n_targets, dim) layers [] input_dim dim * 2 for h in hidden: layers.append(nn.Linear(input_dim, h)) layers.append(nn.ReLU()) layers.append(nn.Dropout(0.2)) input_dim h layers.append(nn.Linear(input_dim, 1)) self.mlp nn.Sequential(*layers) def forward(self, drug_ids, target_ids): d self.drug_emb(drug_ids) t self.target_emb(target_ids) x torch.cat([d, t], dim1) return self.mlp(x).squeeze()Dropout 设 0.2 是防止 embedding 过拟合尤其当某些药物只有个位数交互记录时。隐藏层结构[128, 64]适合万级节点规模再大就加宽第一层。训练时用 Adam 优化器学习率 1e-3batch size 256跑 50 个 epoch 基本收敛。验证指标别只看 AUCDTI 场景更关心 top-10 命中率因为实际应用是给实验人员推荐候选靶点不是做全局二分类。4. 知识图谱 embedding 与推荐模型的融合两条路怎么选4.1 先 embedding 再排序两阶段方案的工程优势把知识图谱和推荐系统串起来最稳的做法是两阶段第一阶段用 TransE 或 RotatE 在知识图谱上预训练实体 embedding第二阶段把药物和靶点的 embedding 作为特征喂给推荐模型。这样做的好处是解耦——图 embedding 可以离线跑推荐模型可以快速迭代不用每次改排序逻辑都重新训图网络。from torch_geometric.nn import TransE # 假设已构建 PyG 的异构图数据对象 data model TransE(num_nodesdata.num_nodes, num_relationsdata.num_edges, hidden_channels128) optimizer torch.optim.Adam(model.parameters(), lr0.001) for epoch in range(100): optimizer.zero_grad() loss model(data.edge_index, data.edge_type) loss.backward() optimizer.step()TransE 的 hidden_channels 设 128 是平衡表达能力和内存的常用值。训练完后把药物和靶点节点的 embedding 取出来拼到 NCF 的输入层相当于给模型加了先验知识。注意 TransE 对一对多、多对一关系处理不好如果图谱里存在大量「一个药物靶向多个靶点」的情况换 RotatE 或 ComplEx 更合适。4.2 端到端联合训练什么时候值得上两阶段方案的缺点是图 embedding 的优化目标和排序目标不一致。端到端联合训练把图神经网络和推荐排序损失接在一起梯度可以从排序损失回传到图网络。代价是训练慢、调参难、容易过拟合。我的经验是数据量超过十万级交互、且图谱关系类型丰富时端到端能比两阶段提升 3-5 个点的 top-10 命中率数据量小的时候两阶段反而更稳。别一上来就搞端到端先用两阶段把 baseline 跑出来再决定要不要换。5. 避坑与排查DTI 预测 pipeline 里最容易翻车的五个地方5.1 负样本泄漏导致验证指标虚高现象验证集 AUC 跑到 0.95 以上但拿真实候选靶点做预测时命中率很低。原因构建负样本时没有排除「已知但未标注」的交互模型在训练时见过这些样本的正例版本。解决负采样时从「确认无交互」的集合里抽或者用药物-靶点对的文献共现频率做过滤把高频共现但未标注的 pair 从负样本里剔除。5.2 实体 ID 不统一导致图谱断链现象Neo4j 里查某个药物的邻居发现只有两三个靶点但原始数据里明明有几十条交互。原因不同数据源用了不同的 ID 体系比如 DrugBank ID 和 PubChem CID 混用MERGE 时当成两个节点。解决建图前先做 ID 映射表所有实体统一到一套主键上映射不上的记录宁可丢掉也别硬拼。5.3 embedding 维度选太大导致过拟合现象训练集损失持续下降验证集损失先降后升top-10 命中率卡在低位。原因embedding 维度设了 256 甚至 512而实际交互数据只有几千条参数量远超样本量。解决从 32 或 64 起步用验证集 top-k 命中率做早停别盯着训练损失调维度。5.4 忽略靶点冷启动问题现象模型对训练集中出现过的靶点预测很准但新靶点没有任何已知交互的预测得分全是随机水平。原因纯协同过滤方法无法处理冷启动靶点 embedding 没有学到有效表示。解决把知识图谱里的靶点-疾病、靶点-通路关系作为辅助特征用图神经网络聚合邻居信息让新靶点也能拿到有意义的初始 embedding。5.5 评估指标选错导致模型选型偏差现象AUC 最高的模型在实际推荐场景里表现最差。原因DTI 预测是 top-k 推荐任务AUC 衡量的是全局排序能力而实际只关心前十个候选里有多少是真阳性。解决主指标用 Hit10 和 NDCG10AUC 作为辅助参考。另外要做时间切分验证用早期数据训练、后期数据测试模拟真实发现场景。6. 进阶技巧用路径排序做可解释的靶点推荐模型跑通之后真正让实验人员愿意用你结果的关键是可解释性。他们不关心 embedding 内积是多少他们想知道「为什么这个药物可能作用于这个靶点」。知识图谱在这件事上有天然优势——可以提取药物到靶点之间的路径按路径置信度排序作为推荐理由。具体做法是在 Neo4j 里查两跳和三跳路径比如药物 - 靶向 - 靶点A - 关联疾病 - 疾病X - 治疗 - 药物B - 靶向 - 靶点B把路径上的关系类型序列作为特征训练一个轻量级 GBDT 做路径打分。路径特征包括路径长度、关系类型组合、中间节点度数。下面这段 Cypher 查的是药物到靶点的两跳路径MATCH path (d:Drug {id: $drug_id})-[*2..3]-(t:Target {id: $target_id}) RETURN [r IN relationships(path) | type(r)] AS rel_types, length(path) AS path_len ORDER BY path_len ASC LIMIT 20拿到路径后用 LightGBM 训练一个二分类器标签是路径是否对应已知交互。预测时对每个候选靶点提取 top-20 路径取平均分作为可解释性得分和协同过滤得分加权融合。权重我一般设 0.7 给协同过滤、0.3 给路径得分具体看验证集表现调。还有一个实用技巧是 embedding 可视化排查。用 UMAP 把药物 embedding 降到二维按药理类别着色如果同类药物聚在一起说明 embedding 学到了有意义的表示如果散得到处都是回去检查图构建阶段是不是丢了关键关系。这个步骤花不了十分钟但能帮你省下大量盲目调参的时间。我自己踩过最深的坑是早期太迷信模型复杂度上来就搭端到端图神经网络结果数据量不够调了两周还不如矩阵分解加好的负采样。后来养成习惯任何新数据集先用最简单的模型跑通全流程把评估指标和可视化做扎实再逐步加复杂度。每一步改动都要有验证集指标支撑别凭感觉堆模块。希望帮到你。本文还有配套的精品资源点击获取