图学习【参考资料2】-知识补充与node2vec代码注解( 三 )

NOTE:Node2Vec会根据与上个节点的距离按不同概率采样得到当前节点的下一个节点 。<img src="http://shimg.jingyanzongjie.com/230728/1100335b7-4.jpg" width="85%" height="85%" /><br>参考; PGL/pgl/graph_kernel.pyx 中用Cython语言实现了节点采样函数node2vec_sample3.1 Node2Vec采样算法转换代码注解

  1. 这部分代码 , 用于随机游走后得到的路径 , 然后对这些路径进行吸收学习 , 训练图结构
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 intp - 控制回到上一节点的概率 floatq - 控制偏向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/qprob = 1. / qelse:prob = 0.# case 4 : otherprobs.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_sumsampled_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中

经验总结扩展阅读