node2vec: Scalable Feature Learning for Networks
Github整理代码链接,欢迎讨论和交流,觉得有用的可以Star一下。
1.主要思想
目的:是将图中的顶点映射到一个低维连续空间,并且能够保存图的空间结构。
论文的主要亮点,设计了与Deepwalk不同的随机游走方式,每次随机游走会选择是进行宽度游走Breadth-first Sampling (BFS)还是深度游走Depth-first Sampling (DFS) ,如图1所示 。
获得游走路径集合后,与Deepwalk类似使用Skip-Gram模型进行顶点的Embedding学习
![](https://i-blog.csdnimg.cn/blog_migrate/f5dfc8fcf9dd84fb981518a7af8722e7.png)
2.流程
2.1 优化目标
设 f ( u ) f(u) f(u)是将顶点 u u u映射为Embedding向量的映射函数, N s ( u ) N_{s}(u) Ns(u)为通过采样策略 S S S采样出的顶点 u u u的近邻顶点集合。
将现有的Skip-gram模型扩展到网络中来,优化以下目标函数
优化目标函数:给定一个顶点,令其近邻顶点出现的概率最大
max
f
∑
u
∈
V
l
o
g
P
r
(
N
s
(
u
)
∣
f
(
u
)
)
\mathop {\max }\limits_{f}\sum_{u\in V}logPr(N_s(u)|f(u))
fmaxu∈V∑logPr(Ns(u)∣f(u))
为了使公式1能够得到很好的优化求解,提出了两个假设:
1.条件独立
给定一个顶点,其近邻顶点出现的概率与近邻集合中的其他顶点无关,得到公式2:
P
r
(
N
s
(
u
)
∣
f
(
u
)
)
=
∏
n
i
∈
N
s
(
u
)
P
r
(
n
i
∣
f
(
u
)
)
Pr(N_s(u)|f(u))=\prod_{n_i\in N_s(u)}Pr(n_i|f(u))
Pr(Ns(u)∣f(u))=ni∈Ns(u)∏Pr(ni∣f(u))
2.特征空间中的对称性
源顶点和近邻顶点在特征空间中具有对称性,不管顶点是源顶点还是近邻点Embedding表达是一样的。(对比Line中的二阶相似度有所不同),得到公式3:
P
r
(
n
i
∣
f
(
u
)
)
=
e
x
p
(
f
(
n
i
)
⋅
f
(
u
)
)
∑
v
∈
V
e
x
p
(
f
(
v
)
⋅
f
(
u
)
)
Pr(n_i|f(u))=\frac{exp(f(n_i)\cdot f(u))}{\sum_{v\in V}exp(f(v)\cdot f(u))}
Pr(ni∣f(u))=∑v∈Vexp(f(v)⋅f(u))exp(f(ni)⋅f(u))
根据以上两个假设条件,最终的目标函数公式1简化为公式4,由于
Z
u
=
∑
v
∈
V
e
x
p
(
f
(
n
i
)
⋅
f
(
u
)
)
Z_u=\sum_{v\in V}exp(f(n_i)\cdot f(u))
Zu=∑v∈Vexp(f(ni)⋅f(u))计算复杂度高,所以采用负采样近似优化
max
f
∑
u
∈
V
[
−
l
o
g
Z
u
+
∑
n
i
∈
N
s
(
u
)
f
(
n
i
)
⋅
f
(
u
)
]
\mathop {\max }\limits_{f}\sum_{u\in V}[-logZ_u+\sum_{n_i\in N_s(u)}f(n_i)\cdot f(u)]
fmaxu∈V∑[−logZu+ni∈Ns(u)∑f(ni)⋅f(u)]
2.2 采样策略
2.2.1 路径采样策略
Node2vec依然延续了Deepwalk采用随机游走的方式获取顶点的近邻序列,Node2vec不同的是采用的是一种有偏的随机游走。
给定当前顶点
v
v
v ,访问下一个顶点
x
x
x 的概率为公式5,其中
π
v
x
\pi_{vx}
πvx为顶点
v
v
v和顶点
x
x
x为归一化的转移概率,
Z
Z
Z为一个常量,设
π
v
x
=
α
p
q
(
t
,
x
)
⋅
w
v
x
\pi_{vx}=\alpha_{pq}(t,x)\cdot w_{vx}
πvx=αpq(t,x)⋅wvx,
α
p
q
(
t
,
x
)
\alpha_{pq}(t,x)
αpq(t,x)是
t
t
t到
x
x
x的游走策略生成的概率,
w
v
x
w_{vx}
wvx是顶点
v
v
v和顶点
x
x
x之间的边权。
P
(
c
i
=
x
∣
c
i
−
1
=
v
)
=
{
π
v
x
Z
(
v
,
x
)
∈
V
0
(
v
,
x
)
∉
V
P(c_i=x|c_{i-1}=v)=\left\{\begin{matrix} \frac{\pi_{vx}}{Z} &(v,x)\in V \\ 0 & (v,x)\notin V \end{matrix}\right.
P(ci=x∣ci−1=v)={Zπvx0(v,x)∈V(v,x)∈/V
Node2vec引入两个超参数
p
p
p和
q
q
q来控制随机游走的策略。如图2所示,假设当前随机游走顶点
t
t
t经过边
(
t
,
v
)
(t,v)
(t,v) 到达顶点
v
v
v,顶点
v
v
v的下一个访问顶点
x
x
x的概率根据公式6计算得到,公式6中
d
t
x
d_{tx}
dtx为下一个访问顶点
x
x
x和当前顶点
v
v
v的上一个顶点
t
t
t之间的距离
α
p
q
(
t
,
x
)
=
{
1
p
d
t
x
=
0
1
d
t
x
=
1
1
q
d
t
x
=
2
\alpha_{pq}(t,x)=\left\{\begin{matrix} \frac{1}{p}& d_{tx}=0 \\ 1&d_{tx}=1\\ \frac{1}{q}&d_{tx}=2 \end{matrix}\right.
αpq(t,x)=⎩⎨⎧p11q1dtx=0dtx=1dtx=2
- 返回参数
p
p
p
仅作用于 d t x = 0 d_{tx}=0 dtx=0的情况,控制顶点 v v v重复访问上一步顶点的概率,如果 p p p较大,则下一步游走访问上一步顶点的概率越小,反之越大。 - 输入输出参数
q
q
q
仅作用于 d t x = 2 d_{tx}=2 dtx=2的情况,控制顶点 v v v随机游走下一步的游走策略类型,BFS和DFS
- Breadth-first Sampling (BFS)
如果 q q q较大,则顶点 v v v下一步游走倾向于访问顶点 v v v上一步顶点近邻顶点,构成了宽度游走策略 - Depth-first Sampling (DFS)
如果 q q q较小,则顶点 v v v下一步游走倾向于访问顶点 v v v上一步顶点距离远的顶点,构成了深度游走策略
2.2.2 顶点采样策略
顶点采样策略区别于Deepwalk,不是随机采样顶点,使用Alias Sample方法进行采样。Alias Sample具体实现方式见Line论文整理3.3.4。本文中Alias Sample与Line中不同之处在于创建邻居顶点的Node Alias Sample Table,而Line中是创建的全局Node Alias Sample Table。
3.向量学习
通过带策略的随机游走获得路径集合,根据所获得的路径集合,与Deepwalk类似使用Skip-Gram模型进行顶点的Embedding学习。具体Skip-Gram模型见Deepwalk论文整理。
4.代码实现和实验
4.1 游走策略代码
Node2Vec中Edge Alias Sample根据公式6计算,给定一个顶点src,计算src的邻居顶点访问概率,为了在随机游走下一个顶点中使用;Node Alias Sample根据顶点的出度归一化生成概率计算得到
# _*_ coding: utf-8 _*_
"""
Time: 2020/9/9 17:50
Author: Cheng Ding(Deeachain)
Version: V 0.1
File: node2vec.py
Describe: Write during the internship at Hikvison, Github link: https://github.com/Deeachain/GraphEmbeddings
"""
# Edge Alias Sample
def get_alias_edge(self, 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: # dx=0
unnormalized_probs.append(G[dst][dst_nbr]['weight'] / p)
elif G.has_edge(dst_nbr, src): # dx=1
unnormalized_probs.append(G[dst][dst_nbr]['weight'])
else: # dx=2
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)
'''
Node Alias Sample
'''
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)
- 游走策略代码
# _*_ coding: utf-8 _*_
"""
Time: 2020/9/9 17:50
Author: Cheng Ding(Deeachain)
Version: V 0.1
File: node2vec.py
Describe: Write during the internship at Hikvison, Github link: https://github.com/Deeachain/GraphEmbeddings
"""
def node2vec_walk(self, walk_length, start_node):
G = self.G
alias_nodes = self.alias_nodes
alias_edges = self.alias_edges
walk = [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: # 路径中只有一个顶点时,从邻居顶点中根据边权生成概率,使用alias采样下一个顶点,不直接使用第二种情况采样的原因是为了构成采样策略
walk.append(
cur_nbrs[alias_sample(alias_nodes[cur][0], alias_nodes[cur][1])])
else: # 路径中大于一个顶点时,根据邻居顶点p和q的采样策略生成概率,使用alias采样下一个顶点
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:
break
return walk
4.2 实验
Node2Vec源码[1]中仅使用karate.edges数据集,该数据集是关于跆拳道俱乐部成员之间关系的社交网络,节点没有标签,作者源码没有复现原文中的数据集。整理代码采用了另外的三个公开数据集,分别是:Cora数据集由机器学习论文组成,总共有2708篇论文,应用关系有5429个,论文总共七类(基于案例、遗传算法、神经网络、概率方法、强化学习、规则学习、理论);DBLP数据集也是引文网络组成的图,只选用了4类;BlogCatalog数据集是Blog用户之间关系构成的社交网络,相关详细参数见表1
数据集 | cora | dblp | BlogCatalog |
---|---|---|---|
V | 2708 | 17725 | 10312 |
E | 5429 | 105781 | 333983 |
Class | 7 | 4 | 39 |
实验参数:
- Deepwalk:词向量维度 d = 128 d=128 d=128、每个顶点游走路径数 γ = 50 \gamma=50 γ=50、游走路径长度 t = 20 t=20 t=20、 E p o c h = 5 Epoch=5 Epoch=5、SkipGram窗口大小 w = 10 w=10 w=10
- Line:词向量维度
d
=
128
d=128
d=128、二阶相似度生成128维词向量、
E
p
o
c
h
=
150
Epoch=150
Epoch=150、
l
r
=
0.005
lr=0.005
lr=0.005、
n
u
m
_
n
e
g
a
t
i
v
e
=
5
num\_negative=5
num_negative=5。(目前Line模型存在一定问题,不能复现论文效果。)
- Cora数据集有向图实验,一阶相似度:节点分类 f 1 = 0.44 f1=0.44 f1=0.44,可视化能区分一部分节点,;二阶相似度节点分类 f 1 = 0.52 f1=0.52 f1=0.52,,可视化能区分一部分节点
- COra数据集无向图实验,一阶相似度:节点分类 f 1 = 0.48 f1=0.48 f1=0.48,可视化能区分一部分节点,;二阶相似度节点分类 f 1 = 0.62 f1=0.62 f1=0.62,,可视化能区分一部分节点
- Node2vec:词向量维度 d = 128 d=128 d=128、返回参数 p = 0.25 p=0.25 p=0.25、输入输出参数 q = 0.25 q=0.25 q=0.25( p 、 q ∈ ( 0.25 , 0.5 , 1 , 2 , 4 ) p、q\in ({0.25,0.5,1,2,4}) p、q∈(0.25,0.5,1,2,4)文中实验证明 p p p和 q q q越小越好)、每个顶点游走路径数 γ = 50 \gamma=50 γ=50、游走路径长度 t = 20 t=20 t=20、 E p o c h = 5 Epoch=5 Epoch=5、SkipGram窗口大小 w = 10 w=10 w=10
Node2Vec通过带偏策略的游走得到路径集合,接着SkipGram模型学习顶点的Embedding,最后使用Embedding设计顶点分类任务。数据集划分80%的数据用于训练,20%用于评估,分类任务使用SVM分类,得到顶点的分类指标f1-score如表2所示。
cora | dblp | |
---|---|---|
Deepwalk(f1-micro) | 0.8542 | 0.8327 |
Line(f1-micro) | 0.6218 | 0.6262 |
Node2vec(f1-micro) | 0.8561 | 0.8386 |
节点Embedding表达在空间维度上,相同类别的节点距离会越靠近,最后将学习得到的Embedding进行可视化展示。可视化使用的是TSNE实现,将向量降维到二维平面上。可视化效果如图3所示。
参考和引用
[1]. 论文源码