一文读懂图卷积神经网络(GCN)

本文围绕经典论文介绍GCN,解释其定义,即处理图结构数据的网络,输入为带特征的图,输出为节点特征。解析核心公式,通过添加自环、对称归一化邻接矩阵解决信息丢失和尺度问题,还提及在Cora数据集上的应用及数学证明参考。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

一文读懂图卷积神经网络(gcn) - 创想鸟

GCN 学习

本文讲的GCN理论知识来源于论文:

SEMI-SUPERVISED CLASSIFICATION WITH GRAPH CONVOLUTIONAL NETWORKS

这是在GCN领域最经典的论文之一

1. 什么是GCN

GCN结构图
一文读懂图卷积神经网络(GCN) - 创想鸟        

我们可以根据这个GCN的图看到,一个拥有 CC 个input channel的graph作为输入,经过中间的hidden layers,得到 FF 个 output channel的输出。

图卷积网络主要可以由两个级别的作用变换组成:

注意本文讲的图都特指无向无权重的图。

graph level:

例如说通过引入一些形式的pooling 操作. 然后改变图的结构。但是本次讲过GCN并没有进行这个级别的操作。所以看到上图我们的网络结构的输出和输出的graph的结构是一样的。

node level:

通常说node level的作用是不改变graph的结构的,仅通过对graph的特征/信号(特征信号 XX 作为输入:一个 N∗DN∗D 矩阵( NN: 输入图的nodes的个数, DD 输入的特征维度) ,得到输出 ZZ:一个 N∗FN∗F 的矩阵( FF 输出的特征维度)。

a) 一个特征描述(feature description) xixi : 指的是每个节点 ii 的特征表示

b) 每一个graph 的结构都可以通过邻接矩阵 AA 表示(或者其他根据它推导的矩阵)

举例:

我们可以很容易的根据一个邻接矩阵重构出一个graph。 例如下图: G=(V,E)G=(V,E) 其中 VV 代表节点, EE 代表边

一文读懂图卷积神经网络(GCN) - 创想鸟        

根据图,我们就可以得出下面的维度图和邻接矩阵

一文读懂图卷积神经网络(GCN) - 创想鸟        

论文公式提出

因为 AA 可以确定唯一的一张图,这么重要的一个属性我们肯定是要把他放到神经网络传递函数里面去的,所以网络中间的每一个隐藏层可以写成以下的非线性函数:

一文读懂图卷积神经网络(GCN) - 创想鸟        

其中输入层 H(0)=XH(0)=X , 输出层 H(L)=ZH(L)=Z , LL 是层数。 不同的GCN模型,采用不同 f(⋅,⋅)f(⋅,⋅)函数。

上面是我们理想中的函数的形式,论文中最终推导出来的函数是这样的:

一文读懂图卷积神经网络(GCN) - 创想鸟        

一文读懂图卷积神经网络(GCN) - 创想鸟        

论文公式逐步解析

每一个节点下一层的信息是由前一层本身的信息以及相邻的节点的信息加权加和得到,然后再经过线性变换 WW 以及非线性变换 σ()σ() 。

我们一步一步分解,我们要定义一个简单的f(H(l),A)f(H(l),A) 函数,作为基础的网络层。

可以很容易的采用最简单的层级传导(layer-wise propagation)规则:

f(H(l),A)=σ(AH(l)W(l))f(H(l),A)=σ(AH(l)W(l))

我们直接将 AHAH 做矩阵相乘,然后再通过一个权重矩阵 W(l)W(l)做线性变换,之后再经过非线性激活函数 σ(⋅)σ(⋅) , 比如说 ReLUReLU,最后得到下一层的输入 Hl+1Hl+1 。

In [ ]

import paddleA = paddle.to_tensor([    [0,1,0,0,1,0],    [1,0,1,0,1,0],    [0,1,0,1,0,0],    [0,0,1,0,1,1],    [1,1,0,1,0,0],    [0,0,0,1,0,0]],dtype='float32')print(A)H_0 = paddle.to_tensor([[1],[2],[3],[4],[5],[6]],dtype='float32')print(H_0)x = paddle.matmul(A,H_0)print(x)

       

Tensor(shape=[6, 6], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[0., 1., 0., 0., 1., 0.],        [1., 0., 1., 0., 1., 0.],        [0., 1., 0., 1., 0., 0.],        [0., 0., 1., 0., 1., 1.],        [1., 1., 0., 1., 0., 0.],        [0., 0., 0., 1., 0., 0.]])Tensor(shape=[6, 1], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[1.],        [2.],        [3.],        [4.],        [5.],        [6.]])Tensor(shape=[6, 1], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[7. ],        [9. ],        [6. ],        [14.],        [7. ],        [4. ]])

       

从上面输出可以看的出来:每个值保留了相邻节点的值

输入层的 x1=[1]x1=[1] , 根据矩阵的运算公式我们可以很容易地得到下一层的该节点的表示 X1′=[7]X1′=[7], 也很容易发现 X1′(1)=x2+x5X1′(1)=x2+x5,而 x2,x5x2,x5就是节点1的相邻节点。

就是可以看图:

[1] = 2 + 5
[2] = 1+ 3 + 5
······
[6] = 4

所以我们 AHAH 就是快速将相邻的节点的信息相加得到自己下一层的输入。

但是这样就出现了新的问题:

问题一
我们虽然获得了周围节点的信息了,但是自己本身的信息却没了

解决方案:
对每个节点手动增加一条self-loop 到每一个节点,即 A^=A+IA=A+I
其中 II是单位矩阵identity matrix。

问题二
从上面的结果也可以看出,在经过一次的AHAH 矩阵变换后,得到的输出会变大,即特征向量 XX 的scale会改变,在经过多层的变化之后,将和输入的scale差距越来越大。

解决方案:
可以将邻接矩阵 AA 做归一化使得最后的每一行的加和为1,使得 AHAH 获得的是weighted sum。

我们可以将 AA 的每一行除以行的和,这就可以得到normalized的 AA 。而其中每一行的和,就是每个节点的度degree。

用矩阵表示则为: A=D−1AA=D−1A ,对于Aij=AijdiAij=diAij

代码展示:

In [8]

import paddleimport numpy as npA = paddle.to_tensor([    [0,1,0,0,1,0],    [1,0,1,0,1,0],    [0,1,0,1,0,0],    [0,0,1,0,1,1],    [1,1,0,1,0,0],    [0,0,0,1,0,0]],dtype='float32')print(A)D = paddle.to_tensor([    [2,0,0,0,0,0],    [0,3,0,0,0,0],    [0,0,2,0,0,0],    [0,0,0,3,0,0],    [0,0,0,0,3,0],    [0,0,0,0,0,1]], dtype='float32')print(D)# 防止为0时取倒数变成无穷大D[D==0] = D.max() + 1e100DD = paddle.reciprocal(D)print(DD)hat_A = paddle.matmul(DD,A)print(hat_A)

       

Tensor(shape=[6, 6], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[0., 1., 0., 0., 1., 0.],        [1., 0., 1., 0., 1., 0.],        [0., 1., 0., 1., 0., 0.],        [0., 0., 1., 0., 1., 1.],        [1., 1., 0., 1., 0., 0.],        [0., 0., 0., 1., 0., 0.]])Tensor(shape=[6, 6], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[2., 0., 0., 0., 0., 0.],        [0., 3., 0., 0., 0., 0.],        [0., 0., 2., 0., 0., 0.],        [0., 0., 0., 3., 0., 0.],        [0., 0., 0., 0., 3., 0.],        [0., 0., 0., 0., 0., 1.]])Tensor(shape=[6, 6], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[0.50000000, 0.        , 0.        , 0.        , 0.        , 0.        ],        [0.        , 0.33333334, 0.        , 0.        , 0.        , 0.        ],        [0.        , 0.        , 0.50000000, 0.        , 0.        , 0.        ],        [0.        , 0.        , 0.        , 0.33333334, 0.        , 0.        ],        [0.        , 0.        , 0.        , 0.        , 0.33333334, 0.        ],        [0.        , 0.        , 0.        , 0.        , 0.        , 1.        ]])Tensor(shape=[6, 6], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[0.        , 0.50000000, 0.        , 0.        , 0.50000000, 0.        ],        [0.33333334, 0.        , 0.33333334, 0.        , 0.33333334, 0.        ],        [0.        , 0.50000000, 0.        , 0.50000000, 0.        , 0.        ],        [0.        , 0.        , 0.33333334, 0.        , 0.33333334, 0.33333334],        [0.33333334, 0.33333334, 0.        , 0.33333334, 0.        , 0.        ],        [0.        , 0.        , 0.        , 1.        , 0.        , 0.        ]])

       

但是在实际运用中采用的是对称的normalization:

用矩阵表示则为:

A=D−12AD−12A=D−21AD−21

对于Aij=AijdidjAij=didjAij

这其实是跟Laplacian Matrix 有关,拉普拉斯算子是这样的:

L=IN−D−12AD−12L=IN−D−21AD−21

In [9]

import paddleimport numpy as npA = paddle.to_tensor([    [0,1,0,0,1,0],    [1,0,1,0,1,0],    [0,1,0,1,0,0],    [0,0,1,0,1,1],    [1,1,0,1,0,0],    [0,0,0,1,0,0]],dtype='float32')print(A)D = paddle.to_tensor([    [2,0,0,0,0,0],    [0,3,0,0,0,0],    [0,0,2,0,0,0],    [0,0,0,3,0,0],    [0,0,0,0,3,0],    [0,0,0,0,0,1]], dtype='float32')print(D)# 取逆D[D==0] = D.max() + 1e100DD = paddle.reciprocal(D)print(DD)# 开方_DD = paddle.sqrt(DD)# 求积hat_A = paddle.matmul(paddle.matmul(_DD,A),_DD)print(hat_A)

       

Tensor(shape=[6, 6], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[0., 1., 0., 0., 1., 0.],        [1., 0., 1., 0., 1., 0.],        [0., 1., 0., 1., 0., 0.],        [0., 0., 1., 0., 1., 1.],        [1., 1., 0., 1., 0., 0.],        [0., 0., 0., 1., 0., 0.]])Tensor(shape=[6, 6], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[2., 0., 0., 0., 0., 0.],        [0., 3., 0., 0., 0., 0.],        [0., 0., 2., 0., 0., 0.],        [0., 0., 0., 3., 0., 0.],        [0., 0., 0., 0., 3., 0.],        [0., 0., 0., 0., 0., 1.]])Tensor(shape=[6, 6], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[0.50000000, 0.        , 0.        , 0.        , 0.        , 0.        ],        [0.        , 0.33333334, 0.        , 0.        , 0.        , 0.        ],        [0.        , 0.        , 0.50000000, 0.        , 0.        , 0.        ],        [0.        , 0.        , 0.        , 0.33333334, 0.        , 0.        ],        [0.        , 0.        , 0.        , 0.        , 0.33333334, 0.        ],        [0.        , 0.        , 0.        , 0.        , 0.        , 1.        ]])Tensor(shape=[6, 6], dtype=float32, place=CUDAPlace(0), stop_gradient=True,       [[0.        , 0.40824828, 0.        , 0.        , 0.40824828, 0.        ],        [0.40824828, 0.        , 0.40824828, 0.        , 0.33333331, 0.        ],        [0.        , 0.40824828, 0.        , 0.40824828, 0.        , 0.        ],        [0.        , 0.        , 0.40824828, 0.        , 0.33333331, 0.57735026],        [0.40824828, 0.33333331, 0.        , 0.33333331, 0.        , 0.        ],        [0.        , 0.        , 0.        , 0.57735026, 0.        , 0.        ]])

       

A0,1=A0,1d0d1=123=0.4082A0,1=d0d1A0,1=231=0.4082

把这两个tricks结合起来,我们就可以原文的公式:

一文读懂图卷积神经网络(GCN) - 创想鸟        

其中 A^=A+IA=A+I , D^D 是 A^A 的degree matrix。 而 D^−12A^D^−12D−21AD−21 是对 AA 做了一个对称的归一化。

在Cora数据集上训练的两层GCN的隐藏层激活的可视化效果如下:

一文读懂图卷积神经网络(GCN) - 创想鸟        

2. 数学证明:

附上参考的视频:

https://www.bilibili.com/video/BV1Vw411R7Fj

一文读懂图卷积神经网络(GCN) - 创想鸟        

一文读懂图卷积神经网络(GCN) - 创想鸟        

一文读懂图卷积神经网络(GCN) - 创想鸟        

一文读懂图卷积神经网络(GCN) - 创想鸟        

以上就是一文读懂图卷积神经网络(GCN)的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/59377.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月10日 13:50:41
下一篇 2025年11月10日 14:13:03

相关推荐

  • 在Django模型中实现余额扣减与可用余额的自动计算

    本文详细介绍了如何在Django模型中通过重写`save()`方法,实现从当前余额中扣除指定金额以自动计算可用余额的功能。文章通过具体代码示例,展示了如何在模型保存前执行业务逻辑,确保数据一致性,并探讨了在处理财务数据时需要注意的事务性、数据类型选择及替代方案等最佳实践。 Django模型中实现余额…

    2025年12月23日
    000
  • HTML表单键盘事件怎么监听_HTML表单键盘按键事件的监听与处理方法

    答案:在HTML表单中通过JavaScript监听keydown和keyup事件实现键盘交互,可用于回车提交、输入限制和实时搜索;推荐使用event.key判断按键,结合preventDefault控制输入,并利用事件冒泡统一处理表单行为。 在HTML表单中监听键盘事件,主要是通过JavaScrip…

    2025年12月23日
    000
  • CSS ::after 伪元素实现按钮缩放效果:定位与动画实践

    本文详细讲解如何利用 css 的 `::after` 伪元素为按钮创建动态缩放效果,并重点解决伪元素定位不准确(出现在按钮旁边而非下方)的问题。通过设置 `position: absolute` 和明确的定位属性(如 `left: 0`),结合 `z-index` 和 `transform` 动画,…

    2025年12月23日
    000
  • 实现下拉菜单在地图上方显示:CSS层叠上下文与z-index实践

    本教程旨在解决将下拉菜单叠加在全屏地图之上的常见前端布局问题。通过详细讲解css的`position`属性和`z-index`属性的工作原理,我们将演示如何利用它们创建层叠上下文,确保下拉菜单始终显示在地图图层之上,从而优化用户交互体验。 在现代Web应用中,将交互式组件(如下拉菜单)叠加在占据整个…

    2025年12月23日
    000
  • FormSubmit.co表单电子邮件验证失败重定向优化指南

    本教程旨在解决使用formsubmit.co服务时,表单因电子邮件格式无效而意外重定向至其他页面的问题。我们将通过优化前端html表单元素,利用html5的`type=”email”`属性,实现浏览器原生验证,从而提供即时用户反馈,避免不必要的页面跳转,显著提升用户体验。 理…

    2025年12月23日
    000
  • 使用Flexbox实现内容居中布局:从页脚固定到内容对齐

    本文深入探讨了如何利用CSS Flexbox实现网页内容的精确居中对齐,尤其是在包含固定页脚的复杂布局中。我们将通过分析一个常见的布局问题,逐步讲解如何配置Flex容器及其子项的属性,如`display: flex`、`flex-direction`、`justify-content`和`text-…

    2025年12月23日
    000
  • WordPress自定义导航栏外部点击关闭功能实现教程

    本教程旨在解决wordpress自定义导航栏在点击其外部区域时无法自动关闭的问题。通过引入一个半透明的叠加层(overlay)并结合javascript和css,我们提供了一种简洁高效的解决方案。该方法利用`z-index`管理元素层级,确保外部点击事件被叠加层捕获,从而实现主导航菜单的平滑显示与隐…

    2025年12月23日
    000
  • 高效管理PHP滑块页面重定向后的状态:保持当前滑块可见

    本文旨在解决php驱动的滑块页面在表单提交并重定向后,无法返回到提交前的活跃滑块的问题。我们将深入探讨传统重定向方法的局限性,并提供两种实用的解决方案:通过url参数传递滑块状态和利用浏览器本地存储。通过详细的代码示例和最佳实践,您将学会如何确保用户在操作后能无缝返回到其之前的浏览位置,显著提升用户…

    2025年12月23日
    000
  • 解决嵌套iframe中YouTube视频无法播放的指南

    本文探讨了在嵌套`iframe`结构中嵌入youtube视频时,因`sandbox`属性限制导致javascript脚本无法执行的问题。通过分析默认的`sandbox`行为及其对视频播放的影响,文章提供了明确的解决方案:在嵌入youtube视频的`iframe`的`sandbox`属性中明确添加`a…

    2025年12月23日
    000
  • CSS教程:在全屏地图上叠加显示下拉菜单

    本教程详细讲解如何利用css的定位属性(`position`)和层叠顺序(`z-index`)来实现在全屏地图上方正确显示下拉菜单。通过将下拉菜单和地图都设置为绝对定位,并为下拉菜单分配更高的`z-index`值,确保下拉菜单始终可见并可交互,从而解决ui元素层叠覆盖的问题。 在现代Web开发中,将…

    2025年12月23日
    000
  • 如何在Flex容器中固定两列,实现中间列滚动

    本文旨在详细讲解如何在flexbox布局中实现一个经典三列布局,其中左右两侧列保持固定并占满整个视口高度,而中间内容区域则允许独立滚动。我们将深入探讨如何巧妙运用css的`position: sticky`属性结合视口单位`vh`,以构建一个兼具功能性和良好用户体验的布局方案,并提供完整的代码示例及…

    2025年12月23日 好文分享
    000
  • 在Rails应用中集成Bootstrap 5垂直选项卡并解决点击不生效的问题

    本教程旨在解决rails应用中集成bootstrap 5垂直选项卡时,点击选项卡内容面板无法正确显示的问题。核心原因在于动态生成选项卡时,未能为每个选项卡按钮和内容面板分配唯一的id,并正确关联`data-bs-target`、`aria-labelledby`等属性,以及初始化首个选项卡的激活状态…

    2025年12月23日 好文分享
    000
  • 如何防止网站目录列表泄露

    当用户通过URL访问网站文件夹而非特定页面时,服务器可能会默认显示该文件夹内所有文件和子目录的“索引页”,这可能导致敏感信息泄露。本文将详细介绍两种主要方法来解决此问题:一是通过在每个目录下放置默认索引文件,二是直接在服务器配置中禁用目录列表功能,从而提升网站的安全性和隐私保护。 理解“索引页”问题…

    2025年12月23日
    000
  • Flexbox布局中align-items与子元素宽度的交互及居中策略解析

    本文深入探讨了flexbox布局中`align-items: center`属性在`flex-direction: column`模式下对子元素宽度的影响,以及它与水平居中的关系。文章将澄清`align-items`的实际作用,解释为何子元素宽度可能看似“改变”,并提供在flex容器中实现精确居中的…

    2025年12月23日
    000
  • Vaadin中处理客户端生成SVG元素的策略:从DOM同步到服务器端渲染

    vaadin默认不自动同步客户端javascript创建的dom元素到服务器端,以避免性能问题。本文将探讨两种主要策略来处理客户端动态生成的svg内容:一是通过`littemplate`结合`@id`注解实现对模板中特定元素的服务器端访问,但需注意vaadin对svg的深度支持有限;二是针对用户下载…

    2025年12月23日
    000
  • 解决 Nginx 自定义 400 错误页面 SSL 资源加载问题

    本文旨在解决在使用 Nginx 配置自定义 400 错误页面时,当用户通过 HTTP 访问 HTTPS 端口时,页面资源无法正确加载的问题。通过配置 Nginx 的 default_server,确保所有进入服务器的请求都能被正确处理,并提供一个统一的错误页面,从而提升用户体验。 当配置 Nginx…

    2025年12月23日
    000
  • Django UpdateView 关联模型与图片上传:更新用户档案的完整指南

    本文详细指导如何在django `updateview`中同时更新用户(`user`)模型及其关联的档案(`profile`)模型,特别是如何正确处理用户头像等文件上传。我们将探讨文件上传时使用`request.files`的关键性,并提供优化的视图代码和前端html配置,确保数据(包括图片)能够被…

    2025年12月23日
    000
  • React与Tailwind CSS:实现可点击链接与自定义样式

    在react与tailwind css项目中,实现可点击链接并进行样式定制是一个常见需求。由于tailwind的预设样式可能覆盖浏览器默认的链接样式,导致标签与普通文本无异。本文将详细指导如何在不依赖额外npm包的情况下,通过classname属性为标签添加视觉区分和交互效果,同时探讨更复杂的路由场…

    2025年12月23日
    000
  • CSS选择器:根据子元素文本内容选择父元素的限制与替代方案

    标准css无法直接根据子元素文本内容选择其父元素。本文将深入探讨这一局限性,并提供多种替代方案,包括利用结构化css选择器、原生javascript以及jquery的`:contains()`选择器。通过这些方法,开发者可以实现基于子元素文本内容的动态样式调整,从而提升网页交互性和视觉表现力。 在网…

    2025年12月23日
    000
  • 掌握CSS层叠上下文:将下拉菜单叠加在地图之上

    本文将深入探讨如何利用css的position和z-index属性,解决将下拉菜单等交互元素精确叠加在全屏背景元素(如地图)上方的问题。通过调整元素的定位方式和层叠顺序,确保下拉菜单在视觉上处于地图之上,实现更灵活和用户友好的界面布局。 在现代网页设计中,将交互式UI元素(如下拉菜单、模态框)叠加在…

    2025年12月23日
    000

发表回复

登录后才能评论
关注微信