算子与计算图基础:算子定义、计算图概念、前向与反向传播、算子粒度分类
各位同学好,我是你们的老朋友。今天咱们聊聊算子与计算图的基础。说实话,这部分内容看起来简单,但很多人在实际开发中栽过跟头。我自己刚入行那会儿,也把算子粒度和计算图的关系搞混过,调试了整整两天才发现问题出在哪。
一、算子到底是什么?
算子,英文叫Operator,说白了就是神经网络中的一个基本计算单元。你可以把它想象成一个黑盒子——输入一些数据,经过某种数学变换,输出新的数据。
举个例子,卷积算子(Conv2D)接收输入特征图和卷积核,输出卷积后的特征图。ReLU算子接收一个张量,把负数全部置零,正数保持不变。
算子的核心三要素:
- 输入:张量(Tensor),可能有多个
- 输出:张量,也可能有多个
- 属性:控制算子行为的参数,比如卷积的stride、padding
我个人习惯把算子分为两类:有参数算子和无参数算子。卷积、全连接这种带权重的是有参数算子;ReLU、加法、乘法这种是无参数算子。为什么这么分?因为反向传播时,有参数算子需要更新权重,无参数算子只需要传递梯度。这个区别在算子映射时特别重要。
二、计算图——神经网络的骨架
计算图(Computation Graph)是什么?说白了,它就是用一个有向无环图(DAG)来表示整个神经网络的计算过程。
每个节点代表一个算子,每条边代表张量的流动方向。你想想看,一个ResNet-50可能有上百个算子节点,它们按照拓扑顺序依次执行,就完成了前向推理。
我曾在项目中遇到过一个问题:两个算子之间共享了同一个中间张量,结果反向传播时梯度累加错了。嗯,这就是计算图设计时没考虑清楚张量的生命周期。
小技巧:在NPU上做算子映射时,我建议你先画出完整的计算图,再逐个节点分析。别一上来就盯着单个算子优化,整体图结构往往决定了性能上限。
下面我用一张SVG图来展示计算图的基本结构:
三、前向传播与反向传播
前向传播,就是从输入到输出,按照计算图的拓扑顺序依次执行每个算子。这个好理解,就是推理过程。
反向传播呢?它是训练的核心。从损失函数开始,沿着计算图反向走,用链式法则计算每个算子的梯度。
这里有个关键点:反向传播的图结构和前向传播是对称的。每个前向算子都有一个对应的反向算子。比如Conv2D的前向是卷积,反向就是卷积转置(或者叫反卷积)。
避坑指南:我曾经在映射一个自定义算子时,只写了前向实现,忘了写反向。结果训练时梯度传不过去,损失函数死活不下降。后来花了半天才定位到问题。所以,算子映射时一定要成对考虑——前向和反向都得实现。
我建议你在设计算子库时,把前向和反向算子放在一起管理。比如用命名约定:conv2d_fwd 和 conv2d_bwd,这样不容易遗漏。
四、算子粒度分类
算子粒度,说白了就是算子的大小。这个在NPU算子映射中特别重要,因为不同粒度的算子映射策略完全不同。
| 粒度类型 | 典型算子 | 特点 | 映射策略 |
|---|---|---|---|
| 细粒度 | 加法、乘法、ReLU | 计算量小,数据搬运开销占比大 | 尽量融合到相邻算子中 |
| 中粒度 | 卷积、全连接、池化 | 计算量适中,有明确的数据复用模式 | 直接映射到NPU硬件单元 |
| 粗粒度 | LayerNorm、Softmax、Reshape | 计算量大,可能涉及全局归约 | 拆分为多个子算子,或使用专用硬件 |
为什么粒度这么重要?你想想看,NPU的硬件资源是有限的。如果算子粒度过细,比如把每个加法都单独映射,那数据在内存和计算单元之间来回搬运,效率极低。我见过一个项目,把Element-wise加法单独映射,结果性能比CPU还差。
反过来,如果算子粒度过粗,比如把整个Transformer Block映射成一个算子,那硬件利用率可能很高,但灵活性就没了——换个模型结构就得重新设计算子。
我的经验:在实际项目中,我通常采用混合粒度策略。核心计算用中粒度算子(卷积、矩阵乘),辅助计算用细粒度算子融合(比如把ReLU和加法融合到卷积后面),特殊操作用粗粒度算子(比如Softmax、LayerNorm)。这样既保证了性能,又保留了灵活性。
嗯,说到算子融合,这里有个经典案例:Conv+BN+ReLU融合。很多NPU都支持这种融合模式,因为BN在推理时可以吸收到卷积的权重中,ReLU又可以和卷积的输出合并计算。这样一来,三个算子变成了一个,数据搬运次数减少了一半以上。
最后,我想强调一点:算子粒度不是一成不变的。同一个算子,在不同硬件上可能采用不同的粒度策略。比如在GPU上,你可能把卷积和ReLU分开;但在NPU上,由于硬件支持融合,你可能会把它们合并。所以,算子映射一定要结合具体硬件特性来设计。
好了,今天的内容就到这里。算子定义、计算图、前向反向、粒度分类,这四个概念是后续所有算子映射工作的基础。把这些搞清楚了,后面学起来会轻松很多。
核心要点回顾:
- 算子 = 输入 + 输出 + 属性
- 计算图 = 有向无环图,节点是算子,边是张量
- 前向传播执行计算,反向传播计算梯度
- 算子粒度影响映射策略,需要根据硬件特性选择
想要更多资料,联系可可