3、AI算子分类与特性:卷积(Conv)系列、矩阵乘(GEMM)系列、激活函数(ReLU/Sigmoid)系列、归一化(BatchNorm/LayerNorm)系列

好,咱们今天聊聊AI算子的分类。说实话,这话题看着基础,但里面坑不少。我当年刚接触芯片映射时,就吃过“算子特性没吃透”的亏——明明硬件算力够,跑起来却慢得离谱。后来才明白,不同算子的计算模式、数据流特征完全不同,映射策略也得跟着变。

我把AI算子分成四大类:卷积、矩阵乘、激活函数、归一化。每一类都有它的脾气。咱们一个一个看。

3.1 卷积(Conv)系列:计算密集型的“老大哥”

卷积是CNN的绝对核心。你想想看,一张224x224的图片,经过几十层卷积,计算量能到几GFLOPs。为什么会这么重?因为它是典型的“计算密集型”算子。

我个人习惯把卷积拆成三个维度看:

  • 输入特征图:H x W x C(高、宽、通道数)
  • 卷积核:K x K x C x M(核大小、输入通道、输出通道)
  • 输出特征图:H' x W' x M

这里有个关键点:数据复用。卷积核在输入图上滑动时,同一个输入像素会被多个卷积核重复使用。我在项目中遇到过,如果芯片的缓存设计不合理,这种复用反而会变成瓶颈——数据反复从DDR搬进搬出,功耗直接飙升。

映射要点:卷积映射到芯片上,核心是“循环展开”。通常把输出通道M作为最外层循环,输入通道C作为中间层,空间维度H/W作为最内层。这样能最大化数据局部性。

举个例子,一个3x3卷积,步长为1,padding为1。计算量大约是:

计算量 ≈ H' * W' * M * C * K * K
       ≈ 224 * 224 * 64 * 3 * 3 * 3
       ≈ 86.7 MFLOPs

嗯,这里要注意:这只是单层。实际网络里几十层堆起来,计算量轻松上G。

3.2 矩阵乘(GEMM)系列:Transformer的“心脏”

矩阵乘,说白了就是线性代数里的矩阵乘法。但它在AI里地位特殊——Transformer全靠它撑场面。你想想看,自注意力机制里的Q、K、V计算,本质上全是矩阵乘。

GEMM的特性是:计算密度极高,但数据复用性差。为什么?因为矩阵乘的每个输出元素,都需要输入矩阵的一整行和一整列。数据访问模式是“行-列”交叉,对缓存很不友好。

我建议把GEMM映射到芯片时,重点关注分块(tiling)策略。比如把大矩阵切成小块,让每个小块的数据能塞进片上缓存。我曾经踩过一个坑:没做分块,直接拿大矩阵去算,结果缓存命中率不到30%,性能直接腰斩。

矩阵大小 计算量(FLOPs) 数据量(Bytes) 计算密度(FLOPs/Byte)
1024x1024 2.1G 8M 262
4096x4096 137G 128M 1070

你看,矩阵越大,计算密度越高。但数据量也大得吓人。所以映射时,内存带宽往往是瓶颈,而不是算力。

避坑指南:我曾经在映射BERT模型时,发现GEMM占了80%的耗时。后来把矩阵分块从64x64改成128x128,性能提升了15%。分块大小不是越大越好,得根据芯片缓存大小来调。

3.3 激活函数(ReLU/Sigmoid)系列:计算轻量,但“暗藏杀机”

激活函数,说白了就是给神经网络加非线性。ReLU最简单:y = max(0, x)。Sigmoid复杂点:y = 1 / (1 + e^{-x})。

你可能会想:这玩意儿计算量这么小,有啥好优化的?嗯,这里要注意:激活函数虽然计算轻,但数据流模式特殊——它是逐元素操作,没有数据复用。每个输入只产生一个输出,计算和访存的比例是1:1。

我个人的经验是:激活函数映射到芯片上,关键是利用向量化指令。比如ReLU,可以用一条SIMD指令同时处理16个元素。Sigmoid稍微麻烦点,需要查表或近似计算。

举个例子,ReLU的映射代码(伪代码):

for i in range(N):
    y[i] = (x[i] > 0) ? x[i] : 0

这代码看着简单,但如果你用标量指令一个一个算,效率极低。我建议用向量化版本:

for i in range(0, N, 16):
    vec_x = load(x[i:i+16])
    vec_y = max(vec_x, 0)
    store(y[i:i+16], vec_y)

你看,一次处理16个元素,效率直接提升16倍。当然,前提是芯片支持SIMD。

注意:Sigmoid的指数运算在硬件上很贵。我见过一些芯片用分段线性近似来加速,精度损失在1%以内,但速度能快5倍。如果你的应用对精度不敏感,可以试试。

3.4 归一化(BatchNorm/LayerNorm)系列:训练和推理的“双面人”

归一化,说白了就是让数据分布更稳定。BatchNorm在CNN里用得多,LayerNorm在Transformer里用得多。它们的计算模式类似:先算均值和方差,再对每个元素做归一化。

BatchNorm的计算公式:

y = (x - mean) / sqrt(var + eps) * gamma + beta

这里有个关键点:训练和推理的行为完全不同。训练时,mean和var是从当前batch算的;推理时,用的是训练时累积的全局mean和var。我刚开始做芯片映射时,没注意这个区别,结果推理时结果全错——因为用了训练时的动态mean。

LayerNorm的计算类似,但它是沿着特征维度归一化,而不是batch维度。在Transformer里,LayerNorm是每个token独立做的。

映射要点:归一化的计算量不大,但数据依赖性强。你得先算完mean,才能算var,最后才能算归一化。这导致流水线很难做。我建议把mean和var的计算合并到一个pass里,减少数据搬运。

举个例子,BatchNorm的映射流程:

  1. 加载输入数据x(H x W x C)
  2. 计算每个通道的mean(C个值)
  3. 计算每个通道的var(C个值)
  4. 加载gamma和beta(C个值)
  5. 对每个元素做归一化

你看,步骤2和3都需要遍历整个输入数据。如果芯片的缓存不够大,数据得反复从DDR搬,效率很低。我建议把步骤2和3合并:遍历一次数据,同时累加mean和var的中间结果。

避坑指南:我曾经在映射LayerNorm时,发现计算var需要先知道mean,导致流水线停顿。后来用了“在线更新”的方法——边遍历数据边更新mean和var的估计值,虽然精度略有损失,但速度提升了30%。

3.5 知识体系总览

好了,四大类算子都聊完了。我画了一张图,帮你理清它们的关系:

AI算子分类与特性总览 卷积(Conv) 矩阵乘(GEMM) 激活函数 归一化 计算密集型 数据复用高 循环展开映射 计算密度极高 数据复用差 分块策略关键 计算轻量 逐元素操作 向量化加速 数据依赖强 训练/推理不同 合并pass优化 典型应用:CNN(Conv+ReLU+BatchNorm) | Transformer(GEMM+LayerNorm) 映射策略总结 • 卷积:输出通道M为外层循环,空间维度为内层循环,最大化数据局部性 • 矩阵乘:分块大小根据缓存容量调优,避免缓存miss • 激活函数:使用SIMD向量化指令,Sigmoid可用近似计算 • 归一化:合并mean/var计算pass,减少数据搬运次数

这张图把四大类算子的核心特性和映射策略都串起来了。你想想看,卷积和矩阵乘都是计算密集型,但数据复用模式完全不同;激活函数和归一化计算量小,但数据流模式各有特点。映射时,得根据这些特性“对症下药”。

好了,这一章就到这儿。记住:算子特性决定映射策略,没有万能药方。多动手调参,多踩坑,慢慢就有感觉了。

可可精品资料库