本篇文章是关于VGAE的介绍,VGAE是变分自编码器再图神经网络上的应用
- 图变分自编码器
- 就是将变分自编码器用到了图上
- 是一种无监督学习
- 目的是重构误差最小
- 损失函数的设计和VAE的一致,考虑的是极大似然估计
论文考虑的是无向无权重图,G=V,E,N=V表示节点数量。这里的邻接矩阵A有点不一样的是,这里考虑到了自连接也就是A的对角线不为1,为0。(这里为什么要考虑自连接了)
对于VAE中采用的是神经网络来拟合隐变量的方差和均值——这里隐变量的数量和样本(节点)数量是一致的。
对于VGAE而言论文利用了两层图卷积网络来拟合均值和方差。
推断模型
q(Z∣X,A)=i=1∏Nq(zi∣X,A),with    q(zi∣X,A)=N(zi∣μi,diag(σi2))
其中μ=GCNμ(X,A)表示均值向量μ的矩阵。同理论文对方差的拟合用的是logσ=GCNσ(X,A)
其中GCN(X,A)的公式如下:
GCN(X,A)=A^RELU(A^XW0)W1
其中对于A^=D−21AD−21对称归一化邻接矩阵。
生成模型:是由潜在变量的内积给出来的
P(A∣Z)=i=1∏Nj=1∏NP(Aij∣zi,zj),with   P(Aij∣zi,zj)=σ(ziTzj)
学习目标
- 
L=Eq(Z∣X,A)[logp(A∣Z)]−KL[q(Z∣X,A)∣∣p(Z)] 
Non-probabilistic graph auto-encoder (GAE) model(这里因该是一般的GAE的形式)
A^=σ(ZZT),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
| 12
 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)
 
 
 
 
 
 |