4、模型剪枝与稀疏化:结构化剪枝与非结构化剪枝、权重剪枝与神经元剪枝、稀疏矩阵的存储与计算、NVIDIA的稀疏化技术(2:4稀疏)

模型剪枝,说白了就是给神经网络“瘦身”。

你想想看,一个训练好的大模型,里面其实有很多参数是冗余的。就像一张高清照片,去掉一些不重要的像素点,人眼根本看不出来。剪枝的目的就在于此——去掉那些不重要的连接或神经元,让模型更轻、更快,同时尽量不掉精度。

我在实际部署中遇到过不少情况,模型精度明明很高,但一上嵌入式设备就卡成PPT。后来发现,剪枝一下,推理速度能翻倍。嗯,这活儿值得好好聊聊。

4.1 结构化剪枝 vs 非结构化剪枝

这是剪枝领域最基础的一个分法。我个人习惯这么理解:

  • 非结构化剪枝:把单个权重置为零。粒度很细,想剪哪个权重就剪哪个。但问题是,剪完后的权重矩阵变得稀疏且不规则,普通硬件很难加速。
  • 结构化剪枝:按“块”来剪。比如剪掉整个卷积核、整个通道,甚至整个层。剪完后模型结构还是规整的,硬件友好,加速效果立竿见影。

核心区别一句话:非结构化剪枝是“点”级别的修剪,结构化剪枝是“面”级别的修剪。

我在项目中遇到过这样一个坑:非结构化剪枝后,模型大小确实变小了,但推理速度几乎没变。为什么?因为底层库(比如cuDNN)对稀疏矩阵的支持并不好,计算时还是按稠密矩阵来算的。白忙活一场。

所以我的建议是:如果你追求实际加速,优先考虑结构化剪枝。非结构化剪枝更适合学术研究,或者配合专门的稀疏计算硬件(比如NVIDIA的Ampere架构)。

4.2 权重剪枝 vs 神经元剪枝

这两个概念容易混淆,我简单拆解一下。

  • 权重剪枝:剪掉的是单个权重参数。比如一个全连接层有1000个权重,我们剪掉其中300个(置为零)。这是最细粒度的剪枝。
  • 神经元剪枝:剪掉的是整个神经元(或整个通道)。比如一个卷积层有64个输出通道,我们直接砍掉16个。这属于结构化剪枝的一种。

你可能会问:那到底该用哪种?

我的经验是:权重剪枝适合压缩模型体积,因为零权重多了,可以用稀疏格式存储。而神经元剪枝适合加速推理,因为通道数少了,计算量直接下降。

小技巧:在实际工程中,我经常把两者结合使用。先用神经元剪枝把通道数砍到合理范围,再用权重剪枝做微调。这样既保证了加速效果,又不会损失太多精度。

4.3 稀疏矩阵的存储与计算

剪枝之后,模型里会出现大量零值。如果还用稠密矩阵存,那简直是浪费。所以我们需要稀疏矩阵的存储格式。

常见的稀疏格式有几种:

格式 说明 适用场景
CSR(Compressed Sparse Row) 按行压缩,存储非零值、列索引、行偏移 行稀疏、列稠密的矩阵
CSC(Compressed Sparse Column) 按列压缩,类似CSR的转置 列稀疏、行稠密的矩阵
COO(Coordinate) 直接存储非零值的坐标和数值 简单直观,适合构建稀疏矩阵
BSR(Block Sparse Row) 按块压缩,适合结构化稀疏 NVIDIA的2:4稀疏就用这种思路

我记得有一次,我用CSR格式存了一个剪枝后的模型,模型文件从50MB降到了12MB。但推理时发现,CPU上计算稀疏矩阵乘法反而比稠密还慢。后来查资料才知道,稀疏计算需要专门的硬件或库支持,否则得不偿失。

避坑指南:我曾经在ARM CPU上尝试用稀疏矩阵加速,结果性能反而下降了20%。原因是ARM的NEON指令集对稀疏计算支持有限。所以,用稀疏格式前,先确认你的硬件平台是否支持

4.4 NVIDIA的稀疏化技术(2:4稀疏)

说到稀疏化,不得不提NVIDIA的2:4稀疏。这是Ampere架构引入的一项硬核技术。

什么叫2:4稀疏?简单说就是:每4个权重中,强制让2个为零。而且这2个零的位置是固定的——必须是每4个一组中的前2个或后2个。

为什么这么设计?因为NVIDIA的硬件专门为这种模式做了优化。在Tensor Core中,如果遇到2:4稀疏的矩阵,计算速度可以翻倍,同时精度几乎不掉。

我在部署一个BERT模型时试过这个技术。原模型有1.1亿参数,剪枝后只剩一半的非零值。用NVIDIA的A100跑推理,速度提升了1.8倍,而精度只掉了0.3%。效果相当惊艳。

使用2:4稀疏的步骤大致如下:

# 伪代码示例
import torch
from torch.sparse import to_sparse_semi_structured

# 假设我们有一个训练好的权重矩阵 W
# 首先进行剪枝,保留每4个中最大的2个
mask = torch.zeros_like(W)
for i in range(0, W.shape[1], 4):
    _, idx = torch.topk(W[:, i:i+4], 2, dim=1)
    mask[:, i + idx] = 1

W_pruned = W * mask

# 转换为半结构化稀疏格式
W_sparse = to_sparse_semi_structured(W_pruned)

# 推理时,Tensor Core会自动加速
output = torch.nn.functional.linear(input, W_sparse)

个人经验:2:4稀疏对精度的影响通常很小,但也不是万能的。如果你的模型本身就很紧凑(比如MobileNet),剪枝后精度可能会掉得比较明显。建议先在小数据集上验证一下。

另外,NVIDIA的2:4稀疏目前只支持FP16和BF16精度。如果你用的是INT8量化,那就没法享受这个加速了。嗯,这里要注意一下。

小结

模型剪枝和稀疏化,说到底就是“用更少的参数,做同样的事”。

  • 结构化剪枝适合实际部署,非结构化剪枝适合学术探索。
  • 权重剪枝压缩体积,神经元剪枝加速推理。
  • 稀疏矩阵的存储格式要选对,否则可能适得其反。
  • NVIDIA的2:4稀疏是当前最成熟的硬件加速方案,值得优先考虑。

最后说一句:剪枝不是万能的。它和量化、蒸馏等技术配合使用,效果才最好。我一般会在剪枝后做一次微调,让模型重新适应稀疏结构。这一步很关键,别偷懒。