ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

深度学习模型压缩三步法:剪枝、量化与霍夫曼编码实践解析

深度学习模型压缩三步法:剪枝、量化与霍夫曼编码实践解析 2016年那篇引爆模型压缩方向的论文我翻来覆去读了很多遍。每次看都有新的体会从第一遍惊叹于它的工程美感到后来带着实际问题回看它一步步的设计选择和取舍越来越觉得这篇工作不只是一套压缩流程更是一套完整的、可迁移的深度学习系统优化方法论。这篇随笔就把我读这篇论文的理解、思考和在实际工作中验证过的东西都写出来。它适合谁读准备部署模型上线的工程向同学、研究模型压缩算法的人、还有那些总觉得模型推理遇到瓶颈但说不出具体卡在哪的开发者。1. 这篇论文到底解决了什么问题1.1 背景模型越来越大存储和带宽成为瓶颈深度学习模型性能提升的另一面是文件体积和计算量的爆炸式增长。一个经典的AlexNet权重文件大约240MBVGG-16更夸张超过500MB。放在服务器上可能觉得还好但一旦到了移动端、嵌入式设备、自动驾驶的车载芯片这个体积就成了大问题。存储占用只是一部分更隐蔽的问题是内存带宽。推理的时候权重数据要不停从内存搬运到计算单元。搬运速度跟不上计算速度芯片就饿死了。所以模型变小不只是省硬盘更直接的好处是减少带宽压力让推理更快。Deep Compression这篇论文瞄准的就是这个痛点通过系统性的压缩在不损失精度的前提下把模型体积大幅缩小。1.2 Deep Compression的三步流水线概述论文提出的方法很清晰就是三步走每一步都针对模型存储的不同冗余剪枝Pruning把绝对值很小的权重直接清零让网络变稀疏。这一步砍掉的是参数里冗余的连接。量化Quantization让多个权重共享同一个数值用查表代替直接存浮点数。这步压缩掉的是权重间的数值冗余。霍夫曼编码Huffman Coding对前两步产出的数值做可变长编码进一步压缩存储空间。这三步是串行流水线每一步的输出就是下一步的输入。我读这篇论文最喜欢的一点就是它的每一步都有扎实的理论依据和实验验证不是简单堆砌技巧。1.3 读之前请先区分稀疏、量化和熵编码是三个维度的事刚开始接触压缩的同学容易把这三个概念混在一起其实它们解决的是完全不同的问题。稀疏代表着结构上的冗余很多权重本身就不重要直接砍掉。量化代表着数值上的冗余相近的权重可以用同一个代表值。熵编码代表着统计上的冗余频繁出现的数值用短编码。这三者有本质区别也决定了它们可以叠加使用而不是互相替代。论文把三者有效结合这才达到35到49倍的压缩率。2. 第一板斧剪枝——把不重要的连接删掉2.1 剪枝的原理小于阈值的权重直接归零神经网络里面有大量参数其实贡献很小。论文里提到一个现象训练完的网络里很多权重绝对值非常接近零它们的存在更多是微调阶段的噪声而不是真正学到了有效特征。具体操作上剪枝分三步走正常训练一个网络直到收敛。设定一个阈值把绝对值低于这个阈值的权重全部置零。就是一个简单的比较操作。对剩下的非零权重做微调fine-tune让网络适应这种变化。这里有个细节很关键置零之后不能直接就算完必须要重新训练。权重一旦被砍掉整个网络的输出分布已经变了不做微调恢复不了精度。2.2 迭代剪枝一次剪太猛会掉点严重我在实际复现中发现如果一次性把90%的权重全剪掉网络精度会断崖式下降再怎么微调也很难完全恢复。论文里的做法是分多次迭代每次剪掉一小部分然后微调让网络逐渐适应稀疏结构比一步到位要稳得多。迭代式剪枝的操作大概是初始训练一个模型记录基线精度。设定每轮剪枝比例比如一次剪掉10%到20%。剪完做短训练恢复精度。重复剪枝和训练直到整体达到目标稀疏度。这里的阈值选择还有一个直观方法画一个权重的绝对值分布直方图会发现大多数权重集中在0附近。这个分布的中心高度和展宽决定了阈值该定在哪。数据分布偏向0比较厉害的时候剪枝空间就很大。2.3 细节微调的学习率要怎么调剪枝后的微调和正常训练不太一样。正常训练的学习率可能从0.01开始慢慢衰减但剪枝微调阶段学习率要小得多。我试过用0.001到0.0001的量级优化器一般还是用SGD加动量。原因不复杂剪枝后的网络已经接近一个局部最优直接作用在这个最优附近的微小扰动上适合慢慢修整。如果学习率太大网络很容易跳出这个最优区域精度不但没恢复反而更差。论文里对此着墨不多但实际操作中这个点非常关键。2.4 我的思考非结构化剪枝的“坑”论文采用的剪枝是非结构化的也就是说它在所有权重里挑出绝对值大的保留具体位置完全由数值决定。这带来一个很现实的问题稀疏矩阵里的非零元素分布是不规则的。这种不规则稀疏给CPU和GPU上的计算优化带来很大麻烦。常规的密集矩阵乘法优化得淋漓尽致但排除了中间元素后数据读取变成跳着读利用不了缓存连续性。论文里专门提到稀疏矩阵的存储需要用CSR或CSC格式来编码非零元素的位置和值不然只省了存储计算效率反而上不去。现在很多结构化剪枝方法比如按通道剪枝就是为了解决这个问题而出现的。它们砍掉整个滤波器或者整个通道让稀疏保持规则的形状方便硬件加速。Deep Compression文章主要讨论存储压缩如果目标是不牺牲精度把模型压到最小非结构化剪枝依然是最优选择。3. 第二板斧权值量化——让多个权重共享一个数字3.1 量化思想权重聚类用查表代替存储剪枝把网络变成稀疏的但幸存下来的权重依然是32位浮点数。Deep Compression的第二步就是对幸存权重做量化。量化的核心思想是权重共享weight sharing。也就是说一个网络层里随着训练收敛很多权重的数值其实很接近。我们把这些相近的权重归为同一类用同一个代表值表示存储时只需要存这个代表值即码本而不是每个权重独立存一个32位浮点数。我举个具体的例子来帮助理解。假设一层有100万个权重经过聚类后聚成32类。那么每个权重只需要一个5位的索引来指明它属于哪一类。码本里存32个实际数值。这样存储量就从100万乘32位变成100万乘5位加32乘32位压缩比很可观。3.2 量化的参数选择为什么通常选择2的幂次个聚类数论文里具体的聚类数量不是拍脑袋决定的而是考虑到硬件的位宽限制。常用的选择是聚类到256个代表值8位或者32个代表值5位为了后续存储和计算方便一般用2的幂次。聚类数的选择本质上是压缩率和精度之间的权衡。聚类数越少压缩率越高但每个权重的精度损失越大网络的表达能力下降。论文里实验显示5位量化在多个网络上几乎不损失精度低于5位就开始有明显掉点。这个经验值在后来的很多量化方法里也频繁出现。3.3 k-means聚类的初始化方式对结果影响很大论文里对k-means聚类的初始化做了专门研究比较了三种方式随机初始化Forgy随机选k个样本作为初始质心。简单但迭代次数多容易陷入局部最优。基于密度初始化Density-based顺着权重的概率密度分布选初始质心让质心尽量落在密度高的区域。基于线性初始化Linear-based在权重值范围[min, max]上均匀线性取k个点作为初始质心。实验结果表明线性初始化在多数情况下精度恢复最好。原因在于权重的分布通常在零附近聚集而线性初始化让质心覆盖了整个数值范围尤其是数值大的权重区域。大权重对网络输出的影响更大把它们保留得更精细自然精度损失小。这个结论后来被很多量化论文借鉴包括一些我实际用过的量化工具核心思想都来源于此。3.4 量化后的微调梯度分桶更新和三元组更新量化完成后依然要微调但这里有一个非常巧妙的细节。聚类之后属于同一类的多个权重共享一个码本值。反向传播时每个权重都会产生自己的梯度但被共享的码本值只能更新一次。论文的做法是把同一类内所有权重的梯度累加起来乘以学习率然后更新对应的码本值。这就是梯度分桶gradient assignment更新。我在复现时踩过一个坑如果直接对每个权重单独做梯度更新而不是按类累加码本里同一个值会被多个方向相反的梯度拉扯训练过程很容易震荡。论文的累加方式是把类内所有梯度的合力作用到代表值上本质上是在做一个降维优化稳定得多。还有一个细节剪枝过程中被置零的权重在微调阶段一直保持为零。也就是说反向传播时的梯度也只更新被保留下来的权重对应的聚类代表值零权重不再参与更新。这就能保证剪枝的结构在整个训练过程中不被破坏。3.5 我的思考量化到底损失了什么读这篇论文时我一直在想一个问题量化之后网络到底损失了什么能力我的理解是权重聚类相当于给网络的参数空间做了一个离散化约束。原本每个权重可以独立微调现在它们被绑定了共享值的更新带着所有同类权重的平均意图。这相当于给网络增加了一种正则化让模型更倾向于用少数几种数值组合来表达特征。如果聚类数足够大这种约束造成的表达能力损失很小甚至有时候因为正则化的存在量化网络的泛化能力反而比原网络好。当然这是有限度的。聚类数太小时两个作用差异很大的权重被硬凑成同一个值它们各自负责的特征模式就会互相干扰精度就会明显下降。这是量化方法本质上的限制不可能完全消除。4. 第三板斧霍夫曼编码——对稀疏权重再做熵编码4.1 为什么还需要霍夫曼编码经过剪枝和量化模型已经变成一堆整数索引加小数码本。但这些数依然是按固定位宽存储的。仔细观察分布会发现索引值的出现频率差别很大有的值频繁出现有的值很少出现。固定位宽意味着不论出现频率每个索引都占一样的存储空间。这明显有信息冗余。霍夫曼编码的原理就是让出现频率高的数值用短编码表示频率低的用长编码。整体期望编码长度会小于固定位宽。第三步做的事情就是统计所有数值出现的频率构建霍夫曼树然后生成可变长编码表把索引和码本值都编码成二进制流。4.2 权重分布与编码收益论文里展示过一个有权重值分布的图其中剪枝后的权重索引分布高度不均匀少数几个值占据了绝大多数这让霍夫曼编码的压缩效率很高。量化后的权重索引本质上是一个偏态分布偏态越严重熵编码的收益越明显。这个偏态分布其实反映了剪枝的另一个好处剪枝之后大部分索引指向的都是聚类里数值接近零的那些类因此这些类的出现频率非常高。剪枝这一步实际上把权重分布变得更“极端”为后续霍夫曼编码创造了更大的压缩空间。这个连锁效应非常有意思。4.3 霍夫曼编码的工程实现要点工程实现霍夫曼编码有几个注意点需要统计所有非零索引的频次构建编码表。编码和解码需要同一张表因此编码表本身也要存下来部署时加载权重文件时需要同时加载码表。对于分布极不均匀的数据霍夫曼编码压缩率很高但极端情况下可能退化为接近定长编码收益有限。论文里提到的稀疏矩阵存储格式CSR/CSC和霍夫曼编码表是可以结合在一起的。具体设计因框架而异核心问题是编码和解码的速度开销。理想情况下解码速度足够快推理时几乎感知不到额外开销。5. 实验效果与个人复盘5.1 经典模型上的压缩效果论文的实验数据每次看到都觉得震撼。以AlexNet为例原来240MB的模型被压缩到6.9MB压缩率约35倍。VGG-16从552MB压到11.3MB约49倍。而且在这些压缩下模型的top-5精度完全没掉甚至还有微小的提升。之前我总认为这么大的压缩一定会牺牲精度但论文展示了另一种可能性在大模型里存在大量冗余剪掉它们不仅不影响性能有时还能提升泛化能力。这对很多做部署的人来说是观念上的颠覆。5.2 压缩后为什么精度不降反升论文里给出了一些解释。剪枝类似一种结构正则化砍掉的是过拟合带来的噪声连接权重聚类类似一种浮点参数的离散化正则化让网络在更少的参数自由度下工作。二者叠加起来相当于给原始模型加了一个强力正则项在训练数据有限的情况下泛化能力提升反而盖过了参数量减少带来的能力下降。这个洞察也解释了为什么这个方法在当时的各种主流网络上都能稳定生效。大规模网络本身就存在大量冗余深度学习模型的冗余程度远超直觉。5.3 复现与实践中遇到的典型问题列表我在实际复现和产品落地过程中整理了一些高频问题和解法写出来供参考问题现象排查方法解决方案一次性剪枝比例过大微调很久精度无法恢复检查剪枝后非零比例是否过低改为迭代剪枝每轮剪10%到20%k-means聚类质心初始化不当量化后精度下降明显对比不同初始化方式的指标曲线采用线性初始化覆盖全数值范围微调学习率过大精度震荡甚至发散观察loss曲线波动幅度初始学习率降到正常训练的1/10以下忽略梯度分桶更新码本被梯度拉扯对比更新前后码本值变化按聚类索引累加梯度后再更新霍夫曼编码表未打包部署时解码失败检查权重文件完整性将编码表与权重数据一起导出另外一个经验上的心得量化后的网络在低精度硬件如FPGA和嵌入式芯片上需要特别注意。虽然压缩后的权重是整型索引但解码后依然是浮点权重推理时的计算精度依然受限于硬件的浮点精度。如果目标硬件只支持INT8计算5位索引的压缩权重还需要再走一遍INT8推理适配。论文的压缩方案主要在存储层面计算层面的瓶颈还需要结合具体硬件进一步优化。6. 读这篇论文对我后续工作的启发6.1 从工程角度看方法论的迁移价值Deep Compression给我的最大启发不是某一步的具体技术而是这种系统性的压缩思路先找冗余类型再分别设计对应的去除手段最后统一整合验证。我后来在处理模型部署任务时遇到体积和延迟问题第一反应也是从结构、数值、统计三个维度去拆解问题而不是盲目套用一个量化工具。这种“分而治之、按冗余类型系统性消除”的思路在很多场景下都适用。比如做推荐系统模型上线我会先看参数分布是否过度集中、有没有无效的稀疏特征再针对性地做裁剪和量化。这套思路在落地场景中非常抗用。6.2 从学术角度看后续技术路线顺着Deep Compression这条线往下延伸能看到一条清晰的技术演化路径。剪枝方向演化出学习率重调度、动态剪枝、结构化稀疏量化方向演化出混合精度量化、量化感知训练、低比特量化二值/三值网络。Hardware-aware方向的后续工作也越来越多比如把压缩后的稀疏格式直接映射到专用加速器。我还特别注意到近年大模型和生成模型流行后“推理时压缩”和“预训练后压缩”的思想重新火了起来。很多大模型压缩工作依然能看到Deep Compression的影子。可以说现代模型压缩大部分工作本质上都在不同层级延续着这篇论文“识别冗余、定向消除”的基本方法论。6.3 自己动手做一遍胜过读十遍如果给你一个可操作的实践建议我强烈建议找一个小网络比如在CIFAR-10上训练一个简单的CNN手动实现一遍剪枝、k-means量化、霍夫曼编码一定要自己亲手串起整条流水线。亲手跑一遍比读十遍论文理解都要深。具体操作时建议步骤拆开验证先只做剪枝观察稀疏率和精度的关系曲线再在稀疏网络上叠加量化对比5位和8位的差异最后加入霍夫曼编码测算真实存储体积。每一步的收益都分开记录这样你对每一步的贡献会有最直观的感受。等跑通了这套流程再看任何压缩相关的论文都会发现自己已经有了全链路视角不再只是看一个孤立的算法点。我自己的体会是真正把一个模型压下来深入理解每一步的实现原理、边界条件和它们之间的联动整个过程走下来对深度学习系统的理解会提升一个段位。这也是这篇论文作为经典文献最值得反复阅读的地方它不只是讲了一个好方法更展示了一种思维方式面对资源约束时如何系统性地找到冗余、逐个击破、最终拿出可落地的整体方案。
返回列表