想象一下,你是一家景区公司的地图设计师,要给游客做一张“智慧导览图”。手机屏幕就这么大,一次只能显示一层信息。游客点一下“全景”按钮,看到的是整个景区——山顶、湖区、森林三条主路线;再放大一级,看到的是每个区域的景点分布;继续放大,连某棵古树、某座石桥的细节都清晰可见。每一层缩放都提供了不同粒度的信息,合在一起,游客才对景区有了完整的认知。 图金字塔做的正是这件事——只不过对象不是景区,而是图数据。社交网络、分子结构、交通路网,这些图数据同样需要“多尺度”地看:全局看社区结构,局部看节点细节,中间看模块划分。图金字塔通过一层层地“粗化”图(把相似的节点合并成超节点),构建出一个多分辨率的图层次结构,让神经网络既能抓住大格局,又能保留小细节。这就像给你的 AI 配上了一副“变焦眼镜”,从远景拉到特写,画面始终清晰。

岗位关联

掌握图金字塔,你就掌握了让 GNN 同时看全局与局部的核心能力。无论是分子性质预测还是社交网络挖掘,多尺度特征提取都是从工程调参迈向算法设计的必修课。

目录
  1. 可视化演示
  2. 图金字塔概述
  3. 图粗化方法
  4. 多尺度特征融合
  5. 金字塔池化
  6. 金字塔GNN架构
  7. 应用场景
  8. 挑战与展望
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示编译器从源代码到目标代码的完整处理流程,涵盖词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成六大阶段。

本节配有交互动效(进入视口后自动加载,约 3KB)。

词法分析(Lexical Analysis) 扫描源代码字符流 识别Token:关键字、标识符、常量 output = Token序列 消除空白和注释

图金字塔概述

打开手机地图,你可以从“全国概览”一路缩放到“街道实景”,每一级缩放都呈现不同的信息密度。图金字塔的灵感正来源于此——它把一张复杂的图,通过逐层粗化构建成一个“多级缩放的导览图”:底层是原始图,保留每个节点的细节;往上一层层变“粗糙”,节点被合并成超节点,直到顶层只剩下寥寥几个最抽象的全局结构。

在传统 GNN 中,消息传递的层数决定了模型能看到的“视野范围”:层数少,只能看到邻居的邻居;层数多,所有节点特征趋于同质化,局部细节被“平均”掉了。图金字塔绕开了这个两难困境:它在不同尺度上分别做特征提取,然后把这些特征组合起来——既保留了节点级的精细信息,又获取了社区级的全局结构,就像一份同时印着全景图和街区图的导览手册。

图金字塔的核心流程可以概括为三步:粗化(Coarsening)、池化(Pooling)和融合(Fusion)。粗化负责构建多层结构,池化负责在每层提取关键特征,融合负责把各层特征整合成最终表示。

回到地图的比喻:没有图金字塔的 GNN 就像只提供单一缩放级别的地图,要么看不到全局路线,要么看不到具体景点。图金字塔让模型手里同时握着“全景图”和“特写图”,自然能做出更明智的判断。

zG=[P1(H1)⊕P2(H2)⊕⋯⊕PL(HL)]z_G = \big[ P_1(H_1) \oplus P_2(H_2) \oplus \cdots \oplus P_L(H_L) \big]
这个公式的含义是:把第 1 层到第 L 层的图特征分别池化后拼接在一起,构成最终的多尺度图表示。
  1. 理解 \(H_l\)表示第 \(l\) 层图的节点特征矩阵,每一行是一个节点的特征向量。粗化后节点变少,所以 \(H_l\) 的行数逐层递减。
    Hl∈Rnl×dlH_l \in \mathbb{R}^{n_l \times d_l}
  2. 理解 \(P_l\)表示第 \(l\) 层的池化操作,作用是把该层的所有节点特征压缩成一个向量——相当于从这一层“导览图”中提取要点。
    Pl:Rnl×dl→RdlP_l: \mathbb{R}^{n_l \times d_l} \rightarrow \mathbb{R}^{d_l}
  3. 理解 \(\oplus\)表示向量拼接操作,把不同层提取到的特征向量首尾相连,组成一个更长维度的向量——就像把不同缩放级别看到的画面拼成一幅“全景长图”。
    zG=[z(1);z(2);⋯ ;z(L)]∈R∑ldlz_G = [z^{(1)}; z^{(2)}; \cdots; z^{(L)}] \in \mathbb{R}^{\sum_l d_l}

图粗化方法

地图简化时,设计师会把相邻的、特征相似的区域合并成一个“通票区”,比如把几个相邻的小景点圈成一个大的游览区。图粗化正是这个逻辑:找到图中的“相似节点”(邻居密集、特征相近),把它们合并成一个超节点,从而得到一张更小的“简化图”。

最直接的粗化思路是节点选择 + 节点合并。第一步,按某种重要性指标(如度中心性、特征显著度)选出“代表节点”;第二步,把每个代表节点周围的邻居归入它所在的簇,一个簇就是一个超节点。合并完成后,新图里超节点之间的边权重,由原图中跨簇的边累加得到。

更优雅的做法来自谱聚类粗化。谱聚类先把图的拉普拉斯矩阵做特征分解,得到节点的“谱嵌入”,再在谱空间里做聚类——这相当于先在地图上画出“等高线”,再把同一等高线区域内的景点合并。DeepWalk 池化则换了一条路:先用随机游走学出节点的向量表示,再对向量做聚类合并。三种方法路线不同,但目标一致:让粗化后的图最大程度保留原图的结构信息。

粗化过程中,节点的归属关系被记录在一个分配矩阵中,它是连接相邻两层的桥梁:

这个“两步折叠”就像景区合并:第一步把每个小片区的内部连接数清点出来,第二步再把片区之间的通道连接起来。两次折叠之后,一张新地图就诞生了,比原来小,但骨架还在。

Al+1=SlTAlSlA_{l+1} = S_l^{\mathsf{T}} A_l S_l
这个公式描述相邻两层图的转换关系:分配矩阵 \(S\) 把第 \(l\) 层的节点折叠成第 \(l+1\) 层的超节点。
  1. 理解 \(S_l\)分配矩阵,尺寸为 \(n_l \times n_{l+1}\),每一列对应第 \(l+1\) 层的一个超节点,列内非零元素表示哪些底层节点被合并进来。
    Sl∈Rnl×nl+1S_l \in \mathbb{R}^{n_l \times n_{l+1}}
  2. 理解 \(A_l\)第 \(l\) 层的邻接矩阵,记录了该层节点间的连接关系(边的权重)。
    Al∈Rnl×nlA_l \in \mathbb{R}^{n_l \times n_l}
  3. 计算 \(S_l^{\mathsf{T}} A_l\)这是第一步“折叠”,把每个超节点内所有节点的连边汇总到超节点上。
    (SlTAl)ij=∑kSkiAkj(S_l^{\mathsf{T}} A_l)_{ij} = \sum_k S_{ki} A_{kj}
  4. 计算 \((S_l^{\mathsf{T}} A_l) S_l\)这是第二步“折叠”,把边的两个端点都归并到超节点,得到新图的邻接矩阵。
    (SlTAlSl)ij=∑p∈Ci∑q∈CjApq(S_l^{\mathsf{T}} A_l S_l)_{ij} = \sum_{p\in C_i} \sum_{q\in C_j} A_{pq}

多尺度特征融合

你站在景区观景台上,用三台相机分别装广角、标准和长焦镜头拍下三张照片。单看任何一张都不完整,但把三张照片的信息综合起来,你就能在脑中拼出景区的完整风貌。多尺度特征融合干的就是这件事——把图金字塔每一层提取到的特征按一定策略合并,形成最终的多尺度表示。

最简单的融合方式是特征拼接:把每一层池化后的特征向量首尾相连,维度直接叠加。拼接的优点是实现简单、信息无损,但缺点也很明显——层数多了,向量维度膨胀,计算开销增大。

逐层聚合则更聪明:它不是把各层“并列”起来,而是把各层“串”起来。从最顶层开始,把高层特征向下传播,与低层特征逐层加权求和。这个过程类似导游讲解时不断“回到上一张幻灯片”——每讲一层细节,就回顾一下全局背景。跳跃连接(Skip Connection)在聚合时额外保留原始层的信息通道,像地图上的“快速路”一样,让底层细节不经中间层稀释,直接到达顶层融合处。

一个经典的融合方式是对各层特征做加权平均,其中权重由注意力机制自动学习:

用地图比喻收尾:特征拼接像是把三张照片贴在同一张相册页上,逐层聚合像是把照片按时间顺序叠成延时摄影,而注意力融合则像是让 AI 当摄影师,自动决定每张照片该占多大版面。三种方式各有千秋,但目标一致:让不同尺度的信息在最终表示中各得其所。

Hfused=∑l=1Lαl⋅Pool(Hl),αl=exp⁡(wl)∑k=1Lexp⁡(wk)H_{\text{fused}} = \sum_{l=1}^{L} \alpha_l \cdot \text{Pool}(H_l), \quad \alpha_l = \frac{\exp(w_l)}{\sum_{k=1}^{L} \exp(w_k)}
每一层的特征先池化成向量,再用一组可学习的权重 \(\alpha_l\) 加权求和。权重越大,说明该层特征对最终任务越重要。
  1. 理解 \(\text{Pool}(H_l)\)对第 \(l\) 层节点特征做池化(如求和、取平均或取最大),得到该层图的全局特征向量。
    Pool(Hl)=1nl∑i=1nlhi(l)\text{Pool}(H_l) = \frac{1}{n_l}\sum_{i=1}^{n_l} h_i^{(l)}
  2. 理解 \(\alpha_l\)第 \(l\) 层的融合权重,它是一个 0 到 1 之间的数,且所有层的权重之和为 1,保证融合后特征的尺度不变。
    ∑l=1Lαl=1,αl≥0\sum_{l=1}^{L} \alpha_l = 1, \quad \alpha_l \geq 0
  3. 计算 \(w_l\) 的 softmax 归一化先把每个 \(w_l\) 取指数放大差异,再除以总和做归一化,让权重分布变得“尖锐”——重要的层权重高,次要的层权重低。
    αl=softmax⁡(w1,…,wL)l\alpha_l = \operatorname{softmax}(w_1, \ldots, w_L)_l

金字塔池化

景区里有三个观景台:山顶的“一览台”能看到整个景区轮廓,半山的“云台”能看到主要分区,山脚的“近水台”能看到每个景点的细节。金字塔池化模拟的正是这种“站在不同高度俯瞰”的智慧——把同一层图特征,用多种尺度的池化窗口分别压缩,再把结果拼在一起。

具体来说,金字塔池化把节点特征按照“粒度”分成几个层级:最粗的粒度把整张图池化成一个向量(全局池化),较细的粒度把图分成若干区域分别池化,最细的粒度则直接保留节点级特征。这种层级化取样确保了最终特征既有全局概括性,又有局部分辨力。

与图金字塔的“多层图结构”不同,金字塔池化作用于同一层的特征上,通过不同大小的“池化窗口”模拟多尺度。这就像用同一个地图,分别用拳头大小、手掌大小和指尖大小的“取景框”去框选,每次框选后取一个代表值。自适应多尺度意味着不必手动指定每个尺度怎么划分,模型通过注意力机制自动学习不同粒度特征的组合权重。

其数学形式如下——对节点特征做多级池化后拼接:

观景台再多,最终都要汇聚成对景区的整体评价。金字塔池化把“平均印象”“最惊艳的瞬间”和“重点关注的对象”综合起来,让图的表示既有温度又有重点——这正是单一池化难以兼顾的。

z=[GAP(H)⊕GMP(H)⊕GSP(H)]z = \big[ \text{GAP}(H) \oplus \text{GMP}(H) \oplus \text{GSP}(H) \big]
GAP 是全局平均池化,GMP 是全局最大池化,GSP 是可以学习的软池化。三种池化从不同角度概括图的全局信息。
  1. 理解 GAP(全局平均池化)把所有节点的特征向量逐维求平均,得到“平均图景”,它刻画了图的整体分布。
    GAP(H)=1n∑i=1nhi\text{GAP}(H) = \frac{1}{n}\sum_{i=1}^{n} h_i
  2. 理解 GMP(全局最大池化)对每个特征维度取所有节点的最大值,得到“最显著信号”,它捕捉了图中最突出的局部特征。
    GMP(H)j=max⁡i=1,…,nhij\text{GMP}(H)_j = \max_{i=1,\ldots,n} h_{ij}
  3. 理解 GSP(软池化)用可学习的权重对节点特征加权求和,权重由网络自动学习,相当于让模型自己决定关注哪些节点。
    GSP(H)=∑i=1nsi⋅hi,si=softmax(uThi)\text{GSP}(H) = \sum_{i=1}^{n} s_i \cdot h_i, \quad s_i = \text{softmax}(u^{\mathsf{T}} h_i)

金字塔GNN架构

一个成熟的景区导游团队会有明确分工:资深领队负责站在全景沙盘前讲整体路线,分区导游负责带游客走具体景点。金字塔 GNN 架构也遵循类似的分工逻辑,它把图金字塔的构建与 GNN 的特征提取编排进一个端到端可训练的流程中。

典型架构是编码-粗化-解码三段式。编码阶段,用几层 GCN 或 GraphSAGE 对原始图做消息传递,让每个节点聚合邻居信息;粗化阶段,根据学到的节点表示决定如何合并节点、构建上一层图;解码阶段,将各层的表示融合后送入分类器或回归头。整个过程像一条流水线:细加工(编码)→ 归纳概括(粗化)→ 综合报告(解码)。

注意力机制在这条流水线中扮演“调度员”的角色。在粗化时,注意力权重决定哪些节点应该被分到同一个簇——不是只看结构距离,还要看特征相关性。在融合时,注意力决定各层特征的权重——哪一层对当前任务更有用,就加大它的话语权。这种“软决策”让整个金字塔的构建过程变得可微,从而可以端到端地用梯度下降训练。

金字塔架构中每一层的图卷积可以写作:

导游团队的分工逻辑在架构中得到了完美映射:编码器是“讲解员”,把每个景点的故事讲透;粗化器是“规划师”,把零散景点归类成主题线路;解码器是“领队”,把各条线路汇总成整体的游览体验。每个环节都各司其职,且通过端到端训练不断优化协作方式。

Hl+1=σ(D~−12A~lD~−12HlWl)H_{l+1} = \sigma\left( \tilde{D}^{-\frac{1}{2}} \tilde{A}_l \tilde{D}^{-\frac{1}{2}} H_l W_l \right)
这是带自环的图卷积公式,对第 \(l\) 层的图做一次消息传递,得到第 \(l+1\) 层的节点表示。
  1. 理解 \(\tilde{A}_l\)第 \(l\) 层图的邻接矩阵加上自环(每个节点与自己相连),让卷积时能保留节点自己的特征。
    A~l=Al+I\tilde{A}_l = A_l + I
  2. 理解 \(\tilde{D}\)\(\tilde{A}_l\) 的度矩阵,对角线上 \(\tilde{D}_{ii}=\sum_j \tilde{A}_{ij}\),它记录了每个节点有多少条有效连边。
    D~ii=∑j=1nlA~ij\tilde{D}_{ii} = \sum_{j=1}^{n_l} \tilde{A}_{ij}
  3. 计算 \(\tilde{D}^{-\frac{1}{2}} \tilde{A} \tilde{D}^{-\frac{1}{2}}\)对邻接矩阵做对称归一化,防止节点度数高时特征被过度放大,度数低时被过度削弱。
    A^=D~−12A~D~−12\hat{A} = \tilde{D}^{-\frac{1}{2}} \tilde{A} \tilde{D}^{-\frac{1}{2}}
  4. 理解 \(H_l W_l\)\(W_l\) 是可学习的权重矩阵,对节点的特征做线性变换,相当于调整每个特征的“说话音量”。
    Hl+1=σ(A^HlWl)H_{l+1} = \sigma(\hat{A} H_l W_l)

应用场景

同一套“多级缩放导览图”的思路,放在不同领域就是不同的产品。对制药公司来说,分子是一张由原子(节点)和化学键(边)组成的图;对社交平台来说,用户是节点、关注关系是边;对自动驾驶来说,激光雷达扫描出来的点云也是一张图。图金字塔在这些场景中扮演的,都是“变焦镜头”的角色。

在分子性质预测中,分子的局部结构(如苯环、羟基)和全局拓扑(如整体分子大小、疏水性)共同决定其性质。传统 GNN 层数多了会“过平滑”,层数少了看不到全局。图金字塔通过底层捕捉局部官能团,逐层粗化捕捉分子骨架,顶层捕捉整体构象,显著提升预测准确率。

在社交网络分析中,个体的影响力不仅取决于自身,还取决于所在社区的结构。底层能看到“谁和谁频繁互动”,中层能看到“哪些小圈子构成了一个社区”,顶层能看到“社区与社区之间的联系强度”。图金字塔让分析师同时从用户级、社区级、全网级三个层面理解网络。点云分类同理,局部几何特征(尖角、平面)与全局形状(车辆轮廓、建筑轮廓)分层提取,分类更鲁棒。

不同任务的最优融合策略通常不同,一个通用的做法是通过任务损失和辅助正则共同约束训练:

不同“景区”有不同的游客需求,但一份好的导览图总能在信息量与可读性之间找到平衡。图金字塔通过任务损失与正则损失的双重约束,为每个领域定制最合适的“变焦策略”。

L=Ltask+λ⋅Lreg\mathcal{L} = \mathcal{L}_{\text{task}} + \lambda \cdot \mathcal{L}_{\text{reg}}
总损失 = 任务损失(如分类交叉熵)+ 正则损失(如重建原图结构)。正则项促使金字塔各层在提取特征时不丢失过多结构信息。
  1. 理解 \(\mathcal{L}_{\text{task}}\)主任务损失,对分类任务是交叉熵,对回归任务是均方误差,它衡量模型预测与真实标签的差距。
    Ltask=−∑c=1Cyclog⁡y^c\mathcal{L}_{\text{task}} = -\sum_{c=1}^{C} y_c \log \hat{y}_c
  2. 理解 \(\mathcal{L}_{\text{reg}}\)正则损失,例如用粗化后的图预测原图的邻接矩阵,鼓励粗化过程保留尽可能多的结构信息。
    Lreg=∥A1−A^recon∥F2\mathcal{L}_{\text{reg}} = \lVert A_1 - \hat{A}_{\text{recon}} \rVert_F^2
  3. 理解 \(\lambda\)权衡系数,控制任务目标和结构保留之间的主次关系。太大了模型只顾还原结构而忽略任务;太小了粗化可能破坏关键信息。
    λ∈[0,1]\lambda \in [0, 1]

挑战与展望

地图做得再精美,也有力所不及之处:过度简化的地图会丢失关键转角的路况信息,像素过高的地图则加载太慢。图金字塔同样面临类似的两难困境。

最大的挑战是信息丢失问题。粗化的本质是做“信息压缩”,但压缩必然伴随着损耗。两个不同的节点被合并成一个超节点后,它们的个体差异就永久消失了;如果粗化策略不当,可能把两个语义完全相反的节点强拧在一起,导致后续层的信息严重失真。

第二个挑战是计算复杂度。图金字塔需要维护每一层的图结构并执行消息传递,随着网络规模增大,多层的存储和计算开销成倍增长。对动辄上亿节点的工业级社交网络图,直接构建完整金字塔是不现实的。

第三个挑战来自动态图场景。现实世界的图不断变化:社交网络每天有新人加入、新关系建立。静态图金字塔一次性构建,但动态图需要金字塔结构也随之演化。如何用增量更新的方式维护金字塔,是当前研究的前沿方向。

理论上,粗化的信息保留率可以用重建误差度量,它刻画了金字塔的“保真度”上限:

景区地图永远不会完美——但它可以持续优化。图金字塔同样如此:信息丢失提醒我们粗化要“手下留情”,计算复杂度催促我们设计更高效的层次结构,动态图的挑战则指引着金字塔从“静态建筑”进化为“自适应生态”。

ReconErr=1n∑i=1n∥hi−∑j∈C(i)h^j∥2\text{ReconErr} = \frac{1}{n} \sum_{i=1}^{n} \left\lVert h_i - \sum_{j \in C(i)} \hat{h}_j \right\rVert^2
重建误差衡量:每个原始节点的特征,是否能由其所属超节点的特征较好还原。误差越小,金字塔的粗化越“无损”。
  1. 理解 \(C(i)\)节点 \(i\) 所属的超节点簇,记录原始节点被合并到了哪一个高层节点。
    C(i)={j:Sij=1}C(i) = \{ j : S_{ij} = 1 \}
  2. 理解 \(\hat{h}_j\)超节点的特征向量,它是所属原始节点特征的加权汇总,例如取平均。
    h^j=1∣C(j)∣∑i∈C(j)hi\hat{h}_j = \frac{1}{|C(j)|} \sum_{i \in C(j)} h_i
  3. 理解求和与平均每个原始节点的特征与其超节点特征之差做平方求和,再除以总节点数。误差越小,说明粗化后信息保真度越高。
    ReconErr=1n∑i∥hi−h^C(i)∥2\text{ReconErr} = \frac{1}{n} \sum_i \left\lVert h_i - \hat{h}_{C(i)} \right\rVert^2

AI 应用场景

制药行业的分子筛选

阶段:特征工程 + 模型推理

解决的问题:传统药物筛选中,化学家需要人工设计分子描述符(如分子量、脂水分配系数),工作量大且难以捕捉三维构象的全局特征。图金字塔自动构建从原子级、官能团级到分子骨架级的多尺度表示,让 AI 无需人工特征即可从分子图直接预测水溶性、毒性等性质,减少 80% 的筛选时间。

具体案例:某 AI 制药公司用图金字塔模型处理 ChEMBL 数据库中的 150 万分子。底层 GCN 捕捉原子局部环境,中层谱聚类粗化识别官能团,顶层池化获取分子全局轮廓。多尺度融合后的特征在血浆蛋白结合率预测任务上,比单尺度 GNN 提升 12% 的 AUC。

社交网络中的社区发现与影响力分析

阶段:图表示学习

解决的问题:社交平台的推荐系统需要同时理解用户个体兴趣和社区集体趋势。传统方法用固定层数的 GNN,无法自适应不同规模的社区。图金字塔通过对用户关注图做多层粗化,在每层应用社区检测,产出“用户级—小圈子级—大社区级”的层次化向量表示。

具体案例:某社交媒体平台用图金字塔分析微博转发网络。底层捕捉用户兴趣特征,中层识别话题小组,顶层发现宏观舆论场。层级化特征送入分类器后,突发事件检测的 F1 值从 0.72 提升到 0.85,且能提前 30 分钟识别出舆论热点。

自动驾驶点云场景分类

阶段:多模态特征融合

解决的问题:自动驾驶的激光雷达点云是无序的、密度不均的 3D 点集合,直接分类很难兼顾局部几何细节与全局障碍物轮廓。图金字塔将点云构造成 K 近邻图,逐层粗化,底层识别路面点、车辆轮廓点等局部几何特征,顶层识别“前方是十字路口”的全局场景。

具体案例:某自动驾驶公司用图金字塔处理 SemanticKITTI 点云数据集。与 PointNet++ 相比,图金字塔在多尺度特征融合下,对“自行车”“行人”等小目标的分割 IoU 提升了 8 个百分点,远距离障碍物的识别率提升了 15%。

Python 演示

python84 行
import numpy as np  class GraphPyramid:    """一个极简图金字塔实现:度中心性节点选择 + 邻居贪婪合并 + 多尺度特征拼接。"""     def __init__(self, adj, features, levels=3):        self.adj = np.array(adj, dtype=float)        self.features = np.array(features, dtype=float)        self.levels = levels        self.pyramid = []     def _greedy_cluster(self):        """按度从大到小选择未访问节点,把它的邻居并入同一簇。"""        n = self.adj.shape[0]        degrees = self.adj.sum(axis=1)        order = np.argsort(-degrees)        visited = np.zeros(n, dtype=bool)        clusters = []        for v in order:            if visited[v]:                continue            cluster = [v]            visited[v] = True            for u in range(n):                if not visited[u] and self.adj[v, u] > 0:                    cluster.append(u)                    visited[u] = True            clusters.append(cluster)        return clusters     def _coarsen(self):        """根据簇划分构建分配矩阵,计算粗化后的邻接矩阵和特征矩阵。"""        clusters = self._greedy_cluster()        n = self.adj.shape[0]        k = len(clusters)        S = np.zeros((n, k))        for j, cluster in enumerate(clusters):            for i in cluster:                S[i, j] = 1.0 / len(cluster)  # 归一化分配权重        adj_new = S.T @ self.adj @ S          # 两步折叠:S^T A S        feats_new = S.T @ self.features       # 特征按簇汇总        return adj_new, feats_new     def build(self):        """从底层开始逐层粗化,保存每一层的(邻接矩阵, 特征矩阵)。"""        adj, feats = self.adj, self.features        self.pyramid.append((adj.copy(), feats.copy()))        for _ in range(self.levels - 1):            adj, feats = self._coarsen()            self.pyramid.append((adj.copy(), feats.copy()))     def multi_scale_feature(self):        """对每层特征做全局平均池化,拼接成多尺度特征向量。"""        vecs = []        for adj, feats in self.pyramid:            vecs.append(feats.mean(axis=0))   # 每层取平均作为全局表示        return np.concatenate(vecs)  if __name__ == "__main__":    # 构造一个6节点环图,每个节点4维特征    adj = np.zeros((6, 6))    for i in range(6):        adj[i, (i + 1) % 6] = 1        adj[(i + 1) % 6, i] = 1    features = np.array([        [1.0, 0.2, 0.5, 0.0],        [0.9, 0.3, 0.6, 0.1],        [0.2, 1.0, 0.1, 0.7],        [0.3, 0.8, 0.2, 0.6],        [0.5, 0.1, 1.0, 0.3],        [0.6, 0.2, 0.9, 0.4],    ])     gp = GraphPyramid(adj, features, levels=3)    gp.build()     for idx, (a, f) in enumerate(gp.pyramid):        print(f"Level {idx}: nodes={a.shape[0]}, edges={int((a > 0).sum() // 2)}, features={f.shape}")     z = gp.multi_scale_feature()    print("Multi-scale feature dim:", z.shape[0])    print("Multi-scale vector:", z)
目的:实现一个可运行的极简图金字塔——从原始图出发,用度中心性指导的贪婪合并逐层粗化,最后把各层的全局平均池化特征拼接成多尺度向量。
  1. self.pyramid.append((adj.copy(), feats.copy()))把每一层的图结构保存下来,构成金字塔;用 copy 防止后续粗化修改影响已保存层。
  2. order = np.argsort(-degrees)按照度从大到小排序节点,保证高影响力的节点优先成为簇中心,优先保留关键结构。
  3. S[i, j] = 1.0 / len(cluster)分配矩阵的列做了归一化,让特征合并时取平均而非累加,避免超节点特征尺度异常。
  4. adj_new = S.T @ self.adj @ S公式中 \(A_{l+1}=S^TA_lS\) 的矩阵实现,先折叠行再折叠列,得到新图邻接矩阵。
  5. feats_new = S.T @ self.features用同一分配矩阵对节点特征做汇总,让特征维度随节点数同步缩减。
  6. vecs.append(feats.mean(axis=0))对每一层的节点特征做全局平均池化,把变尺寸的图固定成长度一致的特征向量。

常见误区

误区我以为图金字塔是拿同一张图的多个副本堆叠起来,就像复印机印好几份——层与层之间只是重复,深度变多了,信息没变多。

正解图金字塔的每一层都是对上一层图的抽象概括,而不是复制。底层是原始图,往上一层节点被合并成超节点,图的规模不断缩小、语义不断抽象。就像景区导览图从“街道级”缩放到“区域级”,每层展示的信息不同。如果只是复制,那就是给模型加了多层全连接网络,毫无意义。

为何常见:因为“金字塔”这个意象容易让人联想到层层堆叠,图像金字塔的“多分辨率”概念也可能被误解为单纯的缩放复制。

误区我觉得粗化就是抽样,把一些不重要的节点直接删掉,剩下一个稀疏的图就完事了。

正解直接删节点会破坏图的结构完整性——一个节点被删,它连接的边也断了,邻居间的间接关系会丢失。正确的粗化是合并而非删除:多个节点按结构或特征相似性聚成簇,簇内信息通过加权汇总保留到超节点中。类比来说,景区合并不是把某个景点关掉,而是把相邻景点圈成一个大区域统一管理。

为何常见:受传统图采样方法的影响,很多人习惯把图缩小等同于减样本,忽略了图的结构约束。

误区标题说了要“融合”,那不就是把每层的特征向量加起来吗?反正都是同一张图的特征,相加应该能增强表达。

正解直接相加会丢失每层的尺度语义。底层特征和顶层特征不在同一语义空间:底层是细粒度局部特征,顶层是粗粒度全局特征,直接相加会让两类信息互相冲刷。正确做法是拼接(保留各尺度独立性)或注意力加权(按任务需要动态调节各层贡献)。就像广角和长焦照片不能直接像素叠加,但可以拼成全景或按场景选择主图。

为何常见:很多入门教程里“融合”一词用得宽泛,初学者容易套用简单算术操作。

误区金字塔池化把整张图压成一个向量,那它只能做图级任务,节点级任务用不上。

正解金字塔池化确实常用于图级任务(图分类、图回归),但它的思想同样可以嵌入节点级任务。比如在粗化的每一层保留池化前的节点表示,通过跳跃连接把多尺度信息传回原始节点,辅助节点分类;或者用高层池化结果作为全局上下文,与节点特征拼接后增强节点表示。这相当于每个景点虽然属于某个大区,但它心里装着整个景区的地图。

为何常见:看到“全局池化”就直接联想到图级输出,忽略了池化结果也可以反向注入到节点特征中。

面试题

Q1. 图金字塔的核心目标是什么?

单选题 · easy

  • A.减少图的节点数量以加速训练
  • B.在不同尺度上提取图的多粒度特征← 正确答案
  • C.将图转换为树结构方便遍历
  • D.增加图的边数以增强表达能力

答案:B。图金字塔的核心目标是多尺度特征提取——像变焦镜头一样,从全局到局部分层理解图结构。减少节点数只是手段而非目的,转换为树或增加边都是无关操作,只有 B 准确描述了核心目标。

Q2. 图金字塔中的“粗化”指的是什么?

单选题 · easy

  • A.用更粗的画笔重绘节点
  • B.把相似节点合并成超节点← 正确答案
  • C.放大图的局部区域细节
  • D.删除图中所有孤立节点

答案:B。粗化是把图结构的粒度变粗——多个节点按相似性聚成一个簇,簇成为新图的超节点。这是构建金字塔层次的基础操作,类似地图把相邻景点圈成一个大区。B 准确;A、C 是图像处理概念;D 是数据清洗操作,不是粗化。

Q3. 分配矩阵 \(S\) 中,每一列的含义是什么?

单选题 · mid

  • A.每个底层节点所属的超节点编号
  • B.一个超节点包含哪些底层节点及其权重← 正确答案
  • C.每对节点之间的最短路径长度
  • D.每层图卷积的注意力权重

答案:B。分配矩阵 \(S\) 的行对应底层节点,列对应超节点,第 \(j\) 列的非零元素表示第 \(j\) 个超节点由哪些底层节点以多大权重合并而成。它记录了“哪些节点归入同一个簇”,是粗化的数学表达。

Q4. 多尺度特征融合时,为什么拼接通常优于直接相加?

单选题 · mid

  • A.拼接不需要额外的计算开销
  • B.拼接保留了各尺度特征的独立语义信息← 正确答案
  • C.拼接可以减少最终特征向量的维度
  • D.拼接能消除不同层之间的冗余信息

答案:B。不同层的特征语义不同——底层细粒度、顶层粗粒度,拼接能保留每层特征的独立性,让后续网络自己决定如何利用。直接相加会把不同尺度的信息混合在一起,丢失分层语义。拼接不减少维度,也不能消除冗余,排除 C、D。

Q5. 下列哪些方法可以用于图粗化?

多选题 · mid

  • A.谱聚类粗化← 正确答案
  • B.DeepWalk 池化← 正确答案
  • C.贪婪节点合并← 正确答案
  • D.全连接层降维

答案:ABC。谱聚类在拉普拉斯特征空间聚类后合并节点,DeepWalk 先用随机游走学表示再聚类,贪婪合并按节点重要性逐个聚簇——三者是常见的粗化方法。全连接层做的是特征变换,不改变图结构,因此不是粗化方法。

Q6. 金字塔池化中 GAP 和 GMP 的区别是什么?

单选题 · mid

  • A.GAP 取最大值,GMP 取平均值
  • B.GAP 取平均值,GMP 取最大值← 正确答案
  • C.两者都取平均值但维度不同
  • D.两者都取最大值但作用域不同

答案:B。GAP(全局平均池化)对节点特征逐维取平均,反映图的整体分布;GMP(全局最大池化)逐维取最大值,捕捉最显著信号。两者互补——一个是“平均印象”,一个是“最惊艳的瞬间”,因此常拼接在一起使用。

Q7. 公式 \(A_{l+1} = S^{\mathsf{T}} A_l S\) 中,两次矩阵乘法的各自作用是?

单选题 · hard

  • A.第一次加权求和节点特征,第二次归一化权重
  • B.第一次把边的起点并入超节点,第二次把边的终点并入超节点← 正确答案
  • C.第一次做拉普拉斯分解,第二次重建邻接矩阵
  • D.第一次计算最短路径,第二次计算最短路径树

答案:B。\(S^\mathsf{T}A\) 先对邻接矩阵的行做折叠——每个簇内所有节点连出的边汇总到超节点;再乘以 \(S\) 对列做折叠——边的两个端点都归并到超节点。两步折叠分别处理边的“起点”和“终点”,共同完成结构粗化。

Q8. 图金字塔端到端训练时,为什么粗化过程必须“可微”?

单选题 · mid

  • A.因为不可微的层无法参与反向传播← 正确答案
  • B.因为可微可以加快训练速度
  • C.因为可微可以减少参数量
  • D.因为可微能自动确定金字塔层数

答案:A。端到端训练通过梯度下降更新所有参数,粗化中决定节点归属的分配策略如果不可微,梯度就无法通过它回传到编码器。所以现代图金字塔常用注意力权重做“软分配”,让粗化过程对参数可导,才能用反向传播统一训练。

Q9. 图金字塔在分子性质预测中,底层特征主要负责捕捉什么?

单选题 · hard

  • A.分子整体构象的疏水性
  • B.原子局部化学环境如官能团← 正确答案
  • C.分子在数据库中的索引编号
  • D.分子间相互作用力大小

答案:B。底层是原始分子图,每个原子保留独立的特征,GCN 消息传递后能识别局部化学环境——如羟基、苯环、氨基等官能团模式。全局构象信息需要多层粗化到顶层才能体现,而底层正是“局部细节”层。

Q10. 图金字塔面临的主要挑战有哪些?

多选题 · extreme

  • A.粗化过程可能导致关键信息丢失← 正确答案
  • B.多层图结构的存储和计算开销大← 正确答案
  • C.动态图场景下金字塔难以增量更新← 正确答案
  • D.无法处理超过 1000 个节点的图

答案:ABC。粗化本质是有损压缩,策略不当会丢失关键节点差异;维护多层图结构确实带来额外的空间和时间开销;动态图需要金字塔随结构变化增量演化,这三点都是当前研究的真实挑战。D 是错误陈述——图金字塔本身不限制节点数,只是大规模时会放大复杂度和信息丢失问题。