【源码复现】《Simple and Deep Graph Convolutional Networks》——GCNII模型

目录

  • 1、论文简介
  • 2、论文核心介绍
    • 2.1、研究动机
    • 2.2、创新点
    • 2.3、具体实现
      • 2.3.1、初始残差链接
      • 2.3.2、恒等映射
      • 2.3.3、模型
  • 3、源码复现
    • 3.1、Torch复现
    • 3.2、DGL复现

1、论文简介

  • 论文题目——《Simple and Deep Graph Convolutional Networks》
  • 论文作者——Ming Chen,Zhewei Wei ,Zengfeng Huang,Bolin Ding&Yaliang Li
  • 论文地址——Simple and Deep Graph Convolutional Networks》
  • 源码———论文源码链接

2、论文核心介绍

2.1、研究动机

 图神经网络是一种强大的处理图结构数据的有效学习方法。图神经网络及其变体在真实数据集上显示出了优异的性能。但目前的大多数GCN模型都很浅,GCN和GAT都是在两层的模型上实现最佳性能。浅层的网络结构限制了他们从高阶邻居中提取信息的能力。
 然而,堆叠多层和添加非线性往往会降低模型的性能,出现过平滑的现象,原因是随着堆叠层数的增加,GCN中节点的表示会收敛到某个值,因此变得无法区分。
 那么,那图神经网络真的可以达到深层吗?答案是肯定的!

2.2、创新点

 在传统GCN的基础上,引入两个简单的技术:初始剩余连接和恒等映射来真正实现深层的图卷积神经网络。

  • 初始残差连接是将 H ( l ) H^{(l)} H(l)与第一层 H ( 0 ) H^{(0)} H(0)的初始残差连接相结合;
  • 增加了一个恒等式映射 I n I_n In到第 l l l层权重矩阵 W ( ℓ ) W^{(ℓ)} W()中。

2.3、具体实现

 GCNII模型灵感来自于2016年计算机视觉领域解决相似问题的ResNet模型,ResNet模型采用残差连接使模型可以训练更深层的网络。同样采用初始残差的APPNP模型,采用线性聚合方式很难达到深层,说明仅仅在图神经网络模型中添加残差连接是不充分的,并不能使图神经网络引向更深的模型。为此,GCNII模型采用初始残差连接和恒等映射两个技巧,可以达到深层的模型而不会导致过平滑的现象。

2.3.1、初始残差链接

 为了模拟ResNet中的残差连接,GCN提出了将平滑表示 P H ( ℓ ) \mathrm{PH^{(ℓ)}} PH() H ( ℓ ) \mathrm{H^{(ℓ)}} H()相结合的残差连接,但是这种残差连接并没有实质上解决过平滑的问题,随着网络层数的增多,模型的性能仍会降低。
 因此,GCNII模型中没有使用上一层的残差连接,而是初始表 H ( 0 ) \mathrm{H^{(0)}} H(0),称为初始残差连接。这样即使堆叠了很多层,初始剩余连接确保每个节点的最终表示都至少保留输入层的一小部分信息,作者简单将 α l \alpha_{l} αl设置为0.1或0.2,以便每个节点的最终表示都至少包含输入特征的一小部分信息。
 值得注意的是, H ( 0 ) \mathrm{H^{(0)}} H(0)不一定是特征矩阵 X \mathrm{X} X。如果特征维数 d \mathrm{d} d比较大,可以在 X \mathrm{X} X上应用全连接神经网络,以在前向传播之前获得较低维的初始表示 H ( 0 ) \mathrm{H^{(0)}} H(0)

2.3.2、恒等映射

  • 恒等映射确保深层GCNII模型至少实现与其浅层版本相同的性能。通过将 β l \beta_{l} βl设置得足够小,深度的GCNII会忽略权重矩阵 W ( ℓ ) W^{(ℓ)} W()

  • 已经观察到特征矩阵在不同维度之间的频繁交互降低了模型在半监督任务中的性能。然而,将平滑表示 P H ( ℓ ) \mathrm{PH^{(ℓ)}} PH()直接映射到输出则会减少了这种交互。

  • 恒等映射被证明在半监督任务中特别有用。Hardt&Ma,2017证明了形式为 H ( l + 1 ) = H ( l ) ( W ( l ) + I n ) \mathrm{H^{(l+1)}=H^{(l)}(W^{(l)}+I_n)} H(l+1)=H(l)(W(l)+In)满足以下性质:

    • 最优权矩阵 W ( ℓ ) W^{(ℓ)} W()具有小范数;
      这个性质允许我们强正则化 W ( ℓ ) W^{(ℓ)} W()避免过度拟合
    • 唯一的临界点是全局极小值。
      在训练数据有限的半监督任务中是可取的
  • Oono&Suzuki从理论上证明了K层GCNS的节点特征会收敛到一个子空间,从而导致信息丢失。特别地,收敛速度依赖于 S K S^K SK,S是权重矩阵 W ( ℓ ) W^{(ℓ)} W(),ℓ=0,…,K−1的最大奇异值。通过将 W ( ℓ ) W^{(ℓ)} W()替换为 ( 1 − β l ) I n + β l W ( l ) (1-\beta_l)\mathrm{I_n}+\beta_lW^{(l)} (1βl)In+βlW(l)并对 W ( ℓ ) W^{(ℓ)} W()施加正则化,强制 W ( ℓ ) W^{(ℓ)} W()的范数小。因此,上式的奇异值将接近1。因此,最大奇异值s也将接近1,这意味着 S K S^K SK过大,信息损失得到缓解。设置 β l \beta_l βl的原则是确保权重矩阵的衰减随着层数的增加而自适应地增加。

2.3.3、模型

GCNII模型分层结构可以被描述为
H ( l + 1 ) = σ ( ( ( 1 − α l ) A ^ H ( l ) + α l H ( 0 ) ) ( ( 1 − β l ) I n + β l W ( l ) ) ) H^{(l+1)} = \sigma(((1-\alpha_l)\hat{A}H^{(l)}+\alpha_lH^{(0)})((1-\beta_l)I_n+\beta_lW^{(l)})) H(l+1)=σ(((1αl)A^H(l)+αlH(0))((1βl)In+βlW(l)))
 初始残差连接确保每个结点堆叠多层的最终表征至少包含来自输入的部分表征。
 为了弥补APPNP的缺陷,GCNII借鉴ResNet模型中恒等映射的思想。恒等映射可以使深层GCNII模型实现与浅层GCNII模型达到相同的性能,同时使模型能够关注局部的邻域信息,最后还可以防止过拟合。
 从谱域的角度来看,一个k层的GCNII可以模型任意的系数的k阶多项式谱图滤波器,感兴趣的读者可以去读这篇论文。

参考连接

3、源码复现

详细源码请参考百度网盘连接

链接:https://pan.baidu.com/s/1IV9Kdg9TyWSWxMC9qZsIiA
提取码:6666

3.1、Torch复现

import torch
from torch import nn
from torch.nn import functional as F
from torch.nn import Module
from torch.nn import Linear
from torch.nn.parameter import Parameter
from torch.nn import functional as F
import torch 
import mathclass GraphConvLayer(Module):def __init__(self, unit_num,alpha,beta,device) -> None:super(GraphConvLayer,self).__init__()self.alpha = alphaself.beta = betaself.device = deviceself.W = Parameter(torch.empty(size=(unit_num,unit_num)))self.init_param()passdef init_param(self):torch.nn.init.xavier_uniform_(self.W.data,gain=1.414)def forward(self,P,H,H0):#P : N*N#H : N*C#H0 : N *F#W : C*Cinitial_res_connect = (1-self.alpha)*torch.mm(P,H)+self.alpha * H0I = torch.eye(self.W.shape[0]).to(self.device)identity_map = (1-self.beta) * I + self.beta * self.Woutput = torch.mm(initial_res_connect,identity_map)return F.relu(output)class GCNII(Module):def __init__(self, input_dim,hidden_dim,output_dim,k,alpha,lamda,dropout,device):super(GCNII,self).__init__()self.layer1 = Linear(input_dim,hidden_dim)self.layer2 = Linear(hidden_dim,output_dim)self.convs = nn.ModuleList()self.convs.append(self.layer1)self.k = kself.dropout = dropout#self.layers = []for i in range(k):beta = math.log(lamda / (i+1) + 1)self.convs.append(GraphConvLayer(hidden_dim,alpha,beta,device))self.convs.append(self.layer2)self.reg_param = list(self.convs[1:-1].parameters())self.non_linear_param = list(self.convs[0:1].parameters())+list(self.convs[-1:].parameters())#直接在低维变换效果不好,高维变换再映射到低维空间#self.layers.append(GraphConvLayer(output_dim,alpha,beta,device))# for i,layer in enumerate(self.layers):#     self.add_module(f'{i}',layer)def forward(self,features,adj):#H0 = self.layer1(features)H0 = F.dropout(features,self.dropout,training= self.training)H0 = F.relu(self.convs[0](H0))H = H0#for layer in self.layers:for layer in self.convs[1:-1]:H = F.dropout(H,self.dropout,training= self.training)H = layer(adj,H,H0)H = F.dropout(H,self.dropout,training= self.training)output = self.convs[-1](H)#output = self.layer2(H)return F.log_softmax(output,dim=1)
class GCNII_START(Module):def __init__(self, input_dim,hidden_dim,output_dim,k,alpha,lamda,dropout,device) :super(GCNII_START,self).__init__()self.layer1 = Linear(input_dim,hidden_dim)self.layer2 = Linear(hidden_dim,output_dim)self.dropout = dropoutself.layers = []for i in range(k):beta = lamda /(i+1)self.layers.append(GraphConvLayer_START(hidden_dim,alpha,beta,device))for i,layer in enumerate(self.layers):self.add_module(f'{i}',layer)def forward(self,features,adj):H0 = F.dropout(features,self.dropout,training= self.training)H0 = F.relu(self.layer1(H0))H = H0for layer in self.layers:H = F.dropout(H,self.dropout,training=self.training)H = layer(adj,H,H0)H = F.dropout(H,self.dropout,training= self.training)output = self.layer2(H)return F.log_softmax(output,dim=1)pass
class GraphConvLayer_START(Module):def __init__(self, unit_num,alpha,beta,device):super(GraphConvLayer_START,self).__init__()self.alpha = alphaself.beta = betaself.device = deviceself.W1 = Parameter(torch.empty(size=(unit_num,unit_num)))self.W2 = Parameter(torch.empty(size=(unit_num,unit_num)))self.init_param()def init_param(self):nn.init.xavier_uniform_(self.W1.data,gain=1.414)nn.init.xavier_uniform_(self.W2.data,gain=1.414)def forward(self,P,H,H0):I = torch.eye(self.W1.shape[0]).to(self.device)propagation = torch.mm(P,H)initial_res = H0identity_map1 =(1-self.beta)*I+self.beta*self.W1identity_map2 =(1-self.beta)*I+self.beta*self.W2output = (1-self.alpha)*torch.mm(propagation,identity_map1) + self.alpha * torch.mm(initial_res,identity_map2)return F.relu(output)

3.2、DGL复现

DGL复现精度可达0.8510(仅在cpu上)

import os
os.environ["DGLBACKEND"] = "pytorch"
import dgl
import dgl.function as fn
import torch 
import torch.nn as nn
import torch.nn.functional as F
import mathclass GraphConvLayer(nn.Module):def __init__(self,infeat,alpha,beta) -> None:super(GraphConvLayer,self).__init__()self.alpha = alphaself.beta = betaself.W = nn.Parameter(torch.empty(size=(infeat,infeat)))nn.init.xavier_uniform_(self.W.data,gain=1.414)def forward(self,x,g):with g.local_scope():h0 = g.ndata['init_feat']#添加自环g = dgl.add_self_loop(g)#计算正则degs = g.in_degrees().to(x).clamp(min=1)norm = torch.pow(degs,-0.5)norm = norm.to(x.device).unsqueeze(1)x = x * normg.ndata['h'] = xg.update_all(fn.copy_u('h','m'),fn.sum('m','h'))h = g.ndata['h'].to(x)h = h * normr = (1-self.alpha) * h + self.alpha * h0out = (1-self.beta) * r + self.beta * torch.mm(r,self.W)return out
class GCNII(nn.Module):def __init__(self,infeat,hidfeat,outfeat,alpha,lamda,dropout,k) -> None:super(GCNII,self).__init__()self.dropout = dropoutself.convs = nn.ModuleList()for i in range(k):#theta = lamda /(i+1)beta = math.log(lamda/(i+1)+1)self.convs.append(GraphConvLayer(hidfeat,alpha,beta))self.fcs = nn.ModuleList()self.fcs.append(nn.Linear(infeat,hidfeat))self.fcs.append(nn.Linear(hidfeat,outfeat))self.params1 = list(self.convs.parameters())self.params2 = list(self.fcs.parameters())self.act_fn = nn.ReLU()self.sg = nn.Sigmoid()self.init_param()def init_param(self):self.fcs[0].reset_parameters()self.fcs[1].reset_parameters()def forward(self,x,g):x = F.dropout(x,self.dropout,training=self.training)x = self.act_fn(self.fcs[0](x))print(x.device())g.ndata['init_feat'] = xh = xfor conv in self.convs:h = self.act_fn(conv(h,g))h = F.dropout(h,self.dropout,training=self.training)h = self.fcs[-1](h)return F.log_softmax(h,dim=1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/218872.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【模型训练】SD-大模型

【模型训练】SD-大模型

破局创新,天翼云HBlock如何以小见大、以柔克刚?

引言:另辟蹊径开拓创新 不走传统存储厂商的“寻常路” 【全球存储观察 | 科技热点关注】 在分布式块存储领域,大部分厂商的安装软件套件大小都在GB级。然而,天翼云破天荒地将存储资源盘活系统HBlock的软件安装包浓缩到了170MB&a…

linux中proc与sys的区别

在Linux系统中,/sys目录和/proc目录都是特殊的虚拟文件系统,用于提供对系统内核和设备信息的访问。 虽然它们的作用有一些重叠,但它们在功能和用途上有一些区别。 功能: /sys目录主要用于提供对设备和驱动程序的信息和配置的访…

Python-乒乓球小游戏【附完整源码】

乒乓球小游戏 乒乓球小游戏是一个简单而有趣的2D页面交互式游戏,玩家可以通过键盘输入来控制球拍上下移动来接球,从而体验乒乓球的乐趣。该游戏有单人和双人两种模式 运行效果: 一:主程序: import sys import cfg …

Jupyter Notebook: 交互式数据科学和编程工具

Jupyter Notebook: 功能强大的交互式编程和数据科学工具 简介 Jupyter Notebook是一个开源的Web应用程序,广泛用于数据分析、科学计算、可视化以及机器学习等领域。它允许创建和共享包含实时代码、方程式、可视化和解释性文本的文档。总而言之,我认为它…

3D Font

在游戏中使用3D文本 只需添加预制件并立即生成您的文本。 特点: *真实3D字母,可用作游戏对象*移动友好低聚 *VR兼容 *WebGL兼容 *30种以上不同字体 *材料和颜色可定制 WebGL演示 https://indiechest.itch.io/3d-font-engine 下载: ​​Unity资源商店链…

【tips】base64编码怎么反显出图片

格式 <img width"400" height"300" src"data:image/jpeg;base64,这里存放base64的图片内容/>实际的数据展示是这样的 然后把以上的文件内容放置到html文件中 最终展示样例 点击这个 展示出来是这样的

opencv 十五 红外图像中虹膜的提取

一、算法需求 在医疗检测中&#xff0c;需要使用红外相机拍摄眼睛照片&#xff0c;然后提取出虹膜的区域。在拍摄过程瞳孔需要进行运动&#xff0c;其通常不在正前方&#xff0c;无法形成圆形&#xff0c;不能使用常规的霍夫圆检测进行提取定位。且在在红外图像中&#xff0c;…

将输入的字符串反向输出(c语言)

#include<stdio.h> #include<string.h> int main() {int i, j, k;char s[50], temp;gets(s);//输入k strlen(s);//计算字符的长度//反向输出for (i 0, j k - 1;i < k / 2;i, j--){temp s[i];s[i] s[j];s[j] temp;}puts(s);//输出 }

ARM TZC-400原理及配置方式

吧ARM TZC-400是一种TrustZone Controller&#xff0c;用于ARM Cortex-A系列处理器中的硬件支持。TrustZone是ARM提供的一种安全技术&#xff0c;通过在处理器上创建两个不同的安全区域&#xff08;Secure Zone和Non-Secure Zone&#xff09;来提供硬件级别的安全隔离。 TZC-4…

最近面试了一位5年的测试,一问三不知,还反怼我...

最近看了很多简历&#xff0c;很多候选人年限不小&#xff0c;但是做的都是一些非常传统的项目&#xff0c;想着也不能通过简历就直接否定一个人&#xff0c;何况现在大环境越来 越难&#xff0c;大家找工作也不容易&#xff0c;于是就打算见一见。 在沟通中发现&#xff0c;由…

MD5解密详解

MD5解密详解 大家好&#xff0c;我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; MD5解密揭秘&#xff1a;解谜编码的神奇之路 在信息安全领域&#xff0c;MD5&#xff08;Message Di…

GB28181学习(十八)——图像抓拍

前言 本文主要介绍图像抓拍功能&#xff0c;通过自研的sip库&#xff08;mysipsdk.dll&#xff09;对接真实设备&#xff0c;使用http方式实现图像数据传输&#xff0c;最终达到图像抓拍与保存的目的。 基本要求 图像格式宜使用JPEG&#xff1b;图像分辨率宜采用与主码流相同…

linux ksm实现与代码简述

KSM 全称是 Kernel Samepage Merging&#xff0c;表示相同的物理页只映射一份拷贝。 原理 在ksm初始化时&#xff08;ksm_init&#xff09;&#xff0c;注册了一个ksm_scan_thread线程&#xff0c;这个线程的核心入口是ksm_do_scan。当对一个进程第一次通过madvice(MADV_MERGE…

Linux高级管理-基于域名的虚拟Web主机搭建

客服机限制地址 通过 Require 配置项&#xff0c;可以根据主机的主机名或P地址来决定是否允许客户端访问。在httpd服 务器的主配置文件的<Location>&#xff0c;<Directory>、<Files>、<Limit>配置段中均可以使用Require 配置 项来控制客户端的访问。使…

Java基础:如何创建多层文件夹

一、单层多个 代码实现如下&#xff1a; public class Main {public static void main(String[] args) {//在D盘中创建File file new File("D:"File.separator"docum");file.mkdir();//在D盘中的docum目录中创建file new File("D:\\docum" Fi…

Redission从入门到入门

1. Redisson简介 Redisson 是一个在 Java 环境中使用的 Redis 客户端库。它提供了丰富的功能&#xff0c;使得在 Java 应用中与 Redis 交互变得更加简单和高效。Redisson 不仅提供了基本的 Redis 操作&#xff0c;还提供了许多高级功能&#xff0c;使其成为在 Java 项目中实现…

以下是一些自然语言处理(NLP)技术的例子:

以下是一些自然语言处理&#xff08;NLP&#xff09;技术的例子&#xff1a; 语音识别&#xff1a;将说话者的语音转换为文本形式&#xff0c;从而使机器能够理解和分析语音内容。 文本分类&#xff1a;将大量的文本分成不同的类别&#xff0c;例如新闻、评论、博客等。 情感…

IO / 线程同步互斥概念总结

1. 为什么要引入线程同步互斥机制 线程共享用户空间&#xff0c;当多个线程访问临界资源时会产生竟态&#xff0c;引入线程同步互斥机制是为了保护临界资源 2. 同步互斥的基本概念 临界资源&#xff1a;多线程任务执行时&#xff0c;能够同时访问的公共资源叫做临界资源临界区…

富时中国a50指数期货论文

富时中国A50指数期货概述 富时中国A50指数期货是中国境内市场上的一种衡量蓝筹股表现的期货合约&#xff0c;其跟踪富时中国A50指数的表现。富时中国A50指数是由富时罗素指数有限公司编制的一种反映中国大陆A股市场50只流动性好、市值大的股票的指数。 市场背景与意义 富时中…