PyTorch深度学习框架60天进阶学习计划-第31天:图神经网络基础

PyTorch深度学习框架60天进阶学习计划-第31天:图神经网络基础

目录

  1. 引言:图数据和图神经网络
  2. 图的基本概念与表示
  3. 图卷积网络的消息传递机制
  4. 谱域与空域方法对比
  5. 节点嵌入的聚合公式推导
  6. PyTorch实现图卷积网络
  7. 代码运行流程与可视化
  8. 实验与结果分析
  9. 总结与进阶方向

引言:图数据和图神经网络

欢迎来到PyTorch深度学习框架60天进阶学习计划的第31天!今天我们将深入学习图神经网络(Graph Neural Networks, GNN)的基础知识。

在我们的日常生活和科研中,许多数据天然地以图结构存在:社交网络中的人与关系、分子中的原子与化学键、知识图谱中的实体与关联、交通网络中的路口与道路等。传统的深度学习模型如CNN和RNN主要处理欧几里得空间的数据(如图像、序列),但对于这些非欧几里得空间的图数据,它们往往力不从心。

图神经网络应运而生,它能够同时捕捉节点特征和拓扑结构信息,为图数据分析提供了强大工具。今天,我们将重点解析图卷积网络的消息传递机制,对比谱域与空域方法的计算差异,并推导节点嵌入的聚合公式。

让我们开始这段精彩的图神经网络之旅吧!

图的基本概念与表示

在深入GNN之前,先来复习一下图的基本概念。

图的定义和表示

G = ( V , E ) G = (V, E) G=(V,E) 由节点集合 V V V 和边集合 E E E 组成。其中:

  • V = { v 1 , v 2 , . . . , v N } V = \{v_1, v_2, ..., v_N\} V={v1,v2,...,vN} 表示 N N N 个节点
  • E ⊆ V × V E \subseteq V \times V EV×V 表示节点间的边

在计算机中,图通常通过以下几种方式表示:

1. 邻接矩阵

邻接矩阵 A ∈ R N × N A \in \mathbb{R}^{N \times N} ARN×N 是一个方阵,其中:

  • A i j = 1 A_{ij} = 1 Aij=1 如果节点 i i i 和节点 j j j 之间有边
  • A i j = 0 A_{ij} = 0 Aij=0 如果节点 i i i 和节点 j j j 之间没有边
  • 对于带权图, A i j A_{ij} Aij 可以是边的权重
2. 邻接表

邻接表是一种更节省空间的表示方法,特别是对于稀疏图。对每个节点,我们存储与其相连的所有节点的列表。

3. 特征矩阵

节点特征矩阵 X ∈ R N × F X \in \mathbb{R}^{N \times F} XRN×F,其中 N N N 是节点数, F F F 是特征维度。每个节点 v i v_i vi 对应一个特征向量 x i ∈ R F x_i \in \mathbb{R}^F xiRF

图的类型

图可以分为多种类型:

  • 无向图/有向图:边是否有方向
  • 带权图/无权图:边是否有权重
  • 同构图/异构图:节点和边是否属于同一类型
  • 静态图/动态图:图的结构是否随时间变化

下面用表格总结不同类型图的特点和应用场景:

图类型特点应用场景表示方法
无向图边没有方向社交网络中的朋友关系对称邻接矩阵
有向图边有方向引用网络、网页链接非对称邻接矩阵
带权图边有权重交通网络中的距离权重邻接矩阵
异构图多种类型的节点和边知识图谱多关系邻接矩阵
动态图随时间变化随时间演化的社交网络时序邻接矩阵序列

了解了图的基本概念,下面让我们正式进入图神经网络的世界!

图卷积网络的消息传递机制

消息传递框架

图神经网络的核心思想是消息传递,即每个节点通过聚合邻居节点的信息来更新自己的表示。这一过程可以形式化为:

h v ( k ) = UPDATE ( k ) ( h v ( k − 1 ) , AGGREGATE ( k ) ( { h u ( k − 1 ) : u ∈ N ( v ) } ) ) h_v^{(k)} = \text{UPDATE}^{(k)}\left(h_v^{(k-1)}, \text{AGGREGATE}^{(k)}\left(\{h_u^{(k-1)} : u \in \mathcal{N}(v)\}\right)\right) hv(k)=UPDATE(k)(hv(k1),AGGREGATE(k)({hu(k1):uN(v)}))

其中:

  • h v ( k ) h_v^{(k)} hv(k) 是节点 v v v 在第 k k k 层的隐藏表示
  • N ( v ) \mathcal{N}(v) N(v) 是节点 v v v 的邻居集合
  • AGGREGATE 函数聚合邻居节点的信息
  • UPDATE 函数更新节点自身的表示

这一框架如此优雅,它允许我们设计各种不同的聚合和更新函数,从而产生不同的GNN变体。让我们通过一个简单的例子来理解这一过程:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

想象一个简单的社交网络,其中每个人(节点)有自己的特征(如年龄、兴趣爱好等)。通过消息传递,每个人可以了解到朋友的信息,从而更新自己对社交圈的理解。经过多轮消息传递,每个人都能获得一个融合了社交网络结构和个人特征的表示。

图卷积网络(GCN)的消息传递

图卷积网络(Graph Convolutional Network, GCN)是最经典的GNN模型之一,它的消息传递机制可以表示为:

h v ( k ) = σ ( ∑ u ∈ N ( v ) ∪ { v } 1 ∣ N ( v ) ∣ ⋅ ∣ N ( u ) ∣ ⋅ W ( k ) ⋅ h u ( k − 1 ) ) h_v^{(k)} = \sigma\left(\sum_{u \in \mathcal{N}(v) \cup \{v\}} \frac{1}{\sqrt{|\mathcal{N}(v)|} \cdot \sqrt{|\mathcal{N}(u)|}} \cdot W^{(k)} \cdot h_u^{(k-1)}\right) hv(k)=σ uN(v){v}N(v) N(u) 1W(k)hu(k1)

这个公式看起来有点复杂,我们可以拆解开来理解:

  1. 邻居聚合 ∑ u ∈ N ( v ) ∪ { v } \sum_{u \in \mathcal{N}(v) \cup \{v\}} uN(v){v} 表示聚合节点 v v v 自身和其所有邻居的表示
  2. 归一化 1 ∣ N ( v ) ∣ ⋅ ∣ N ( u ) ∣ \frac{1}{\sqrt{|\mathcal{N}(v)|} \cdot \sqrt{|\mathcal{N}(u)|}} N(v) N(u) 1 是归一化因子,防止度数大的节点主导聚合过程
  3. 线性变换 W ( k ) ⋅ h u ( k − 1 ) W^{(k)} \cdot h_u^{(k-1)} W(k)hu(k1) 将节点表示投影到新的特征空间
  4. 非线性激活 σ ( ⋅ ) \sigma(\cdot) σ() 是非线性激活函数,如ReLU

用矩阵形式表示,GCN的一层传播规则为:

H ( k ) = σ ( D ~ − 1 2 A ~ D ~ − 1 2 H ( k − 1 ) W ( k ) ) H^{(k)} = \sigma(\tilde{D}^{-\frac{1}{2}} \tilde{A} \tilde{D}^{-\frac{1}{2}} H^{(k-1)} W^{(k)}) H(k)=σ(D~21A~D~21H(k1)W(k))

其中:

  • A ~ = A + I N \tilde{A} = A + I_N A~=A+IN 是添加了自环的邻接矩阵( I N I_N IN 是单位矩阵)
  • D ~ \tilde{D} D~ A ~ \tilde{A} A~ 的度矩阵, D ~ i i = ∑ j A ~ i j \tilde{D}_{ii} = \sum_j \tilde{A}_{ij} D~ii=jA~ij
  • H ( k ) H^{(k)} H(k) 是第 k k k 层所有节点的隐藏表示矩阵
  • W ( k ) W^{(k)} W(k) 是第 k k k 层的权重矩阵

消息传递过程示例

让我们通过一个具体的例子来理解GCN的消息传递过程。考虑下面这个小型图:

    2
   / \
  1---3
   \ /
    4
  • 节点集合: V = { 1 , 2 , 3 , 4 } V = \{1, 2, 3, 4\} V={1,2,3,4}
  • 边集合: E = { ( 1 , 2 ) , ( 1 , 3 ) , ( 1 , 4 ) , ( 2 , 3 ) , ( 3 , 4 ) } E = \{(1,2), (1,3), (1,4), (2,3), (3,4)\} E={(1,2),(1,3),(1,4),(2,3),(3,4)}

假设每个节点的初始特征都是2维向量:

  • h 1 ( 0 ) = [ 1 , 0 ] h_1^{(0)} = [1, 0] h1(0)=[1,0]
  • h 2 ( 0 ) = [ 0 , 1 ] h_2^{(0)} = [0, 1] h2(0)=[0,1]
  • h 3 ( 0 ) = [ 1 , 1 ] h_3^{(0)} = [1, 1] h3(0)=[1,1]
  • h 4 ( 0 ) = [ 0 , 0 ] h_4^{(0)} = [0, 0] h4(0)=[0,0]

现在我们计算第一层GCN后节点1的表示。节点1的邻居是2、3和4。

邻接矩阵 A A A 和添加自环后的邻接矩阵 A ~ \tilde{A} A~ 为:

A = [ 0 1 1 1 1 0 1 0 1 1 0 1 1 0 1 0 ] , A ~ = [ 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 ] A = \begin{bmatrix} 0 & 1 & 1 & 1 \\ 1 & 0 & 1 & 0 \\ 1 & 1 & 0 & 1 \\ 1 & 0 & 1 & 0 \end{bmatrix}, \tilde{A} = \begin{bmatrix} 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 0 \\ 1 & 1 & 1 & 1 \\ 1 & 0 & 1 & 1 \end{bmatrix} A= 0111101011011010 ,A~= 1111111011111011

度矩阵 D ~ \tilde{D} D~ 为:

D ~ = [ 4 0 0 0 0 3 0 0 0 0 4 0 0 0 0 3 ] \tilde{D} = \begin{bmatrix} 4 & 0 & 0 & 0 \\ 0 & 3 & 0 & 0 \\ 0 & 0 & 4 & 0 \\ 0 & 0 & 0 & 3 \end{bmatrix} D~= 4000030000400003

归一化邻接矩阵 D ~ − 1 2 A ~ D ~ − 1 2 \tilde{D}^{-\frac{1}{2}} \tilde{A} \tilde{D}^{-\frac{1}{2}} D~21A~D~21 为:

D ~ − 1 2 A ~ D ~ − 1 2 = [ 1 4 1 12 1 4 1 12 1 12 1 3 1 12 0 1 4 1 12 1 4 1 12 1 12 0 1 12 1 3 ] \tilde{D}^{-\frac{1}{2}} \tilde{A} \tilde{D}^{-\frac{1}{2}} = \begin{bmatrix} \frac{1}{4} & \frac{1}{\sqrt{12}} & \frac{1}{4} & \frac{1}{\sqrt{12}} \\ \frac{1}{\sqrt{12}} & \frac{1}{3} & \frac{1}{\sqrt{12}} & 0 \\ \frac{1}{4} & \frac{1}{\sqrt{12}} & \frac{1}{4} & \frac{1}{\sqrt{12}} \\ \frac{1}{\sqrt{12}} & 0 & \frac{1}{\sqrt{12}} & \frac{1}{3} \end{bmatrix} D~21A~D~21= 4112 14112 112 13112 104112 14112 112 1012 131

假设我们的权重矩阵 W ( 1 ) ∈ R 2 × 2 W^{(1)} \in \mathbb{R}^{2 \times 2} W(1)R2×2 为单位矩阵,激活函数为ReLU。

则节点1的新表示为:

h 1 ( 1 ) = ReLU ( 1 4 ⋅ [ 1 , 0 ] + 1 12 ⋅ [ 0 , 1 ] + 1 4 ⋅ [ 1 , 1 ] + 1 12 ⋅ [ 0 , 0 ] ) h_1^{(1)} = \text{ReLU}\left(\frac{1}{4} \cdot [1, 0] + \frac{1}{\sqrt{12}} \cdot [0, 1] + \frac{1}{4} \cdot [1, 1] + \frac{1}{\sqrt{12}} \cdot [0, 0]\right) h1(1)=ReLU(41[1,0]+12 1[0,1]+41[1,1]+12 1[0,0])

h 1 ( 1 ) = ReLU ( [ 1 4 + 0 + 1 4 + 0 , 0 + 1 12 + 1 4 + 0 ] ) h_1^{(1)} = \text{ReLU}\left(\left[\frac{1}{4} + 0 + \frac{1}{4} + 0, 0 + \frac{1}{\sqrt{12}} + \frac{1}{4} + 0\right]\right) h1(1)=ReLU([41+0+41+0,0+12 1+41+0])

h 1 ( 1 ) = ReLU ( [ 1 2 , 1 12 + 1 4 ] ) h_1^{(1)} = \text{ReLU}\left(\left[\frac{1}{2}, \frac{1}{\sqrt{12}} + \frac{1}{4}\right]\right) h1(1)=ReLU([21,12 1+41])

这样,我们就完成了节点1在第一层的消息传递。通过多层消息传递,节点能够获取更大范围内的结构信息。

谱域与空域方法对比

图卷积网络可以从两个角度来定义:谱域方法和空域方法。这两种方法各有优缺点,下面我们对它们进行深入对比。

谱域方法

谱域方法基于图信号处理理论,将图卷积定义为图傅里叶域中的乘积。

图拉普拉斯矩阵

图拉普拉斯矩阵是谱域方法的核心,定义为:

L = D − A L = D - A L=DA

其中 D D D 是度矩阵, A A A 是邻接矩阵。

归一化的拉普拉斯矩阵为:

L s y m = D − 1 2 L D − 1 2 = I − D − 1 2 A D − 1 2 L_{sym} = D^{-\frac{1}{2}} L D^{-\frac{1}{2}} = I - D^{-\frac{1}{2}} A D^{-\frac{1}{2}} Lsym=D21LD21=ID21AD21

拉普拉斯矩阵有许多良好的性质:

  • 它是对称半正定矩阵
  • 它的特征值和特征向量提供了图结构的重要信息
  • 最小特征值为0,对应的特征向量与图的连通分量有关
谱域图卷积

L = U Λ U T L = U \Lambda U^T L=UΛUT 是拉普拉斯矩阵的特征分解,其中 U U U 是特征向量矩阵, Λ \Lambda Λ 是特征值对角矩阵。

给定信号 x ∈ R N x \in \mathbb{R}^N xRN,其图傅里叶变换定义为:

x ^ = U T x \hat{x} = U^T x x^=UTx

逆变换为:

x = U x ^ x = U \hat{x} x=Ux^

图卷积定义为信号 x x x 与滤波器 g g g 在傅里叶域的乘积:

x ∗ G g = U ( ( U T x ) ⊙ ( U T g ) ) = U diag ( g ^ ) U T x x *_G g = U((U^T x) \odot (U^T g)) = U \text{diag}(\hat{g}) U^T x xGg=U((UTx)(UTg))=Udiag(g^)UTx

其中 g ^ \hat{g} g^ 是滤波器 g g g 的傅里叶变换。

切比雪夫多项式近似

直接计算特征分解计算成本高,因此通常使用切比雪夫多项式来近似滤波器:

g θ ( Λ ) ≈ ∑ k = 0 K θ k T k ( Λ ~ ) g_{\theta}(\Lambda) \approx \sum_{k=0}^{K} \theta_k T_k(\tilde{\Lambda}) gθ(Λ)k=0KθkTk(Λ~)

其中 T k T_k Tk 是k阶切比雪夫多项式, Λ ~ = 2 Λ / λ m a x − I \tilde{\Lambda} = 2\Lambda/\lambda_{max} - I Λ~=2Λ/λmaxI

这样,图卷积可以表示为:

g θ ∗ x ≈ ∑ k = 0 K θ k T k ( L ~ ) x g_{\theta} * x \approx \sum_{k=0}^{K} \theta_k T_k(\tilde{L}) x gθxk=0KθkTk(L~)x

其中 L ~ = 2 L / λ m a x − I \tilde{L} = 2L/\lambda_{max} - I L~=2L/λmaxI

空域方法

空域方法更直观,直接在图的节点和边上定义卷积操作,不需要进行图傅里叶变换。

消息传递框架

空域方法通常基于消息传递框架,节点通过聚合邻居信息来更新表示:

h v ( k ) = ϕ ( h v ( k − 1 ) , □ u ∈ N ( v ) ψ ( h v ( k − 1 ) , h u ( k − 1 ) , e v u ) ) h_v^{(k)} = \phi\left(h_v^{(k-1)}, \square_{u \in \mathcal{N}(v)} \psi(h_v^{(k-1)}, h_u^{(k-1)}, e_{vu})\right) hv(k)=ϕ(hv(k1),uN(v)ψ(hv(k1),hu(k1),evu))

其中 □ \square 是可微的置换不变函数(如求和、平均、最大值等), ϕ \phi ϕ ψ \psi ψ 是可学习的函数。

GraphSAGE

GraphSAGE是一种典型的空域方法,它的更新规则为:

h v ( k ) = σ ( W ( k ) ⋅ CONCAT ( h v ( k − 1 ) , AGG ( { h u ( k − 1 ) , ∀ u ∈ N ( v ) } ) ) ) h_v^{(k)} = \sigma\left(W^{(k)} \cdot \text{CONCAT}(h_v^{(k-1)}, \text{AGG}(\{h_u^{(k-1)}, \forall u \in \mathcal{N}(v)\}))\right) hv(k)=σ(W(k)CONCAT(hv(k1),AGG({hu(k1),uN(v)})))

其中AGG可以是均值聚合、LSTM聚合或池化聚合。

谱域与空域方法对比表

下面用表格总结谱域方法和空域方法的主要差异:

特性谱域方法空域方法
理论基础图信号处理、图傅里叶变换直接在图结构上定义的卷积
计算复杂度高(需要特征分解)或中等(使用多项式近似)低(直接聚合邻居信息)
适用性适用于固定图结构适用于动态变化的图
表达能力可以设计复杂的频域滤波器依赖于聚合函数的设计
可解释性基于频域解释,不直观基于消息传递,较直观
代表模型ChebNet、GCNGraphSAGE、GAT
归纳能力弱(难以泛化到新图结构)强(易于泛化到新图结构)

从谱域到空域的桥梁:GCN

有趣的是,GCN模型可以同时从谱域和空域解释:

  • 从谱域角度,GCN使用一阶切比雪夫多项式近似滤波器
  • 从空域角度,GCN通过归一化的邻居聚合来更新节点表示

GCN的更新规则:

H ( k ) = σ ( D ~ − 1 2 A ~ D ~ − 1 2 H ( k − 1 ) W ( k ) ) H^{(k)} = \sigma(\tilde{D}^{-\frac{1}{2}} \tilde{A} \tilde{D}^{-\frac{1}{2}} H^{(k-1)} W^{(k)}) H(k)=σ(D~21A~D~21H(k1)W(k))

这使得GCN成为连接谱域和空域方法的重要桥梁。

节点嵌入的聚合公式推导

在图神经网络中,节点嵌入的聚合是核心操作。下面我们推导几种常见的聚合公式。

GCN的聚合公式

GCN的聚合公式可以从谱域推导得出。首先,考虑图信号 x x x 上的卷积:

g θ ∗ x = U g θ ( Λ ) U T x g_{\theta} * x = U g_{\theta}(\Lambda) U^T x gθx=Ugθ(Λ)UTx

使用一阶切比雪夫多项式近似 g θ ( Λ ) ≈ θ 0 + θ 1 ( 2 Λ / λ m a x − I ) g_{\theta}(\Lambda) \approx \theta_0 + \theta_1 (2\Lambda/\lambda_{max} - I) gθ(Λ)θ0+θ1(2Λ/λmaxI),并设 θ 0 = θ 1 \theta_0 = \theta_1 θ0=θ1 λ m a x = 2 \lambda_{max} = 2 λmax=2,我们得到:

g θ ∗ x ≈ θ 0 ( I − D − 1 2 A D − 1 2 ) x = θ 0 D − 1 2 ( D − A ) D − 1 2 x g_{\theta} * x \approx \theta_0(I - D^{-\frac{1}{2}} A D^{-\frac{1}{2}})x = \theta_0 D^{-\frac{1}{2}} (D - A) D^{-\frac{1}{2}} x gθxθ0(ID21AD21)x=θ0D21(DA)D21x

为了避免梯度消失/爆炸问题,添加自环并重新归一化:

g θ ∗ x ≈ θ 0 D ~ − 1 2 A ~ D ~ − 1 2 x g_{\theta} * x \approx \theta_0 \tilde{D}^{-\frac{1}{2}} \tilde{A} \tilde{D}^{-\frac{1}{2}} x gθxθ0D~21A~D~21x

其中 A ~ = A + I \tilde{A} = A + I A~=A+I D ~ i i = ∑ j A ~ i j \tilde{D}_{ii} = \sum_j \tilde{A}_{ij} D~ii=jA~ij

这样,GCN的层间传播规则为:

H ( k ) = σ ( D ~ − 1 2 A ~ D ~ − 1 2 H ( k − 1 ) W ( k ) ) H^{(k)} = \sigma(\tilde{D}^{-\frac{1}{2}} \tilde{A} \tilde{D}^{-\frac{1}{2}} H^{(k-1)} W^{(k)}) H(k)=σ(D~21A~D~21H(k1)W(k))

对于单个节点 v v v,聚合公式为:

h v ( k ) = σ ( ∑ u ∈ N ( v ) ∪ { v } 1 d ~ v d ~ u h u ( k − 1 ) W ( k ) ) h_v^{(k)} = \sigma\left(\sum_{u \in \mathcal{N}(v) \cup \{v\}} \frac{1}{\sqrt{\tilde{d}_v \tilde{d}_u}} h_u^{(k-1)} W^{(k)}\right) hv(k)=σ uN(v){v}d~vd~u 1hu(k1)W(k)

其中 d ~ v \tilde{d}_v d~v 是节点 v v v 添加自环后的度。

GraphSAGE的聚合公式

GraphSAGE提出了多种聚合函数,下面我们推导其中的均值聚合:

  1. 邻居聚合: h N ( v ) ( k ) = 1 ∣ N ( v ) ∣ ∑ u ∈ N ( v ) h u ( k − 1 ) h_{\mathcal{N}(v)}^{(k)} = \frac{1}{|\mathcal{N}(v)|} \sum_{u \in \mathcal{N}(v)} h_u^{(k-1)} hN(v)(k)=N(v)1uN(v)hu(k1)
  2. 拼接自身表示: z v ( k ) = [ h v ( k − 1 ) ∥ h N ( v ) ( k ) ] z_v^{(k)} = [h_v^{(k-1)} \| h_{\mathcal{N}(v)}^{(k)}] zv(k)=[hv(k1)hN(v)(k)]
  3. 线性变换和激活: h v ( k ) = σ ( W ( k ) ⋅ z v ( k ) ) h_v^{(k)} = \sigma(W^{(k)} \cdot z_v^{(k)}) hv(k)=σ(W(k)zv(k))
  4. L2正则化: h v ( k ) = h v ( k ) / ∥ h v ( k ) ∥ 2 h_v^{(k)} = h_v^{(k)} / \|h_v^{(k)}\|_2 hv(k)=hv(k)/∥hv(k)2

完整的均值聚合公式为:

h v ( k ) = σ ( W ( k ) ⋅ [ h v ( k − 1 ) ∥ 1 ∣ N ( v ) ∣ ∑ u ∈ N ( v ) h u ( k − 1 ) ] ) h_v^{(k)} = \sigma\left(W^{(k)} \cdot \left[h_v^{(k-1)} \Big\| \frac{1}{|\mathcal{N}(v)|} \sum_{u \in \mathcal{N}(v)} h_u^{(k-1)}\right]\right) hv(k)=σ W(k) hv(k1) N(v)1uN(v)hu(k1)

GAT的注意力聚合公式

图注意力网络(GAT)引入了注意力机制来加权聚合邻居信息:

  1. 计算注意力系数: e v u = a ( W h v , W h u ) e_{vu} = a(W h_v, W h_u) evu=a(Whv,Whu),其中 a a a 是注意力函数
  2. 归一化注意力系数: α v u = softmax u ( e v u ) = exp ⁡ ( e v u ) ∑ w ∈ N ( v ) exp ⁡ ( e v w ) \alpha_{vu} = \text{softmax}_u(e_{vu}) = \frac{\exp(e_{vu})}{\sum_{w \in \mathcal{N}(v)} \exp(e_{vw})} αvu=softmaxu(evu)=wN(v)exp(evw)exp(evu)
  3. 加权聚合: h v ( k ) = σ ( ∑ u ∈ N ( v ) α v u W h u ( k − 1 ) ) h_v^{(k)} = \sigma\left(\sum_{u \in \mathcal{N}(v)} \alpha_{vu} W h_u^{(k-1)}\right) hv(k)=σ(uN(v)αvuWhu(k1))

具体地,使用LeakyReLU作为激活函数的单头注意力机制为:

e v u = LeakyReLU ( a ⃗ T [ W h v ∥ W h u ] ) e_{vu} = \text{LeakyReLU}(\vec{a}^T [W h_v \| W h_u]) evu=LeakyReLU(a T[WhvWhu])

多头注意力机制为:

h v ( k ) = ∥ i = 1 K σ ( ∑ u ∈ N ( v ) α v u i W i h u ( k − 1 ) ) h_v^{(k)} = \Big\|_{i=1}^{K} \sigma\left(\sum_{u \in \mathcal{N}(v)} \alpha_{vu}^i W^i h_u^{(k-1)}\right) hv(k)= i=1Kσ uN(v)αvuiWihu(k1)

其中 ∥ \| 表示拼接, K K K 是注意力头数。

聚合函数对比分析

下表总结了不同GNN模型的聚合函数特点:

模型聚合函数优点缺点
GCN ∑ u ∈ N ( v ) ∪ { v } 1 d ~ v d ~ u h u W \sum_{u \in \mathcal{N}(v) \cup \{v\}} \frac{1}{\sqrt{\tilde{d}_v \tilde{d}_u}} h_u W uN(v){v}d~vd~u 1huW简单有效,考虑了节点度所有邻居权重相同
GraphSAGE σ ( W [ h v ∣ AGG ( { h u , ∀ u ∈ N ( v ) } ) ] ) \sigma(W [h_v | \text{AGG}(\{h_u, \forall u \in \mathcal{N}(v)\})]) σ(W[hvAGG({hu,uN(v)})])灵活多样,保留中心节点信息计算复杂度较高
GAT σ ( ∑ u ∈ N ( v ) α v u W h u ) \sigma(\sum_{u \in \mathcal{N}(v)} \alpha_{vu} W h_u) σ(uN(v)αvuWhu)自适应学习边的重要性需要额外的注意力计算
GIN MLP ( ( 1 + ϵ ) ⋅ h v + ∑ u ∈ N ( v ) h u ) \text{MLP}((1+\epsilon) \cdot h_v + \sum_{u \in \mathcal{N}(v)} h_u) MLP((1+ϵ)hv+uN(v)hu)最具表达能力,等价于WL测试参数较多,容易过拟合

PyTorch实现图卷积网络

下面我们使用PyTorch和PyTorch Geometric库实现一个图卷积网络,用于节点分类任务。

环境准备

首先,安装必要的库:

# 安装PyTorch Geometric
# pip install torch-scatter torch-sparse torch-cluster torch-spline-conv torch-geometric

数据集准备

我们使用Cora数据集,它是一个引用网络数据集,包含2708个科学出版物节点和5429条引用边。每个出版物分为7个类别之一,每个出版物节点有1433个词袋特征。

import torch
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid
from torch_geometric.transforms import NormalizeFeatures

# 加载Cora数据集
dataset = Planetoid(root='./data/Cora', name='Cora', transform=NormalizeFeatures())
data = dataset[0]

print(f'数据集信息:')
print(f'  节点数: {data.num_nodes}')
print(f'  边数: {data.num_edges // 2}')  # 无向图,边数除以2
print(f'  节点特征维度: {data.num_features}')
print(f'  类别数: {dataset.num_classes}')
print(f'  训练节点数: {data.train_mask.sum().item()}')
print(f'  验证节点数: {data.val_mask.sum().item()}')
print(f'  测试节点数: {data.test_mask.sum().item()}')

实现GCN模型

下面实现一个两层的GCN模型:

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch_geometric.nn import GCNConv

class GCN(nn.Module):
    def __init__(self, in_channels, hidden_channels, out_channels, dropout=0.5):
        super(GCN, self).__init__()
        self.conv1 = GCNConv(in_channels, hidden_channels)
        self.conv2 = GCNConv(hidden_channels, out_channels)
        self.dropout = dropout
        
    def forward(self, x, edge_index):
        # 第一层GCN
        x = self.conv1(x, edge_index)
        x = F.relu(x)
        x = F.dropout(x, p=self.dropout, training=self.training)
        
        # 第二层GCN
        x = self.conv2(x, edge_index)
        
        return x
    
    def reset_parameters(self):
        self.conv1.reset_parameters()
        self.conv2.reset_parameters()

PyTorch实现图卷积网络

手动实现GCN层

为了更好地理解GCN的工作原理,我们也手动实现一个GCN层:

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch_sparse import SparseTensor, matmul

class CustomGCNConv(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(CustomGCNConv, self).__init__()
        self.in_channels = in_channels
        self.out_channels = out_channels
        
        # 线性变换权重
        self.weight = nn.Parameter(torch.Tensor(in_channels, out_channels))
        self.bias = nn.Parameter(torch.Tensor(out_channels))
        
        self.reset_parameters()
        
    def reset_parameters(self):
        nn.init.xavier_uniform_(self.weight)
        nn.init.zeros_(self.bias)
    
    def forward(self, x, edge_index):
        # 步骤1: 线性变换
        x = torch.matmul(x, self.weight)
        
        # 步骤2: 计算归一化系数
        row, col = edge_index
        deg = torch.bincount(row, minlength=x.size(0))
        deg_inv_sqrt = deg.pow(-0.5)
        deg_inv_sqrt[deg_inv_sqrt == float('inf')] = 0
        
        # 步骤3: 消息传递
        out = torch.zeros_like(x)
        for i in range(edge_index.size(1)):
            # 获取边的源节点和目标节点
            src, dst = edge_index[0, i], edge_index[1, i]
            # 计算归一化系数
            norm = deg_inv_sqrt[src] * deg_inv_sqrt[dst]
            # 更新目标节点的表示
            out[dst] += norm * x[src]
        
        # 步骤4: 添加偏置
        out += self.bias
        
        return out

训练模型

下面我们实现训练和评估函数:

def train(model, data, optimizer):
    model.train()
    optimizer.zero_grad()
    
    # 前向传播
    out = model(data.x, data.edge_index)
    
    # 计算训练损失
    loss = F.cross_entropy(out[data.train_mask], data.y[data.train_mask])
    
    # 反向传播
    loss.backward()
    optimizer.step()
    
    return loss.item()

def test(model, data):
    model.eval()
    
    with torch.no_grad():
        out = model(data.x, data.edge_index)
        
        # 训练集、验证集和测试集的预测
        pred = out.argmax(dim=1)
        
        # 计算准确率
        train_acc = pred[data.train_mask].eq(data.y[data.train_mask]).sum().item() / data.train_mask.sum().item()
        val_acc = pred[data.val_mask].eq(data.y[data.val_mask]).sum().item() / data.val_mask.sum().item()
        test_acc = pred[data.test_mask].eq(data.y[data.test_mask]).sum().item() / data.test_mask.sum().item()
        
    return train_acc, val_acc, test_acc

完整的训练和评估流程

现在,我们将所有内容整合起来,实现完整的训练和评估流程:

import torch
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid
from torch_geometric.transforms import NormalizeFeatures
import matplotlib.pyplot as plt
import numpy as np

# 设置随机种子,确保结果可复现
torch.manual_seed(42)

# 加载Cora数据集
dataset = Planetoid(root='./data/Cora', name='Cora', transform=NormalizeFeatures())
data = dataset[0]

# 初始化GCN模型
model = GCN(in_channels=dataset.num_features, 
            hidden_channels=16, 
            out_channels=dataset.num_classes)

# 使用Adam优化器
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)

# 训练参数
num_epochs = 200
best_val_acc = 0
best_test_acc = 0

# 记录损失和准确率
train_losses = []
train_accs = []
val_accs = []
test_accs = []

# 训练循环
for epoch in range(num_epochs):
    # 训练一个轮次
    loss = train(model, data, optimizer)
    train_losses.append(loss)
    
    # 评估模型
    train_acc, val_acc, test_acc = test(model, data)
    train_accs.append(train_acc)
    val_accs.append(val_acc)
    test_accs.append(test_acc)
    
    # 保存最佳模型
    if val_acc > best_val_acc:
        best_val_acc = val_acc
        best_test_acc = test_acc
    
    # 打印训练进度
    if (epoch+1) % 10 == 0:
        print(f'Epoch: {epoch+1:03d}, Loss: {loss:.4f}, Train Acc: {train_acc:.4f}, Val Acc: {val_acc:.4f}, Test Acc: {test_acc:.4f}')

print(f'最佳验证集准确率: {best_val_acc:.4f}')
print(f'对应的测试集准确率: {best_test_acc:.4f}')

# 绘制训练曲线
plt.figure(figsize=(12, 5))

# 损失曲线
plt.subplot(1, 2, 1)
plt.plot(train_losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss')

# 准确率曲线
plt.subplot(1, 2, 2)
plt.plot(train_accs, label='Train')
plt.plot(val_accs, label='Validation')
plt.plot(test_accs, label='Test')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Accuracy')
plt.legend()

plt.tight_layout()
plt.savefig('gcn_training_curves.png')
plt.show()

成功训练后,我们可以从训练曲线中看到模型逐渐收敛,在Cora数据集上GCN模型通常能达到80%以上的测试准确率。

代码运行流程与可视化

为了更直观地理解GCN的工作原理,下面我们可视化模型的消息传递过程和节点嵌入。

消息传递流程图

下面是GCN消息传递的流程图:

┌───────────────────┐       ┌───────────────────┐       ┌───────────────────┐
│    节点特征 X      │       │   归一化邻接矩阵    │       │     权重矩阵 W     │
│                   │       │                   │       │                   │
│  Node 1: [x1]     │       │     [A_norm]      │       │     [W_matrix]    │
│  Node 2: [x2]     │       │                   │       │                   │
│  ...              │       │                   │       │                   │
│  Node n: [xn]     │       │                   │       │                   │
└─────────┬─────────┘       └─────────┬─────────┘       └─────────┬─────────┘
          │                           │                           │
          │                           │                           │
          ▼                           │                           │
┌───────────────────┐                 │                           │
│    特征传播        │◄────────────────┘                           │
│                   │                                             │
│  X' = A_norm * X  │                                             │
└─────────┬─────────┘                                             │
          │                                                       │
          │                                                       │
          ▼                                                       │
┌───────────────────┐                                             │
│    特征变换        │◄────────────────────────────────────────────┘
│                   │
│  Z = X' * W       │
└─────────┬─────────┘
          │
          │
          ▼
┌───────────────────┐
│    非线性激活       │
│                   │
│  H = ReLU(Z)      │
└─────────┬─────────┘
          │
          │
          ▼
┌───────────────────┐
│    Dropout        │
│                   │
│  H_drop = Dropout(H) │
└───────────────────┘

可视化节点嵌入

我们还可以使用t-SNE算法可视化GCN提取的节点嵌入,看看不同类别的节点是否被很好地分开:

from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import numpy as np

def visualize_embeddings(model, data):
    model.eval()
    
    with torch.no_grad():
        # 获取最后一层GCN前的节点嵌入
        x = model.conv1(data.x, data.edge_index)
        x = F.relu(x)
        
        # 使用t-SNE降维到2D空间
        tsne = TSNE(n_components=2, random_state=42)
        x_2d = tsne.fit_transform(x.detach().numpy())
        
        # 获取节点类别
        y = data.y.numpy()
        
        # 绘制嵌入
        plt.figure(figsize=(10, 8))
        for i in range(dataset.num_classes):
            mask = y == i
            plt.scatter(x_2d[mask, 0], x_2d[mask, 1], label=f'Class {i}')
        
        plt.legend()
        plt.title('Node Embeddings Visualization using t-SNE')
        plt.savefig('node_embeddings.png')
        plt.show()

# 训练完成后可视化
visualize_embeddings(model, data)

注意力权重可视化(对于GAT)

如果使用GAT模型,我们可以可视化注意力权重,看看模型如何分配注意力:

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch_geometric.nn import GATConv
import networkx as nx
import matplotlib.pyplot as plt

class GAT(nn.Module):
    def __init__(self, in_channels, hidden_channels, out_channels, heads=8, dropout=0.6):
        super(GAT, self).__init__()
        self.conv1 = GATConv(in_channels, hidden_channels, heads=heads, dropout=dropout)
        # 输出层使用单头注意力
        self.conv2 = GATConv(hidden_channels * heads, out_channels, heads=1, concat=False, dropout=dropout)
        self.dropout = dropout
        
    def forward(self, x, edge_index):
        x = F.dropout(x, p=self.dropout, training=self.training)
        x = F.elu(self.conv1(x, edge_index))
        x = F.dropout(x, p=self.dropout, training=self.training)
        x = self.conv2(x, edge_index)
        return x
    
    def get_attention_weights(self, x, edge_index):
        # 获取第一层的注意力权重
        _, attention_weights = self.conv1(x, edge_index, return_attention_weights=True)
        return attention_weights

def visualize_attention(model, data, num_nodes=10):
    model.eval()
    
    # 获取注意力权重
    edge_index, attention_weights = model.get_attention_weights(data.x, data.edge_index)
    attention_weights = attention_weights.mean(dim=1).detach().numpy()  # 对多头注意力取平均
    
    # 创建子图,只包含前num_nodes个节点
    subset = list(range(num_nodes))
    sub_edge_index = []
    sub_weights = []
    
    for i, (src, dst) in enumerate(edge_index.t().numpy()):
        if src in subset and dst in subset:
            sub_edge_index.append((src, dst))
            sub_weights.append(attention_weights[i])
    
    # 创建图
    G = nx.DiGraph()
    for node in subset:
        G.add_node(node, label=str(node), color=data.y[node].item())
    
    # 添加边
    for i, (src, dst) in enumerate(sub_edge_index):
        G.add_edge(src, dst, weight=sub_weights[i])
    
    # 获取节点颜色
    node_colors = [plt.cm.tab10(G.nodes[node]['color']) for node in G.nodes]
    
    # 获取边权重
    edge_weights = [G[u][v]['weight'] * 5 for u, v in G.edges]
    
    # 绘制图
    plt.figure(figsize=(10, 8))
    pos = nx.spring_layout(G, seed=42)
    nx.draw_networkx_nodes(G, pos, node_color=node_colors, node_size=300)
    nx.draw_networkx_labels(G, pos)
    nx.draw_networkx_edges(G, pos, width=edge_weights, alpha=0.7, 
                          edge_color=edge_weights, edge_cmap=plt.cm.Blues)
    
    plt.title('GAT Attention Weights Visualization')
    plt.axis('off')
    plt.savefig('attention_weights.png')
    plt.show()

实验与结果分析

在这一部分,我们将比较不同GNN模型在节点分类任务上的性能,并分析实验结果。

实验设置

我们在Cora数据集上比较以下模型:

  1. 多层感知机(MLP):仅使用节点特征,不考虑图结构
  2. 图卷积网络(GCN):综合考虑节点特征和图结构
  3. 图注意力网络(GAT):使用注意力机制加权聚合邻居信息
  4. GraphSAGE:使用不同的聚合函数

所有模型都使用两层神经网络,隐藏层维度为16,训练200轮。以下是实验代码:

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch_geometric.nn import GCNConv, GATConv, SAGEConv
from torch_geometric.datasets import Planetoid
import matplotlib.pyplot as plt
import numpy as np
import time

# 设置随机种子
torch.manual_seed(42)

# 加载Cora数据集
dataset = Planetoid(root='./data/Cora', name='Cora', transform=NormalizeFeatures())
data = dataset[0]

# MLP模型
class MLP(nn.Module):
    def __init__(self, in_channels, hidden_channels, out_channels, dropout=0.5):
        super(MLP, self).__init__()
        self.lin1 = nn.Linear(in_channels, hidden_channels)
        self.lin2 = nn.Linear(hidden_channels, out_channels)
        self.dropout = dropout
        
    def forward(self, x, edge_index=None):
        x = F.dropout(x, p=self.dropout, training=self.training)
        x = F.relu(self.lin1(x))
        x = F.dropout(x, p=self.dropout, training=self.training)
        x = self.lin2(x)
        return x

# GCN, GAT和GraphSAGE模型定义同前面的代码

# 实验函数
def run_experiment(model_name, model, data, epochs=200, lr=0.01, weight_decay=5e-4):
    optimizer = torch.optim.Adam(model.parameters(), lr=lr, weight_decay=weight_decay)
    
    best_val_acc = 0
    best_test_acc = 0
    train_losses = []
    train_accs = []
    val_accs = []
    test_accs = []
    
    start_time = time.time()
    
    for epoch in range(epochs):
        # 训练
        model.train()
        optimizer.zero_grad()
        out = model(data.x, data.edge_index)
        loss = F.cross_entropy(out[data.train_mask], data.y[data.train_mask])
        loss.backward()
        optimizer.step()
        
        train_losses.append(loss.item())
        
        # 评估
        model.eval()
        with torch.no_grad():
            pred = out.argmax(dim=1)
            train_acc = pred[data.train_mask].eq(data.y[data.train_mask]).sum().item() / data.train_mask.sum().item()
            val_acc = pred[data.val_mask].eq(data.y[data.val_mask]).sum().item() / data.val_mask.sum().item()
            test_acc = pred[data.test_mask].eq(data.y[data.test_mask]).sum().item() / data.test_mask.sum().item()
            
            train_accs.append(train_acc)
            val_accs.append(val_acc)
            test_accs.append(test_acc)
            
            if val_acc > best_val_acc:
                best_val_acc = val_acc
                best_test_acc = test_acc
    
    end_time = time.time()
    training_time = end_time - start_time
    
    print(f'{model_name}:')
    print(f'  最佳验证集准确率: {best_val_acc:.4f}')
    print(f'  对应的测试集准确率: {best_test_acc:.4f}')
    print(f'  训练时间: {training_time:.2f} 秒')
    
    return {
        'model_name': model_name,
        'train_losses': train_losses,
        'train_accs': train_accs,
        'val_accs': val_accs,
        'test_accs': test_accs,
        'best_val_acc': best_val_acc,
        'best_test_acc': best_test_acc,
        'training_time': training_time
    }

# 运行实验
results = []

# MLP
model = MLP(in_channels=dataset.num_features, hidden_channels=16, out_channels=dataset.num_classes)
results.append(run_experiment('MLP', model, data))

# GCN
model = GCN(in_channels=dataset.num_features, hidden_channels=16, out_channels=dataset.num_classes)
results.append(run_experiment('GCN', model, data))

# GAT
model = GAT(in_channels=dataset.num_features, hidden_channels=8, out_channels=dataset.num_classes)
results.append(run_experiment('GAT', model, data))

# GraphSAGE
model = GraphSAGE(in_channels=dataset.num_features, hidden_channels=16, out_channels=dataset.num_classes)
results.append(run_experiment('GraphSAGE', model, data))

# 绘制结果
def plot_results(results):
    plt.figure(figsize=(15, 10))
    
    # 损失曲线
    plt.subplot(2, 2, 1)
    for result in results:
        plt.plot(result['train_losses'], label=result['model_name'])
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.title('Training Loss')
    plt.legend()
    
    # 训练准确率
    plt.subplot(2, 2, 2)
    for result in results:
        plt.plot(result['train_accs'], label=result['model_name'])
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.title('Training Accuracy')
    plt.legend()
    
    # 验证准确率
    plt.subplot(2, 2, 3)
    for result in results:
        plt.plot(result['val_accs'], label=result['model_name'])
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.title('Validation Accuracy')
    plt.legend()
    
    # 测试准确率
    plt.subplot(2, 2, 4)
    for result in results:
        plt.plot(result['test_accs'], label=result['model_name'])
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.title('Test Accuracy')
    plt.legend()
    
    plt.tight_layout()
    plt.savefig('model_comparison.png')
    plt.show()
    
    # 绘制模型比较柱状图
    plt.figure(figsize=(10, 6))
    model_names = [result['model_name'] for result in results]
    test_accs = [result['best_test_acc'] for result in results]
    training_times = [result['training_time'] for result in results]
    
    x = np.arange(len(model_names))
    width = 0.35
    
    fig, ax1 = plt.subplots(figsize=(10, 6))
    ax2 = ax1.twinx()
    
    bars1 = ax1.bar(x - width/2, test_accs, width, label='Test Accuracy', color='skyblue')
    bars2 = ax2.bar(x + width/2, training_times, width, label='Training Time (s)', color='salmon')
    
    ax1.set_xlabel('Model')
    ax1.set_ylabel('Test Accuracy')
    ax2.set_ylabel('Training Time (s)')
    ax1.set_title('Model Performance Comparison')
    ax1.set_xticks(x)
    ax1.set_xticklabels(model_names)
    
    ax1.legend(loc='upper left')
    ax2.legend(loc='upper right')
    
    plt.tight_layout()
    plt.savefig('model_performance.png')
    plt.show()

# 绘制结果
plot_results(results)

实验结果与分析

通过实验,我们对比了不同GNN模型的性能,结果通常如下:

  1. 准确率对比

    • MLP:~70%(仅使用节点特征)
    • GCN:~81%(利用图结构信息)
    • GAT:~83%(注意力机制带来提升)
    • GraphSAGE:~80%(灵活的聚合函数)
  2. 训练时间对比

    • MLP:最快(不需要处理图结构)
    • GCN:中等(简单的邻居聚合)
    • GAT:最慢(注意力机制增加计算量)
    • GraphSAGE:较慢(复杂的聚合操作)
  3. 模型收敛性

    • GAT通常收敛较慢但最终性能更好
    • GCN收敛速度适中,性能稳定
    • GraphSAGE的收敛行为取决于聚合函数的选择

从实验结果可以看出:

  1. 利用图结构信息(GCN、GAT、GraphSAGE)比仅使用节点特征(MLP)效果更好,证明了图结构在节点分类任务中的重要性。

  2. 注意力机制(GAT)能够自适应地学习边的重要性,在异构图或噪声较大的图上尤为有效。

  3. 不同的聚合函数(GraphSAGE)适用于不同的图结构和任务,需要根据具体场景选择。

  4. 模型复杂度与性能之间存在权衡,更复杂的模型(如GAT)可能性能更好但训练更慢。

总结与进阶方向

在今天的学习中,我们深入探讨了图神经网络的基础知识,特别是图卷积网络的消息传递机制,对比了谱域与空域方法的计算差异,推导了节点嵌入的聚合公式,并通过PyTorch实现了GCN模型。

主要学习要点回顾

  1. 图的基本概念与表示

    • 图由节点和边组成,可以用邻接矩阵、邻接表等方式表示
    • 图的类型包括无向图/有向图、带权图/无权图、同构图/异构图等
  2. 图卷积网络的消息传递机制

    • 消息传递是GNN的核心思想,节点通过聚合邻居信息来更新自己的表示
    • GCN使用归一化的邻居聚合来传递信息
  3. 谱域与空域方法对比

    • 谱域方法基于图信号处理理论,将图卷积定义为图傅里叶域中的乘积
    • 空域方法直接在图的节点和边上定义卷积操作,更直观且计算效率更高
    • GCN可以同时从谱域和空域解释,是连接两种方法的桥梁
  4. 节点嵌入的聚合公式推导

    • GCN聚合:归一化的邻居加权求和
    • GraphSAGE聚合:灵活的聚合函数(均值、最大值、LSTM等)
    • GAT聚合:基于注意力机制的加权聚合
  5. PyTorch实现与实验分析

    • 使用PyTorch和PyTorch Geometric实现GNN模型
    • 在节点分类任务上比较不同GNN模型的性能
    • 可视化节点嵌入和注意力权重,帮助理解模型行为

进阶方向

  1. 更复杂的GNN架构

    • 图Transformer:结合Transformer架构的注意力机制
    • 图神经常微分方程(GNODE):连续深度的图神经网络
    • 深度图生成模型:如图变分自编码器(GVAE)和图生成对抗网络(GraphGAN)
  2. 大规模图学习

    • 图采样技术:Neighbor Sampling、GraphSAINT等
    • 分布式GNN训练:如DistDGL、PyTorch Geometric Temporal等
    • 图神经网络压缩:模型量化、知识蒸馏等
  3. 图神经网络的可解释性

    • GNN Explainer:解释GNN的预测
    • 注意力可视化:理解模型关注的图结构部分
    • 子图挖掘:寻找对预测最有影响的子图结构
  4. 图神经网络的鲁棒性与公平性

    • 对抗攻击与防御:增强GNN对结构扰动的鲁棒性
    • 去偏技术:减少GNN中的社会偏见
    • 不确定性估计:量化GNN预测的可靠性
  5. 结合领域知识的图学习

    • 分子图学习:结合化学知识的GNN
    • 知识图谱推理:融合逻辑规则的GNN
    • 脑网络分析:结合神经科学知识的GNN

清华大学全五版的《DeepSeek教程》完整的文档需要的朋友,关注我私信:deepseek 即可获得。

怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值