目录
- 1、论文简介
- 2、论文核心介绍
- 2.1、现有方法局限
- 2.2、PageRank&Personalized PageRank
- 2.3、PPNP&APPNP
- 3、源码复现
- 3.1、模型总体框架
- 3.2、PPNP
- 3.3、APPNP
- 3.4、MLP(两层)
1、论文简介
- 论文题目——《PREDICT THEN PROPAGATE: GRAPH NEURAL NETWORKS MEET PERSONALIZED PAGERANK》
- 论文作者——Johannes Klicpera, Aleksandar Bojchevski & Stephan Gu ̈nnemann
- 论文地址——PREDICT THEN PROPAGATE: GRAPH NEURAL NETWORKS MEET PERSONALIZED PAGERANK
- 源码——源码链接
2、论文核心介绍
2.1、现有方法局限
现有的方法,仅仅使用了局部有限的邻域信息,更大的邻域信息并没有考虑到。例如,GCN,它采用平均的方法来聚合一阶邻域信息,通过堆叠多层来考虑到更高阶的邻域信息(论文中实际是两层);GAT则是采用注意力机制来学习不同邻域结点信息对当前结点的重要性,也就是说它是对周围邻域结点信息的加权平均。上述方法仍然是浅层的网络,并没有利用到深层邻域信息。
现有方法的另外一个缺点就是过平滑现象(oversmoothing),这也是GCN不能堆叠多层的原因所在。另有作者,通过建立GCN和随机游走(random walk)的关系,发现当GCN的层数增加,GCN会收敛到随机游走的极限分布,会使不同类的结点之间变得不可分,导致GCN性能下降。
为了解决上述的问题,作者提出了一个新的传播方案,这个方案的灵感来自于个性化PageRank算法,它平衡了局部邻域信息与更大的邻域信息的需要,允许更多的传播步骤而不会导致过平滑现象。此外,作者将神经网络从信息传播中分开来,允许去实现更大范围的传播而不用改变神经网络结构,由于这种特性,也可以将SOTA预测方法与文中的传播方案进行融合。
2.2、PageRank&Personalized PageRank
PageRank算法通过网页链接重要性得分计算。重要性可认为是网页链接点击。PageRank算法给定一个概率值,定义为网页访问的概率。一般地,
1
N
\frac{1}{N}
N1 表示为每个网页节点初始化的概率,
P
R
\rm{PR}
PR也是一个初始化的概率值。PageRank 是一个迭代算法,因此
P
R
\rm{PR}
PR 值初始化
1
N
\frac{1}{N}
N1 ,
N
N
N表示为节点的数量。
P
R
\rm{PR}
PR值的总和一般为1,当
P
R
{\rm{PR}}
PR越大,说明重要性越大。
给定节点
v
v
v,求节点
v
v
v的
P
R
{\rm{PR}}
PR值,
P
R
(
v
)
=
∑
u
∈
N
v
P
R
(
u
)
O
(
u
)
PR(v) = \sum_{u \in \mathcal{N}_v }\frac{PR(u)}{O(u)}
PR(v)=u∈Nv∑O(u)PR(u)
N
v
\mathcal{N}_v
Nv表示所有链接到节点
v
v
v的集合。
O
(
u
)
O(u)
O(u)表示节点
u
u
u的对外链接数。最早提出的PageRank算法存在着一些缺点,例如当一些节点存在自链接,或者是一些节点的出链节点形成循环圈时,PageRank在迭代过程中会出现
P
R
{\rm{PR}}
PR持续增大,不会减小的情况。对于上述问题,PageRank算法被重新进行改进
P
R
(
v
)
=
α
∑
u
∈
N
v
P
R
(
u
)
O
(
u
)
+
(
1
−
α
)
N
\mathrm{PR(v)=}\alpha\sum_{\mathrm{u}\in\mathcal{N}_v}\frac{\mathrm{PR(u)}}{\mathrm{O(u)}}+\frac{(1-\alpha)}{\mathrm{N}}
PR(v)=αu∈Nv∑O(u)PR(u)+N(1−α)
α
\alpha
α是一个超参数,取值一般为0.85。
α
\alpha
α表示节点跳转时的概率,不依据节点之间的链接进行跳转。
PageRank算法衍生出的模型个性化的PageRank算法,主要利用图中节点的链接关系来迭代计算节点的权重。PageRank算法使用随机游走的策略来访问图中节点。PageRank算法与个性化Page Rank算法的区别在于随机游走时的跳转行为不同。个性化的PageRank算法对跳转行为进行约束,指定调转到的对外链接为特定的节点。例如在个性化排序时,用户只能跳转到一些特定的节点,这些节点表示用户偏好的那些节点。
PPR
′
(
v
)
=
α
∑
u
∈
N
v
P
R
(
u
)
O
(
u
)
+
(
1
−
α
)
r
v
\text{PPR}^{'}(\mathrm{v})=\alpha\sum_{\mathrm{u}\in\mathcal{N}_v}\frac{\mathrm{PR(u)}}{\mathrm{O(u)}}+(1-\alpha)\mathrm{r}_\mathrm{v}
PPR′(v)=αu∈Nv∑O(u)PR(u)+(1−α)rv
r
v
=
{
1
v
=
u
0
v
≠
u
\mathrm r_\mathrm{v}=\begin{cases}1&\mathrm{~v=u}\\0&\mathrm{~v\neq u}\end{cases}
rv={10 v=u v=u
个性化PageRank算法中,用户的偏好表示为
r
∣
v
∣
=
1
\mathrm r|\mathrm{v}| = 1
r∣v∣=1,原始的PageRank采用的计算方式为
Π
p
r
=
A
r
w
Π
p
r
\Pi_{pr} = A_{rw}\Pi_{pr}
Πpr=ArwΠpr,
Π
p
r
是
A
r
w
\Pi_{pr}是A_{rw}
Πpr是Arw的特征向量,
A
r
w
=
A
D
−
1
A_{rw}=AD^{-1}
Arw=AD−1。类似的,个性化的PageRank 算法可以表示为
Π
p
p
r
(
i
x
)
=
(
1
−
α
)
A
~
Π
p
p
r
(
i
x
)
+
α
i
x
\Pi_{\mathrm{ppr}}(\mathbf{i_x})=(1-\alpha)\tilde{{A}}\Pi_{\mathrm{ppr}}(\mathbf{i_x})+\alpha\mathbf{i_x}
Πppr(ix)=(1−α)A~Πppr(ix)+αix
参考连接
2.3、PPNP&APPNP
上一节,我们知道了Personalized PageRank算法及其他的表达式,对上式进行求解,求得
Π
p
p
r
\Pi_{\mathrm{ppr}}
Πppr为
Π
p
p
r
(
i
x
)
=
α
(
I
n
−
(
1
−
α
)
A
~
)
−
1
i
x
\Pi_{\mathrm{ppr}}(\mathbf{i_{x}})=\alpha(\mathbf{I_n}-(1-\alpha)\tilde{\mathbf{A}})^{-1}\mathbf{i_{x}}
Πppr(ix)=α(In−(1−α)A~)−1ix
其中,
A
~
=
D
~
−
1
2
A
^
D
~
−
1
2
,
A
^
=
A
+
I
,
i
x
是传送向量
\tilde{A}=\tilde{D}^{-\frac{1}{2}}\hat{A}\tilde{D}^{-\frac{1}{2}},\hat{A} = A+I,\mathrm{i_x}是传送向量
A~=D~−21A^D~−21,A^=A+I,ix是传送向量。最终的PPNP算法公式表达如下:
Z
p
p
n
p
=
s
o
f
t
m
a
x
(
α
(
I
n
−
(
1
−
α
)
A
~
)
−
1
H
)
Z_{\mathrm{ppnp}} = \mathrm{softmax}(\alpha(\mathbf{I_n}-(1-\alpha)\tilde{\mathbf{A}})^{-1}\mathbf{H})
Zppnp=softmax(α(In−(1−α)A~)−1H)
H
i
,
:
=
f
θ
(
X
i
,
:
)
\mathbf{H}_{i,:} = f_{\theta}(\mathbf{X}_{i,:})
Hi,:=fθ(Xi,:)
其中
X
\mathbf{X}
X是特征向量矩阵,
f
θ
f_{\theta}
fθ是具有参数集合
θ
\theta
θ的神经网络,
H
∈
R
n
×
c
\mathbf{H} \in R^{n \times c}
H∈Rn×c。
由于在计算上式的时候,需要求矩阵的逆运算,这是一个耗时的操作,为了加速PPNP的训练速度,作者采用一种近似操作来求解,称为APPNP。
Z
(
0
)
=
H
=
f
θ
(
X
)
,
Z
(
k
+
1
)
=
(
1
−
α
)
A
~
Z
(
k
)
+
α
H
,
Z
(
K
)
=
s
o
f
t
m
a
x
(
(
1
−
α
)
A
~
Z
(
K
−
1
)
+
α
H
)
Z^{(0)}=H=f_\theta(\mathbf{X}),\\ Z^{(k+1)} =(1-\alpha)\tilde{A}Z^{(k)}+\alpha H,\\ Z^{(K)}=\mathrm{softmax}((1-\alpha)\tilde{A}Z^{(K-1)}+\alpha H)
Z(0)=H=fθ(X),Z(k+1)=(1−α)A~Z(k)+αH,Z(K)=softmax((1−α)A~Z(K−1)+αH)
其中,
K
K
K是迭代次数。作者也在后面的附录中也证明了APPNP当
K
⟶
∞
K \longrightarrow \infty
K⟶∞时,收敛到PPNP,所以APPNP可以看作PPNP的迭代解。
模型的框架如下图所示:
3、源码复现
模型复现源码链接链接:点我点我 提取码:6666
3.1、模型总体框架
import torch
from torch.nn import Module
import torch.nn as nn
from torch.nn import functional as F
import numpy as np
class PPNP(nn.Module):
def __init__(self,model,propagation):
super(PPNP,self).__init__()
self.model = model
self.propagation = propagation
def forward(self,feature,adj):
#Generate Prediction
#用于生成预测
if self.model.__class__.__name__ =='MLP':
output = self.model(feature)
else:
output = self.model(feature,adj)
#通过个性化PageRank传播
if self.propagation is not None:
output = self.propagation(output)
#返回最后一层的结果
return F.log_softmax(output,dim=1)
3.2、PPNP
class PPNPExtract(Module):
def __init__(self,alpha,adj,dropout):
super(PPNPExtract,self).__init__()
self.alpha = alpha
self.adj = adj
self.dropout = dropout
pass
def forward(self,H):
inv = self.PPR()
inv = F.dropout(inv,self.dropout,training=self.training)
return self.alpha * torch.mm(inv,H)
def PPR(self):
if isinstance(self.adj,torch.Tensor):
ADJ = self.adj.to_dense().numpy()
I_n = np.eye(self.adj.shape[0])
M = I_n-(1-self.alpha)*ADJ
inv_M = np.linalg.inv(M)
return torch.Tensor(inv_M)
3.3、APPNP
class PowerIteration(Module):
def __init__(self,adj,alpha,k,dropout):
super(PowerIteration,self).__init__()
self.adj = adj
self.alpha = alpha
self.k = k
self.dropout = dropout
def forward(self,H):
Z = H
for _ in range(self.k):
Z = F.dropout(Z,self.dropout,training=self.training)
Z = (1-self.alpha)*torch.mm(self.adj,Z) + self.alpha * H
return Z
3.4、MLP(两层)
class MLP(Module):
def __init__(self,input_dim,hid_dim,output_dim,dropout):
super(MLP,self).__init__()
self.input_dim = input_dim
self.hid_dim = hid_dim
self.output_dim = output_dim
self.dropout = dropout
self.layer1 = nn.Linear(input_dim,hid_dim,bias=False)
self.layer2 = nn.Linear(hid_dim,output_dim,bias=False)
def forward(self,X):
X = F.dropout(X,self.dropout,training=self.training)
X = self.layer1(X)
X = F.relu(X)
X = F.dropout(X,self.dropout,training=self.training)
X = self.layer2(X)
return X
def __repr__(self) -> str:
return self.__class__.__name__