算子与计算图基础:什么是算子?计算图的概念、前向传播与反向传播

好,咱们今天聊聊算子与计算图。这两个概念,说白了就是NPU世界的“砖块”和“施工图纸”。你想想看,没有砖块盖不了楼,没有图纸你都不知道砖往哪儿砌。算子就是砖块,计算图就是图纸。

什么是算子?

算子,英文叫Operator,简称Op。我个人的理解,它就是神经网络里最基础的计算单元。比如卷积、池化、矩阵乘法、激活函数(ReLU、Sigmoid),这些都是算子。

你可能会问:“这不就是函数吗?”嗯,可以这么理解。但算子更强调“操作”本身,以及它跟硬件(比如NPU)的映射关系。我在项目中遇到过,很多刚入行的同学把算子当成黑盒,只关心输入输出,不关心它底层怎么跑。结果模型一上NPU,性能惨不忍睹。

核心要点:算子 = 计算逻辑 + 硬件映射。你不仅要懂它算什么,还得懂它怎么在NPU上高效执行。

常见的算子类型,我列个表给你看:

算子类别 典型算子 说明
卷积类 Conv2D, DepthwiseConv 图像特征提取的核心
池化类 MaxPool, AvgPool 降采样,减少参数量
激活类 ReLU, Sigmoid, Tanh 引入非线性
矩阵运算 MatMul, Add, Mul 全连接层、残差连接的基础
归一化类 BatchNorm, LayerNorm 稳定训练,加速收敛

计算图的概念

计算图,说白了就是用图的方式,把算子串起来。每个节点是一个算子,每条边是数据的流向(通常是张量)。

我刚开始做NPU算子映射时,总觉得计算图是框架的事,跟我没关系。后来发现,不懂计算图,你根本不知道算子之间的依赖关系,也就没法做内存复用和流水线优化。

举个例子,一个简单的计算图:

输入张量 → Conv2D → ReLU → MaxPool → 输出张量

这个图里,Conv2D的输出是ReLU的输入,ReLU的输出又是MaxPool的输入。数据流是单向的,没有分支。但在实际模型中,计算图要复杂得多,比如有残差连接(ResNet)、多分支(Inception)等。

我的经验:拿到一个模型,先画它的计算图。不用画太细,画出主干和关键分支就行。这能帮你快速定位哪些算子是性能瓶颈。

计算图还有一个重要特性:它是静态的。也就是说,图的结构在模型编译时就确定了,运行时不会改变。这跟TensorFlow的静态图、PyTorch的动态图有点区别,但NPU上我们更倾向于静态图,因为方便做编译优化。

前向传播与反向传播

前向传播,就是数据从输入层,经过一层层算子,最终得到输出。这个过程,说白了就是“算一遍”。

反向传播,则是根据损失函数,从输出层往回走,计算每个算子的梯度。这个过程,是“求导”。

你可能会问:“为什么NPU算子开发要懂反向传播?”

嗯,这个问题问得好。我告诉你,因为NPU不仅要跑推理,还要跑训练。训练时,反向传播的算子跟正向传播不一样。比如正向传播是Conv2D,反向传播可能就是Conv2DTranspose(转置卷积)。如果你不懂反向传播的算子逻辑,你写出来的算子可能只支持推理,不支持训练。

避坑指南:我曾经在开发一个自定义算子时,只实现了前向逻辑,没实现反向。结果模型训练到一半就崩了,梯度传不回来。后来花了整整两天才定位到问题。所以,如果你要写算子,一定要同时考虑前向和反向。

前向传播和反向传播的关系,我用一个简单的线性层来演示:

前向:y = W * x + b
反向:
  ∂L/∂W = ∂L/∂y * x^T
  ∂L/∂x = W^T * ∂L/∂y
  ∂L/∂b = ∂L/∂y

你看,反向传播的算子(矩阵转置、矩阵乘法)跟前向不一样。这就是为什么算子开发要同时支持两个方向。

知识体系总览

为了让你更直观地理解,我画了一张图:

算子与计算图知识体系 算子 (Operator) 卷积、池化、激活 矩阵乘法、归一化 基础计算单元 计算图 (Graph) 节点 = 算子 边 = 数据流 静态图结构 前向传播 数据正向流动 反向传播 梯度反向计算 算子映射到NPU的核心逻辑 1. 解析计算图,识别所有算子 2. 为每个算子找到对应的NPU硬件指令 3. 优化内存访问和流水线,提升吞吐

这张图把算子、计算图、前向传播、反向传播串在了一起。你从左边看到右边,就是一条完整的学习路径。

最后说一句,算子映射这件事,说白了就是“翻译”。把框架里的算子,翻译成NPU能执行的指令。翻译得好不好,直接决定了模型的性能和精度。所以,打好这个基础,后面的路会顺很多。

一个小建议:刚开始学的时候,别贪多。先搞懂3-5个核心算子(Conv2D、MatMul、ReLU、Add、MaxPool)的计算图映射,其他的自然就通了。

想要更多资料,联系可可