图神经网络实战(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 nodes
    neighbors = 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 neighbor
    for neighbor in neighbors:
        # Distance = 0: probability to return to the previous node
        if neighbor == previous:
            alpha = 1/p
        # Distance = 1: probability of visiting a local node
        elif G.has_edge(neighbor, previous):
            alpha = 1
        # Distance = 2: probability to explore an unknown node
        else:
            alpha = 1/q
        alphas.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 None
        next = 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 softmax
                sg=1,   # Skip-gram
                vector_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创建节点表示

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue源码系列讲解——过滤器篇【三】(解析过滤器)

目录 1. 前言 2. 在何处解析过滤器 3. parseFilters函数分析 4. 小结 1. 前言 在上篇文章中我们说了,无论用户是以什么方式使用过滤器,终归是将解析器写在模板中,既然是在模板中,那它肯定就会被解析编译,通过解析用…

【ESP32 IDF快速入门】点亮第一个LED灯与流水灯

文章目录 前言一、有哪些工作模式?1.1 GPIO的详细介绍1.2 GPIO的内部框图输入模式输出部分 二、GPIO操作函数2.1 GPIO 汇总2.2 GPIO操作函数gpio_config配置引脚reset 引脚函数设置引脚电平选中对应引脚设置引脚的方向 2.3 点亮第一个灯 三、流水灯总结 前言 ESP32…

【Godot4.2】GDScript数组分类及类型化数组和紧缩数组概述

概述 GDScript的数组是一种很常用的数据类型。本文主要阐述一下GDScript数组分类,以及官方文档和大多数视频或教程较少提及的类型化数组和紧缩数组。 GDScript数组分类 通过反复查阅GDScript内置文档并进行细节比较,发现GDScript的数组,可…

Qt for WebAssembly : Application exit (SharedArrayBuffer is not defined)

用Qt开发 WebAssembly,放到nginx里面,用127.0.0.1访问没问题,用局域网IP访问就提示如下: 总结了以下两种解决办法: ①:配置 nginx http 头 [ 支持:WebAssembly Qt (single-threaded) ] ②&#…

关于 selinux 规则

1. 查看selinux状态 SELinux的状态: enforcing:强制,每个受限的进程都必然受限 permissive:允许,每个受限的进程违规操作不会被禁止,但会被记录于审计日志 disabled:禁用 相关命令&#xf…

ElevenLabs用AI为Sora文生视频模型配音 ,景联文科技提供高质量真人音频数据集助力生成逼真音效

随着Open AI公司推出的Sora文生视频模型惊艳亮相互联网,AI语音克隆创企ElevenLabs又为Sora的演示视频生成了配音,所有的音效均由AI创造,与视频内容完美融合。 ElevenLabs的语音克隆技术能够从一分钟的音频样本中创建逼真的声音。为了实现这一…

LVS集群 ----------------(直接路由 )DR模式部署

一、LVS集群的三种工作模式 lvs-nat:修改请求报文的目标IP,多目标IP的DNAT lvs-dr:操纵封装新的MAC地址(直接路由) lvs-tun:隧道模式 lvs-dr 是 LVS集群的 默认工作模式 NAT通过网络地址转换实现的虚拟服务器&…

Ubuntu 22.04修改静态ip

1. 备份原网络配置文件 # 配置文件名称因机器设置有异 cd /etc/netplan cp 01-network-config.yaml 01-network-config.yaml.bak# 文件内容如下 network:version: 2renderer: NetworkManager2. 修改配置文件 使用 ipconfig 命令查看网络信息,ip addr 命令也可 我这…

【S32DS报错】-8-调用初始化函数Port_Init后,S32DS断开与调试器PEmicro/J-Link的连接,无法调试Debug(基于MCAL)

问题背景: 在S32DS IDE中,调用初始化函数Port_Init后,S32DS断开与调试器PEmicro / J-Link的连接,无法调试Debug: 问题原因: 调用初始化函数Port_Init时,MCU的JTAG接口被初始化,导致…

Echarts 配置项 series 中的 data 是多维度

文章目录 需求分析 需求 如下图数据格式所示,现要求按照该格式进行绘制折线图 分析 在绘制折线图时,通常我们的 series 中的 data 数据是这样的格式 option {title: {text: Stacked Area Chart},tooltip: {trigger: axis,axisPointer: {type: cross…

紧握时代契机链接亿万家庭 创维汽车2024全球经销商大会圆满召开

3月6日,以“极致 见新境”创维汽车2024全球经销商大会在徐州隆重举行。徐州经开区管委会副主任季洪志,缅甸驻华大使馆商务参赞 Win Myat Aung,法国中小企业联盟主席 Xavier Michon-Lehnebach,创维集团、创维汽车创始人黄宏生&…

【项目】图书管理系统

目录 前言: 项目要求: 知识储备: 代码实现: Main: Books包: Book: BookList: Operate包: Operate: addOperate: deleteOperate: exitOperate: findOperate:…

Python与FPGA——膨胀腐蚀

文章目录 前言一、膨胀腐蚀二、Python实现腐蚀算法三、Python实现膨胀算法四、Python实现阈值算法五、FPGA实现腐蚀算法总结 前言 腐蚀是指周围的介质作用下产生损耗与破坏的过程,如生锈、腐烂等。而腐蚀算法也类似一种能够产生损坏,抹去部分像素的算法。…

代码随想录算法训练营第13天

239. 滑动窗口最大值 &#xff08;一刷至少需要理解思路&#xff09; 方法&#xff1a;暴力法 &#xff08;时间超出限制&#xff09; 注意&#xff1a; 代码&#xff1a; class Solution { public:vector<int> maxSlidingWindow(vector<int>& nums, int k…

掌握 Vue3、Vite 和 SCSS 实现一键换肤的魔法步骤

前言 一个网站的换肤效果算是一个比较常见的功能&#xff0c;尤其是在后台管理系统中&#xff0c;我们几乎都能看到他的身影&#xff0c;这里给大家提供一个实现思路。 搭建项目 vitevue3搭建项目这里就不演示了&#xff0c;vite官网里面讲得很清楚。 注&#xff1a;这里使…

【YOLO v5 v7 v8 v9小目标改进】辅助超推理SAHI:分而治之,解决高分辨率图像中小物体检测的问题

辅助超推理SAHI&#xff1a;分而治之&#xff0c;解决高分辨率图像中小物体检测的问题 设计思路结构小目标涨点YOLO v5 魔改YOLO v7 魔改YOLO v8 魔改YOLO v9 魔改 论文&#xff1a;https://arxiv.org/pdf/2202.06934.pdf 代码&#xff1a;https://github.com/obss/sahi 设计思…

Java+SpringBoot+Vue+MySQL:农业管理新篇章

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

Git基础知多少

什么是Git Git 是分布式版本控制系统&#xff08;DVCS&#xff09;。它可以跟踪文件的更改&#xff0c;并允许你恢复到任何特定版本的更改。与 SVN 等其他版本控制系统&#xff08;VCS&#xff09;相比&#xff0c;其分布式架构具有许多优势&#xff0c;一个主要优点是它不依赖…

本地知识库搭建成功后,企业效率真的翻倍了

在如今这个快节奏的信息时代&#xff0c;对企业来说&#xff0c;拥有一套高效的知识管理系统早已不再是选项&#xff0c;而是必要。而本地知识库&#xff0c;它这个集信息存储、管理和查询于一体的平台&#xff0c;不仅改变了公司信息资源共享的方式&#xff0c;还帮助进一步提…

DataLoader

import torchvision from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter# 准备的测试数据集 数据放在了CIFAR10文件夹下test_data torchvision.datasets.CIFAR10("./CIFAR10",trainFalse, transformtorchvision.transfor…