想象你是一家电商仓库的库管员。仓库里横平竖直地排着货架,每一格都放着一件货物。模型太大,就像仓库塞得太满,出库要绕很多路、花很长时间。这时候你要压缩仓库。有两种思路:一种是把每排货架上零零散散不常用的箱子丢掉,货架骨架不动,只是空位变多;另一种是直接把一整排货架撤掉,剩余货物重新码成一个小而紧凑的仓库。前者像非结构化剪枝,后者像结构化剪枝。选错方法,可能你花大力气清出许多空位,搬运路线却一点没缩短,等于白忙一场。这篇文章就用这个仓库故事,把两种剪枝方式的原理、硬件差异和部署选择讲明白。

岗位关联

对 AI 从业者的价值:搞懂两种剪枝的取舍,才能在部署时让模型真正变快变轻,而不是只看到参数变少,实际推理却纹丝不动。

目录
  1. 可视化演示
  2. 剪枝基本概念
  3. 非结构化剪枝
  4. 结构化剪枝
  5. 硬件加速差异
  6. 效果对比
  7. 部署选择策略
  8. 工具与生态
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

通过结构化与非结构化稀疏矩阵对比、Filter/Channel/Layer三级剪枝粒度、以及N:M半结构化稀疏的硬件加速效果三个模式,展示模型剪枝方法的核心差异与选择策略。

本节配有交互动效(进入视口后自动加载,约 3KB)。

结构化与非结构化剪枝原理对比 非结构化剪枝移除任意位置权重稀疏度可达90%但GPU无法加速 结构化剪枝按通道移除整列权重硬件友好实际加速效果显著 Unstructured: 90% sparse, 0x speedup vs Structured: 50% sparse, 3x speedup 稀疏度高不等于实际加速需匹配硬件能力

剪枝基本概念

仓库里看似满当当的货物,其实很多并不常被取用。一个训练好的神经网络也是这样,大量权重参数对最终判断的贡献很小。它们可能只是“陪跑”选手,留着只会增加搬运负担。剪枝要做的,就是把这些低价值的货物找出来并清走,让仓库变小。

在技术层面,剪枝通常会先评估每个权重的重要性,再生成一个与权重形状相同的掩码。掩码就像每格货架上的“保留”或“清空”标签:1 表示保留,0 表示清空。把掩码和原有权重相乘后,不重要的权重就变成了 0。接下来,我们用稀疏率来量化清理程度:稀疏率越高,表示清出的空位越多。

有了稀疏率这个尺子,我们就能横着比较不同剪枝方法的“清理程度”。但仓库整理的关键从来不是空位多不多,而是整理之后,搬运路线到底有没有缩短。这也是接下来要区分两种剪枝方式的起点。

s=1−∥W⊙M∥0∥W∥0s = 1 - \frac{\|W \odot M\|_0}{\|W\|_0}
通俗说明:稀疏率就是被清空的仓位占比,它描述“零”在参数矩阵里占了多少。
  1. 权值矩阵 W所有待剪参数组成的集合,相当于仓库里全部货物。
    WW
  2. 掩码矩阵 M每个货位上的保留标记,1 表示留,0 表示清走。
    M∈{0,1}dM \in \{0,1\}^{d}
  3. 逐元素相乘用标记把不重要的参数变成 0。
    W⊙MW \odot M
  4. L0 范数统计非零位置的个数。
    ∥⋅∥0\|\cdot\|_0
  5. 稀疏率 s1 减去清空后非零数量与原来非零数量的比值。
    s=1−∥W⊙M∥0∥W∥0s = 1 - \frac{\|W \odot M\|_0}{\|W\|_0}

非结构化剪枝

非结构化剪枝很像“零散丢箱子”:在每一排货架上挑出那些最碍事、最不常用的单个箱子扔掉。它不关心货架结构,只看单个货物有没有价值。于是清理完之后,仓库的骨架还是原来的样子,只是很多格子里出现了零散的空位。

在神经网络里,这种剪枝的粒度是一单个权重。判断标准通常很简单:权重绝对值越小,说明它对输出的影响越小,越可以被归零。只要设定一个阈值,绝对值低于阈值的权重就被清掉,高于阈值的保留。

非结构化剪枝的优点是灵活,几乎每个权重都能被单独评价,所以可以达到很高的稀疏率。但问题是,空位分布毫无规律,货架还是原来的货架,搬运路线并不会自动变短。想真正受益,需要专门的稀疏存储格式和稀疏算子来跳过这些零,而这些支持在普通硬件上往往并不齐全。

Mij={1,∣wij∣≥λ0,∣wij∣<λM_{ij} = \begin{cases} 1, & |w_{ij}| \ge \lambda \\ 0, & |w_{ij}| < \lambda \end{cases}
通俗说明:绝对值大的权重视为重要货物,低于阈值的清空。
  1. 权值坐标下标 i 和 j 标记矩阵里的第 i 行第 j 列,相当于货架的层号和排号。
    wijw_{ij}
  2. 重要性度量用绝对值大小估算单个权重的贡献。
    ∣wij∣|w_{ij}|
  3. 阈值 lambda一个经验门槛,低于它就进入清理名单。
    λ\lambda
  4. 掩码规则重要标记为 1,不重要标记为 0。
    MijM_{ij}

结构化剪枝

结构化剪枝是另一种思路:不零散地丢单件货物,而是直接判定“哪一排货架整体没用”,然后整排撤走。剩余货物重新码放后,仓库本身就是个更小的仓库。在模型里,这个“一排货架”对应的就是一组卷积核或一个输出通道。

要判断一排货架的价值,不能只看其中一个箱子,而要看这一整排所有箱子的综合贡献。常见做法是计算一个通道内所有权重的平方和,平方和越大,说明这一组卷积核整体越重要,越应该保留。

按通道剪掉低价值的整组卷积核后,模型的通道数变少,权重矩阵的维度也真正变小了。这就像仓库从“12排8层”变成“8排8层”,后续所有搬运和计算都可以在一个更小的空间里完成。当然,整排货架被撤走,对模型的“信息结构”冲击更大,所以剪完之后通常需要微调,让剩余通道学会弥补被清走部分的功能。

Ic=∑i,j(Wc,i,j)2I_c = \sum_{i,j} \left(W_{c,i,j}\right)^2
通俗说明:每个输出通道的权重平方和,代表整排货架的价值。
  1. 输出通道 c卷积层的第 c 组卷积核,对应仓库中一排货架。
    WcW_c
  2. 通道内权重该通道里所有属于它的小货箱。
    Wc,i,jW_{c,i,j}
  3. 平方求和把货箱价值平方后累加,避免正负相消。
    ∑i,j(Wc,i,j)2\sum_{i,j} \left(W_{c,i,j}\right)^2
  4. 通道重要度\(I_c\) 越大,说明这一排货架越值得保留。
    IcI_c

硬件加速差异

仓库整理完,能不能真的跑得更快,最终要看搬运设备认不认这种整理方式。普通叉车按固定路线工作,不管货架上有没有空位,它都会一格一格走完。在硬件里,这种“普通叉车”就是稠密矩阵算子。它对零散空位视而不见,即使权重有一半是 0,计算量仍然不变。

结构化剪枝则不同,它把仓库整体改小了。剩下的是一个小而密的仓库,普通叉车可以直接少跑很多排,所以不需要特殊硬件也能获得加速。非结构化剪枝要想提速,必须等专门认识空位的“智能叉车”出现,也就是支持稀疏计算的算子。

从公式能看出,非结构化剪枝并没有改变 \(C_{\text{out}}\)、\(C_{\text{in}}\)、\(K\) 这些结构因子,所以稠密算子执行的计算量几乎不变。而结构化剪枝直接砍掉一部分输出通道,\(C_{\text{out}}\) 变小,整条计算链路的规模也随之缩小。这就像普通叉车面对重新规划过的小仓库,天然就能少走路。

FLOPs∝Cout×Cin×K2×H×W\text{FLOPs} \propto C_{\text{out}} \times C_{\text{in}} \times K^2 \times H \times W
通俗说明:计算量由输出通道、输入通道、卷积核大小和特征图尺寸共同决定。
  1. 输出通道数每多一个输出通道就多一组滤波器。
    CoutC_{\text{out}}
  2. 输入通道数每个滤波器都要和输入通道逐位相乘再相加。
    CinC_{\text{in}}
  3. 卷积核面积卷积核越大,乘法次数越多。
    K2K^2
  4. 特征图尺寸输出特征图的高和宽决定运算重复次数。
    H×WH \times W

效果对比

很多人一看到“稀疏率 90%”就觉得模型一定快了十倍。可惜,参数少和真正跑得快是两回事。非结构化剪枝可以把稀疏率做得很高,模型体积也能显著下降,但如果没有配套的稀疏算子,推理延迟几乎不会变。结构化剪枝虽然不像非结构化那么“狂野”,却能稳定地把通道数降下来,让实际计算量同步下降。

我们还需要关注算力利用率。它反映的是“硬件干的有效活”占“硬件实际干的活”的比例。结构化剪枝把需要计算的矩阵缩小了,硬件做的几乎都是有效计算;非结构化稀疏在普通稠密算子下,硬件仍然要遍历所有位置,有效计算占比很低。

从精度影响看,非结构化剪枝保留的“零散空位”对模型结构破坏小,但过度剪也会让信息碎片化;结构化剪枝一次性砍掉整组通道,初始精度下降可能更明显,但通过微调和结构重建,往往能找回更多精度。所以选型不能只看压缩比,要看最终在目标设备上测出的延迟、功耗和精度。

U=C有效C执行U = \frac{C_{\text{有效}}}{C_{\text{执行}}}
通俗说明:算力利用率是“真正有用的计算”占“硬件实际执行计算”的比例。
  1. 有效计算量真正改变模型输出的必要计算。
    C有效C_{\text{有效}}
  2. 实际执行量硬件在推理时实际完成的乘加次数。
    C执行C_{\text{执行}}
  3. 利用率 U非结构化稀疏没有稀疏算子供能时,分母不降,U 很低。
    UU

部署选择策略

回到仓库现场,库管员决定用哪种整理方式,先要问一句:手里的叉车支持哪种仓库?如果设备只有普通 CPU 和稠密算子,那非结构化稀疏做得再漂亮,推理时叉车依然要走完整条过道,等于白剪。这种情况下,应该优先考虑通道结构化剪枝,把仓库整体改小。

如果目标是减少模型占用的存储空间,而非追求极致加速,那么非结构化剪枝配合稀疏存储格式是合理的。如果硬件支持 2:4 结构化稀疏,比如 NVIDIA 的 Ampere 及后续架构,那么把权重按“每四个保留两个”的规则剪枝,既能享受较高稀疏率,又能触发硬件加速。

这套决策逻辑放在端侧设备上同样适用。手机 CPU、嵌入式 NPU 大多喜欢规整的通道结构,因为内存访问连续、算子实现简单。于是边缘计算里,通道剪枝往往是第一选择;只有遇到专门支持稀疏计算的加速器时,非结构化路线才真正有发挥空间。

R≈1−L目标L当前R \approx 1 - \frac{L_{\text{目标}}}{L_{\text{当前}}}
通俗说明:如果想让延迟从当前值降到目标值,可粗略估算需要砍掉多少通道。
  1. 当前延迟剪枝前模型一次推理的耗时。
    L当前L_{\text{当前}}
  2. 目标延迟希望达到的耗时上限。
    L目标L_{\text{目标}}
  3. 剪枝比例 R需要削减计算资源的粗略比例,之后再根据硬件实测回调。
    RR

工具与生态

好消息是,两类剪枝都不是从零手写。PyTorch 提供了权重剪枝工具,可以按阈值或比例生成掩码,适合快速搭建非结构化剪枝流程。社区里也有针对通道剪枝的开源库,能帮助我们把卷积层、批归一化层和全连接层的通道对齐裁掉,从而得到真正可部署的小模型。

在硬件生态方面,NVIDIA 提供了 2:4 结构化稀疏的支持。这种模式要求权重矩阵每四个连续元素里恰好保留两个非零,硬件可以据此设计高效的稀疏计算单元。它像一种“标准规格的半空托盘”,既保留了一定稀疏度,又让叉车能按固定规律跳过空位。

不同推理框架对稀疏的支持也参差不齐。TensorRT 对 2:4 结构化稀疏有专门优化,而许多轻量级框架仍然只擅长稠密计算。因此,工具选型必须跟着部署硬件走:先确认推理引擎认哪种稀疏模式,再决定训练时用结构化还是非结构化剪枝。

∑k=03Mi,4j+k=2\sum_{k=0}^{3} M_{i,4j+k} = 2
通俗说明:NVIDIA 2:4 稀疏要求每四个连续权值里恰好保留两个非零值。
  1. 掩码矩阵 M记录哪些权重保留的 0/1 矩阵。
    MM
  2. 行号 i当前处理的是第 i 行权重。
    ii
  3. 块起点 4j每 4 个权值看作一个块,j 是块编号。
    4j+k4j+k
  4. 块内非零数每个块内保留两个非零权值。
    ∑k=03Mi,4j+k=2\sum_{k=0}^{3} M_{i,4j+k} = 2

AI 应用场景

手机端人脸识别模型压缩

阶段:边缘推理部署

解决的问题:传统手机 CPU 跑人脸识别模型延迟偏高,App 卡顿。如果只用非结构化剪枝,普通 CPU 没有稀疏算子,压缩后的模型依旧跑不快。

具体案例:算法工程师优先对卷积层做通道结构化剪枝,把 FLOPs 降低一半,再配合微调恢复精度。最终模型在手机 CPU 上推理从 80 毫秒降到 42 毫秒,且体积减小近一半。

云端 GPU 推理服务降本

阶段:推理引擎适配

解决的问题:云端服务使用 NVIDIA GPU,传统权重稀疏模式无法触发硬件加速,算力被浪费。

具体案例:工程师利用 NVIDIA 的 2:4 结构化稀疏能力,将部分权重矩阵裁剪成“每四个保留两个”的固定模式,再通过 TensorRT 部署。结果显示吞吐量显著提升,单卡可以承接更多请求。

智能摄像头模型存储受限

阶段:模型压缩与交付

解决的问题:摄像头设备闪存非常小,传统稠密模型装不进去,同时推理框架不支持复杂稀疏算子。

具体案例:团队先把模型做非结构化剪枝,再用稀疏格式存储权重,让模型体积从 40MB 降到 12MB;同时保留稠密计算结构,避免推理时无算子可用。最终模型成功烧录进设备。

Python 演示

python39 行
import numpy as np def unstructured_prune(weight, ratio):    # 按绝对值大小给所有权重排序    flatten = np.abs(weight).ravel()    # 找出裁掉 ratio 比例的阈值    cutoff = np.percentile(flatten, ratio * 100)    # 高于阈值保留,低于阈值置零    mask = np.where(np.abs(weight) > cutoff, 1.0, 0.0)    pruned = weight * mask    return pruned, mask def structured_prune_by_channel(weight, keep_ratio, axis=0):    # 计算每个输出通道的 L2 范数    norm = np.sqrt((weight ** 2).sum(axis=tuple(range(1, weight.ndim))))    # 用分位数确定保留通道的阈值    threshold = np.percentile(norm, (1 - keep_ratio) * 100)    keep = norm >= threshold    pruned = weight.copy()    if axis == 0:        pruned[~keep, :, :] = 0    else:        pruned[:, ~keep, :] = 0    return pruned, keep def sparsity(mask):    # 统计零元素占比    return 1.0 - np.count_nonzero(mask) / mask.size if __name__ == "__main__":    np.random.seed(0)    # 模拟一个卷积层权重,16 个输出通道,32 个输入通道,3x3 卷积核    weight = np.random.randn(16, 32, 3, 3)    _, mask_u = unstructured_prune(weight, 0.8)    _, keep_s = structured_prune_by_channel(weight, 0.5, axis=0)    print("非结构化稀疏率:", sparsity(mask_u))    print("结构化保留通道数:", keep_s.sum(), "/", weight.shape[0])    print("非结构化后仍参与计算的矩阵形状:", weight.shape)    print("结构化后矩阵可直接缩成:", (int(keep_s.sum()), weight.shape[1], weight.shape[2], weight.shape[3]))
目的:用 NumPy 分别演示非结构化逐权重剪枝和结构化按通道剪枝的掩码生成差异,帮助理解两种方法对模型结构的后续影响。
  1. flatten = np.abs(weight).ravel()把权重矩阵展平,方便计算绝对值阈值。
  2. cutoff = np.percentile(flatten, ratio * 100)找出“剪掉多少比例”对应的权重绝对值门槛。
  3. mask = np.where(np.abs(weight) > cutoff, 1.0, 0.0)生成非结构化掩码,每个权重独立决定去留。
  4. norm = np.sqrt((weight ** 2).sum(axis=tuple(range(1, weight.ndim))))按输出通道计算整组卷积核的综合重要度。
  5. threshold = np.percentile(norm, (1 - keep_ratio) * 100)用分位数选出需要保留的通道。
  6. pruned[~keep, :, :] = 0把低价值通道整组置零,体现结构化剪枝的“整排撤货架”。

常见误区

误区我觉得模型剪到 90% 都是零,推理速度也应该变快十倍。很多硬件不是专门为稀疏设计,零散空位并不会自动减少计算量。

正解稀疏率只说明“空位多”,不代表“路更短”。在没有稀疏算子的设备上,普通稠密计算仍然会遍历所有位置。真正加速取决于结构化剪枝带来的形状缩小,或硬件对稀疏模式的专门支持。

为何常见:人们容易把参数数量与计算量混为一谈,忽略了硬件执行模型的方式。

误区一次性砍掉整排通道,听起来就很粗暴,精度损失肯定比逐权重剪枝大。

正解结构化剪枝确实对模型结构冲击更集中,但通过剪枝后的微调,剩余通道有机会重新学习并补偿损失。非结构化剪枝虽然单个权重影响小,一旦剪到很高稀疏率,信息碎片化和不规则稀疏同样会严重损害精度。

为何常见:人们容易把“剪得更细”等同于“剪得更轻”,忽视结构重建和微调的作用。

误区只要权重变成 0,保存下来的模型文件就应该变小。

正解许多框架默认仍然按稠密矩阵保存权重,0 也占着存储位置。只有转成稀疏存储格式,或刻意外部压缩,模型文件才会真正缩小。所以剪枝后还要配合存储格式改造。

为何常见:因为训练日志里只显示稀疏率,人们误以为稀疏率自动等同于磁盘占用下降。

误区我的设备没有稀疏加速算子,所以剪枝没有意义。

正解结构化剪枝不需要稀疏算子,它把稠密矩阵直接变小,普通 CPU 和 GPU 也能受益。即使设备不支持稀疏,只要选择通道剪枝等结构化方案,依然可以明显降低延迟和功耗。

为何常见:很多人把“剪枝”和“非结构化稀疏”画等号,忽略了结构化剪枝本身就是一条完整的工程路线。

面试题

Q1. 非结构化剪枝的裁剪基本单位是什么?

单选题 · easy

  • A.单个权重参数← 正确答案
  • B.整个卷积核
  • C.输出特征图通道
  • D.整个网络层

答案:A。非结构化剪枝逐个评价权重参数,哪个不重要就单独置零。卷积核、通道和网络层都是更大的颗粒度,属于结构化剪枝的范畴。就像零散丢单件货物,而不是整排货架撤走。

Q2. 结构化剪枝通常按哪个粒度裁剪?

单选题 · easy

  • A.单个权重参数
  • B.通道或卷积核← 正确答案
  • C.浮点数值位宽
  • D.学习率参数

答案:B。结构化剪枝把一组卷积核或一个通道作为一个整体来裁剪,裁剪后矩阵形状会变小。单个权重参数量级太小,属于非结构化剪枝的粒度。

Q3. 以下哪种结果最适合普通 CPU 或 GPU 直接获得加速?

单选题 · mid

  • A.细粒度权重置零
  • B.随机稀疏九成
  • C.通道剪枝一半← 正确答案
  • D.稀疏格式存储

答案:C。普通 CPU 和 GPU 主要跑稠密算子,零散权重即便归零仍会参与计算。通道剪枝把矩阵真正变小,不需要特殊稀疏算子就能直接减少计算量。

Q4. 2:4 结构化稀疏的含义是?

单选题 · mid

  • A.每两个权重保留四个
  • B.每四层网络保留两层
  • C.每四个权重保留两个← 正确答案
  • D.每四个通道保留两个

答案:C。2:4 稀疏表示每四个连续权重里恰好保留两个非零,这是一种固定规律的稀疏模式,便于硬件跳过零值,实现专门加速。

Q5. 模型剪枝后通常需要什么操作来恢复精度?

单选题 · mid

  • A.剪完直接部署
  • B.降低输入分辨率
  • C.微调或重训练← 正确答案
  • D.增加十倍训练数据

答案:C。剪枝会破坏原来的信息分布,直接部署往往精度下降明显。微调或重训练可以让剩余权重重新适应数据,逐步找回精度。降低分辨率是另类优化,不是剪枝后的标准步骤。

Q6. NVIDIA Ampere 架构原生支持的权重稀疏模式是?

单选题 · hard

  • A.任意非结构化稀疏
  • B.二比四结构化稀疏← 正确答案
  • C.只支持通道剪枝
  • D.完全不支持稀疏

答案:B。Ampere 架构设计了针对 2:4 结构化稀疏的计算单元,要求每四个权值保留两个非零。任意非结构化稀疏虽然灵活,但无法触发这种硬件加速。

Q7. 目标是在手机 CPU 端降低推理延迟,优先选哪种方案?

单选题 · hard

  • A.九成随机稀疏化
  • B.通道结构化剪枝← 正确答案
  • C.不做任何压缩
  • D.只做权重聚类

答案:B。手机 CPU 大多没有通用稀疏算子,随机稀疏化难以降低延迟。通道结构化剪枝可以缩小矩阵尺寸,让普通稠密计算直接跑得更快。

Q8. 非结构化稀疏在通用硬件上的缺点有哪些?

多选题 · mid

  • A.访存模式不规则← 正确答案
  • B.专用算子依赖强← 正确答案
  • C.稀疏库支持有限← 正确答案
  • D.压缩效果总是差

答案:ABC。非结构化稀疏的空位没有规律,读取权重时访存跳跃严重,必须依赖专用稀疏算子和库才能加速。但它的压缩效果并不总是差,反而往往很高,所以 D 不正确。

Q9. 结构化剪枝的优势包括哪些?

多选题 · mid

  • A.保持稠密规则形状← 正确答案
  • B.硬件加速更友好← 正确答案
  • C.可减少实际计算量← 正确答案
  • D.不需要任何微调

答案:ABC。结构化剪枝保留的是规则稠密矩阵,形状变小后普通硬件也能提速,实际计算量同步减少。但它仍然需要剪后微调来恢复精度,所以 D 错误。

Q10. 实际部署选择剪枝方案时应重点考虑哪些因素?

多选题 · extreme

  • A.目标硬件稀疏支持← 正确答案
  • B.精度敏感程度← 正确答案
  • C.部署框架适配性← 正确答案
  • D.只看论文的流行度

答案:ABC。硬件是否支持稀疏、模型精度是否敏感、推理框架能否适配,都会直接影响剪枝收益和上线效果。论文流行度不能替代实际工程验证,所以 D 不正确。