VGAE

本篇文章是关于VGAE的介绍,VGAE是变分自编码器再图神经网络上的应用

  • 图变分自编码器
  • 就是将变分自编码器用到了图上
  • 是一种无监督学习
  • 目的是重构误差最小
  • 损失函数的设计和VAE的一致,考虑的是极大似然估计

论文考虑的是无向无权重图,G=V,E\mathcal{G}={\mathcal{V},\mathcal{E}}N=VN=\mathcal{V}表示节点数量。这里的邻接矩阵A有点不一样的是,这里考虑到了自连接也就是A的对角线不为1,为0。(这里为什么要考虑自连接了

对于VAE中采用的是神经网络来拟合隐变量的方差和均值——这里隐变量的数量和样本(节点)数量是一致的。
对于VGAE而言论文利用了两层图卷积网络来拟合均值和方差。

推断模型

q(ZX,A)=i=1Nq(ziX,A),with    q(ziX,A)=N(ziμi,diag(σi2))q(Z|X,A)=\prod_{i=1}^{N}q(z_i|X,A),with\ \ \ \ q(z_i|X,A)=\mathcal{N}(z_i|\mu_i,diag(\sigma_i^2))

其中μ=GCNμ(X,A)\mu=GCN_{\mu}(X,A)表示均值向量μ\mu的矩阵。同理论文对方差的拟合用的是logσ=GCNσ(X,A)\log \sigma=GCN_{\sigma}(X,A)

其中GCN(X,A)GCN(X,A)的公式如下:

GCN(X,A)=A^RELU(A^XW0)W1GCN(X,A)=\hat ARELU(\hat AXW_0)W_1

其中对于A^=D12AD12\hat A=D^{-\frac{1}{2}}AD^{-\frac{1}{2}}对称归一化邻接矩阵。

生成模型:是由潜在变量的内积给出来的

P(AZ)=i=1Nj=1NP(Aijzi,zj),with   P(Aijzi,zj)=σ(ziTzj)P(A|Z)=\prod_{i=1}^N\prod_{j=1}^{N}P(A_{ij}|z_i,z_j),with \ \ \ P(A_{ij}|z_i,z_j)=\sigma(z_i^Tz_j)

学习目标

  • L=Eq(ZX,A)[logp(AZ)]KL[q(ZX,A)p(Z)]\mathcal{L}=\mathbb{E}_{q(Z|X,A)}[\log p(A|Z)]-KL[q(Z|X,A)||p(Z)]

Non-probabilistic graph auto-encoder (GAE) model(这里因该是一般的GAE的形式)

A^=σ(ZZT),with   Z=GCN(X,A)\hat A=\sigma(ZZ^{T}),with\ \ \ Z=GCN(X,A)

一些参考

一文理解变分自编码器(VAE)

VGAE(Variational graph auto-encoders)论文详解

深度学习中常见的互信息的变分上下界(详细推导)

【GNN五大类 VGAE】(变分图自编码器):Variational Graph Auto-Encoders
第六周.02.VGAE带读+代码实操

CS224W摘要09.Theory of Graph Neural Networks

机器学习方法—优雅的模型(一):变分自编码器(VAE)

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

class VGAE(nn.Module):#最终Z的维度是(N,hidden2_dim),其中N表示节点数
def __init__(self, adj):#唯一的参数是邻接矩阵
super(VGAE,self).__init__()
self.base_gcn = GraphConvSparse(args.input_dim, args.hidden1_dim, adj)#第一个权重是共享的
self.gcn_mean = GraphConvSparse(args.hidden1_dim, args.hidden2_dim, adj, activation=lambda x:x)#用于求均值 μ
self.gcn_logstddev = GraphConvSparse(args.hidden1_dim, args.hidden2_dim, adj, activation=lambda x:x)#用于log σ

def encode(self, X):
hidden = self.base_gcn(X)
self.mean = self.gcn_mean(hidden)
self.logstd = self.gcn_logstddev(hidden)
gaussian_noise = torch.randn(X.size(0), args.hidden2_dim)
sampled_z = gaussian_noise*torch.exp(self.logstd) + self.mean#这里是重参数技巧Z=μ+σ*ε
return sampled_z

def forward(self, X):#X是特征矩阵
Z = self.encode(X)#在这里调用了encode,然后当使用VGAE时会自动调用forward,因此会顺便调用encode这个函数
A_pred = dot_product_decode(Z)#这里是最后的点成,即生成模型
return A_pred

class GraphConvSparse(nn.Module):
def __init__(self, input_dim, output_dim, adj, activation = F.relu, **kwargs):
super(GraphConvSparse, self).__init__(**kwargs)
self.weight = glorot_init(input_dim, output_dim)
self.adj = adj
self.activation = activation

def forward(self, inputs):
x = inputs
# print(self.weight.dtype)
x = torch.mm(x,self.weight)#torch.mm是矩阵乘法
x = torch.mm(self.adj, x)
outputs = self.activation(x)
return outputs


def dot_product_decode(Z):#这里是sigmoid函数乘法
A_pred = torch.sigmoid(torch.matmul(Z,Z.t()))#torch.matmul是矩阵乘法
return A_pred

def glorot_init(input_dim, output_dim):#用于参数的初始化
init_range = np.sqrt(6.0/(input_dim + output_dim))
initial = torch.rand(input_dim, output_dim)*2*init_range - init_range#这里有广播机制
return nn.Parameter(initial)