1. 模型构造思路
本文的核心是将GNN模型给解耦了。
大概思路是 predictor → correct:平滑error → smooth:平滑标签
平滑的思路很像APPNP那篇。那篇我也写了笔记,可资参考:Re0:读论文 PPNP/APPNP Predict then Propagate: Graph Neural Networks meet Personalized PageRank
predictor可以用任一预测模型,所以这个模型也可以结合到别的GNN模型上。
别的,因为我没咋看懂这篇论文,所以不多赘述。
这篇论文原理上我主要有两大迷惑:
2. Notation和模型介绍
2.1 notation
2.2 模型介绍
我觉得模型有问题,以下我的理解都标色了
predictor + post-processing
不是端到端的训练,也就是说correct+smooth是不跟predictor一起train的。就C&S阶段就是直接加上去的。
不像APPNP,propagate阶段是跟着predict一起train的,是端到端的。
2.2.1 Predictor
2.2.2 Correct阶段
2.2.3 Smooth阶段
3. 详细的数学推导和证明
还没看,待补。
4. 实验结果
还没看,待补。
5. 代码实现和复现
5.1 论文官方实现
CUAI/CorrectAndSmooth: [ICLR 2021] Combining Label Propagation and Simple Models Out-performs Graph Neural Networks (https://arxiv.org/abs/2010.13993)
讲解待补。
5.2 PyG官方实现
CorrectAndSmooth类官方文档
源代码:torch_geometric.nn.models.correct_and_smooth — pytorch_geometric 1.7.2 documentation
作为卷都卷不动的post-processing模型,终于不用MessagePassing而正常用torch.nn.Module基类了。这是什么值得开心的事情吗?值得个屁,这个模型用了LabelPropagation类……
呀咩咯,我受够了看PyG这个大佬作者虽然很牛逼但是没有注释而且严重依赖他自己写的其他没有注释但是很牛逼的包的源码了,放过我吧!
所以还没看,待补。
5.3 我自己写的复现
def edge_index2sparse_tensor(edge_index,node_num): sizes=(node_num,node_num) v=torch.ones(edge_index[0].numel()).to(hp['device']) #边数 return torch.sparse_coo_tensor(edge_index, v, sizes) class CNS_self(torch.nn.Module): #参数照抄PyG了,意思也一样 def __init__(self,correct_layer,correct_alpha,smooth_layer,smooth_alpha,autoscale,scale): super(CNS_self,self).__init__() self.correct_layer=correct_layer self.correct_alpha=correct_alpha self.smooth_layer=smooth_layer self.smooth_alpha=smooth_alpha self.autoscale=autoscale self.scale=scale def correct(self,Z,Y,mask,edge_index): """ Z:base prediction Y:true label(第一维尺寸是训练集节点数) mask:训练集mask """ #将Y扩展为独热编码矩阵 Y=F.one_hot(Y) num_nodes=Z.size()[0] num_features=Z.size()[1] E=torch.zeros(num_nodes,num_features).to(hp['device']) E[mask]=Z[mask]-Y edge_index, _ = pyg_utils.add_self_loops(edge_index,num_nodes=num_nodes) #添加自环(\slide{A}) adj=edge_index2sparse_tensor(edge_index,num_nodes) degree_vector=torch.sparse.sum(adj,0) #度数向量 degree_vector=degree_vector.to_dense().cpu() degree_vector=np.power(degree_vector,-0.5) degree_matrix=torch.diag(degree_vector).to(hp['device']) adj=torch.sparse.mm(adj.t(),degree_matrix.t()) adj=adj.t() adj=torch.mm(adj,degree_matrix) adj=adj.to_sparse() x=E.clone() if self.autoscale==True: for k in range(self.correct_layer): x=torch.sparse.mm(adj,x) x=x*self.correct_alpha x=x+(1-self.correct_alpha)*E sigma=1/(mask.sum().item())*(E.sum()) Z=Z-x Z[~mask]=Z[~mask]-sigma*F.softmax(x[~mask],dim=1) else: for k in range(self.correct_layer): x=torch.sparse.mm(adj,x) x=x*self.correct_alpha x=x+(1-self.correct_alpha)*E x[mask]=E[mask] Z=Z-self.scale*x return Z def smooth(self,Z,Y,mask,edge_index): #将Y扩展为独热编码矩阵 Y=F.one_hot(Y) num_nodes=Z.size()[0] G=Z.clone() G[mask]=Y.float() edge_index, _ = pyg_utils.add_self_loops(edge_index,num_nodes=num_nodes) #添加自环(\slide{A}) adj=edge_index2sparse_tensor(edge_index,num_nodes) degree_vector=torch.sparse.sum(adj,0) #度数向量 degree_vector=degree_vector.to_dense().cpu() degree_vector=np.power(degree_vector,-0.5) degree_matrix=torch.diag(degree_vector).to(hp['device']) adj=torch.sparse.mm(adj.t(),degree_matrix.t()) adj=adj.t() adj=torch.mm(adj,degree_matrix) adj=adj.to_sparse() #adj就是S x=G.clone() for k in range(self.smooth_layer): x=torch.sparse.mm(adj,x) x=x*self.smooth_alpha x=x+(1-self.smooth_alpha)*G return x
5.4 复现实验结果对比
我在一次非常粗糙的实验之中超过了PyG的官方实现!
论文官方实现我都没看懂,暂无法实现
未完待续。
详见:APPNP和C&S复现