3. 融合策略分类

各位同学,今天我们来聊聊算子融合的核心——融合策略的分类。说实话,我刚入行那会儿,看到各种融合方案头都大了。后来在项目中摸爬滚打,才慢慢理清了头绪。其实说白了,融合策略就四种:水平融合、垂直融合、跨层融合、复合融合。咱们一个一个来拆解。

一、水平融合(Horizontal Fusion)

水平融合,我习惯叫它“兄弟融合”。什么意思呢?就是把同一层里、处理不同数据的同类算子合并到一起。你想想看,如果模型里有多个并行的卷积操作,各自算各自的,多浪费计算资源啊。

核心思想:将同一计算图中、同一层级、相同类型的算子合并,减少内核启动开销和内存访问次数。

我在项目中遇到过这样一个场景:一个目标检测模型里,有四个并行的 3x3 卷积,分别处理不同的特征图通道。如果不做融合,GPU 要启动四次内核,每次都要搬运数据。融合之后,一次内核启动搞定所有计算。嗯,这里要注意,水平融合的前提是算子之间没有数据依赖。

// 伪代码示例:水平融合前后对比
// 融合前:四个独立卷积
conv1 = Conv2D(input[:, :, :, 0:64], kernel1)
conv2 = Conv2D(input[:, :, :, 64:128], kernel2)
conv3 = Conv2D(input[:, :, :, 128:192], kernel3)
conv4 = Conv2D(input[:, :, :, 192:256], kernel4)

// 融合后:一个大卷积搞定
fused_conv = FusedConv2D(input, [kernel1, kernel2, kernel3, kernel4])

我的经验:水平融合在 MobileNet 这类轻量级网络上效果特别明显。因为这类网络通道数多、卷积核小,内核启动开销占比高。我曾经把一个 MobileNetV2 的推理延迟从 12ms 降到了 8ms,靠的就是水平融合。

二、垂直融合(Vertical Fusion)

垂直融合,说白了就是“上下游合并”。把计算图中前后相连的算子合并成一个。比如 Conv + BN + ReLU 这种经典组合,就是垂直融合的典型代表。

为什么会这样?因为每个算子执行完都要把中间结果写回显存,下一个算子再读出来。这一写一读,带宽就浪费了。垂直融合就是把这些中间结果“焊死”在寄存器里,不落地。

融合模式 典型组合 收益来源
Conv + BN 卷积 + 批归一化 消除中间张量读写
Conv + ReLU 卷积 + 激活函数 减少内核启动次数
Conv + BN + ReLU 卷积 + 批归一化 + 激活 双重收益叠加
MatMul + Add 矩阵乘法 + 加法 减少内存带宽消耗

避坑指南:我曾经在融合 Conv + BN 时踩过坑。BN 在推理时虽然有固定的均值和方差,但如果你把 BN 的缩放因子直接融进卷积权重里,精度可能会掉一点点。建议融合后做一次精度验证,尤其是低比特量化场景。

三、跨层融合(Cross-Layer Fusion)

跨层融合,这个就有点意思了。它不是简单的相邻算子合并,而是跨越多个网络层,把结构相似的算子组合在一起。比如 Transformer 里的 Multi-Head Attention,本质上就是多个 QKV 计算的跨层融合。

我记得有一次优化 BERT 模型,发现 Self-Attention 里的 Q、K、V 三个线性变换是独立的。如果分别计算,要启动三次矩阵乘法内核。跨层融合后,把三个权重矩阵拼成一个大的,一次计算搞定。嗯,这里要注意,跨层融合对内存布局有要求,需要做好数据重排。

// 跨层融合示例:QKV 合并计算
// 融合前
Q = Linear(input, W_q)  // [batch, seq_len, hidden]
K = Linear(input, W_k)  // [batch, seq_len, hidden]
V = Linear(input, W_v)  // [batch, seq_len, hidden]

// 融合后
QKV = Linear(input, concat(W_q, W_k, W_v))  // [batch, seq_len, 3*hidden]
Q, K, V = split(QKV, 3)  // 在寄存器中直接切分

关键点:跨层融合的收益往往比前两种更大,但实现复杂度也更高。你需要仔细分析计算图,找到那些“长得像”的算子组合。我个人习惯先用 profiling 工具跑一遍,看看哪些算子的计算模式相似。

四、复合融合(Compound Fusion)

复合融合,就是前面三种策略的组合使用。实际项目中,很少只用一种融合策略。你想想看,一个复杂的神经网络,既有水平并行的结构,又有垂直串行的结构,还有跨层的跳跃连接。这时候就需要复合融合了。

我参与过的一个工业级部署项目,把 ResNet-50 的推理优化做到了极致。我们用了以下组合策略:

  1. 水平融合:合并同一 stage 里的多个 3x3 卷积
  2. 垂直融合:Conv + BN + ReLU 三合一
  3. 跨层融合:把 shortcut 路径上的 1x1 卷积和主路径的 3x3 卷积做跨层合并
  4. 复合融合:最终把整个 residual block 融合成一个大的 kernel

我的建议:复合融合不是简单的“堆砌”。你要先做 profiling,找到真正的瓶颈。我曾经见过有人盲目融合,结果因为寄存器压力过大,性能反而下降了。记住,融合不是越多越好,要适可而止。

五、四种策略对比总结

为了让你看得更清楚,我画了一张对比图。这张图展示了四种融合策略的核心逻辑和适用场景。

融合策略分类对比 水平融合 同一层级,同类算子合并 Conv1 Conv2 Conv3 融合 垂直融合 上下游算子合并,消除中间结果 Conv BN ReLU 融合 跨层融合 跨越多个网络层,合并相似结构 Q K V QKV融合 复合融合 多种策略组合使用 水平+垂直 跨层+垂直 全融合 实际项目中,复合融合是最常用的策略,但需要根据硬件特性灵活调整

好了,四种融合策略就讲到这里。水平融合解决并行问题,垂直融合解决串行问题,跨层融合解决结构相似问题,复合融合则是前三种的综合运用。你在实际项目中,可以根据模型结构和硬件特性灵活选择。

一句话总结:融合策略没有银弹。水平融合适合通道数多的网络,垂直融合适合深层网络,跨层融合适合 Transformer 这类结构,复合融合则是工业级部署的标配。多试、多调、多 profiling,才能找到最优解。