图神经网络实战(4)——基于Node2Vec改进嵌入质量

图神经网络实战(4)——基于Node2Vec改进嵌入质量

    • 0. 前言
    • 1. Node2Vec 架构
      • 1.2 定义邻居
      • 1.2 在随机游走中引入偏向性
      • 1.3 实现有偏随机游走
    • 2. 实现 Node2Vec
    • 小结
    • 系列链接

0. 前言

Node2Vec 是一种基于 DeepWalk 的架构,DeepWalk 主要由随机游走和 Word2Vec 两个组件构成,Node2Vec 通过改进随机游走的生成方式改进嵌入质量。
在本节中,我们将学习这些改进以及如何为给定的图找到最佳参数,实现 Node2Vec 架构,并将其与在 Zachary's Karate Club 数据集上使用的 DeepWalk 进行比较,以理解两种架构之间的差异。

1. Node2Vec 架构

Node2VecGroverLeskovec2016 年提出,它保留了 DeepWalk 的两个主要组件:随机游走和 Word2Vec。不同之处在于, Node2Vec 中的随机游走不是使用均匀分布生成节点序列,而是进行了有偏处理。接下来,我们将了解为什么这些有偏随机游走表现更好,以及如何实现它们:

  • 定义邻居
  • 在随机游走中引入偏向性

1.2 定义邻居

Node2Vec 引入的关键概念是灵活的邻居概念。直观地说,我们认为邻居是与初始节点接近的节点,但在图中,应该如何定义“接近”呢?以下图为例:

示例图数据

我们希望探索节点 A 相邻的三个节点,该探索过程也称为采样策略 (sampling strategy):

  • 一种解是考虑在连接关系方面最接近的三个节点。在这种情况下,A 的邻居为 N ( A ) = { B , C , D } N(A) = \{B,C,D\} N(A)={B,C,D}
  • 另一种采用策略是首先选择与前一个节点不相邻的节点。在这种情况下,A 的邻居为 N ( A ) = { D , E , F } N(A) = \{D, E, F\} N(A)={D,E,F}

换句话说,在第一种情况下,需要执行广度优先搜索 (Breadth-First Search, BFS),而在第二种情况下,需要执行深度优先搜索 (Depth-First Search, DFS)。BFS 侧重于节点周围的局部网络,而 DFS 则建立了更宏观的图视图。
考虑到我们对邻居的直观定义,首先会想到舍弃 DFS。然而,Node2Vec 则认为这是种错误认知,其认为每种方法都捕捉到了网络的不同但有价值的表示。
将这些算法与以下两种网络属性联系起来:

  • 结构等价性 (Structural equivalence),这意味着如果节点共享许多相同的邻居,则它们在结构上是等效的。因此,如果它们共享许多邻居,则它们的结构等价性就更高
  • 同质性 (Homophily),表示相似的节点更有可能相互连接

Node2Vec 认为 BFS 是突出结构等效性的理想策略,因为该策略仅查看相邻节点。在这些随机游走序列中,节点经常重复出现并保持彼此接近。而 DFS 则通过创建远距离节点序列来强调非同质性。这些随机游走序列会采样距离源节点很远的节点,因此降低了代表性。因此,我们需要在这两种属性之间进行权衡:同质性可能更有助于理解某些图,反之亦然。
通常认为,结合同质性和结构等价性的图是理想的解决方案,因此,我们希望使用这两种采样策略来创建数据集。接下来,我们使用它们来生成随机游走序列。

1.2 在随机游走中引入偏向性

我们已经知道,随机游走是在图中随机选择的节点序列,通过使用一个给定起点(也可以是随机的)和一个预定义的长度创建。在这些图中经常出现在一起的节点就像句子中经常出现在一起的单词一样:根据同质性假设,它们具有相似的含义,因此也具有相似的表示。

Node2Vec 中,我们的目标是将这些随机游走的随机性偏向以下两者之一:

  • 提升与前一个节点没有连接的节点(类似于 DFS)
  • 提升与前一个节点相近的节点(类似于 BFS)

以下图为例,当前节点为 j j j,前一节点为 i i i,特征节点为 k k k。从节点 j j j 到节点 k k k 的非归一化转移概率 π j k \pi_{jk} πjk,该概率可以分解为 π j k = a ( i , k ) ⋅ w j k \pi_jk= a( i, k)\cdot w_{jk} πjk=a(i,k)wjk, 其中 a ( i , k ) a(i , k) a(i,k) 是节点 j j j k k k 之间的搜索偏置 (search bias), w j k w_{jk} wjk j j j k k k 的边的权重。

示例图

DeepWalk 中,对于任意一对节点 a a a b b b,它们之间的边权重 a ( a , b ) = 1 a(a,b)=1 a(a,b)=1。在 Node2Vec 中, a ( a , b ) a( a, b) a(a,b) 的值是根据节点间的距离和两个附加参数定义的:回退参数 p 和进出参数 q,分别用于近似 DFSBFS a ( a , b ) a(a, b) a(a,b) 值的定义如下:
a ( a , b ) = { 1 p d a b = 0 1 d a b = 1 1 q d a b = 2 a(a, b)=\begin{cases} \frac 1 p & d_{ab}=0\\ 1 & d_{ab}=1\\ \frac 1 q & d_{ab}=2\\ \end{cases} a(a,b)= p11q1dab=0dab=1dab=2
其中, d a b d_{ab} dab 是节点 a a a b b b 之间的最短路径距离,可以按如下方式更新上图中的非归一化转移概率:

更新非归一化转移概率

在非归一化转移概率中:

  • 回到前一个节点 i i i 的概率由参数 p p p 控制,参数 p p p 越大,随机游走就会越趋向于探索新的节点,而不是重复相同的节点,看起来就像 DFS
  • 前往 k 1 k_1 k1 的非归一化概率为 1,因为该节点是前一个节点 i i i 的直接邻居
  • 到达节点 k 2 k_2 k2 的概率由参数 q q q 控制,参数 q q q 越大,随机游走就越集中在与前一个节点相近的节点上,看起来就像 BFS

为了理解这些概念,最好的办法就是实际实现这一架构,并对参数进行调整。接下来,我们在 Zachary’s Karate Club 数据集上实现此架构。需要注意的是,该图是一个非加权网络,因此转移概率仅由搜索偏置决定。

1.3 实现有偏随机游走

首先,需要创建一个函数,根据前一个节点、当前节点以及参数 p p p q q q 随机选择图中的下一个节点。

(1) 导入所需的库,并创建图:

import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
# Create graph
G = nx.erdos_renyi_graph(10, 0.3, seed=1, directed=False)# Plot graph
plt.axis('off')
nx.draw_networkx(G, pos=nx.spring_layout(G, seed=0))
plt.show()

随机图

(2) 用参数列表定义 next_node() 函数:

def next_node(previous, current, p, q):

(3) 检索当前节点的邻居节点列表,并初始化 alpha 值列表:

    alphas = []# Get the neighboring nodesneighbors = list(G.neighbors(current))

(4) 对于每个邻居,都要计算出相应的 alpha 值:如果该邻居是前一个节点,则为 1 p \frac1 p p1;如果该邻居与前一个节点相连,则为 1 1 1;否则为 1 q \frac 1 q q1

    # Calculate the appropriate alpha value for each neighborfor neighbor in neighbors:# Distance = 0: probability to return to the previous nodeif neighbor == previous:alpha = 1/p# Distance = 1: probability of visiting a local nodeelif G.has_edge(neighbor, previous):alpha = 1# Distance = 2: probability to explore an unknown nodeelse:alpha = 1/qalphas.append(alpha)

(5) 对这些值进行归一化处理,得出概率:

    probs = [alpha / sum(alphas) for alpha in alphas]

(6) 根据上一步计算出的转换概率,使用 np.random.choice() 随机选择下一个节点并返回:

    next = np.random.choice(neighbors, size=1, p=probs)[0]return next

在测试该函数之前,需要编写整个随机游走的代码。随机游走的代码与我们在 DeepWalk 一节中实现的代码类似。不同的是,下一个节点由 next_node() 函数选择,该函数需要额外的参数:pq,以及上一个节点和当前节点。这些节点可以通过查看添加到 walk 变量中的最后两个元素轻松获得,出于兼容性考虑,函数返回字符串而不是整数。
改进后的 random_walk() 函数如下:

def random_walk(start, length, p, q):walk = [start]for i in range(length):current = walk[-1]previous = walk[-2] if len(walk) > 1 else Nonenext = next_node(previous, current, p, q)walk.append(next)return walk

调用 random_walk()函数,生成长度为 5、p=1q=1` 的随机游走序列:

print(random_walk(0, 8, p=1, q=1))

函数返回序列如下所示:

[0, 9, 1, 0, 1, 0, 4, 3, 6]

该序列是随机的,因为每个相邻节点都有相同的转换概率。

接下来,令算法偏向于回到前一个节点,即 q=10

print(random_walk(0, 8, p=1, q=10))

函数返回序列如下所示:

[0, 9, 1, 0, 1, 9, 0, 1, 2]

可以看到,随机游走探索了图中更多的节点。接下来,使用 p=10 调用函数,由于其概率很低,所以不会返回到之前的节点:

print(random_walk(0, 8, p=10, q=1))

函数返回序列如下所示:

[0, 9, 4, 6, 5, 4, 9, 0, 1]

接下来,我们在实际应用中使用这些属性,并将其与 DeepWalk 进行比较。

2. 实现 Node2Vec

现在,我们已经实现了生成有偏随机游走的函数,Node2Vec 的实现与 DeepWalk 相似,我们可以重复使用相同的代码,并使用 p = 1q = 1DeepWalk 作为 Node2Vec 的一个特例,我们重用 Zachary’s Karate Club 数据集实现 Node2Vec 架构。
我们的目标是将俱乐部的每位成员归类为正确的类别 (“Mr. Hi” 和 “Officer”),使用 Node2Vec 输出的节点嵌入作为机器学习分类器(本节中使用随机森林)的输入。

(1) 首先,在 shell 中使用 pip 命令安装 gensim 库以使用 Word2Vec

pip install gensim

(2) 导入所需的库:

from gensim.models.word2vec import Word2Vec
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

(3) 加载 Zachary’s Karate Club 数据集:

G = nx.karate_club_graph()

(4) 将节点标签转换为数值 (01):

# Process labels (Mr. Hi = 0, Officer = 1)
labels = []
for node in G.nodes:label = G.nodes[node]['club']labels.append(1 if label == 'Officer' else 0)

(5) 指定参数 p=3q=2 调用 random_walk() 函数为图中的每个节点生成 80 个随机游走列表:

walks = []
for node in G.nodes:for _ in range(80):walks.append(random_walk(node, 10, 3, 2))

(6) 使用分层 softmax 函数创建一个 Word2Vec (skip-gram 模型)实例:

node2vec = Word2Vec(walks,hs=1,   # Hierarchical softmaxsg=1,   # Skip-gramvector_size=100,window=10,workers=2,min_count=1)

(7) 在生成的序列上对 skip-gram 模型进行 30 次训练:

node2vec.train(walks, total_examples=node2vec.corpus_count, epochs=30, report_delay=1)

(8) 创建掩码训练并测试分类器:

train_mask = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]
test_mask = [0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 26, 27, 28, 29, 30, 31, 32, 33]
labels = np.array(labels)

(9) 在训练数据上训练随机森林分类器:

clf = RandomForestClassifier(random_state=0)
clf.fit(node2vec.wv[train_mask], labels[train_mask])

(10) 在测试数据集上使用准确率作为评估模型性能的度量标准:

y_pred = clf.predict(node2vec.wv[test_mask])
acc = accuracy_score(y_pred, labels[test_mask])
print(f'Node2Vec accuracy = {acc*100:.2f}%')
# Node2Vec accuracy = 95.45%

要实现 DeepWalk,我们可以在 p = 1q = 1 的情况下重复完全相同的过程。但是,为了进行公平的比较,我们不能使用单次实验的准确率,因为这其中涉及很多随机过程,可能会出现从最差的模型中得到更好的结果。
为了限制结果的随机性,我们可以重复此过程 100 次,然后取平均值。这样的结果会稳定得多,也可以使用标准差 (np.std()) 来测量准确率的变化。
我们已经知道,Zachary’s Karate Club 是一个具有同质性的网络。这种特性通过 DFS 得到了强调,而增加参数 p 可以鼓励 DFS。假如这一说法和 DFS 与同质性之间的联系是正确的,那么 p 值越大就能够获得更好的结果。
对参数 p 和q在 17 之间重复进行同样的实验。在实际的机器学习项目中,我们会使用验证数据进行参数搜索,在本例中,我们将模型直接作为最终应用,因此直接使用测试数据。结果总结如下表所示:

模型性能对比

从上表中,可以看出:

  • DeepWalk(p = 1,q = 1) 的性能比表中其他 pq 组合都要差。这证明了有偏随机游走对于该数据集而言的有效性,但情况并非总是如此,在其他数据集上,无偏随机游走也可能表现得更好
  • p 值越高,性能越好,这也与我们的假设完全吻合。在社交网络中,通常可以将随机游走偏向于同质性。

我们可以通过调整参数观察使用不同参数时的结果。例如,我们可以探索 p 值较高 (> 7) 时的情况,或者 p 值和 q 值较低(介于 01 之间)时的情况。

小结

在本节中,我们了解了 Node2Vec,这是一种基于 Word2Vec 的架构。我们实现了有偏随机游走,并解释了其参数与两个网络属性(同质性和结构等价性)之间的联系。通过比较 Node2VecDeepWalkZachary's Karate Club 数据集上的性能表现,验证了有偏随机游走的有效性。

系列链接

图神经网络实战(1)——图神经网络(Graph Neural Networks, GNN)基础
图神经网络实战(2)——图论基础
图神经网络实战(3)——基于DeepWalk创建节点表示

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

苍穹外卖-day01

苍穹外卖-day01 目录 苍穹外卖-day01课程内容1. 软件开发整体介绍1.1 软件开发流程1.2 角色分工1.3 软件环境 2. 苍穹外卖项目介绍2.1 项目介绍2.2 产品原型2.3 技术选型 3. 开发环境搭建3.1 前端环境搭建3.2 后端环境搭建3.2.1 熟悉项目结构3.2.2 Git版本控制3.2.3 数据库环境…

Linux学习:权限

目录 1. shell命令的工作原理与存在意义1.1 shell命令解释器存在的意义1.2 shell解释器的工作原理 2. Linux操作系统:用户2.1 什么是用户2.2 用户的切换操作2.3 用户权限划分的意义 3. Linux中权限的种类和意义3.1 什么是权限3.2 sudo指令与短暂提权 4. 文件类型与文…

vant van-field 密码输入框小程序里隐藏、显示密码bug总结

老规矩先上效果图: vant 输入框组件 密码的隐藏与显示功能: 注: 用password属性控制密码的显示与隐藏 不要用type属性,type属性在真机上有时会没有效果 1、当然如果只用typepassword 不需要切换显示、隐藏也可以使用。 2、如果用到了密码的显示与…

Matlab|【EI复现】电动汽车集群并网的分布式鲁棒优化调度模型

目录 1 内容简介 2 关键知识点 2.1 三类电动汽车模型 3 程序结果 4 下载链接 1 内容简介 电动汽车的数据模型种类繁多,但是用到比较高阶数学方法的并不多,本次分享的程序是下图所示的文章。 采用分布鲁棒优化模型,用到鲁棒对等转换&…

构建高效可靠的消息队列系统:设计与实现

✨✨谢谢大家捧场,祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心哦!✨✨ 🎈🎈作者主页: 喔的嘛呀🎈🎈 目录 一、引言 二、设计目标 2.1、高可用性 1. 集群搭建 1.1 …

再探再报 除 0 这件事有不同

首先,在数学中,一个数除以0是没有意义的。 其次,在计算机中,对于除零,传统概念里是会上报一个异常。首先是CPU内部实现会报异常。最早学组成原理和汇编的时候,都是说CPU寄存器中有个表示除零异常的位。在L…

高分辨率全球海洋温度和盐度再分析数据Global Ocean Physics Reanalysis(0.083°),并利用matlab读取绘图

1.引言 在研究全球海平面变化的问题中,卫星测高获得总的海平面变化,而海平面变化包含质量变化和比容变化。因此测高数据和海洋物理分析数据对于海平面研究至关重要。 测高数据下载网址: Global Ocean Gridded L 4 Sea Surface Heights And …

1分钟带你学会使用装饰器编写Python函数

1.需求 向 test() 函数中,新增一个功能,多输出一句话"给他补铁" def test():print("水中放吸铁石") # test()# 第一种方式:重写函数 def test():print("水中放吸铁石")print("给他补铁") test()# …

在Blender中清理由Instant-NGP等几何学习技术生成的网格

使用布尔运算: 创建一个大的立方体或其他简单几何体包裹住全部网格。使用布尔修改器对两个网格进行“差集”运算。这将移除超出包裹体之外的多余网格部分。 手动选择并删除: 进入编辑模式(按Tab键)。按A键取消选择所有顶点。按B键并拖动以选择您想要删除…

C# CallerMemberName、CallerFilePath、CallerLineNumber

CallerMemberName:调用某个方法的主方法名称 CallerFilePath:调用某个方法的主方法所在的类文件地址 CallerLineNumber:调用这个方法所在的行号 用这三个附加属性,需要设置默认值。

重学SpringBoot3-内容协商机制

重学SpringBoot3-内容协商机制 ContentNegotiationConfigurer接口配置内容协商URL参数Accept头使用Url扩展名 自定义内容协商格式步骤1: 注册自定义媒体类型步骤2: 实现HttpMessageConverter接口步骤3: 使用自定义HttpMessageConverter 注意点 在 Spring Boot 3 中,…

【每日刷题】栈与队列-LC394、LC347、LC215

题外话:感觉脑子没长到栈这块…最近刷栈的题都好难啊…哭哭…坚持坚持!多刷几遍就好了!! 1. LC394.字符串解码 题目链接 先说数据结构。 维护两个栈:一个栈存之前的字符串,另一个栈存之后的字符串的重复…

腾讯云轻量服务器流量用完了怎么办?停机吗?

腾讯云轻量服务器流量用完了怎么办?超额流量另外支付流量费,流量价格为0.8元/GB,会自动扣你的腾讯云余额,如果你的腾讯云账号余额不足,那么你的轻量应用服务器会面临停机,停机后外网无法访问,继…

探索React中的类组件和函数组件

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

类与对象-对象特性

师从黑马程序员 对象的初始化和清理 构造函数和析构函数 用于完成对象的初始化和清理工作 如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现 构造函数:主要用于创建对象时为对象的成员属性赋值,构造函数由编…

每日学习笔记:C++ STL 的Vector

Vector定义 Vector的大小与容量 Vector的函数 操作注意事项 Vector当作C数组 vector<bool>

瑞芯微第二代8nm高性能AIOT平台 RK3576 详细介绍

RK3576处理器 RK3576瑞芯微第二代8nm高性能AIOT平台&#xff0c;它集成了独立的6TOPS&#xff08;Tera Operations Per Second&#xff0c;每秒万亿次操作&#xff09;NPU&#xff08;神经网络处理单元&#xff09;&#xff0c;用于处理人工智能相关的任务。此外&#xff0c;R…

【C语言】深入理解指针(进阶篇)

一、数组名的理解 数组名就是地址&#xff0c;而且是数组首元素的地址。 任务&#xff1a;运行以下代码&#xff0c;看数组名是否是地址。 #include <stdio.h> int main() {int arr[] { 1,2,3,4,5,6,7,8,9,0 };printf("&arr[0] %p\n", &arr[0]);pri…

数据结构——算法的空间复杂度

【本节内容】 1.空间复杂度 2.常见空间复杂度 1.空间复杂度 空间复杂度也是一个数学表达式&#xff0c;是对一个算法在运行过程中临时占用额外存储空间大小的量度。 空间复杂度不是程序占用了多少bytes的空间&#xff0c;因为这个也没太大意义&#xff0c;所以空间复杂度算…

动态规划课堂4-----子数组系列

目录 引入&#xff1a; 例题1&#xff1a;最大子数组和 例题2&#xff1a;环形子数组的最大和 例题3&#xff1a;乘积最大子数组 例题4&#xff1a;乘积为正数的最长子数组 总结&#xff1a; 结语&#xff1a; 引入&#xff1a; 在动态规划&#xff08;DP&#xff09;子…