文章目录
- 1. 基于图的基础推荐方式
- 1.1 链路预测(Link Prediction)
- 1.2 什么是路径
- 1.3 基于路径的基础链路预测
- 1.4 图游走算法DeepWalk
- 1.4.1 Word2Vec
- 1.4.2 DeepWalk原理
- 1.4.3 DeepWalk代码示例
- 1.5 图游走算法Node2Vec
- 1.5.1 Node2Vec原理
- 1.5.2 Node2Vec公式
- 1.5.3 Node2Vec代码
- 2. 参考
1. 基于图的基础推荐方式
基于图的基础推荐方式,在了解一定图论的知识之后,我们可以利用图来做一些基础推荐任务。
1.1 链路预测(Link Prediction)
链路预测是一个利用图网络做预测的经典任务。所谓链路(Link)指节点与节点之间的连接,也就是图谱中的边。
所谓链路预测就是预测原本不相连的两个节点之间是否有边存在,如下图所示:
-
若是在有权图中预测,那就顺便预测一下相邻边的权重。
-
如果是一个社交网络图,则链路预测的任务就好比在预测某个用户对另一用户感兴趣,即好友推荐任务。
-
如果是一个用户物品图,则链路预测就是物品推荐任务。
链路预测本身是一门学科,已经有几十年的历史,推荐是它最主要的应用方向。如今链路预测总是不温不火。究其原因还是因为跳开它一样能做推荐,例如有的文献中提到基于近邻的链路预测,其实等同于基于近邻的协同过滤,而学习协同过滤不需要懂图论。且如今神经网络的兴起又直接导致链路预测中一些复杂的算法过时,因为图神经网络可以更有效的解决90%以上的链路预测问题。
但如果能摸清算法发展的来龙去脉,则对理解算法会有很大的帮助,所以我们还是来学一下比较基础的链路预测。
1.2 什么是路径
路径是从某一个节点到另一个节点之间经过的边与节点组成的子图,包含头尾节点,如下图:
上图中,由节点1开始游走,到到达节点4可以经过节点2或者节点3,所以节点1与节点4之间存在路径1–>2–>4和1–>3–>4这两条路径,而节点1到节点5只有1条路径,所以该路径是1–>5。
一条路径上的边数被称为路径的阶数。例如1–>2–>4和1–>3–>4属于二阶路径。1–>5属于一阶路径,所以又可以把节点2、3、5称为节点1的一阶邻居,节点4称为节点1的二阶邻居。
1.3 基于路径的基础链路预测
回顾下最简单的近邻指标,CN(Common Neighbors)相似度,公式:Sxy=|N(x)∩N(y)|
其中,N(x)在这里就表示节点x的邻居集,所以CN相似度是节点x的一阶邻居集与节点y的一阶邻居集的交集数量。
了解了路径后,可以发现两个节点一阶邻居集的交集数量其实等于他们之间的二阶路径数。
如下图:
节点1与节点4之间有交集节点2和3,有二阶路径1–>2–>4和1–>3–>4。
所以CN相似度公式在这样的条件下可以写成一个新的形式:Sxy=pxy(2)
其中,pxy(2)就代表节点x与节点y之间的二阶路径数。
可以用一张表记录所有节点与节点之间的二阶路径数:
该表也是所有节点之间的CN相似度矩阵,并且可被视作另一个有权图的邻接矩阵,中间的数字正是边的权重。通常被称为原图的二阶路径图。它的邻接矩阵记为A(2),所以原图中所有节点相似度矩阵S在目前计算环境下可写成S=A(2)
到此,我们便能将节点间的二阶路径数作为相似度指标,显然也可以将三阶路径甚至更多阶的路径数作为相似度指标。可以先来考虑三阶路径数的情况:
上图右边列出了从节点1出发的三阶路径。
结合节点1的二阶路径,统计表格:
假设定义相似度公式为:Sxy=pxy(2) + pxy(3)
则节点1与节点4、7、9的相似度均为3且是最高的。对于推荐任务来说,将节点4、7、9推荐给节点1即可。
虽然挺合理,但是总感觉哪里不太对劲。是的,不对劲的地方是凭什么三阶路径权重和二阶路径权重相等。如果将式子改为:
Sxy=pxy(2) + α*pxy(3)
就会好很多,在式子中加入α可以作为稀释高阶路径对相似度影响的权重。因为从常识来看,越遥远的距离自然应该影响越小。这个α可以用标注数据学出来,也可以作为超参自己设置。假设α为0.5,重新计算上述例子中节点1与各个节点的相似度表:
首先节点2、3、5本身就是节点1的一阶邻居,在链路预测中代表本身就有链路,所以不需要将节点2、3、5推荐给1做邻居。除此之外,可以看到在α=0.5的情况下,节点1与节点4的相似度>节点7>节点9,这似乎更有道理了。
所以如果要考虑所有路径的阶数,公式可以写成:
该公式是算法科学家Katz早在1953年提出的Katz相似度指标。写成矩阵形式:
该公式一眼看过去就知道计算量很大,所以之后又有很多新的算法。
1.4 图游走算法DeepWalk
DeepWalk算法的中心思想是在图中随机游走生成节点序列,之后用Word2Vec的方式得到节点Embedding,然后利用节点Embedding做下游任务,例如计算相似度排序得到近邻推荐。
1.4.1 Word2Vec
Word2Vec是经典的自然语言处理技术,于2013年被谷歌提出。
Word2Vec的目的是将词数字化后以向量表示,实现的手段
-
是用相邻的词去预测中间词(每个词都是由相邻的词决定的,CBOW模型的主要原理)。
-
或者,每个词都决定了相邻的词(Skip-gram模型的主要原理)。
如下图所示:
下面介绍CBOW模型结构:
例如有一句话由[w1,w2,w3,w4,w5,w6,w7,w8]组成的句子,则可以取w1,w2,w4,w5预测w3,然后滑动一下窗口接着用w2,w3,w5,w6预测一下w4。
所谓的预测就是取周围词随机初始化的Embedding,进行平均池化后与中心词Embedding进行点积,进行Softmax多分类的预测,类别是所有的候选词,然后反向传播更新周围词与中心词的Embedding,通过不断迭代最终得到每个词的词向量,如上图所示。
这么做的好处就是使每个词都由其周围词定义,从而保留了词与词之间的相关性,得到词向量可以计算它们的夹角余弦值来得到余弦相似度。
实现Word2Vec的Python工具库Gensim已经非常成熟,仅需要一句代码即可实现:
from gensim.models import word2vec
# s1=['今天','天气','真好']
# s2=['今天','天气','很好']
s1=[1,2,3]
s2=[1,2,4]
seqs = [s1,s2]
model = word2vec.Word2Vec(seqs, vector_size=10,min_count=1)
print(model.wv.most_similar(1,topn=3))
当然这个Word2Vec()函数也有很多的参数可以调整,例如vector_size是每个词向量的维度,min_count是训练语料中的最小词频,小于这个数字的词将忽略。其他参数也可以到Gensim的官网查看API。model.wv.most_similar(1,topn=3)
表示返回节点1相似度排序前3个的词。
1.4.2 DeepWalk原理
不难发现,上面的Word2Vec很像topN推荐。如果把每个“词”看作节点,则Word2Vec算法是在得到每个节点的Embedding之后,求取两两Embedding之间的余弦相似度,得到topN的近邻排序之后推荐给目标节点,所以在Word2Vec被提出1年之后,即2014年DeepWalk被提出。
DeepWalk指在一张图随机的游走,以便生成节点序列,然后利用这些节点序列以Word2Vec的方法生成Embedding。下面分别是有向图和无向图的随机游走:
在无向图的随机游走中,需要多考虑一个问题,即需不需要回头。如果回头是被允许的,则可能出现在两个节点之间反反复复的游走。
1.4.3 DeepWalk代码示例
import networkx as nx
import numpy as np
from tqdm import tqdm
from gensim.models import word2vec
def walkOneTime(g, start_node, walk_length):
walk = [str(start_node)] # 初始化游走序列
for _ in range(walk_length): # 最大长度范围内进行采样
current_node = int(walk[-1])
successors = list(g.successors(current_node)) # graph.successor: 获取当前节点的后继邻居
if len(successors) > 0:
next_node = np.random.choice(successors, 1)
walk.extend([str(n) for n in next_node])
else:
break
return walk
def getDeepwalkSeqs(g, walk_length, num_walks):
seqs=[]
for _ in tqdm(range(num_walks)):
start_node = np.random.choice(g.nodes)
w = walkOneTime(g,start_node, walk_length)
seqs.append(w)
return seqs
def deepwalk( g, dimensions = 10, walk_length = 80, num_walks = 10, min_count = 3 ):
seqs = getDeepwalkSeqs(g, walk_length = walk_length, num_walks = num_walks)
model = word2vec.Word2Vec(seqs, vector_size = dimensions, min_count = min_count)
return model
if __name__ == '__main__':
g = nx.fast_gnp_random_graph(n = 100, p = 0.5,directed = True) #快速随机生成一个有向图
model = deepwalk( g, dimensions = 10, walk_length = 20, num_walks = 100, min_count = 3 )
print(model.wv.most_similar('2',topn=3)) # 观察与节点2最相近的三个节点
model.wv.save_word2vec_format('e.emd') # 可以把emd储存下来以便下游任务使用
model.save('m.model') # 可以把模型储存下来以便下游任务使用
打印出来结果:
[('47', 0.9088792204856873), ('9', 0.880308985710144), ('48', 0.8693126440048218)]
这意味着与节点2最相似的三个节点是47、9、48。右边的数字是它们与节点2之间的余弦相似度,所以如果节点47、9、48本身不是节点2的一阶邻居,则在这个场景中就可以把它们推荐给节点2。
1.5 图游走算法Node2Vec
1.5.1 Node2Vec原理
Node2Vec在2016年发布,与DeepWalk的区别是多了控制游走方向的参数。
按照DeepWalk的思想,所有邻居节点游走的概率都是相等的,而Node2Vec可通过调整方向的参数来控制模型更倾向宽度优先的游走还是深度优先的游走。
- 宽度优先采样(Breadth-First Sampling,BFS)更能体现图网络的“结构性”,因为BFS生成的序列往往是由起始点周边组成的网络结构。这就能让最终生成的Embedding具备更多结构化的特征。
- 深度优先采样(Depth-First Sampling,DFS)更能体现图网络的“同质性”,因为DFS更有可能游走到当前节点远方的结点,所以生成的序列会具备更纵深的远端信息。
1.5.2 Node2Vec公式
在实际计算过程中,如何在数学上体现BFS和DFS呢?
首先Node2Vec整体的公式如下:
-
等号左边的表达是指从当前节点v走到下一节点x的概率。
-
E表示当前节点v所有的后继邻居节点。
-
Z是归一化常量。
-
πvx 是转换概率。例如在一个有向无权图的DeepWalk中,Z是当前节点后继邻居的数量,πvx 则等于1。
πvx 的计算公式:
- Wvx 是考虑有权图中边的权重
- αpq(t,x)在这里可以被认为是元转化概率。
αpq(t,x)的计算公式:
我们借助下面的图来说明上面的αpq(t,x)的计算公式,
dtx 是指t时刻也是下一游走的候选节点x的节点类型,有0、1、2三个枚举类型。
- 如果dtx=0,则代表该候选节点是前一时刻游走时的起始节点,是下图中节点t,往这个方向走就代表走回头路,而α=1/p。由此可见超参数p用于控制游走以多大概率回头。
- 如果dtx=1,则代表该候选节点x与前一时刻的起始节点t以及当前节点v是等距的,如下图中的x1节点,此时α=1。往这个方向游走时BFS宽度优先游走。
- 如果dtx=2,则代表其他,α=1/q。往此方向走就是DFS深度优先游走,所以q用于控制游走更偏向BFS还是DFS。
- 当q<1时,更倾向DFS。
- 当q>1时,更倾向BFS。
- 当q=1时,Node2Vec退化为DeepWalk。
1.5.3 Node2Vec代码
Node2Vec的代码实现仅仅需要再之前DeepWalk的代码基础上加上一些新逻辑,但是在实际工作中其实有专门的Python API可供调用。
安装方式是pip install node2vec
。其实也是基于Networkx与Gensim封装。利用node2vec API实现的Node2Vec代码如下:
import networkx as nx
from node2vec import Node2Vec
graph = nx.fast_gnp_random_graph(n=100, p=0.5)#快速随机生成一个无向图
node2vec = Node2Vec ( graph, dimensions=64, walk_length=30, num_walks=100, p=0.3,q=0.7,workers=4)#初始化模型
model = node2vec.fit()#训练模型
print(model.wv.most_similar('2',topn=3))# 观察与节点2最相近的三个节点
该库还能通过调成参数workers设置同时游走的线程数,下面是结果:
2. 参考
- 《动手学推荐系统——基于pytorch的算法实现》
- 《深度学习推荐系统》
- Katz L. A new status index derived from sociometric analysis[J]. Psychometrika, 1953, 18(1): 39-43.
- Perozzi B, Al-Rfou R, Skiena S. Deepwalk: Online learning of social representations[C]//Proceedings of the 20th ACM SIGKDD international conference on Knowledge discovery and data mining. 2014: 701-710.
- Grover A, Leskovec J. node2vec: Scalable feature learning for networks[C]//Proceedings of the 22nd ACM SIGKDD international conference on Knowledge discovery and data mining. 2016: 855-864.