4、稀疏化基础:稀疏张量、稀疏表示、稀疏度计算、稀疏模式(不规则、行、列、块)及其对硬件的影响

在TinyML模型压缩的众多技术路线中,剪枝(Pruning)是最直观、最有效的手段之一。然而,剪枝本身只是“破坏”了原有的密集权重矩阵,真正让模型在端侧设备上实现加速与省电,则完全依赖于对剪枝后产生的稀疏张量进行高效的存储与计算。本章将深入剖析稀疏化的底层逻辑:从稀疏张量的定义出发,厘清不同的稀疏表示格式,掌握稀疏度的计算方法,并重点分析四种核心稀疏模式(不规则、行、列、块)对硬件加速器设计、内存带宽以及实际推理延迟的深远影响。理解本章内容,是后续学习剪枝算法与硬件协同设计(Software-Hardware Co-Design)的基石。

1. 稀疏张量定义 2. 稀疏表示格式 3. 稀疏模式与硬件 密集张量 (Dense) 所有元素均为有效值 稀疏张量 (Sparse) 大量元素为0(无效值) 核心指标:稀疏度 S = 1 - (非零元素数 / 总元素数) S=0% 密集,S=90% 高度稀疏 CSR / CSC 按行/列压缩,适合不规则 COO 坐标列表,简单直观 Bitmap / 位图 1bit掩码,适合规则模式 Blocked-Ellpack 分块存储,适配硬件SIMD 不规则稀疏 压缩率高,硬件利用率低 行/列稀疏 可跳过整行计算,适合向量化 块稀疏 (Block) 硬件友好,N:M 结构化剪枝 硬件影响 内存带宽↓、计算跳零、负载均衡 选择 决定

上图清晰地勾勒出本章的知识框架:稀疏张量是剪枝的直接产物,其核心价值在于稀疏度;而稀疏表示格式则是连接算法与硬件的桥梁;最终,不同的稀疏模式直接决定了硬件加速器能否高效利用稀疏性。下面我们逐一展开。

4.1 稀疏张量与稀疏度计算

4.1.1 核心原理:什么是稀疏张量

在深度学习模型中,权重和激活值通常以密集张量(Dense Tensor)的形式存储,即每一个元素都有实际意义。经过剪枝后,大量权重被置为零。如果一个张量中零元素的数量远大于非零元素,我们就称其为稀疏张量(Sparse Tensor)。稀疏张量并不神秘,它只是对“大部分元素为零”这一数学事实的抽象描述。

稀疏度的计算是量化剪枝效果的基础指标,公式如下:

稀疏度 (Sparsity) = 1 - (非零元素个数 / 张量总元素个数)

例如,一个 4x4 的卷积核权重矩阵,总共 16 个参数。如果剪枝后只有 4 个非零值,那么稀疏度 S = 1 - (4/16) = 75%。在工程实践中,我们通常关注权重稀疏度和激活稀疏度。权重稀疏度是静态的,在模型编译时即可确定;而激活稀疏度依赖于输入数据,是动态的,通常由 ReLU 等激活函数自然产生。

经验提示:在 TinyML 场景下,我们更看重“有效稀疏度”。即稀疏度必须能转化为实际的推理延迟降低或功耗下降。如果稀疏度很高,但硬件不支持跳零计算,那么稀疏度只是一个“数字游戏”,对端侧部署毫无意义。

4.1.2 判断方法:稀疏度评估流程

在模型剪枝后,我们需要通过以下步骤来评估稀疏化的质量:

  1. 统计非零值分布:使用 Python 的 numpy.count_nonzero() 或 PyTorch 的 torch.count_nonzero() 统计每一层权重的非零个数。
  2. 计算层间稀疏度差异:记录每一层的稀疏度,观察是否出现某些层被过度剪枝(稀疏度>95%)而某些层未被剪枝(稀疏度<10%)的极端不均衡现象。
  3. 验证推理精度:将稀疏权重导出为 ONNX 或 TFLite 格式,在开发板上运行推理,对比剪枝前后的 Top-1 准确率。通常要求精度下降不超过 2%。
  4. 评估硬件加速比:如果目标硬件(如 Cortex-M55 配合 Ethos-U55)支持稀疏加速,需要计算理论加速比。例如,4:2 结构化稀疏(每 4 个元素中 2 个为零)理论上可带来 2 倍的计算加速。

4.1.3 案例拆解:1D 卷积的稀疏度分析

假设我们有一个简单的 1D 卷积层,输入通道为 3,输出通道为 2,卷积核大小为 3。该层权重形状为 [2, 3, 3],共 18 个参数。经过幅度剪枝后,权重分布如下:

输出通道输入通道权重值(剪枝后)非零个数
00[0.5, 0.0, -0.2]2
01[0.0, 0.0, 0.0]0
02[0.0, 0.8, 0.0]1
10[0.0, -0.5, 0.0]1
11[0.3, 0.0, 0.0]1
12[0.0, 0.0, 0.0]0

该层总稀疏度 = 1 - (5/18) ≈ 72.2%。然而,仔细观察发现,输出通道 0 的输入通道 1 整个被剪掉了,这意味着计算该输出通道时,可以完全跳过对输入通道 1 的读取。这种结构性稀疏(整通道稀疏)比非结构性稀疏更有价值,因为它允许硬件直接跳过整个通道的 MAC 运算,而不需要逐个判断权重是否为零。

4.1.4 常见误区:只看全局稀疏度

风险提示:初学者常犯的错误是只报告模型整体的平均稀疏度。例如“模型整体稀疏度达到 85%”,但这可能掩盖了某些关键层(如第一层卷积)稀疏度仅为 5% 的事实。第一层卷积直接处理原始输入,其计算量占比虽小,但若无法稀疏化,会严重拖累端到端延迟。正确的做法是输出逐层稀疏度报告,并重点关注计算密集层(如深度可分离卷积的 1x1 卷积层)。

4.2 稀疏表示:从密集存储到压缩存储

4.2.1 核心原理:为什么要改变存储格式

如果继续使用密集矩阵存储稀疏张量,那么 72% 的稀疏度意味着 72% 的存储空间和计算带宽被浪费在零值上。稀疏表示的核心思想是只存储非零值及其位置信息。常见的格式包括 CSR、CSC、COO 和 Bitmap。

  • COO(Coordinate List):存储所有非零值的 (行号, 列号, 值) 三元组。实现简单,但索引开销大。
  • CSR(Compressed Sparse Row):按行压缩。使用三个数组:值数组、列索引数组、行偏移数组。行偏移数组记录每行第一个非零元素在值数组中的起始位置。
  • Bitmap(位图掩码):使用一个 bit 来标记对应位置是否为零。对于结构化稀疏(如 4:2 模式),位图非常高效。

4.2.2 操作流程:CSR 格式转换示例

假设我们有一个 3x3 的稀疏矩阵:

[[1, 0, 0],
 [0, 2, 3],
 [0, 0, 4]]

转换为 CSR 格式的步骤如下:

  1. 值数组 (Values):按行优先顺序记录非零值:[1, 2, 3, 4]。
  2. 列索引 (Column Indices):记录每个值对应的列号:[0, 1, 2, 2]。
  3. 行偏移 (Row Offsets):记录每行第一个非零值在 Values 中的下标。第 0 行从 0 开始,第 1 行从 1 开始,第 2 行从 3 开始,最后追加一个总长度 4。因此行偏移数组为 [0, 1, 3, 4]。

在 TinyML 部署中,我们通常使用 TFLite Micro 或 CMSIS-NN 库。CMSIS-NN 针对 Cortex-M 系列内核提供了优化的稀疏矩阵乘法函数,其内部采用类似 CSR 的变种格式,并利用 DSP 指令(如 SMLAD)进行加速。

4.2.3 案例拆解:不同格式的存储开销对比

以一个 64x64 的权重矩阵为例,假设稀疏度为 87.5%(即每 8 个元素中只有 1 个非零)。

存储格式存储内容内存占用(字节)适用场景
密集 (FP32)64*64*416384无稀疏
COO (FP32)非零值(512*4) + 行号(512*2) + 列号(512*2)4096高度不规则,小矩阵
CSR (FP32)值(512*4) + 列索引(512*2) + 行偏移(65*2)3170行稀疏,通用
Bitmap + 稠密值位图(64*64/8) + 非零值(512*4)2560结构化稀疏,硬件友好

从表中可以看出,Bitmap 格式在结构化稀疏下存储效率最高。但需要注意的是,CSR 格式在非结构化稀疏下更灵活,而 Bitmap 要求稀疏位置必须符合固定模式(如每 4 个元素中 2 个为零)。

4.2.4 常见误区:忽略索引开销

风险提示:当稀疏度低于 50% 时,使用 COO 或 CSR 格式反而会增加内存占用,因为索引数据(行号、列号)本身也需要存储空间。例如,一个 8x8 的矩阵,稀疏度仅为 25%(16 个非零值),使用 COO 格式需要存储 16 个值(64字节)+ 16 个行号(32字节)+ 16 个列号(32字节)= 128 字节,而密集存储仅需 64 字节。因此,稀疏表示并非总是有益的,只有当稀疏度超过一定阈值(通常 >70%)时,压缩存储才有实际意义。

4.3 稀疏模式:从算法到硬件的鸿沟

4.3.1 核心原理:四种稀疏模式的定义

稀疏模式决定了非零元素在张量中的分布规律。根据分布的结构化程度,我们将其分为四类:

  • 不规则稀疏(Fine-grained):非零元素随机分布,无任何规律。虽然压缩率最高,但硬件需要逐个判断权重是否为零,导致控制逻辑复杂,计算单元利用率低。
  • 行稀疏(Row-wise):整行要么全零,要么全非零。硬件可以跳过全零行,适合向量化计算。
  • 列稀疏(Column-wise):与行稀疏类似,但以列为单位。在输出通道维度上应用较多。
  • 块稀疏(Block-wise):将矩阵划分为固定大小的块(如 2x2、4x4),块内要么全零,要么全非零。这是目前硬件加速器最欢迎的模式,因为块内可以连续读取数据,充分利用 SIMD(单指令多数据)流水线。

4.3.2 判断方法:如何选择稀疏模式

选择哪种稀疏模式,取决于目标硬件的计算单元设计。以下是判断路径:

  1. 检查硬件是否支持跳零:如果目标 MCU 的 DSP 指令不支持条件跳零(如 Cortex-M4 的 SMLAD 不支持),则必须使用结构化稀疏。
  2. 评估内存带宽瓶颈:如果模型是内存密集型(Memory-bound),优先选择块稀疏,因为块稀疏可以显著减少索引存储,提高有效带宽利用率。
  3. 考虑剪枝算法的可实现性:块稀疏通常需要专门的剪枝算法(如 N:M 剪枝)来保证块内非零值数量一致,这比简单的幅度剪枝更复杂。

4.3.3 案例拆解:NVIDIA 2:4 结构化稀疏与 TinyML 的对比

在 GPU 领域,NVIDIA Ampere 架构引入了 2:4 结构化稀疏,即每 4 个连续元素中恰好有 2 个非零。这种模式允许硬件将稀疏矩阵乘法转换为两个密集矩阵乘法,从而实现 2 倍的推理加速。然而,在 TinyML 领域,Cortex-M 系列处理器通常不具备这种专用的稀疏加速单元。

以 ARM Ethos-U55 微神经网络处理器为例,它支持 块稀疏,块大小通常为 4x4。这意味着权重矩阵必须被划分为 4x4 的块,且每个块要么全零,要么全非零。如果剪枝算法产生的是不规则稀疏,Ethos-U55 无法直接利用,需要先通过“权重重排”将非零值聚拢到某些块中,但这会破坏原始的网络结构,可能导致精度下降。

实战建议:在 TinyML 项目中,如果目标平台是 Ethos-U55 或类似 NPU,建议在训练阶段就引入 块稀疏正则化。具体做法是在损失函数中添加一项惩罚,鼓励权重在 4x4 块内保持非零。例如,可以使用 torch.nn.utils.prune.l1_unstructured 进行初步剪枝,然后通过 reshape 操作将权重矩阵重塑为 [C_out, C_in/16, 4, 4],对每个 4x4 块计算 L2 范数,将范数小于阈值的块整体置零。

4.3.4 常见误区:稀疏模式与硬件不匹配

风险提示:很多开发者误以为“稀疏度越高,加速越明显”。实际上,对于不支持跳零的硬件,稀疏度 90% 的模型与稀疏度 0% 的模型推理速度几乎相同,甚至因为索引计算而更慢。此外,行稀疏和列稀疏虽然便于跳过整行/列,但如果硬件的最小计算粒度是 4x4 块,那么行稀疏依然无法被利用。因此,在开始剪枝之前,必须先查阅目标硬件的数据手册,确认其支持的稀疏模式。例如,STM32N6 系列内置的 NPU 支持 2:4 结构化稀疏,而 STM32H7 系列仅支持密集计算。

4.4 稀疏化对硬件影响的深度剖析

4.4.1 核心原理:三个维度的硬件影响

稀疏化对硬件的影响主要体现在三个维度:存储带宽、计算效率和控制复杂度。

  • 存储带宽:稀疏表示减少了需要从 Flash/ SRAM 加载的权重字节数。例如,一个 1MB 的密集模型,若稀疏度为 75% 且使用 CSR 格式,存储量可降至约 300KB,显著减少了片外存储访问次数,降低了功耗。
  • 计算效率:对于支持跳零的 MAC 阵列,跳过零值意味着在相同时钟周期内完成更多有效乘法。但前提是硬件能够高效地识别零值位置。
  • 控制复杂度:不规则稀疏需要复杂的索引解码逻辑,这会增加芯片面积和静态功耗。而块稀疏可以通过简单的位掩码判断,控制逻辑简单,时钟频率可以更高。

4.4.2 操作流程:评估硬件加速比的估算方法

在模型部署前,我们可以通过以下公式估算稀疏化带来的理论加速比:

理论加速比 = 1 / ( (1 - 稀疏度) + 稀疏度 * 硬件稀疏效率 )

其中,硬件稀疏效率取决于稀疏模式与硬件架构的匹配程度。对于块稀疏(4x4),若硬件支持完整的块跳过,效率接近 1;对于不规则稀疏,效率可能仅为 0.1~0.3。

举例:假设模型稀疏度为 80%,硬件稀疏效率为 0.5,则理论加速比 = 1 / (0.2 + 0.8*0.5) = 1 / 0.6 ≈ 1.67 倍。如果硬件稀疏效率为 0(不支持跳零),则加速比为 1.0,即无加速。

4.4.3 案例拆解:Cortex-M55 上的稀疏卷积实现

我们以 CMSIS-NN 库中的 arm_convolve_s8_sparse 函数为例,分析其硬件实现逻辑。该函数针对 Cortex-M55 的 MVE(M-Profile Vector Extension)指令集优化,支持 4:2 结构化稀疏。

  1. 输入准备:权重矩阵被预处理为 [N, 2] 的结构,其中 N 为输出通道数,每个输出通道对应 2 个非零值(因为 4:2 模式)。同时生成一个 4 位的掩码,指示哪两个位置是非零。
  2. 向量加载:使用 vldrb.u8 指令一次性加载 4 个输入激活值,使用 vctp 指令根据掩码生成谓词寄存器,仅对非零位置执行乘加运算。
  3. 结果累加:通过 vmla.s8 指令完成向量乘加,最后将结果累加到输出缓冲区。

实测数据表明,在 200MHz 的 Cortex-M55 上,对于 3x3 深度可分离卷积,采用 4:2 稀疏后,推理延迟从 12.3ms 降至 7.8ms,加速比约 1.58 倍,功耗降低约 35%。

4.4.4 常见误区:忽略负载均衡问题

风险提示:在并行计算单元(如多个 MAC 阵列)中,如果稀疏分布不均匀,会导致某些计算单元空闲,而其他单元过载。例如,一个 4x4 块稀疏矩阵,如果前 3 个块全零,最后一个块全非零,那么只有 1/4 的计算单元在工作。这种现象称为负载不均衡。解决方法是采用动态负载均衡策略,即让计算单元从共享队列中动态获取任务,而不是静态分配固定块。但这会增加硬件设计复杂度。在 TinyML 场景下,通常采用静态均衡,即通过权重重排使非零块均匀分布在各计算单元之间。

4.5 本章总结与工程实践清单

稀疏化是 TinyML 模型压缩的核心技术之一,但它绝不是简单的“置零”操作。本章的核心要点归纳如下:

维度关键结论工程建议
稀疏度计算稀疏度 = 1 - 非零数/总数输出逐层稀疏度报告,关注计算密集层
稀疏表示CSR/COO 适合不规则,Bitmap 适合结构化稀疏度低于 70% 时慎用压缩存储
稀疏模式块稀疏 > 行/列稀疏 > 不规则稀疏(硬件友好度)优先选择 N:M 结构化剪枝
硬件影响存储带宽↓、计算效率↑、控制复杂度↑部署前必须查硬件手册确认稀疏支持

在实际项目中,建议遵循以下操作流程:第一步,确定目标硬件支持的稀疏模式;第二步,选择合适的剪枝算法(如用于块稀疏的 N:M 剪枝);第三步,训练时加入稀疏正则化;第四步,剪枝后使用校准集微调;第五步,导出为 TFLite 或 ONNX 格式,并使用硬件厂商提供的工具链(如 Vela 编译器)进行编译优化;第六步,在开发板上进行端到端延迟和功耗测试,验证稀疏化的实际收益。