图与推荐系统(一):Graph Embedding之node2vec (原理 + 代码实战)

article/2025/11/6 20:52:12

文章目录

  • 一. 介绍
  • 二. 公式
  • 三. 代码细节
  • 四. 代码

一. 介绍

node2vec是一种综合考虑DFS邻域和BFS邻域的graph embedding方法。简单来说,可以看作是deepwalk的一种扩展,是结合了DFS和BFS随机游走的deepwalk。node2vec通过调整方向的参数来控制模型更倾向BFS还是DFS。
在这里插入图片描述

BFS更能体现图网络的“结构性”,因为BFS生成的序列往往是由当前节点周边的组成的网络结构。这就能让最终生成的embedding具备更多局部结构化特征。
DFS更能体现图网络的“同质性”,因为DFS更有可能游走到当前节点远方的节点。但无论怎样,DFS的游走更大概率会在一个大的集合内部进行,这就使得一个集合内部节点的embedding更为相似,从而更多地表达网络的“同质性”

二. 公式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三. 代码细节

采样完顶点序列后,剩下的步骤就和deepwalk一样了,用word2vec去学习顶点的embedding向量。值得注意的是node2vecWalk中不再是随机抽取邻接点,而是按概率抽取,node2vec采用了Alias算法进行顶点采样。

关于Alias算法的一些参考:

  1. https://mp.weixin.qq.com/s/n_EveU9G_MRwuSHW5kyWkw
  2. https://zhuanlan.zhihu.com/p/54867139
  3. https://blog.csdn.net/haolexiao/article/details/65157026

四. 代码

import networkx as nx
from gensim.models import Word2Vec
import pandas as pd
import numpy as np
import itertools
import math
import random
from joblib import Parallel, delayed
from tqdm import trangeimport warningswarnings.filterwarnings('ignore')
G = nx.read_edgelist('./data/wiki/Wiki_edgelist.txt',create_using=nx.DiGraph(), nodetype=None, data=[('weight', int)])
#Alias采样
def create_alias_table(area_ratio):""":param area_ratio: sum(area_ratio)=1:return: accept,alias"""l = len(area_ratio)accept, alias = [0] * l, [0] * lsmall, large = [], []area_ratio_ = np.array(area_ratio) * lfor i, prob in enumerate(area_ratio_):if prob < 1.0:small.append(i)else:large.append(i)while small and large:small_idx, large_idx = small.pop(), large.pop()accept[small_idx] = area_ratio_[small_idx]alias[small_idx] = large_idxarea_ratio_[large_idx] = area_ratio_[large_idx] - \(1 - area_ratio_[small_idx])if area_ratio_[large_idx] < 1.0:small.append(large_idx)else:large.append(large_idx)while large:large_idx = large.pop()accept[large_idx] = 1while small:small_idx = small.pop()accept[small_idx] = 1return accept, aliasdef alias_sample(accept, alias):""":param accept::param alias::return: sample index"""N = len(accept)i = int(np.random.random()*N)r = np.random.random()if r < accept[i]:return ielse:return alias[i]
def partition_num(num, workers):if num % workers == 0:return [num//workers]*workerselse:return [num//workers]*workers + [num % workers]class RandomWalker:def __init__(self, G, p=1, q=1, use_rejection_sampling=0):""":param G::param p: Return parameter,controls the likelihood of immediately revisiting a node in the walk.:param q: In-out parameter,allows the search to differentiate between “inward” and “outward” nodes:param use_rejection_sampling: Whether to use the rejection sampling strategy in node2vec."""self.G = Gself.p = pself.q = qself.use_rejection_sampling = use_rejection_samplingdef deepwalk_walk(self, walk_length, start_node):walk = [start_node]while len(walk) < walk_length:cur = walk[-1]cur_nbrs = list(self.G.neighbors(cur))if len(cur_nbrs) > 0:walk.append(random.choice(cur_nbrs))else:breakreturn walkdef node2vec_walk(self, walk_length, start_node):G = self.Galias_nodes = self.alias_nodesalias_edges = self.alias_edgeswalk = [start_node]while len(walk) < walk_length:cur = walk[-1]cur_nbrs = list(G.neighbors(cur))if len(cur_nbrs) > 0:if len(walk) == 1:walk.append(cur_nbrs[alias_sample(alias_nodes[cur][0], alias_nodes[cur][1])])else:prev = walk[-2]edge = (prev, cur)next_node = cur_nbrs[alias_sample(alias_edges[edge][0],alias_edges[edge][1])]walk.append(next_node)else:breakreturn walkdef node2vec_walk2(self, walk_length, start_node):"""Reference:KnightKing: A Fast Distributed Graph Random Walk Enginehttp://madsys.cs.tsinghua.edu.cn/publications/SOSP19-yang.pdf"""def rejection_sample(inv_p, inv_q, nbrs_num):upper_bound = max(1.0, max(inv_p, inv_q))lower_bound = min(1.0, min(inv_p, inv_q))shatter = 0second_upper_bound = max(1.0, inv_q)if (inv_p > second_upper_bound):shatter = second_upper_bound / nbrs_numupper_bound = second_upper_bound + shatterreturn upper_bound, lower_bound, shatterG = self.Galias_nodes = self.alias_nodesinv_p = 1.0 / self.pinv_q = 1.0 / self.qwalk = [start_node]while len(walk) < walk_length:cur = walk[-1]cur_nbrs = list(G.neighbors(cur))if len(cur_nbrs) > 0:if len(walk) == 1:walk.append(cur_nbrs[alias_sample(alias_nodes[cur][0], alias_nodes[cur][1])])else:upper_bound, lower_bound, shatter = rejection_sample(inv_p, inv_q, len(cur_nbrs))prev = walk[-2]prev_nbrs = set(G.neighbors(prev))while True:prob = random.random() * upper_boundif (prob + shatter >= upper_bound):next_node = prevbreaknext_node = cur_nbrs[alias_sample(alias_nodes[cur][0], alias_nodes[cur][1])]if (prob < lower_bound):breakif (prob < inv_p and next_node == prev):break_prob = 1.0 if next_node in prev_nbrs else inv_qif (prob < _prob):breakwalk.append(next_node)else:breakreturn walkdef simulate_walks(self, num_walks, walk_length, workers=1, verbose=0):G = self.Gnodes = list(G.nodes())results = Parallel(n_jobs=workers, verbose=verbose, )(delayed(self._simulate_walks)(nodes, num, walk_length) for num inpartition_num(num_walks, workers))walks = list(itertools.chain(*results))return walksdef _simulate_walks(self, nodes, num_walks, walk_length,):walks = []for _ in range(num_walks):random.shuffle(nodes)for v in nodes:if self.use_rejection_sampling:walks.append(self.node2vec_walk2(walk_length=walk_length, start_node=v))else:walks.append(self.node2vec_walk(walk_length=walk_length, start_node=v))return walksdef get_alias_edge(self, t, v):"""compute unnormalized transition probability between nodes v and its neighbors give the previous visited node t.:param t::param v::return:"""G = self.Gp = self.pq = self.qunnormalized_probs = []for x in G.neighbors(v):weight = G[v][x].get('weight', 1.0)  # w_vxif x == t:  # d_tx == 0unnormalized_probs.append(weight/p)elif G.has_edge(x, t):  # d_tx == 1unnormalized_probs.append(weight)else:  # d_tx > 1unnormalized_probs.append(weight/q)norm_const = sum(unnormalized_probs)normalized_probs = [float(u_prob)/norm_const for u_prob in unnormalized_probs]return create_alias_table(normalized_probs)def preprocess_transition_probs(self):"""Preprocessing of transition probabilities for guiding the random walks."""G = self.Galias_nodes = {}for node in G.nodes():unnormalized_probs = [G[node][nbr].get('weight', 1.0)for nbr in G.neighbors(node)]norm_const = sum(unnormalized_probs)normalized_probs = [float(u_prob)/norm_const for u_prob in unnormalized_probs]alias_nodes[node] = create_alias_table(normalized_probs)if not self.use_rejection_sampling:alias_edges = {}for edge in G.edges():alias_edges[edge] = self.get_alias_edge(edge[0], edge[1])if not G.is_directed():alias_edges[(edge[1], edge[0])] = self.get_alias_edge(edge[1], edge[0])self.alias_edges = alias_edgesself.alias_nodes = alias_nodesreturn
num_walks = 80    #序列数量
walk_length = 10  #序列长度
workers = 4#根据RandomWalker产生序列
rw = RandomWalker(G, p=0.25, q=4, use_rejection_sampling=0)
rw.preprocess_transition_probs()
sentences = rw.simulate_walks(num_walks=num_walks, walk_length=walk_length, workers=workers, verbose=1)model = Word2Vec(sentences = sentences,vector_size = 128,min_count=5,sg =1,hs=0,workers = workers,window=5, epochs=3)#获取embedding dict
embedding_dict = {}
for word in G.nodes():  embedding_dict[word] = model.wv[word]
[Parallel(n_jobs=4)]: Using backend LokyBackend with 4 concurrent workers.
[Parallel(n_jobs=4)]: Done   2 out of   4 | elapsed:    3.1s remaining:    3.1s
[Parallel(n_jobs=4)]: Done   4 out of   4 | elapsed:    4.3s finished
#计算两个embedding之间的余弦相似度
def cosine_similarity(x,y):num = x.dot(y.T)denom = np.linalg.norm(x) * np.linalg.norm(y)return num / denomresult = cosine_similarity(embedding_dict['1397'],embedding_dict['1470'])   
result
0.80252045


http://chatgpt.dhexx.cn/article/xiwD8NQW.shtml

相关文章

图神经网络之Node2Vec详解

目录 背景传统算法存在的问题算法背景动机 算法随机序列的生成Node2Vec算法算法的具体流程&#xff1a; 总结相关资料 背景 传统算法存在的问题 一些方法中所提出的特征需要依赖人手工定义&#xff0c;这需要特定领域内专业人士来完成&#xff0c;而且依靠人手工定义特征的有…

node2vec算法

图嵌入算法 一、背景1.提出2.应用3.特点 二、DeepWalkRandomWalk 三、node2vec引言node2walkskip-gram(用中心词预测周围词) 一、背景 1.提出 ①使用邻接矩阵表示网络时&#xff0c;由于矩阵中大多数元素是0&#xff0c;难以体现节点间关系&#xff0c;同时数据的稀疏性使得计…

node2vec代码实现及详细解析

目录 前言1.数据导入2.node2vecWalk2.1 归一化转移概率2.1.1 原理解析2.1.2 Alias采样2.1.3 代码实现 2.2 node2vecWalk的实现 3.LearnFeatures4.参数选择5.完整代码 前言 在KDD 2016 | node2vec&#xff1a;Scalable Feature Learning for Networks 中我们详细讨论了node2vec…

Node2vec原理剖析,代码实现

DeepWalk原理介绍 与词嵌入类似&#xff0c;图嵌入基本理念是基于相邻顶点的关系&#xff0c;将目的顶点映射为稠密向量&#xff0c;以数值化的方式表达图中的信息&#xff0c;以便在下游任务中运用。 Word2Vec根据词与词的共现关系学习向量的表示&#xff0c;DeepWalk受其启…

Python(Pycharm)常用快捷键

1. Pycharm一键给所有代码加# CtrlA或者Ctrl/ 2. Pycharm格式化代码快捷键&#xff08;即标准化&#xff09; CtrlAltL 【注】qq也可能有此快捷键&#xff0c;把qq删掉就行 其实改掉Pycharm的里面的设置也可以&#xff08;这样就不会冲突了&#xff09; add keyboard shor…

python 中常见的快捷方式

相信大家在写代码的时候经常会用到快捷键&#xff0c;这不仅使编写代码的效率提高不少&#xff0c;还能装 X...下面整理了部分常见快捷键&#xff0c;后期会接着更新。废话不多说&#xff1a; 1.ctrlshiftA:万能命令行 可以新建一个python文件 2. shift两次:查看资源文件 3…

python的快捷键总结

电脑是解决众多问题的工具&#xff0c;尤其对于程序员而言&#xff0c;拥有一套快捷方式&#xff0c;在编程时&#xff0c;可以事半功倍&#xff0c;从而高效地完成工作任务&#xff0c;今天就带来一套快捷方式&#xff0c;仅供大家参考&#xff1a; 常见快捷方式&#xff1a; …

Python快捷键

1.注释(添加/消除)(Ctrl /) 这里说下Python的单行注释是 # , 多行注释是 注释内容 , java的单行注释是 // , 多行注释 /* 注释内容 */, 文档注释 /** 注释内容 */ 这里说的注释快捷键主要用于多行注释, 当你想把一段代码暂时注释掉的时候, 可以直接选中这段代码, 利用此快…

python 常用快捷键和设置

pycharm常用快捷键 转载自 大哥 1、编辑&#xff08;Editing&#xff09; Ctrl Space 基本的代码完成&#xff08;类、方法、属性&#xff09; Ctrl Alt Space 快速导入任意类 Ctrl Shift Enter 语句完成 Ctrl P 参数信息&#xff08;在方法中调用参数&#…

Python常用快捷键整理

Python常用快捷键整理 文章目录 Python常用快捷键整理一、注释二、删除三、格式四、 查看其它1. 代码自动整理 本文列举了常用的快捷键&#xff0c;暂时刚入门&#xff0c;还没有写太多&#xff0c;如有不足之处还请多多指教。 一、注释 行注释/取消行注释: Ctrl / 多行注释&…

python常用快捷键

一、编辑&#xff08;Editing&#xff09; Ctrl Space 基本的代码完成&#xff08;类、方法、属性&#xff09; Ctrl Alt Space 快速导入任意类 Ctrl Shift Enter 语句完成 Ctrl P 参数信息&#xff08;在方法中调用参数&#xff09; Ctrl Q 快速查看文档 F1 外部文档 S…

python常用快捷键,写代码事半功倍

今天就为大家带来一篇Python常用快捷键。觉得挺不错的&#xff0c;现在就分享给大家&#xff0c;也给大家做个参考。 最重要的快捷键 ctrlshiftA:万能命令行shift两次:查看资源文件 新建工程第一步操作 module设置把空包分层去掉,compact empty middle package设置当前的工…

OSGB数据的纹理压缩

运行环境 OSG3.4.1 VS2017 对osgb数据的压缩关键在于纹理的压缩&#xff0c;即可在writeNodeFIie方法中进行操作。 osgDB::writeNodeFile(*rootNode, f_path, new osgDB::Options("WriteImageHintIncludeFile"));osg老版本中的 writeImageHintIncludeFile 有缺陷(…

unity 加载倾斜摄影-C#解析osgb(一)

下载 https://download.csdn.net/download/WantFK/87009338https://download.csdn.net/download/WantFK/87009338 1.提取osgb的图片信息\mesh顶点 \UV\ 三角序列\下一级name\bounds\视距 和保存 &#xff08;提取 目的 解析osgb的流程过于麻烦 真正用地到的就这几个数据 2.un…

osg加载osgb数据_PCM点云数据处理软件功能使用第十六弹

好久不见!今日为大家带来PCM点云数据处理软件功能使用第十六弹——建筑物应用,快跟小编一起学习吧! 该菜单包含两个建筑物应用模块:建筑点云提取、建筑简易模型一 建筑点云提取 建筑物点云提取输入数据为原始点云文件和地面文件,利用法向量对非地面点进行建筑物墙面点云检…

UE加载osgb倾斜摄影数据

1.支持加载大疆智图和CC导出的osgb格式倾斜摄影数据 2.支持编辑器模式&#xff08;不运行&#xff09;加载预览特定精度级别的osgb数据 3.运行时多线程加载osgb文件&#xff0c;分页LOD算法动态加载卸载&#xff0c;内存占用稳定 4.支持海量的osgb数据量加载&#xff0c;支持…

Osgb转3DTiles工具

三维倾斜摄影生产主要格式为Osgb&#xff0c;目前三维模型主要展示场景为web&#xff0c;大部分使用框架都是Cesium库&#xff0c;格式为 3DTiles&#xff0c;目前市面上osgb转3DTiles的软件已经有好几个&#xff0c;付费免费都有。 先说免费软件&#xff1a; 1、CesiumLab …

osgb倾斜模型顶层合并

经过多年的发展&#xff0c;倾斜摄影模型技术已经成熟&#xff0c;在智慧城市、社区管理&#xff0c;安防演练模拟等应用场合非常多&#xff0c;效果也非常好。 倾斜模型顶层合并是一个比较复杂的问题&#xff0c;常规上倾斜模型制作软件&#xff0c;倾斜模型24级别合并到12级…

Threejs加载倾斜摄影OSGB数据

个人主页&#xff1a; 左本Web3D&#xff0c;更多案例预览请点击》 在线案例 个人简介&#xff1a;专注Web3D使用ThreeJS实现3D效果技巧和学习案例 &#x1f495; &#x1f495;积跬步以至千里&#xff0c;致敬每个爱学习的你。获取模型或源码请点赞收藏加留言&#xff0c;有问…

数据处理-倾斜摄影OSGB合并根节点

数据处理-倾斜摄影OSGB合并根节点 背景介绍 web三维地图引擎我们使用的是cesium&#xff0c;因此我们使用的倾斜摄影数据(OSGB)会转换成3DTiles(.b3dm)进行加载。如果倾斜摄影的范围很大或者数据量大&#xff0c;有不少的建筑物什么的&#xff0c;默认转换的3Dtiles数据在前台…