“Weisfeiler-Lehman Neural Machine for Link Prediction“文章复现工作

本文主要是介绍“Weisfeiler-Lehman Neural Machine for Link Prediction“文章复现工作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1 复现文章:

  • 《Weisfeiler-Lehman Neural Machine for Link Prediction》

2 文章提出的方法思路:

  • 笔者希望能够通过提取目标连边的周围连边所构成的子图,并通过一种编码方法,保留住每个节点在子图中扮演的不同角色,即在不同子图当中扮演相同角色的节点能够有相近的编号。如下面两张图所示,在两个不同的子网络当中,扮演角色相同的节点会得到相同的编号。

Fig.1
在这里插入图片描述

  • 然后笔者要对于网络中的每一条连边生成这样的一张子图,然后将子图都转化为对应的邻接矩阵,输入到一个机器学习分类模型(文章中用到的是单个隐藏层的神经网络)当中进行训练。

3 难点:

  • 如何对于不同的子图当中的节点进行编码,并保证其能够保存节点的相对角色?

4 解决:

  • 笔者的思路就是,首先将一个连边所对应的两个节点的一阶、二阶…邻居节点提取出来,然后根据一个笔者提出的哈希函数对节点进行排序,大致流程如下:其做法为:首先根据子图中的节点与目标连边所连接的两个节点之间的距离初始化节点的标签,然后开始迭代,每次迭代,计算每个节点的哈希值,然后根据哈希值来更新该节点的标签,其中哈希值最小的编码为1,第二小的为2,若有相同取值的就分配到同样的数字。
    在这里插入图片描述
    哈希函数为:
    在这里插入图片描述
    其中 P ( n ) P(n) P(n)为第n个素数。

5 文章提出方法的流程图:

  • 整个算法的基本流程:首先对于每一条连边,提取K个以上邻居节点构成的子图。提取顺序是:先一阶邻居,再二阶邻居…;接着对提取的子图进行图编码,然后选择前K个进行提取。提取完子图之后为每个节点建立一个上三角邻接矩阵,将邻接矩阵输入到神经网络中进行学习。
    在这里插入图片描述

6 代码:

6.1 首先导入所需库
import networkx as nx
import numpy as np
import pandas as pd
import math
import random
import matplotlib.pyplot as plt
import scipy
from scipy.io import loadmat
from functools import partial
from sklearn import svm
from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import MLPClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.utils import shuffle
from sklearn import metrics
6.2 载入数据,构建网络及可视化
data = loadmat('./USAir.mat')
G = nx.from_numpy_matrix(data['net'].todense(),create_using=nx.Graph()) #构建网络
print(nx.info(G))
pos=nx.spring_layout(G)
plt.figure(figsize=(10,8))
nx.draw(G,pos=pos,node_size=50,alpha=0.5,with_labels=False)
plt.title('USAir Network',fontsize=16,fontweight='bold')
plt.show()
  • 结果:
    在这里插入图片描述
    在这里插入图片描述
6.3 正采样
G_train = G.copy() 
G_test = nx.empty_graph(G_train.number_of_nodes())
n_links = int(G_train.number_of_edges()) # 获得网络中的连边总数ratio = 0.1 #取10%的连边作为测试集连边
n_test_link = int(np.ceil(n_links*ratio))
print(n_test_link)
selected_link_id = np.random.choice(np.arange(n_links),size=n_test_link,replace=False)
adj_matrix = nx.adj_matrix(G)
adj_matrix = scipy.sparse.triu(adj_matrix,k=1)
rol,col = adj_matrix.nonzero() #取非零元素
links = [(i,j) for i,j in zip(rol,col)]
selected_links = []
for link_id in selected_link_id:selected_links.append(links[link_id]) #根据连边的索引获得连边
G_train.remove_edges_from(selected_links)
G_test.add_edges_from(selected_links)
print(G_train.number_of_edges(),G_test.number_of_edges())
  • 结果:213;1913,213
6.4 负采样
  • 负采样需要首先建立一个与原图节点数相同的图,然后把原图当中不存在的连边全部加入到该图当中,然后随机抽取。
k = 2
neg_train_link = k*G_train.number_of_edges() # 取正样本数的两倍作为负样本数
neg_test_link = k*G_test.number_of_edges()
G_neg = nx.empty_graph(G.number_of_edges())
neg_links = list(nx.non_edges(G)) # 返回原始网络中不存在的连边
G_neg.add_edges_from(neg_links)
print(G_neg.number_of_edges())selected_link_idd = np.random.choice(np.arange(G_neg.number_of_edges()),size=neg_train_link+neg_test_link,replace=False)
G_train_neg = nx.empty_graph(G.number_of_nodes())
G_test_neg = nx.empty_graph(G.number_of_nodes())selected_links = []
for i in range(neg_train_link):inx = selected_link_idd[i]selected_links.append(neg_links[inx])
G_train_neg.add_edges_from(selected_links)selected_links = []
for i in range(neg_test_link):inx = selected_link_idd[i]selected_links.append(neg_links[inx])
G_test_neg.add_edges_from(selected_links)
  • 结果:52820
6.5 将正样本和负样本组合起来
all_train_links = list(G_train.edges) + list(G_train_neg.edges)
label_train = [1]*G_train.number_of_edges()+[0]*neg_train_linkall_test_links = list(G_test.edges) + list(G_test_neg.edges)
label_test = [1]*G_test.number_of_edges()+[0]*neg_test_linky_train,y_test = np.array(label_train),np.array(label_test)
6.6 提取子图
def enclosing_subgraph(fringe,G,subgraph,distance):"""构建enclosing subgraphInput:fringe:用来寻找下一阶邻居的列表G:原网络subgraph:要提取的子图distance:描述连边的距离return:fringe,subgraph:更新后的内容"""neighbor_link = []temp_subgraph = subgraph.copy()for link in fringe:u = link[0]v = link[1]neighbor_link += list(G.edges(u))neighbor_link += list(G.edges(v))temp_subgraph.add_edges_from(neighbor_link)# 除去已有的连边neighbor_link = [l for l in temp_subgraph.edges() if l not in subgraph.edges()] temp_subgraph.add_edges_from(neighbor_link,distance=distance,inverse_distance=1/distance)return neighbor_link,temp_subgraphdef subgraph_extractor(G,link,K):"""为每一条连边提取一个子图Input:G:网络link:目标连边K:子图大小return:subgraph:连边的子图 """distance = 0subgraph = nx.Graph()fringe = [link] #用来存放邻居节点的列表subgraph.add_edge(link[0],link[1],distance=distance)while subgraph.number_of_nodes()<K and len(fringe)>0:distance += 1 fringe,subgraph = enclosing_subgraph(fringe,G,subgraph,distance)temp_subgraph = G.subgraph(subgraph.nodes)additional_edges = [l for l in temp_subgraph.edges() if l not in subgraph.edges]subgraph.add_edges_from(additional_edges,distance=distance+1,inverse_distance=1/(distance+1))return subgraphif __name__ == '__main__':G_512 = subgraph_extractor(G_train,(5,12),10)nx.draw(G_512,with_labels=True)plt.show()
  • 结果:
    在这里插入图片描述
6.7 对节点进行编码排序
def primes(x):"""判断是否为素数"""if x <2:return Falseif x ==2 or x == 3:return Truefor i in range(2,x):if x% i ==0:return Falsereturn Truedef cal_mean_geo_distance(G_subgraph,link):"""计算目标节点到图中其他节点的距离"""u = link[0]v = link[1]G_subgraph.remove_edge(u,v) # 因为不需要计算节点u和节点v之间的距离,所以先去除n = G_subgraph.number_of_nodes()u_reachable = nx.descendants(G_subgraph,source=u)# 找到节点u可以到达的节点v_reachable = nx.descendants(G_subgraph,source=v)for each in G_subgraph.nodes():distance_to_u = 0distance_to_v = 0if each != u: #计算节点到u的距离,如果节点无法到u就设其为2**ndistance_to_u = nx.shortest_path_length(G_subgraph,source=each,target=u) if each in u_reachable else 2**nif each != v:distance_to_v = nx.shortest_path_length(G_subgraph,source=each,target=v) if each in v_reachable else 2**n# 将信息存到节点属性当中G_subgraph.nodes[each]['ave_d'] = math.sqrt(distance_to_u*distance_to_v) G_subgraph.add_edge(u,v,distance=0)return G_subgraphdef PWL(G_sub,link,prime_list):"""对子图中的节点上色Input:G_sub:目标子图link:确定该子图的连边prime_list:素数列表return:nodelist :编码后的节点列表"""tem_subgraph = G_sub.copy()if tem_subgraph.has_edge(link[0],link[1]):tem_subgraph.remove_edge(link[0],link[1])ave_d = nx.get_node_attributes(tem_subgraph,'ave_d') # 获取初始化所需要的数据df = pd.DataFrame.from_dict(ave_d,orient='index',columns=['hash_value']) #转化为一个DataFramedf = df.sort_index() #按照index进行排序df['order'] = df['hash_value'].rank(axis=0,method='min').astype(np.int) # 按照hash_value来获得每个节点的排序编号df['previous_order'] = np.zeros(len(ave_d)) #用来存放前一轮的编号adj_matrix = nx.adj_matrix(tem_subgraph,nodelist=sorted(tem_subgraph.nodes)).todense()while any(df['order']!= df['previous_order']): #只要排序还在变,就执行下面的代码df['log_priem'] = np.log(prime_list[df['order'].values])total_log = np.ceil(np.sum(df['log_priem'].values))df['hash_value'] = adj_matrix*df['log_priem'].values.reshape(-1,1)/total_log + df['order'].values.reshape(-1,1)df['previous_order'] = df['order']df['order'] = df['hash_value'].rank(axis=0,method='min').astype(np.int)nodelist = df['order'].sort_values().index.values #根据排序输出子图中的节点集return nodelistif __name__ == '__main__':e_subgraph = cal_mean_geo_distance(G_01,(5,12))prime_list = np.array([i for i in range(10000) if primes(i)],dtype=np.int)nodelist = PWL(e_subgraph,(5,12),prime_list)print(nodelist)
  • 结果:
    在这里插入图片描述
6.8 用邻居矩阵来embedding每个子图
def sample(subgraph,nodelist,weight='weight',size=10):adj_mat = nx.adj_matrix(subgraph,weight=weight,nodelist=nodelist).todense()vector = np.asarray(adj_mat)[np.triu_indices(len(adj_mat),k=1)] # np.triu_indices:获取矩阵上三角元素,k=1就是不需要对角线元素d = size*(size-1)//2if len(vector) <d:vector = np.append(vector,np.zeros(d-len(vector)))return vector[1:]if __name__ == '__main__':s = sample(e_subgraph_, nodelist, size=10)print(s)
  • 结果:
    在这里插入图片描述
6.9 对每条连边进行embedding
def encode_link(link,G,prime_list,weight='weight',K=10):e_subgraph = subgraph_extractor(G_train,link,K) #首先提取子图e_subgraph = cal_mean_geo_distance(e_subgraph,link) # 然后获得距离属性nodelist = PWL(e_subgraph,link,prime_list) # 排序if len(nodelist)>K: # 返回固定大小的节点集nodelist = nodelist[:K]e_subgraph = e_subgraph.subgraph(nodelist)embedd = sample(e_subgraph,nodelist,weight='weight',size=K)#进行embeddingreturn embeddif __name__ == '__main__':X_train = np.array(list(map(partial(encode_link,G=G_train,prime_list=prime_list,weight='weight',K=10),all_train_links)))X_test = np.array(list(map(partial(encode_link,G=G_test,prime_list=prime_list,weight='weight',K=10),all_test_links)))
6.10 训练模型
X_train_shuffle, y_train_shuffle = shuffle(X_train, y_train)
model1 = MLPClassifier(hidden_layer_sizes=(32, 32, 16),alpha=1e-3,batch_size=128,learning_rate_init=0.001,max_iter=100,verbose=True,early_stopping=False,tol=-10000)
model1.fit(X_train_shuffle,y_train_shuffle)
predictions = model1.predict(X_test)
fpr, tpr, thresholds = metrics.roc_curve(label_test,predictions,pos_label=1)
auc = metrics.auc(fpr, tpr)
print(auc)
  • 结果:
    在这里插入图片描述

这篇关于“Weisfeiler-Lehman Neural Machine for Link Prediction“文章复现工作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/395580

相关文章

工作常用指令与快捷键

Git提交代码 git fetch  git add .  git commit -m “desc”  git pull  git push Git查看当前分支 git symbolic-ref --short -q HEAD Git创建新的分支并切换 git checkout -b XXXXXXXXXXXXXX git push origin XXXXXXXXXXXXXX

嵌入式方向的毕业生,找工作很迷茫

一个应届硕士生的问题: 虽然我明白想成为技术大牛需要日积月累的磨练,但我总感觉自己学习方法或者哪些方面有问题,时间一天天过去,自己也每天不停学习,但总感觉自己没有想象中那样进步,总感觉找不到一个很清晰的学习规划……眼看 9 月份就要参加秋招了,我想毕业了去大城市磨练几年,涨涨见识,拓开眼界多学点东西。但是感觉自己的实力还是很不够,内心慌得不行,总怕浪费了这人生唯一的校招机会,当然我也明白,毕业

husky 工具配置代码检查工作流:提交代码至仓库前做代码检查

提示:这篇博客以我前两篇博客作为先修知识,请大家先去看看我前两篇博客 博客指路:前端 ESlint 代码规范及修复代码规范错误-CSDN博客前端 Vue3 项目开发—— ESLint & prettier 配置代码风格-CSDN博客 husky 工具配置代码检查工作流的作用 在工作中,我们经常需要将写好的代码提交至代码仓库 但是由于程序员疏忽而将不规范的代码提交至仓库,显然是不合理的 所

未来工作趋势:零工小程序在共享经济中的作用

经济在不断发展的同时,科技也在飞速发展。零工经济作为一种新兴的工作模式,正在全球范围内迅速崛起。特别是在中国,随着数字经济的蓬勃发展和共享经济模式的深入推广,零工小程序在促进就业、提升资源利用效率方面显示出了巨大的潜力和价值。 一、零工经济的定义及现状 零工经济是指通过临时性、自由职业或项目制的工作形式,利用互联网平台快速匹配供需双方的新型经济模式。这种模式打破了传统全职工作的界限,为劳动

Smarty模板引擎工作机制(一)

深入浅出Smarty模板引擎工作机制,我们将对比使用smarty模板引擎和没使用smarty模板引擎的两种开发方式的区别,并动手开发一个自己的模板引擎,以便加深对smarty模板引擎工作机制的理解。 在没有使用Smarty模板引擎的情况下,我们都是将PHP程序和网页模板合在一起编辑的,好比下面的源代码: <?php$title="深处浅出之Smarty模板引擎工作机制";$content=

Detectorn2预训练模型复现:数据准备、训练命令、日志分析与输出目录

Detectorn2预训练模型复现:数据准备、训练命令、日志分析与输出目录 在深度学习项目中,目标检测是一项重要的任务。本文将详细介绍如何使用Detectron2进行目标检测模型的复现训练,涵盖训练数据准备、训练命令、训练日志分析、训练指标以及训练输出目录的各个文件及其作用。特别地,我们将演示在训练过程中出现中断后,如何使用 resume 功能继续训练,并将我们复现的模型与Model Zoo中的

java计算机毕设课设—停车管理信息系统(附源码、文章、相关截图、部署视频)

这是什么系统? 资源获取方式在最下方 java计算机毕设课设—停车管理信息系统(附源码、文章、相关截图、部署视频) 停车管理信息系统是为了提升停车场的运营效率和管理水平而设计的综合性平台。系统涵盖用户信息管理、车位管理、收费管理、违规车辆处理等多个功能模块,旨在实现对停车场资源的高效配置和实时监控。此外,系统还提供了资讯管理和统计查询功能,帮助管理者及时发布信息并进行数据分析,为停车场的科学

3.比 HTTP 更安全的 HTTPS(工作原理理解、非对称加密理解、证书理解)

所谓的协议 协议只是一种规则,你不按规则来就无法和目标方进行你的工作 协议说白了只是人定的规则,任何人都可以定协议 我们不需要太了解细节,这些制定和完善协议的人去做的,我们只需要知道协议的一个大概 HTTPS 协议 1、概述 HTTPS(Hypertext Transfer Protocol Secure)是一种安全的超文本传输协议,主要用于在客户端和服务器之间安全地传输数据

ora-01017 ora-02063 database link,oracle11.2g通过dblink连接oracle11.2g

错误图示: 问题解决 All database links, whether public or private, need username/password of the remote/target database. Public db links are accessible by all accounts on the local database, while private

UMI复现代码运行逻辑全流程(一)——eval_real.py(尚在更新)

一、文件夹功能解析 全文件夹如下 其中,核心文件作用为: diffusion_policy:扩散策略核心文件夹,包含了众多模型及基础库 example:标定及配置文件 scripts/scripts_real:测试脚本文件,区别在于前者倾向于单体运行,后者为整体运行 scripts_slam_pipeline:orb_slam3运行全部文件 umi:核心交互文件夹,作用在于构建真