3、深度学习模型基础:卷积、全连接、注意力机制等算子计算流程,模型参数与中间激活的存储需求
在嵌入式端侧推理优化中,理解模型底层算子的计算流程与存储需求是进行一切优化的前提。无论是量化、剪枝还是算子融合,其本质都是围绕“计算”与“存储”这两个核心维度展开。本章将深入剖析卷积、全连接、注意力机制三大核心算子的计算流程,并系统分析模型参数与中间激活(Feature Map)在内存中的占用规律,为后续章节的端侧优化策略奠定坚实的理论基础。
上图清晰地展示了本章的核心逻辑:三大算子(卷积、全连接、注意力)各自拥有独特的计算模式与权重/激活存储结构,而端侧优化的核心任务就是针对这些算子的特性,在保证精度的前提下,降低计算复杂度与内存峰值。下面我们将逐一展开。
3.1 卷积算子:计算流程与存储特征
3.1.1 核心原理:滑窗乘加与数据复用
卷积神经网络(CNN)的核心是卷积算子。其计算本质是输入特征图(Input Feature Map)与卷积核(Kernel/Weight)之间的滑窗乘加运算。对于输出特征图中的每一个像素点,都需要执行 Cin × K × K 次乘加操作(MAC),其中 Cin 为输入通道数,K 为卷积核尺寸。这一过程在空间维度(H, W)上滑动,在通道维度上累加。
从存储角度看,卷积层的权重参数形状为 [Cout, Cin, K, K],其中 Cout 为输出通道数。而中间激活(即输出特征图)的形状为 [N, H_out, W_out, Cout]。值得注意的是,在嵌入式端侧推理时,我们通常以 N=1(单张图片)进行,但 H_out 和 W_out 往往较大(例如 224×224),且 Cout 可能达到 512 甚至 1024,因此激活内存占用不容小觑。
3.1.2 判断方法:计算量与内存估算公式
在实际工程中,我们需要快速估算一个卷积层的资源消耗。以下是两个核心公式:
- 计算量(FLOPs):
FLOPs = 2 × N × H_out × W_out × Cout × Cin × K × K。乘以 2 是因为一次 MAC 包含一次乘法和一次加法。 - 激活内存占用:
Activation_Memory = N × H_out × W_out × Cout × dtype_size。例如,FP32 类型dtype_size=4字节,INT8 类型为 1 字节。
例如,一个输入为 112×112×64,卷积核为 3×3,输出通道为 128 的卷积层,其输出特征图尺寸为 112×112×128。在 FP32 下,该层激活内存占用为 1 × 112 × 112 × 128 × 4 = 6.25 MB。这个数值在端侧设备(如手机 NPU 或 DSP)上已经相当可观。
3.1.3 案例拆解:MobileNetV1 深度可分离卷积的存储优势
以 MobileNetV1 为例,其采用深度可分离卷积(Depthwise Separable Convolution)替代标准卷积。标准卷积的计算量为 Cin × Cout × K × K 的乘积累加,而深度可分离卷积将其分解为:
- 深度卷积(Depthwise Conv):每个通道独立使用一个
K×K卷积核,计算量为Cin × K × K。 - 逐点卷积(Pointwise Conv):使用
1×1卷积核进行通道融合,计算量为Cin × Cout。
总计算量约为标准卷积的 1/Cout + 1/(K×K)。在存储方面,深度卷积的权重仅为 [Cin, 1, K, K],逐点卷积权重为 [Cout, Cin, 1, 1],两者相加远小于标准卷积的 [Cout, Cin, K, K]。这使得 MobileNetV1 的模型大小和中间激活内存均大幅下降,非常适合嵌入式部署。
3.1.4 常见误区:忽略 Im2Col 带来的内存膨胀
许多端侧推理引擎(如 TFLite、ONNX Runtime)在实现卷积时,会采用 Im2Col + GEMM 的策略。Im2Col 将输入特征图展开成一个大矩阵,以便调用高效矩阵乘库。然而,Im2Col 过程会产生 K×K×Cin 倍的数据冗余。例如,对于 3×3 卷积,输入矩阵会被展开为原来的 9 倍(假设 Cin=1)。这会导致中间激活内存瞬间膨胀,在内存紧张的 MCU 上可能直接导致 OOM(内存溢出)。
3.2 全连接算子:计算流程与存储特征
3.2.1 核心原理:矩阵乘法与权重主导
全连接层(Fully Connected, FC)在数学上等价于矩阵乘法(GEMM)。输入向量 x(长度为 Cin)与权重矩阵 W(形状 [Cin, Cout])相乘,加上偏置 b,得到输出向量 y(长度为 Cout)。在 Transformer 架构中,FC 层通常以 1×1 卷积的形式出现,但其本质仍是 GEMM。
与卷积层不同,FC 层的计算强度(算术强度)较低,即计算量相对较少,但权重参数极大。例如,一个输入 2048 维、输出 2048 维的 FC 层,其权重参数数量为 2048 × 2048 = 4,194,304 个。在 FP32 下,仅此一层权重就占用 16 MB 内存。因此,FC 层是模型大小的主要贡献者,也是量化压缩的重点对象。
3.2.2 判断方法:权重与激活的占比分析
在嵌入式系统中,我们通常关注以下两个指标:
- 权重内存占比:
Weight_Memory / Total_Memory。对于 FC 层,该值通常极高。 - 激活内存峰值:由于 FC 层通常位于网络末端(分类头),其输入激活是前层卷积输出的展平,激活值本身不大,但权重占用是主要矛盾。
例如,在 ResNet50 中,最后的 FC 层(2048→1000)权重约为 2048 × 1000 × 4 = 8 MB,而整个模型权重约为 98 MB(FP32),FC 层占比约 8%。但在一些小型模型(如 LeNet)中,FC 层权重占比可超过 80%。
3.2.3 案例拆解:量化前后 FC 层的内存对比
假设一个 FC 层权重为 [1024, 512],FP32 下占用 1024 × 512 × 4 = 2 MB。若采用 INT8 量化(dtype_size=1),权重内存降至 0.5 MB,减少 75%。同时,若对激活也进行 INT8 量化,则输入输出激活的内存占用也相应减少。在端侧 NPU 上,INT8 的 GEMM 计算速度通常比 FP32 快 2-4 倍,且功耗更低。
3.2.4 常见误区:忽视 Batch Size 对激活内存的影响
在端侧推理中,通常 Batch Size = 1。但在某些场景(如视频流多帧处理)下,可能会增大 Batch Size。此时,FC 层的激活内存会随 Batch 线性增长。例如,输入维度为 2048,Batch Size 从 1 增至 4,激活内存从 8 KB 增至 32 KB(FP32)。虽然值不大,但在内存以 KB 计的 MCU 上,这可能是压垮骆驼的最后一根稻草。
3.3 注意力机制算子:计算流程与存储特征
3.3.1 核心原理:QKV 变换与 Softmax 的内存瓶颈
注意力机制(Attention)是 Transformer 架构的核心。其计算流程如下:
- 投影:输入序列
X(形状[N, L, D],其中 L 为序列长度,D 为特征维度)分别与权重矩阵W_Q, W_K, W_V(形状均为[D, D])相乘,得到 Query(Q)、Key(K)、Value(V)矩阵。 - 相似度计算:计算
Q × K^T,得到注意力分数矩阵S,形状为[N, L, L]。 - 缩放与 Softmax:对
S除以√D后,在最后一个维度上执行 Softmax,得到归一化权重P。 - 加权求和:计算
P × V,得到输出O,形状为[N, L, D]。
从存储角度看,注意力机制的中间激活包括 Q, K, V, S, P, O。其中,S 和 P 的形状为 [N, L, L],其内存占用与序列长度 L 的平方成正比。这是注意力机制内存消耗的主要瓶颈。
3.3.2 判断方法:序列长度与内存的平方关系
假设 N=1, L=1024, D=512,则 S 矩阵在 FP32 下占用 1 × 1024 × 1024 × 4 = 4 MB。这仅仅是一个注意力头的中间结果。若模型有 8 个头,且需要同时保留用于反向传播(训练阶段),内存将爆炸式增长。在推理阶段,虽然不需要保存梯度,但为了计算方便,仍需存储 S 和 P。
因此,在端侧部署 Transformer 模型时,必须考虑序列长度的限制。例如,在语音唤醒词检测中,序列长度可能仅为 64,此时 S 矩阵仅占 64 × 64 × 4 = 16 KB,可以接受。但在处理长文本或高分辨率图像时,序列长度可能达到 4096 甚至更高,此时必须采用稀疏注意力或线性注意力等近似方法。
3.3.3 案例拆解:FlashAttention 的内存优化思想
FlashAttention 是一种 IO 感知的精确注意力算法,其核心思想是分块计算(Tiling),避免实例化完整的 N×N 注意力矩阵。它将 Q、K、V 分块,在 SRAM 中计算局部注意力分数,并在线更新 Softmax 的归一化统计量。这样,中间矩阵 S 和 P 无需写入 HBM(高带宽内存),从而大幅降低内存峰值。
在嵌入式端侧,虽然硬件架构与 GPU 不同,但分块思想同样适用。例如,在 DSP 上,可以将 L 维分块,每次处理 64 或 128 个 ,将中间结果暂存在本地缓存中,避免外部内存的频繁读写。
Q×K^T 的计算拆分为多个 [B, D] × [D, B] 的小矩阵乘,其中 B 为块大小。这样既能利用缓存,又能减少内存占用。
3.3.4 常见误区:忽略 Softmax 的在线计算复杂度
标准的 Softmax 需要先计算最大值(用于数值稳定性),再计算指数和归一化。这要求算法遍历两次数据。在分块计算中,若处理不当,会导致重复计算。FlashAttention 通过在线更新运行最大值和指数和来解决此问题,但实现复杂度较高。在端侧,若内存允许,可以简单地将整个 S 矩阵存储在内存中,执行标准 Softmax,以换取实现简单性。但若内存受限,则必须实现在线 Softmax。
exp() 函数可能没有硬件加速,导致 Softmax 计算耗时较长。建议使用查表法或多项式近似来加速 exp() 计算,但需注意精度损失。
3.4 模型参数与中间激活的存储需求综合分析
3.4.1 存储需求对比表
| 算子类型 | 权重形状 | 激活形状 | 计算强度(FLOPs/Byte) | 主要存储瓶颈 |
|---|---|---|---|---|
| 卷积 (Conv2D) | [Cout, Cin, K, K] | [N, H, W, Cout] | 高(计算密集) | 激活内存(高分辨率时) |
| 全连接 (FC) | [Cin, Cout] | [N, Cout] | 低(内存密集) | 权重内存(模型大小) |
| 注意力 (Attention) | [D, D] 投影矩阵 | [N, L, D] + [N, L, L] | 中等(受 L 影响) | 中间矩阵 S/P(L² 增长) |
3.4.2 内存优化策略清单
- 权重优化:量化(FP32→INT8/INT4)、剪枝(结构化/非结构化)、低秩分解、权重共享。
- 激活优化:算子融合(如 Conv+ReLU 融合,减少一次激活读写)、激活内存池化(复用内存块)、混合精度(部分层用 FP16,部分层用 INT8)。
- 架构优化:使用深度可分离卷积、线性注意力替代标准注意力、减少序列长度(如池化或步长卷积)。
3.4.3 案例拆解:一个 1MB Flash 内存 MCU 上的模型部署分析
假设我们有一个 1MB Flash 和 512KB RAM 的 MCU,需要部署一个关键词识别(KWS)模型。模型结构为:2 个卷积层 + 1 个 FC 层。
- 初始状态:FP32 模型,权重 800KB,激活峰值 300KB。显然,权重无法放入 Flash,激活也无法放入 RAM。
- 优化步骤:
- 第一步:将权重量化为 INT8,权重降至 200KB,可放入 Flash。
- 第二步:将激活量化为 INT8,激活峰值降至 75KB,可放入 RAM。
- 第三步:进行算子融合(Conv+ReLU),激活峰值进一步降至 60KB。
- 第四步:对 FC 层进行剪枝(保留 50% 权重),权重降至 100KB。 - 最终结果:模型总权重 100KB,激活峰值 60KB,成功部署。
3.4.4 总结与复盘
本章详细剖析了卷积、全连接、注意力机制三大算子的计算流程与存储需求。核心要点如下:
- 卷积是计算密集型算子,优化重点在于降低计算量(如使用深度可分离卷积)和减少激活内存(如使用 Im2Col 时注意内存膨胀)。
- 全连接是内存密集型算子,优化重点在于权重压缩(量化、剪枝、低秩分解)。
- 注意力的存储瓶颈在于
L×L的中间矩阵,优化重点在于分块计算和线性注意力近似。 - 全局视角:在嵌入式端侧,需综合运用量化、融合、内存池化等手段,并利用推理引擎的内存分析工具进行实测验证,而非仅依赖理论计算。
torchinfo 或 netron)统计每一层的参数和激活形状,并估算峰值内存。没有基线,优化就是盲目的。