链路预测是网络科学里面的一个经典任务,其目的是利用当前已获取的网络数据(包含结构信息和属性信息)来预测网络中会出现哪些新的连边。

本文计划利用networkx包中的网络来进行链路预测,因为目前PyTorch Geometric包中封装的网络还不够多,而很多网络方便用networkx包生成或者处理。

环境配置

首先,安装一个工具包,DeepSNAP。这个包提供了networkx到PyTorch Geometric的接口,可以方便地将networkx中的网络转换成PyTorch Geometric所要求的数据格式。DeepSNAP有两种安装方法:

第一种安装方法:

pip install deepsnap

第二种安装方法:

$ git clone https://github.com/snap-stanford/deepsnap
$ cd deepsnap
$ pip install .

链路预测

使用图神经网络进行链路预测包含以下基本步骤:

  1. 导入图数据
  2. 分割数据集(划分训练边、测试边)
  3. 标注正边、采样负边
  4. 训练神经网络
  5. 测试模型效果

链路预测最开始是一个无监督学习任务,即根据已经看到的网络结构(或者其他属性信息)来推断未知连边是否存在,但是这样的话就比较难以验证。只有在动态网络(或称时序网络)中才会有这样的数据以供实验验证,可以用前一段时间的网络结构来预测后一段时间的网络结构。然而,很多网络没有时间信息,在这样的网络中如何验证呢?

后来,学者提出了用有监督的方式来进行链路预测,也就是将其视为二分类任务,将网络中存在的边都视为正样本(即正边),不存在的连边都当作负样本(即负边)。然后,将这些边分为两部分,一部分为训练集,一部分为测试集。训练集和测试集中都包含正边和负边,目的是在训练集上训练出一个模型能够准确分类这两种边,然后再在测试集上验证效果。

然而,大多数网络都是稀疏的,也就是说存在边的数量差不多是节点数量的几倍左右,而网络中不存在的边的数量差不多是节点数量的平方(在无向网络中,不存在边的数量等于(n−1)n/2−m(n−1)n/2−m(n−1)n/2−m( n − 1 ) n / 2 − m (n-1)n/2-m(n−1)n/2−m(n−1)n/2−m(n−1)n/2−m(n−1)n/2−m,其中nnn为节点数,mmm 为边数)。这样不存边的数量就远远大于存在边的数量,在有监督学习中就意味着负样本远大于正样本,类别极其不平衡。怎么解决这个问题呢?大家很自然地想到了负采样,就是每次训练的时候随机抽取与正样本等比例的负样本,这样就避免了类别不平衡。

训练结束后,就可以用测试集中的正边和负边来验证模型的效果了。

代码

import networkx as nx
from deepsnap.graph import Graph
import torch
import torch.nn.functional as F
from sklearn.metrics import roc_auc_score
from torch_geometric.utils import negative_sampling
from torch_geometric.nn import GCNConv
from torch_geometric.utils import train_test_split_edgesG = nx.karate_club_graph()
data = Graph(G)  # 将networkx中的graph对象转化为torch_geometric的Data对象
data.num_features = 3
data.edge_attr = None# 构造节点特征矩阵(原网络不存在节点特征)
data.x = torch.ones((data.num_nodes, data.num_features), dtype=torch.float32)# 分割训练边集、验证边集(默认占比0.05)以及测试边集(默认占比0.1)
data = train_test_split_edges(data)# 构造一个简单的图卷积神经网络(两层),包含编码(节点嵌入)、解码(分数预测)等操作
class Net(torch.nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = GCNConv(data.num_features, 128)self.conv2 = GCNConv(128, 64)def encode(self):x = self.conv1(data.x, data.train_pos_edge_index)x = x.relu()return self.conv2(x, data.train_pos_edge_index)def decode(self, z, pos_edge_index, neg_edge_index):edge_index = torch.cat([pos_edge_index, neg_edge_index], dim=-1)  # 将正样本与负样本拼接 shape:[2,272]logits = (z[edge_index[0]] * z[edge_index[1]]).sum(dim=-1)return logitsdef decode_all(self, z):prob_adj = z @ z.t()return (prob_adj > 0).nonzero(as_tuple=False).t()# 将模型和数据送入设备
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model, data = Net().to(device), data.to(device)
# 指定优化器
optimizer = torch.optim.Adam(params=model.parameters(), lr=0.01)# 将训练集中的正边标签设置为1,负边标签设置为0
def get_link_labels(pos_edge_index, neg_edge_index):E = pos_edge_index.size(1) + neg_edge_index.size(1)link_labels = torch.zeros(E, dtype=torch.float, device=device)link_labels[:pos_edge_index.size(1)] = 1.return link_labels# 训练函数,每次训练重新采样负边,计算模型损失,反向传播误差,更新模型参数
def train():model.train()neg_edge_index = negative_sampling(edge_index=data.train_pos_edge_index, num_nodes=data.num_nodes,num_neg_samples=data.train_pos_edge_index.size(1),  # 负采样数量根据正样本force_undirected=True,)  # 得到负采样shape: [2,136]neg_edge_index = neg_edge_index.to(device)optimizer.zero_grad()z = model.encode()  # 利用正样本训练学习得到每个节点的特征 shape:[34, 64]link_logits = model.decode(z, data.train_pos_edge_index, neg_edge_index)  # [272] 利用正样本和负样本 按位相乘 求和  (z[edge_index[0]] * z[edge_index[1]]).sum(dim=-1)link_labels = get_link_labels(data.train_pos_edge_index, neg_edge_index)  # [272] 前136个是1,后136个是0loss = F.binary_cross_entropy_with_logits(link_logits, link_labels)  # binary_cross_entropy_with_logits会自动计算link_logits的sigmoidloss.backward()optimizer.step()return loss# 测试函数,评估模型在验证集和测试集上的预测准确率
@torch.no_grad()
def test():model.eval()perfs = []for prefix in ["val", "test"]:pos_edge_index = data[f'{prefix}_pos_edge_index']neg_edge_index = data[f'{prefix}_neg_edge_index']z = model.encode()link_logits = model.decode(z, pos_edge_index, neg_edge_index)link_probs = link_logits.sigmoid()link_labels = get_link_labels(pos_edge_index, neg_edge_index)perfs.append(roc_auc_score(link_labels.cpu(), link_probs.cpu()))return perfs# 训练模型,每次训练完,输出模型在验证集和测试集上的预测准确率
best_val_perf = test_perf = 0
for epoch in range(1, 11):train_loss = train()val_perf, tmp_test_perf = test()if val_perf > best_val_perf:best_val_perf = val_perftest_perf = tmp_test_perflog = 'Epoch: {:03d}, Loss: {:.4f}, Val: {:.4f}, Test: {:.4f}'print(log.format(epoch, train_loss, best_val_perf, test_perf))# 利用训练好的模型计算网络中剩余所有边的分数
z = model.encode()
final_edge_index = model.decode_all(z)

首先查看原始数据信息:

data:
Graph(G=[], club=[34],   # 总共34个节点edge_label_index=[2, 156],   # 总共156个边name=[], node_label_index=[34], num_features=[1],  test_neg_edge_index=[2, 7],   # 测试集 负样本邻接矩阵test_pos_edge_index=[2, 7],   # 测试集 正样本邻接矩阵train_neg_adj_mask=[34, 34],   # 训练集 负样本邻接矩阵train_pos_edge_index=[2, 136],   # 训练集 正样本邻接矩阵val_neg_edge_index=[2, 3],   # 验证集 负样本邻接矩阵val_pos_edge_index=[2, 3],  # 验证集 正样本邻接矩阵x=[34, 3]  # 节点属性
)

输出情况如下:

Epoch: 001, Loss: 0.8969, Val: 0.3333, Test: 0.9796
Epoch: 002, Loss: 0.6772, Val: 0.3333, Test: 0.9796
Epoch: 003, Loss: 0.6933, Val: 0.3333, Test: 0.9796
Epoch: 004, Loss: 0.7107, Val: 0.3333, Test: 0.9796
Epoch: 005, Loss: 0.6960, Val: 0.3333, Test: 0.9796
Epoch: 006, Loss: 0.6905, Val: 0.3333, Test: 0.9796
Epoch: 007, Loss: 0.6896, Val: 0.3333, Test: 0.9796
Epoch: 008, Loss: 0.6837, Val: 0.3333, Test: 0.9796
Epoch: 009, Loss: 0.6834, Val: 0.3333, Test: 0.9796
Epoch: 010, Loss: 0.6840, Val: 0.3333, Test: 0.9796

训练集中的负样本是每次随机采样得到的(第51-55行),而验证集和测试集中的负样本边则在第14行就已经固定了,所以结果中训练集上的loss一直在变化,而验证集和测试集上的AUC得分没有变化,因为我们的数据量太小导致的。如果换成Cora数据集效果会好点。

更详细的介绍,请看这篇文章。

PYG教程【五】链路预测相关推荐

  1. DGL教程【四】使用GNN进行链路预测

    在之前的介绍中,我们已经学习了使用GNN进行节点分类,比如预测一个图中的节点所属的类别.这一节中我们将教你如何进行链路预测,比如预测任意两个节点之间是不是存在边. 本节你将学到: 构建一个GNN的链路 ...

  2. 使用PyG (PyTorch Geometric) 实现同质图transductive链路预测任务

    诸神缄默不语-个人CSDN博文目录 PyTorch Geometric (PyG) 包文档与官方代码示例学习笔记(持续更新ing-) 本文代码参考自PyG官方示例代码:https://github.c ...

  3. PyTorch图神经网络实践(五)链路预测

    链路预测是网络科学里面的一个经典任务,其目的是利用当前已获取的网络数据(包含结构信息和属性信息)来预测网络中会出现哪些新的连边. 本文计划利用networkx包中的网络来进行链路预测,因为目前PyTo ...

  4. 综述 | 知识图谱(Knowledge graph)链路预测(Link Prediction)

    点上方蓝字计算机视觉联盟获取更多干货 在右上方 ··· 设为星标 ★,与你不见不散 仅作分享,不代表本公众号立场,侵权联系删除 转载于:机器之心,arxiv AI博士笔记系列推荐 周志华<机器学 ...

  5. 【论文翻译】基于分层关注和时间RNN的动态异构网络链路预测建模

    基于分层关注和时间RNN的动态异构网络链路预测建模 摘要 网络嵌入的目的是在获取网络结构信息的同时学习节点的低维表示.它在链路预测.节点分类等网络分析任务中取得了巨大的成功.现有的网络嵌入算法大多集中 ...

  6. 链路预测 matlab,链路预测

    2.4评价指标 2.4.1AUC ROC曲线下的面积.信号探测理论中,ROC曲线用来评价某种分类器的分类效果.这种评价指标可以用来衡量链路预测算法的精确度 事实上,AUC可以理解为在测试集中随机选择一 ...

  7. 西电数据挖掘实验1——二分网络上的链路预测

    一.实验内容   基于网络结构的链路预测算法被广泛的应用于信息推荐系统中.算法不考虑用户和产品的内容特征,把它们看成抽象的节点,利用用户对产品的选择关系构建二部图.为用户评估它从未关注过的产品,预测用 ...

  8. 复杂网络链路预测的研究现状及展望(2010)

    前言:做链路预测这个方向有一年多的时间了,有一些收获和体会.一直想写一个综述进行总结,总是希望这个综述尽可能的包括更多更全面的信息,但是新的思想和结果源源不断的涌现,所谓的综述也就无限期的搁置了下来. ...

  9. SpringCloud系列教程(五)之SpringCloud Gateway 网关聚合开发文档 swagger knife4j 和登录权限统一验证【Hoxton版】

    阅读提醒: 本文面向的是有一定springboot基础者 本次教程使用的Spring Cloud Hoxton RELEASE版本 由于knife4j比swagger更加友好,所以本文集成knife4 ...

最新文章

  1. debian虚拟机装上后开机不行_华为MT9进水不开机, 一步一个“坑”把掌柜修的也是无语,想发火...
  2. Juniper SRX340防火墙恢复出厂设置
  3. webpack4.0 babel配置遇到的问题
  4. 第七章:清楚简洁的英文 --《英语科技写作(文法与修辞原则)》by 方克涛
  5. 洛谷P2234 [HNOI2002]营业额统计 set简易解法
  6. 在线留痕阅卷系统功能说明及其演示
  7. 网页播放器(CKplayer)的视频怎么下载——m3u8简单探索
  8. 【系统集成项目管理工程师】考点:挣值管理 (附计算公式及思维导图)
  9. 计算机软件怎么装,电脑拷贝的软件怎么安装
  10. Excel 提取单元格中的数字、中/英文方法
  11. HighCharts柱状图显示百分比
  12. 十二、项目收尾(华为项目管理法-孙科炎读书摘要)
  13. 一文带你了解机器翻译
  14. HU6206稳压芯片
  15. PHP利用qq邮箱发邮件
  16. laravel很慢_Laravel网站打开速度优化的方法汇总
  17. uni-app开发的APP项目上线后,登录后自动检测更新,点击下载并安装,可手动检测版本更新,实现扫码下载功能
  18. 计算机操作系统学习(七)作业管理
  19. AAAI2020|Asymmetric Co-Teaching for Unsupervised Cross-Domain Person Re-Identification
  20. spark分布式矩阵采坑记

热门文章

  1. Spring过滤器组件自动扫描
  2. C#开发的高性能EXCEL导入、导出工具DataPie(支持MSSQL、ORACLE、ACCESS,附源码下载地址)...
  3. Android开发三 如何安装 Android SDK 和Eclipse 插件
  4. OOAD 面向对象分析与设计
  5. tcp 二次握手时延_一篇搞懂TCP的三次握手 四次挥手
  6. java bigdecimal赋值_Java中BigDecimal类介绍及用法(亲测)
  7. 从零搭建Prometheus监控报警系统
  8. mongodb内存映射原理
  9. oracle10g自带的公共同义词,10g ALL_SYNONYMS同义词查询性能下降
  10. 大数据 清华 覃征_清华大学人工智能研究院大数据智能研究中心揭牌,喊你来参与...