3、深度学习模型基础:卷积、全连接、注意力机制等算子计算流程,模型参数与中间激活的存储需求

在嵌入式端侧推理优化中,理解模型底层算子的计算流程与存储需求是进行一切优化的前提。无论是量化、剪枝还是算子融合,其本质都是围绕“计算”与“存储”这两个核心维度展开。本章将深入剖析卷积、全连接、注意力机制三大核心算子的计算流程,并系统分析模型参数与中间激活(Feature Map)在内存中的占用规律,为后续章节的端侧优化策略奠定坚实的理论基础。

深度学习算子计算与存储需求全景图 卷积 (Conv2D) • 计算: 滑窗乘加 (MAC) • 权重: [Cout, Cin, K, K] • 激活: [N, H, W, Cout] • 计算量: O(N·H·W·Cout·Cin·K²) 全连接 (FC) • 计算: 矩阵乘 (GEMM) • 权重: [Cin, Cout] • 激活: [N, Cout] • 计算量: O(N·Cin·Cout) 注意力 (Attention) • 计算: Q·K^T, Softmax, ·V • 权重: [D, D] 投影矩阵 • 激活: [N, L, D] 及中间矩阵 • 计算量: O(N·L²·D) 存储需求核心维度 • 权重参数: 模型文件大小, 需驻留内存 • 中间激活: 前向推理峰值内存, 与Batch/分辨率强相关 • 优化目标: 降低两者峰值占用 端侧优化策略映射 • 量化: 减少权重/激活位宽 (FP32→INT8) • 算子融合: 减少中间激活的读写 (Conv+ReLU) • 内存复用: 激活内存池化, 降低峰值

上图清晰地展示了本章的核心逻辑:三大算子(卷积、全连接、注意力)各自拥有独特的计算模式与权重/激活存储结构,而端侧优化的核心任务就是针对这些算子的特性,在保证精度的前提下,降低计算复杂度与内存峰值。下面我们将逐一展开。

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 的乘积累加,而深度可分离卷积将其分解为:

  1. 深度卷积(Depthwise Conv):每个通道独立使用一个 K×K 卷积核,计算量为 Cin × K × K。
  2. 逐点卷积(Pointwise Conv):使用 1×1 卷积核进行通道融合,计算量为 Cin × Cout。

总计算量约为标准卷积的 1/Cout + 1/(K×K)。在存储方面,深度卷积的权重仅为 [Cin, 1, K, K],逐点卷积权重为 [Cout, Cin, 1, 1],两者相加远小于标准卷积的 [Cout, Cin, K, K]。这使得 MobileNetV1 的模型大小和中间激活内存均大幅下降,非常适合嵌入式部署。

实战要点:在端侧部署时,应优先检查卷积层是否为 1×1 或 3×3。1×1 卷积本质上是矩阵乘法,可高度复用底层 GEMM 库;3×3 卷积则需考虑 Winograd 或 FFT 等快速算法,但需注意这些算法会增加中间结果的存储开销(即变换后的矩阵),在内存受限时需权衡。

3.1.4 常见误区:忽略 Im2Col 带来的内存膨胀

许多端侧推理引擎(如 TFLite、ONNX Runtime)在实现卷积时,会采用 Im2Col + GEMM 的策略。Im2Col 将输入特征图展开成一个大矩阵,以便调用高效矩阵乘库。然而,Im2Col 过程会产生 K×K×Cin 倍的数据冗余。例如,对于 3×3 卷积,输入矩阵会被展开为原来的 9 倍(假设 Cin=1)。这会导致中间激活内存瞬间膨胀,在内存紧张的 MCU 上可能直接导致 OOM(内存溢出)。

风险提示:在评估模型内存需求时,不能仅计算理论激活值,还需考虑推理引擎内部实现(如 Im2Col 缓冲区、Winograd 变换域数据)带来的额外开销。建议使用推理引擎提供的内存分析工具(如 TFLite 的 Arena 分配模式)进行实测。

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 倍,且功耗更低。

经验提醒:对于 FC 层,除了量化外,还可以采用低秩分解(如 SVD)将权重矩阵分解为两个小矩阵的乘积,从而减少参数存储。但需注意,分解后计算量可能增加,需在存储与计算之间权衡。

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 架构的核心。其计算流程如下:

  1. 投影:输入序列 X(形状 [N, L, D],其中 L 为序列长度,D 为特征维度)分别与权重矩阵 W_Q, W_K, W_V(形状均为 [D, D])相乘,得到 Query(Q)、Key(K)、Value(V)矩阵。
  2. 相似度计算:计算 Q × K^T,得到注意力分数矩阵 S,形状为 [N, L, L]。
  3. 缩放与 Softmax:对 S 除以 √D 后,在最后一个维度上执行 Softmax,得到归一化权重 P。
  4. 加权求和:计算 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 层。

  1. 初始状态:FP32 模型,权重 800KB,激活峰值 300KB。显然,权重无法放入 Flash,激活也无法放入 RAM。
  2. 优化步骤:
    - 第一步:将权重量化为 INT8,权重降至 200KB,可放入 Flash。
    - 第二步:将激活量化为 INT8,激活峰值降至 75KB,可放入 RAM。
    - 第三步:进行算子融合(Conv+ReLU),激活峰值进一步降至 60KB。
    - 第四步:对 FC 层进行剪枝(保留 50% 权重),权重降至 100KB。
  3. 最终结果:模型总权重 100KB,激活峰值 60KB,成功部署。
复盘经验:此案例表明,量化是解决存储瓶颈的首选手段,但需配合算子融合和剪枝才能达到最佳效果。在 MCU 上,内存分配需考虑对齐(如 4 字节对齐)和内存池管理,避免碎片化。

3.4.4 总结与复盘

本章详细剖析了卷积、全连接、注意力机制三大算子的计算流程与存储需求。核心要点如下:

  • 卷积是计算密集型算子,优化重点在于降低计算量(如使用深度可分离卷积)和减少激活内存(如使用 Im2Col 时注意内存膨胀)。
  • 全连接是内存密集型算子,优化重点在于权重压缩(量化、剪枝、低秩分解)。
  • 注意力的存储瓶颈在于 L×L 的中间矩阵,优化重点在于分块计算和线性注意力近似。
  • 全局视角:在嵌入式端侧,需综合运用量化、融合、内存池化等手段,并利用推理引擎的内存分析工具进行实测验证,而非仅依赖理论计算。
最终提醒:在开始任何优化之前,务必先建立模型的“计算-存储”基线画像。使用工具(如 torchinfo 或 netron)统计每一层的参数和激活形状,并估算峰值内存。没有基线,优化就是盲目的。