计算图基础:概念、分类与融合的关系

大家好,我是你们的老朋友。今天我们来聊聊算子融合的前置知识——计算图。说实话,我见过不少同学一上来就扎进融合优化里,结果连计算图是啥都没搞明白,最后踩了一堆坑。所以,咱们先把地基打牢。

一、计算图到底是什么?

计算图,说白了就是一张有向无环图。它用节点表示运算,用边表示数据流动。你想想看,一个神经网络模型,本质上就是一堆矩阵乘、卷积、激活函数串在一起。计算图就是把这些操作画成一张图,方便我们分析和优化。

核心定义:计算图 = 节点(算子)+ 边(张量/数据依赖)

我个人习惯把计算图想象成「工厂流水线」。每个节点是一个工位,负责一道工序。边就是传送带,把半成品从一个工位送到下一个。嗯,这个比喻虽然简单,但很管用。

举个例子,一个简单的表达式 z = (a + b) * c,它的计算图长这样:

     a ──┐
         ├──> (+) ──┐
     b ──┘         ├──> (*) ──> z
               c ──┘

你看,加法节点依赖 a 和 b,乘法节点依赖加法的结果和 c。这就是最朴素的计算图。

二、静态图 vs 动态图:两种哲学

这里有个经典问题:静态图和动态图到底选哪个?我在项目中遇到过不少团队为此争论不休。其实没有绝对的好坏,关键看场景。

特性 静态图 动态图
定义时机 先定义后执行 边定义边执行
调试难度 较高(看不到中间值) 较低(可以随时打印)
优化空间 大(全局可见) 小(局部优化)
典型框架 TensorFlow 1.x, Caffe PyTorch, TensorFlow Eager

静态图:你先把整个网络结构画好,然后交给框架去执行。框架能看到全局,可以做很多激进的优化,比如算子融合、内存复用。但调试起来很痛苦——我曾经为了查一个静态图里的 bug,花了整整两天,最后发现是 shape 推断错了。

动态图:每执行一行代码,图就构建一部分。这种方式更灵活,适合研究和快速迭代。PyTorch 就是典型代表。但代价是优化空间受限,因为框架不知道你下一步要干啥。

我的建议:做研究用动态图,做部署用静态图。如果你非要二选一,看看你的场景更看重灵活性还是性能。

三、计算图与算子融合的关系

好了,重点来了。算子融合为什么离不开计算图?

你想想看,如果没有计算图,你怎么知道哪些算子可以合并?你只能一个个算子单独优化,就像蒙着眼睛修车——能修,但效率极低。

有了计算图,事情就清晰了。框架可以遍历整个图,找到那些「相邻且可以合并」的算子模式。比如:

  • Conv + BN + ReLU:这是最常见的融合模式。卷积后面接批归一化再接激活函数,三个算子可以合并成一个。
  • MatMul + Add:矩阵乘后面接加法,可以融合成带偏置的矩阵乘。
  • 多个连续 Element-wise 操作:比如 Add + Mul + Sigmoid,可以合并成一个 kernel。

我举个例子,假设你有这么一段计算图:

输入 --> Conv2D --> BatchNorm --> ReLU --> 输出

如果不做融合,你需要启动 3 个 kernel,每个 kernel 都要读写显存。做了融合之后:

输入 --> FusedConvBNReLU --> 输出

只需要 1 个 kernel,中间结果不用写回显存。性能提升多少?我实测过,在 ResNet-50 上,光这一项融合就能省掉 20%-30% 的推理时间。

注意:不是所有相邻算子都能融合。融合的前提是:算子之间没有数据依赖冲突,且融合后的计算在数学上等价。我曾经犯过一个错误,强行融合了两个有依赖关系的算子,结果精度直接崩了。嗯,从那以后我再也不敢乱来了。

四、一张图总结核心逻辑

下面我用一张 SVG 图来展示计算图与算子融合的关系。这张图我画了好几次才满意,希望能帮你理清思路。

计算图与算子融合关系图 原始计算图 Conv2D BatchNorm ReLU 输出 算子融合 融合后计算图 FusedConvBNReLU (1个kernel) 输出 性能提升:3个kernel → 1个kernel,减少显存读写,延迟降低20%~30%

五、避坑指南与个人经验

最后,分享几个我踩过的坑:

  1. 不要盲目融合:我曾经为了追求极致性能,把整个残差块都融合成一个算子。结果呢?编译时间暴涨,而且因为寄存器压力太大,实际性能反而下降了。融合不是越多越好,要适可而止。
  2. 注意动态 shape:如果你的模型输入尺寸不固定,静态图的优化可能失效。我遇到过部署时输入从 224 变成 256,之前做的融合全部白费。嗯,后来我学乖了,动态 shape 的场景尽量用动态图。
  3. 调试时关掉融合:算子融合会让中间结果不可见,调试起来非常痛苦。我建议你在开发阶段关掉融合,等模型收敛了再打开。这个习惯救了我很多次。

一句话总结:计算图是算子融合的地图。没有地图,你只能瞎转悠;有了地图,你才知道哪里该修路、哪里该架桥。

好了,这一章就到这里。计算图的概念虽然基础,但它是后续所有优化技巧的基石。下一章我们会深入具体的融合策略,到时候见。

可可精品资料库