本篇文章是关于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
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)
|