AI人工智能知识图谱node2vec论文解读,人工智能知识图谱图计算

论文名称:node2vec: Scalable Feature Learning for Networks
node2vec的思想同DeepWalk一样:生成随机游走,对随机游走采样得到(节点,上下文)的组合,然后用处理词向量的方法对这样的组合建模得到网络节点的表示。
首先介绍了复杂网络面对的几种任务,一种是网络节点的分类,我们关心的是哪些节点具有类似的属性,就将其分到同一个类别中。另一种是链接预测,就是预测网络中哪些顶点有潜在的关联。

要完成这些任务首先要解决的问题就是网络嵌入。

能否设计出又能保持节点邻居信息而且又容易训练的模型呢?这就是本文尝试解决的工作。我们发现很多节点在网络中往往有一些类似的结构特征。一种结构特征是很多节点会聚集在一起,内部的连接远比外部的连接多,我们称之为社区。另一种结构特征是网络中两个可能相聚很远的点,在边的连接上有着类似的特征。

那么要设计的网络表示学习算法的目标必须满足这两点:

同一个社区内的节点表示相似。
拥有类似结构特征的节点表示相似。
然后就是日常吹一下本文的工作了,总之本文的贡献就是:提出了一个有效的、可扩展的表示学习算法,可以体现网络特征和节点邻居特征。

Related Works
之前的工作存在着一些问题:

特征需要依赖人手工定义,不是某个领域内专业人士无法从事工作,而且依靠人手工定义特征这件事本身就很悬,就算是领域专家也不能肯定某个提取出来的特征就一定管用,所以这种方法就很玄学了。
一些非监督学习中的降维方法被拿来使用,但是这些方法计算效率低,准确度也不够,而且还不能反应出网络的结构特征。
最近(当时是最近)在NLP中的方法为本文的作者提供了思路,其实跟DeepWalk的想法是一样的。用skip-gram模型来解决网络表示学习的问题。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上图中,对于一个随机游走,如果已经采样了(t,v),也就是说现在停留在节点v上,那么下一个要采样的节点x是哪个?作者定义了一个概率分布,也就是一个节点到它的不同邻居的转移概率:在这里插入图片描述
在这里插入图片描述
下面来逐行看看论文中提供的算法:
在这里插入图片描述
首先看一下算法的参数,图G、表示向量维度d、每个节点生成的游走个数r,游走长度l,上下文的窗口长度k,以及之前提到的p、q参数。

根据p、q和之前的公式计算一个节点到它的邻居的转移概率。
将这个转移概率加到图G中形成G’。
walks用来存储随机游走,先初始化为空。
外循环r次表示每个节点作为初始节点要生成r个随机游走。
然后对图中每个节点。
生成一条随机游走walk。
将walk添加到walks中保存。
然后用SGD的方法对walks进行训练。
第6步中一条walk的生成方式如下:

将初始节点u添加进去。
walk的长度为l,因此还要再循环添加l-1个节点。
当前节点设为walk最后添加的节点。
找出当前节点的所有邻居节点。
根据转移概率采样选择某个邻居s。
将该邻居添加到walk中。

Experiments
下图是一些实验结果和可视化效果:
在这里插入图片描述
在这里插入图片描述
总结
本文其实思想跟DeepWalk是一样的,不过改进了DeepWalk中随机游走的生成方式,使得生成的随机游走可以反映深度优先和广度优先两种采样的特性,从而提高网络嵌入的效果。

源码赏析:
preprocess_transition_probs(初始生成节点到节点的概率)

def preprocess_transition_probs(self):
		'''
		Preprocessing of transition probabilities for guiding the random walks.
		'''
               ####get_alias_edge这个函数是对每条边设定为二阶randomwalk的概率形式
                ###这个函数的作用是生成每个边界的概率,同时会有alias_setup这个函数将概率进行转换,方便后面抽样
		G = self.G
		is_directed = self.is_directed

		alias_nodes = {}
		for node in G.nodes():
			unnormalized_probs = [G[node][nbr]['weight'] for nbr in sorted(G.neighbors(node))]#读取每个邻点权重
			norm_const = sum(unnormalized_probs)###权重求和,作为公式中正则项常数的那个分母
			normalized_probs =  [float(u_prob)/norm_const for u_prob in unnormalized_probs]###除以分母
			alias_nodes[node] = alias_setup(normalized_probs)
		alias_edges = {}
		triads = {}
		if is_directed:
			for edge in G.edges():
				alias_edges[edge] = self.get_alias_edge(edge[0], edge[1])
		else:
			for edge in G.edges():
				alias_edges[edge] = self.get_alias_edge(edge[0], edge[1])
				alias_edges[(edge[1], edge[0])] = self.get_alias_edge(edge[1], edge[0])

		self.alias_nodes = alias_nodes
		self.alias_edges = alias_edges

		return

get_alias_edge是得到节点到节点的概率

def get_alias_edge(self, src, dst):####二阶ramdom walk
        #src是随机游走序列中的上一个节点,dst是当前节点
		'''
		Get the alias edge setup lists for a given edge.
		'''
		G = self.G
		p = self.p
		q = self.q

		unnormalized_probs = []
		for dst_nbr in sorted(G.neighbors(dst)):
			if dst_nbr == src:
				unnormalized_probs.append(G[dst][dst_nbr]['weight']/p)
			elif G.has_edge(dst_nbr, src):
				unnormalized_probs.append(G[dst][dst_nbr]['weight'])
			else:
				unnormalized_probs.append(G[dst][dst_nbr]['weight']/q)
		norm_const = sum(unnormalized_probs)
		normalized_probs =  [float(u_prob)/norm_const for u_prob in unnormalized_probs]

		return alias_setup(normalized_probs)

alias_setup :输入概率,得到对应的两组数,方便后面的抽样调用

def alias_setup(probs):
	'''
    alias_setup的作用是根据二阶random walk输出的概率变成每个节点对应两个数,被后面的alias_draw函数所进行抽样
	'''
	K = len(probs)
	q = np.zeros(K)
	J = np.zeros(K, dtype=np.int)

	smaller = []
	larger = []
	for kk, prob in enumerate(probs):
	    q[kk] = K*prob
	    if q[kk] < 1.0:
	        smaller.append(kk)
	    else:
	        larger.append(kk)##kk是下标,表示哪些下标小

	while len(smaller) > 0 and len(larger) > 0:
	    small = smaller.pop()##smaller自己也会减少最右边的值
	    large = larger.pop()

	    J[small] = large
	    q[large] = q[large] + q[small] - 1.0
	    if q[large] < 1.0:
	        smaller.append(large)
	    else:
	        larger.append(large)

	return J, q

alias_draw 抽样函数

def alias_draw(J, q):
	'''
	Draw sample from a non-uniform discrete distribution using alias sampling.
	'''
	K = len(J)

	kk = int(np.floor(np.random.rand()*K))
	if np.random.rand() < q[kk]:
	    return kk
	else:
	    return J[kk]

node2vec_walk就是对于给定的长度,对于开始节点开始模拟这个节点的路径,涉及的函数都在上面提及

def node2vec_walk(self, walk_length, start_node):
		'''
		Simulate a random walk starting from start node.
		'''
		G = self.G
		alias_nodes = self.alias_nodes
		alias_edges = self.alias_edges

		walk = [start_node]
      ######alias_draw这个函数是等于是根据二阶random walk概率选择下一个点
		while len(walk) < walk_length:
			cur = walk[-1]
			cur_nbrs = sorted(G.neighbors(cur))###G.neighbors(cur)得到cur一级关联的节点
			if len(cur_nbrs) > 0:
				if len(walk) == 1:
                ####cur[0]
					walk.append(cur_nbrs[alias_draw(alias_nodes[cur][0], alias_nodes[cur][1])])
				else:
					prev = walk[-2]
					next = cur_nbrs[alias_draw(alias_edges[(prev, cur)][0], 
						alias_edges[(prev, cur)][1])]
					walk.append(next)
			else:
				break

		return walk

结果:
在这里插入图片描述

最后总结一下node2vec的模型框架:通过二阶random walk计算每个节点到其他节点的概率,然后将这个概率输出为两组数,方便后面抽样调用。(具体原理可以看

The Alias Method: Efficient Sampling with Many Discrete Outcomes)

然后对于每个节点,设定每个节点被调用作为开始的次数,每次random walk的长度就可以得到一系列节点数排列。

最后将这些节点数排列视为文本,每个节点作为单词,导入word2vec中的Skipgram模型并用SGD进行优化,就得到每个节点的向量。

论文地址:https://arxiv.org/abs/1607.00653
代码地址:https://github.com/aditya-grover/node2vec
别名采样算法:https://hips.seas.harvard.edu/blog/2013/03/03/the-alias-method-efficient-sampling-with-many-discrete-outcomes/
别名采样算法:https://blog.csdn.net/lee813/article/details/51497012

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值