基于图卷积神经网络GCN的二部图链路预测方法实现思路和完整代码【可用于疾病-靶点、miRNA-疾病等相关预测】

news2024/9/30 1:41:30

本文解决的问题

基本假设:二部图只有邻接矩阵,没有节点特征,并且进行链路预测,有部分链路未知。
如果你有初始节点特征,直接换掉即可

实现思路

这段代码主要是用于构建一个基于图卷积神经网络(GCN)的二分类模型,实现对给定邻接矩阵的节点间关系进行分类。具体实现步骤如下:
加载邻接矩阵数据,并将邻接矩阵转换为张量形式。
根据邻接矩阵生成正负样本的边索引。
自己创建节点特征x, 和结果y。
将数据随机分为训练、验证和测试集。
从正负样本中随机选取相同数量的边,形成平衡的训练集。
创建一个GCN模型,用于学习节点特征和边信息之间的关系。
通过训练得到最优的模型参数,并在测试集上评估模型性能。

结果

在这里插入图片描述

代码全览

import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv
from torch_geometric.utils import to_undirected
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score

# 加载邻接矩阵数据
adj_matrix = pd.read_csv('m_d.csv', header=None, index_col=None).values

# 将邻接矩阵转换为张量,这里要将列索引加上总节点数,使得列节点的索引不与行节点重复
num_nodes = adj_matrix.shape[0] + adj_matrix.shape[1]
edge_index_pos = np.column_stack(np.argwhere(adj_matrix != 0))
edge_index_pos[:, 1] += adj_matrix.shape[0]
edge_index_pos = torch.tensor(edge_index_pos, dtype=torch.long)

# 获取不相关连接 为了构建训练数据
edge_index_neg = np.column_stack(np.argwhere(adj_matrix == 0))
edge_index_neg[:, 1] += adj_matrix.shape[0]
edge_index_neg = torch.tensor(edge_index_neg, dtype=torch.long)

# 获取平衡样本
num_pos_edges_number = edge_index_pos.shape[1]
selected_neg_edge_indices = torch.randint(high=edge_index_neg.shape[1], size=(num_pos_edges_number,), dtype=torch.long)
edge_index_neg_selected = edge_index_neg[:, selected_neg_edge_indices]

edg_index_all = torch.cat((edge_index_pos, edge_index_neg_selected), dim=1)

# 创建数据
x = torch.ones((num_nodes, 1)) # 没有节点特征,所以设置为1
y = torch.cat((torch.ones((edge_index_pos.shape[1], 1)),
               torch.zeros((edge_index_neg_selected.shape[1], 1))), dim=0) # 将所有y值设置为1,0

# 将邻接矩阵转换为无向图(会将每条边复制成两条相反的边)
edge_index = to_undirected(edge_index_pos)

# 将数据拆分为训练、验证和测试集
idx = np.arange(y.shape[0])
np.random.shuffle(idx)
train_idx = idx[:int(0.8 * len(idx))] # 用前80%的样本作为训练集
test_idx = idx[int(0.8 * len(idx)):] # 用后20%的样本作为测试集


# 创建一个GCN模型
class GCNII(torch.nn.Module):
    def __init__(self, in_channels, out_channels):
        super(GCNII, self).__init__()
        self.conv1 = GCNConv(in_channels, 2 * out_channels, cached=True)
        self.conv2 = GCNConv(2 * out_channels, out_channels)
        self.fc = torch.nn.Linear(out_channels*2, 1)

    def forward(self, x, edge_index, edge_index_pos, edge_index_neg, edge_weight=None,param={}):
        x = F.relu(self.conv1(x, edge_index, edge_weight))
        x = F.relu(self.conv2(x, edge_index, edge_weight))

        # 按照边的顺序将节点特征向量重新排列
        edg_index_all = torch.cat((edge_index_pos, edge_index_neg), dim=1)
        Em, Ed = self.pro_data(x, edg_index_all)  # 筛选数据 获得源节点特征和目标节点

        # 将源节点特征和目标节点特征拼接起来
        x = torch.cat((Em, Ed),dim=1)
        x = self.fc(x)

        return x

    def pro_data(self, x, edg_index):
        m_index = edg_index[0]
        d_index = edg_index[1]
        Em = torch.index_select(x, 0, m_index)  # 沿着x1的第0维选出m_index
        Ed = torch.index_select(x, 0, d_index)
        return Em, Ed


# 模型构建
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = GCNII(in_channels=x.shape[1], out_channels=16).to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=0.0005)

# 训练模型
model.train()
for epoch in range(1, 201):
    optimizer.zero_grad()
    out = model(x, edge_index, edge_index_pos=edge_index_pos, edge_index_neg=edge_index_neg_selected,
                param={'m_number': adj_matrix.shape[0], 'd_number': adj_matrix.shape[1]})

    # 使用train数据进行训练
    loss = F.binary_cross_entropy_with_logits(out[train_idx], y[train_idx].float())
    loss.backward()
    optimizer.step()
    loss = loss.item()
    print(f'Epoch: {epoch:03d}, Loss: {loss:.4f}')

# 模型验证
model.eval()
with torch.no_grad():
    # 获得所有数据
    out = model(x, edge_index, edge_index_pos=edge_index_pos, edge_index_neg=edge_index_neg_selected,
                param={'m_number':adj_matrix.shape[0],'d_number':adj_matrix.shape[1]})

    # 提取验证集数据
    out_pred = out[test_idx]
    y_pred = y[test_idx]

    # 计算AUC
    auc = roc_auc_score(y_pred, out_pred)
    print('AUC:', auc)

代码详解

以下是更详细的代码解释:

首先,导入需要用到的库:

import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv
from torch_geometric.utils import to_undirected
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score

然后,加载邻接矩阵数据并将其转换为张量形式:

adj_matrix = pd.read_csv('m_d.csv', header=None, index_col=None).values

num_nodes = adj_matrix.shape[0] + adj_matrix.shape[1]
edge_index_pos = np.column_stack(np.argwhere(adj_matrix != 0))
edge_index_pos[:, 1] += adj_matrix.shape[0]
edge_index_pos = torch.tensor(edge_index_pos, dtype=torch.long)

edge_index_neg = np.column_stack(np.argwhere(adj_matrix == 0))
edge_index_neg[:, 1] += adj_matrix.shape[0]
edge_index_neg = torch.tensor(edge_index_neg, dtype=torch.long)

这里的 adj_matrix 是一个邻接矩阵,表示节点之间的连接情况。edge_index_pos 表示有边相连的节点对的索引,edge_index_neg 则表示没有边相连的节点对的索引。

接下来,从正负样本中随机选取相同数量的边,形成平衡的训练集:

num_pos_edges_number = edge_index_pos.shape[1]
selected_neg_edge_indices = torch.randint(high=edge_index_neg.shape[1], size=(num_pos_edges_number,), dtype=torch.long)
edge_index_neg_selected = edge_index_neg[:, selected_neg_edge_indices]

edg_index_all = torch.cat((edge_index_pos, edge_index_neg_selected), dim=1)

这里使用 torch.randint 函数从负样本索引中随机抽取与正样本相同数量的索引,以达到正负样本平衡的目的。

然后,创建数据集,并将邻接矩阵转换为无向图(会将每条边复制成两条相反的边):


x = torch.ones((num_nodes, 1))
y = torch.cat((torch.ones((edge_index_pos.shape[1], 1)),
               torch.zeros((edge_index_neg_selected.shape[1], 1))), dim=0)

edge_index = to_undirected(edge_index_pos)

x 是节点特征矩阵,这里没有节点特征,所以全部设为 1。y 表示分类标签,其中正样本标签为 1,负样本标签为 0。edge_index 是无向图上的边索引。

接着,将数据随机分为训练、验证和测试集:
这里使用 numpy 库的 random.shuffle 函数将数据索引随机打乱,并按照 8:2 的比例划分为训练集和测试集。

idx = np.arange(y.shape[0])
np.random.shuffle(idx)
train_idx = idx[:int(0.8 * len(idx))]
test_idx = idx[int(0.8 * len(idx)):]

然后,创建一个 GCN 模型:
GCNII 是一个继承自 torch.nn.Module 的类,它包含了两层 GCN 卷积 (self.conv1, self.conv2) 和一个全连接层 (self.fc)。在前向传播函数中,首先对节点特征进行两层 GCN 卷积操作,然后将边索引按照顺序重新排列,接着通过 pro_data 函数筛选出源节点和目标节点的特征向量,并将其拼接在一起,最后通过全连接层得到输出。

class GCNII(torch.nn.Module):
    def __init__(self, in_channels, out_channels):
        super(GCNII, self).__init__()
        self.conv1 = GCNConv(in_channels, 2 * out_channels, cached=True)
        self.conv2 = GCNConv(2 * out_channels, out_channels)
        self.fc = torch.nn.Linear(out_channels*2, 1)

    def forward(self, x, edge_index, edge_index_pos, edge_index_neg, edge_weight=None,param={}):
        """前向传播函数"""
        # 对节点特征进行两层 GCN 卷积操作
        x = F.relu(self.conv1(x, edge_index, edge_weight))
        x = F.relu(self.conv2(x, edge_index, edge_weight))

        # 筛选数据,获取源节点特征和目标节点特征
        edg_index_all = torch.cat((edge_index_pos, edge_index_neg), dim=1)
        Em, Ed = self.pro_data(x, edg_index_all)

        # 将源节点特征和目标节点特征拼接起来,并通过全连接层得到输出
        x = torch.cat((Em, Ed),dim=1)
        x = self.fc(x)

        return x

    def pro_data(self, x, edg_index):
        """筛选数据,获取源节点特征和目标节点特征"""
        m_index = edg_index[0]
        d_index = edg_index[1]
        Em = torch.index_select(x, 0, m_index)
        Ed = torch.index_select(x, 0, d_index)
        return Em, Ed

接下来,构建模型并开始训练:
这里首先判断是否有可用的 GPU,然后创建一个 GCNII 模型,并使用 Adam 优化器对模型参数进行训练。在训练过程中,计算模型输出和分类标签之间的二元交叉熵损失,并调用 backward 函数计算梯度并更新模型参数。

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = GCNII(in_channels=x.shape[1], out_channels=16).to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=0.0005)

model.train()
for epoch in range(1, 201):
    optimizer.zero_grad()

    # 计算输出,使用 train_idx 进行训练
    out = model(x, edge_index, edge_index_pos=edge_index_pos, edge_index_neg=edge_index_neg_selected,
                param={'m_number': adj_matrix.shape[0], 'd_number': adj_matrix.shape[1]})
    loss = F.binary_cross_entropy_with_logits(out[train_idx], y[train_idx].float())
    
    # 反向传播计算梯度并更新参数
    loss.backward()
    optimizer.step()
    loss = loss.item()
    print(f'Epoch: {epoch:03d}, Loss: {loss:.4f}')

最后,在测试集上评估模型性能:

model.eval()
with torch.no_grad():
    # 计算模型输出
    out = model(x, edge_index, edge_index_pos=edge_index_pos, edge_index_neg=edge_index_neg_selected,
                param={'m_number':adj_matrix.shape[0],'d_number':adj_matrix.shape[1]})

    # 提取验证集数据并计算 AUC
    out_pred = out[test_idx]
    y_pred = y[test_idx]
    auc = roc_auc_score(y_pred, out_pred)
    print('AUC:', auc)

这里写目录标题

    • 本文解决的问题
    • 实现思路
    • 结果
    • 代码全览
    • 代码详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/429348.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内网域渗透总结(红日靶场一)

一、靶场环境搭建 红日靶场:http://vulnstack.qiyuanxuetang.net/vuln/detail/2/靶机通用密码:hongrisec2019win7win2003win2008 这个搭建靶场的过程是我经过实际操作(踩坑)总结的最简单省事的方式。 首先创建一个仅主机模式的…

使用appuploader工具发布证书和描述性文件教程

使用APPuploader工具发布证书和描述性文件教程 之前用AppCan平台开发了一个应用,平台可以同时生成安卓版和苹果版,想着也把这应用上架到App Store试试,于是找同学借了个苹果开发者账号,但没那么简单,还要用到Mac电脑的…

数据结构之图(最小生成树+最短路径)

基本概念 连通:若a->b存在路径,即为连通 连通图:该图中任意两点均连通,即为连通图 连通分量:下图为无向图,但存在三个连通分量 强连通图:双向的连通图 强连通分量:有向图中的双…

分享一个免梯子智能的问答工具ChatGPT,刷题和学习的好帮手

使用了这个问答工具后,感觉前后端都要被替代了,太强了。 由于本人之前很想体验,但是一直难搞,最近发现了一个免梯子的,重要事情说一遍,免梯子!是我最近发现的最好用,最快的,且不要梯子的,用起来爽,界面也挺好看的,大家快玩儿玩儿。 试了一下写代码,写文案……真棒…

文心一言 VS chatgpt (9)-- 算法导论2.3 7题 3.1 1题

七、描述一个运行时间为O(nlgn)的算法,给定n个整数的集合S和另一个整数,该算法能确定 S中是否存在两个其和刚好为工的元素。 文心一言: 这里是一个运行时间为O(nlgn)的算法,可以用来确定集合 S 中是否存在两个元素,它…

FE_CSS 页面布局之定位

1 为什么需要定位 某个元素可以自由的在一个盒子内移动位置,并且压住其他盒子。 当我们滚动窗口的时候,盒子是固定屏幕某个位置的。 以上效果,标准流或浮动都无法快速实现,此时需要定位来实现。 浮动可以让多个块级盒子一行没有…

第四章 word2vec 的高速化

目录4.1 word2vec 的改进①4.1.1 Embedding 层4.1.2 Embedding 层的实现4.2 word2vec 的改进②4.2.1 中间层之后的计算问题4.2.2 从多分类到二分类4.2.3 sigmoid 函数和交叉熵误差4.2.4 多分类到二分类的实现4.2.5 负采样4.2.6 负采样的采样方法4.2.7 负采样的实现4.3 改进版 w…

组态王与FX5U之间如何快速实现无线通讯?

本方案是基于Modbus RTU协议下实现的1主多从自组网无线通信形式,主站为组态王,从站为两台三菱FX5U PLC。在工厂里,组态王和plc所处位置距离较为分散,重新铺设电缆线工期长,成本高,故采用日系PLC专用无线通讯…

【Halcon 笔记2】参数

一、图形参数 图形参数 Iconic, 包括 image, region, XLD 1.1 image 图像由一个或者多个通道组成,是大小相同的矩阵,包含各种像素类型的灰度值 在图像显示界面,按ctrl健,可以查看当前的像素值 灰度图 一个通道像素点存放在一个…

STM32开发(十四)STM32F103 数据手册 —— 通用定时器 PWN 详解

文章目录主要特点通用定时器内部框图功能描述计数器模式计数器时钟可选择时钟源PWM输入模式STM32F103内部通用定时器包括TIMx (TIM2、 TIM3、 TIM4和TIM5)定时器 主要特点 16位向上、向下、向上/向下自动装载计数器 16位可编程(可以实时修改)预分频器,计数器时钟频…

【MySQL学习】认识MySQL数据库

目录一、什么是数据库二、主流数据库三、MySQL数据库的基本使用3.1 MySQL的安装3.2 MySQL服务器管理3.3 连接MySQL服务器3.4 MySQL服务器,数据库与表之间的关系3.5 使用案例3.6 数据存储四、MySQL架构五、SQL分类六、存储引擎6.1 定义6.2 查看存储引擎6.3 存储引擎对…

检测图中的负循环 | (贝尔曼福特)

我们得到了一个有向图。我们需要计算图形是否有负循环。负循环是循环的总和变为负的循环。 在图形的各种应用中都可以找到负权重。例如,如果我们沿着这条路走,我们可能会得到一些好处,而不是为一条路付出代价。 例子:

基于html+css的图片展示13

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

震惊!竟然有人如此解释关键字中的static

🤩:大家好,我是paperjie,感谢你阅读本文,欢迎一建三连哦。 🥰:这里是C专栏,笔者用重金(时间和精力)打造,基础知识一网打尽,希望可以帮到读者们哦。 &#x1f…

工作中使用即时通讯软件有什么好处?

以前,即时通讯被认定为是一个专供个人使用的通信工具,即时消息软件不仅用于简化通信和快速响应,而且还用于文件共享和信息更新,它可帮助公司中的员工进行沟通、满足需求并实现目标。在即时通讯的帮助下,员工无需离开办…

交互式shell脚本编程2

当你在终端环境下安装新的软件时,你可以经常看到信息对话框弹出,需要你的输入,比如:RHEL/CentOS自带的setup,对话框的类型有密码箱、检查表、菜单等等。他们可以引导你以一种直观的方式输入必要的信息,使用…

3d可视化精炼数字工厂互动大屏展示提高企业竞争力

随着各种新兴技术的不断崛起和进步,结合云计算、5G通信、物联网等技术突破数据孤岛,加速炼钢厂整个行业的转型升级已成为行业的大趋势。 传统的维修场景中,一线员工的双手难以得到解放,一线工作数据难以收集、保存、输出。一辆汽车…

leetcode刷题(4)

各位朋友们,大家好。这两天我将为大家分享我在学习栈的过程中遇到的题目,我们一起来看看。 文章目录逆波兰表达式求值题目要求用例输入提示做题思路代码实现c语言实现代码Java语言实现代码有效的括号Java代码实现逆波兰表达式求值 leetcode之逆波兰表达…

Python基于机器学习实现的股票价格预测、股票预测源码+数据集,机器学习大作业

Feature与预测目标的选取 选择的feature: 开盘价最高成交价最低成交价成交量 选择的预测目标: 收盘价 因为股票价格的影响因素太多,通过k线数据预测未来的价格变化基本不可行,只有当天之内的数据还有一定的关联,故feature与target都选择的…

Oracle daily maintenancy-high active sessions

文章目录1.About check_mk metric:ORA_ORCL_Active_Session2.Solution2.1get the total number of active concurrent sessions2.2 the possible reason for this:2.2.1.High connection for normal behavior that happens occasionally2.2.2.resource competition1.About chec…