请教,刚从github上下载来node2vec的源代码,请问下一步要怎么做,才能实现功能呢?谢谢各位!
NOTE:Node2Vec会根据与上个节点的距离按不同概率采样得到当前节点的下一个节点。
参考; PGL/pgl/graph_kernel.pyx 中用Cython语言实现了节点采样函数node2vec_sample
import numpy as np
# 随机节点的获取
def node2vec_sample(succ, prev_succ, prev_node, p, q):
"""
输入:succ - 当前节点的下一个相邻节点id列表 list (num_neighbors,)
prev_succ - 前一个节点的下一个相邻节点id列表 list (num_neighbors,)
prev_node - 前一个节点id int
p - 控制回到上一节点的概率 float
q - 控制偏向DFS还是BFS float
输出:下一个节点id int
"""
##################################
# 请在此实现node2vec的节点采样函数
# 节点参数信息
succ_len = len(succ) # 获取相邻节点id列表节点长度(相对当前)
prev_succ_len = len(prev_succ) # 获取相邻节点id列表节点长度(相对前一个节点)
prev_succ_set = np.asarray([]) # 前一节点的相邻节点id列表
for i in range(prev_succ_len): # 遍历得到前一节点的相邻节点id列表的新list——prev_succ_set,用于后边概率的讨论
# 将前一节点list,依次押入新的list中
prev_succ_set = np.append(prev_succ_set,prev_succ[i]) # ? prev_succ_set.insert(prev_succ[i])
# 概率参数信息
probs = [] # 保存每一个待前往的概率
prob = 0 # 记录当前讨论的节点概率
prob_sum = 0. # 所有待前往的节点的概率之和
# 遍历当前节点的相邻节点
for i in range(succ_len): # 遍历每一个当前节点前往的概率
if succ[i] == prev_node: # case 1 : 采样节点与前一节点一致,那么概率为--1/q(原地)
prob = 1. / p
# case 2 完整的应该是: np.where(prev_succ_set==succ[i]) and np.where(succ==succ[i])
# 但是因为succ本身就是采样集,所以np.where(succ==succ[i])总成立,故而忽略,不考虑
elif np.where(prev_succ_set==succ[i]): # case 2 : 采样节点在前一节点list内,那么概率为--1 ?cpython中的代码: prev_succ_set.find(succ[i]) != prev_succ_set.end()
prob = 1.
elif np.where(prev_succ_set!=succ[i]): # case 3 : 采样节点不在前一节点list内,那么概率为--1/q
prob = 1. / q
else:
prob = 0. # case 4 : other
probs.append(prob) # 将待前往的每一个节点的概率押入保存
prob_sum += prob # 计算所有节点的概率之和
RAND_MAX = 65535 # 这是一个随机数的最值,用于计算随机值的--根据C/C++标准,最小在30000+,这里取2^16次方
rand_num = float(np.random.randint(0, RAND_MAX+1)) / RAND_MAX * prob_sum # 计算一个随机概率:0~prob_sum. ?cpython中的代码: float(rand())/RAND_MAX * prob_sum
sampled_succ = 0. # 当前节点的相邻节点中确定的采样点
# rand_num => 是0~prob_num的一个值,表示我们的截取概率阈值--即当遍历了n个节点时,若已遍历的节点的概率之和已经超过了rand_num
# 我们取刚好满足已遍历的节点的概率之和已经超过了rand_num的最近一个节点作为我们的采样节点
# 比如: 遍历到第5个节点时,权重概率和大于等于rand_num,此时第5个节点就是对应的采样的节点了
# 为了方便实现:这里利用循环递减--判断条件就变成了————当rand_num减到<=0时,开始采样节点
for i in range(succ_len): # 遍历当前节点的所有相邻节点
rand_num -= probs[i] # 利用rand_num这个随机获得的概率值作为依据,进行一个循环概率检验
if rand_num <= 0: # 当遇到第一次使得rand_num减到<=0后,说明到这个节点为止, 遍历应该终止了,此时的节点即未所求的节点,【停止检验条件】
sampled_succ = succ[i] # 并把当前节点作为确定的节点
return sampled_succ # 返回待采样的节点--节点一定在succ中
根据参考资料中给出的代码实现,使用node2vec功能并不需要安装任何特定的软件或库。下面是使用node2vec的具体步骤:
下载并导入依赖:从GitHub上下载node2vec的源代码,并在你的代码中导入所需的依赖项。
定义图的结构:你需要定义一个表示图的数据结构,并将图加载到内存中。这可以根据你的具体情况选择合适的方式,可以使用现有的图形库,也可以自己实现。
进行随机游走:使用定义的图结构,根据一定的规则进行随机游走,即随机抽取一些点的序列。可以参考参考资料中的随机游走代码实现。
运行word2vec模型:将得到的节点序列输入到word2vec模型中,得到每个节点的embedding向量。可以使用现有的word2vec库,比如gensim库,来实现这一步骤。
下面是一个示例代码,展示了如何使用node2vec:
# 导入所需的库和模块
import numpy as np
from gensim.models import Word2Vec
# 定义图的结构,例如使用邻接表表示图
graph = {
'A': ['B', 'C'],
'B': ['A', 'C', 'D'],
'C': ['A', 'B', 'D'],
'D': ['B', 'C']
}
# 进行随机游走,生成节点序列
def random_walk(graph, start_node, walk_length, p, q):
walk = [start_node]
for _ in range(walk_length):
curr_node = walk[-1]
neighbors = graph[curr_node]
if len(walk) == 1:
# 根据上一个节点的相邻节点采样下一个节点
next_node = np.random.choice(neighbors)
else:
prev_node = walk[-2]
next_node = node2vec_sample(neighbors, graph[prev_node], prev_node, p, q)
walk.append(next_node)
return walk
# 运行node2vec
def node2vec(graph, walk_length, num_walks, p, q):
walks = []
for _ in range(num_walks):
for node in graph.keys():
walk = random_walk(graph, node, walk_length, p, q)
walks.append(walk)
model = Word2Vec(walks, size=128, window=5, min_count=0, sg=1, workers=4)
return model
# 使用node2vec生成节点的embedding向量
embedding_model = node2vec(graph, walk_length=10, num_walks=20, p=0.25, q=0.25)
这是一个简单的示例,你可以根据你的实际情况进行调整和扩展。请注意,上述示例代码中导入的Word2Vec模块是gensim库的一部分,你需要根据实际情况安装该库。