尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

神经网络剪枝与量化:从原理到工程落地的完整指南

神经网络剪枝与量化:从原理到工程落地的完整指南 纯手工码字尽量说人话。这已经是系列的第4篇了前几篇聊完基础概念和简单操作这篇我想换个写法直接从“你准备动手剪一个模型、量化一个模型”时会遇到的真实困惑开始把所有我踩过的坑、绕过的弯、最后觉得“早该这么理解”的东西一次讲透。神经网络剪枝和量化听起来像两个高大上的词汇但实际上就是两件事把一个已经训练好的大网络变得又小又快同时尽量不损失精度。剪枝是“去掉不重要的连接或通道”量化是“用更少的位数存权重和激活值”。原理都不难难的是组合在一起之后各种细节全是坑。这篇文章适合那些已经跑通了一个图像分类模型想把模型搬到手机或边缘设备上却不知道怎么下手的人。1. 刚开始学剪枝量化最容易被绕晕的几件事1.1 网络明明已经能跑了为什么还要“剪”它很多人第一次接触剪枝时都会有一个疑问训练好的模型已经能用了准确率还挺高我为什么要拿它开刀这不是没事找事吗答案是你训练好的模型和你能部署的模型是完全两码事。一个在GPU上训练出来的模型可能参数量有几十MB甚至几百MB推理一次需要几百毫秒内存占用也高。但在手机、嵌入式设备、边缘盒子上存储空间有限内存有限计算能力也有限。你想让模型真正落地就得让它“瘦身”。举个我常举的例子你搬进新家买了一堆家具每个房间都摆得满满当当住着肯定舒服。但如果你要搬家到一个小户型就必须扔掉一些不常用的东西或者把大沙发换成折叠沙发。剪枝和量化就是模型界的“搬家打包术”。剪枝负责扔掉不重要的“家具”量化负责把家具换成更小号的“折叠款”。两者可以单独用也可以叠加用叠加效果往往最好。1.2 剪枝和量化到底是不是一回事这是初学者最容易混淆的地方。剪枝和量化虽然经常一起出现但它们是两个不同维度的优化手段。剪枝动的是“网络结构”是空间维度的事。训练好的模型里很多神经元、通道、甚至整层对最终输出贡献不大把这些删掉网络就变“窄”了计算量自然减小。量化动的是“数据表示”是精度维度的事。原本权重用32位浮点数存储现在改成16位、8位甚至更低存储和计算速度都会变快。打个比方剪枝是把一本500页的书删掉一些废话章节变成300页量化则是把书里每个字从繁体改成简体或者从大号字变成小号字页数变少但内容没变。前者改变结构后者改变表示方式。理解了这一点后面的很多操作就不会搞混。剪枝完了之后模型结构会变量化的模型结构不变变的只是权重数据的载体格式。1.3 谁适合接着往下看这篇文章面向的是这样的读者动手跑过图片分类或目标检测的训练流程见过loss下降、准确率上升的过程想在移动端或嵌入式环境部署模型但不知道怎么压缩模型听说过TensorRT、CoreML这一类的推理加速工具但不知道它们和剪枝量化的关系。如果你还没训练过任何一个深度学习模型建议先拿一个经典的分类网络跑通一遍训练流程再来看这篇。因为剪枝量化都是在“已经训练好的模型”基础上做的你至少得先拥有一个模型才有后面的事情。2. 动手前先想清楚模型、框架和剪枝策略怎么选2.1 选模型别贪大也别图太老我见过不少初学者一上来就拿一个特别大的模型做剪枝实验比如几个百层的大网络。剪完确实瘦了很多但训练和调试的成本极高一次剪枝加重训练的周期可能得好几天。等你发现自己某个参数配错了损失的是一个星期的算力。我的建议是入门阶段选一个和你的实际任务规模匹配的中等模型。目标检测可以选单阶段检测器图像分类可以选轻量化的骨干网络。这类模型参数少、结构规整、剪枝实验跑得快出结果速度也快。等你在小模型上把剪枝量化的流程跑通了再迁移到大模型就是顺理成章的事。选太老的网络也不行。有些早期经典的卷积网络结构里大量使用密集连接和超大卷积核剪枝的逻辑和现代网络完全不一样参考代码也少。遇到问题在社区里都搜不到几条相关讨论全靠自己硬调不是一条好走的路。2.2 剪枝框架别盲目追新选生态成熟的现在讨论剪枝量化框架总是绕不开那几个主流的深度学习框架。我的个人经验是入门期不要追最前沿的框架要选资料最多、你本来就用过的框架。你对框架越熟排错越容易。换个新框架等于同时学剪枝和框架两件事难度直接翻倍。有一个很朴素的判断标准看你追踪的剪枝算法论文官方代码是用什么框架写的就用那个框架。因为剪枝是个implementation细节极多的方向论文里一句话带过的实现细节可能就决定了最后的精度。用论文官方代码至少能保证你读到的解释和代码是一致的。工具链方面我建议搞清楚推理引擎到底支持什么格式的模型。很多剪枝框架导出的是自有格式的模型但部署时别人只认ONNX、TensorRT这类标准格式。这就涉及到格式转换转换过程很容易出问题后面我会专门讲。2.3 全局剪枝还是分层剪枝这个选择很关键剪枝策略粗略分两种一种是对每个层按相同的比例剪叫分层剪枝另一种是对整个网络统一考虑把不重要的参数删掉叫全局剪枝。我刚学的时候想当然地以为全局剪枝肯定更好毕竟它“有大局观”。实际上全局剪枝对调参能力要求很高。因为网络里不同层的重要性差别很大有些层剪掉50%都没事有些层剪掉10%就崩了。全局剪枝如果剪错了层后面重训练怎么拉都拉不回来。分层剪枝虽然看起来笨但每个层的剪枝比例你可以单独控制先跑一个低比例的实验看看哪些层比较敏感再针对性调整。入门阶段我强烈建议先做分层剪枝把每个层的敏感度摸清楚再尝试全局剪枝。这个“先局部、后全局”的思路能让你少走很多弯路。3. 剪枝实操从权重剪到通道剪边界在哪里3.1 剪枝的三步走训练、剪、微调不管用什么剪枝算法完整流程都逃不开三个步骤预训练一个模型、按某个标准裁掉模型的一部分、对裁完的模型重新微调训练。听起来简单实际每一步都有无数细节。预训练模型的质量直接决定剪枝的上限。一个本来就没训好的模型剪完更容易崩。我见过有人拿一个验证准确率只有70%的模型去做剪枝还指望剪完能保住60%最后整个实验都是无效的因为基线模型本身就代表不了“正常性能”这个词。剪枝这一步的核心是确定“什么算不重要”。按权重绝对值大小来剪权重小的就删掉这是最朴素的方法。但要注意权重绝对值小不代表它对最终输出没影响。所以现在更常用的做法是看剪掉某个结构后对loss的影响影响小的就是可以剪的候选对象。入门阶段可以先从结构化的通道剪枝做起它删除的是整个卷积核对应的输出通道结构规整对硬件友好部署后实实在在减少计算量。最后是微调。剪完枝的模型不可能直接有好精度必须重新训练一段时间。这个微调的学习率要设得很低一般取原模型最后训练阶段学习率的十分之一甚至更低。我见过有人用初始大学习率微调剪完的模型结果模型直接震荡损失下不去。3.2 细粒度剪枝的坑存储小了推理反而慢细粒度剪枝是指对单个权重做裁剪网络结构本身没变权重矩阵里出现了很多“空洞”值为0。这样做的优点是灵活可以按全局重要性剪掉任意位置的权重精度损失往往更小。缺点也非常明显剪完的模型结构不规整非常依赖硬件和推理库对稀疏矩阵的加速支持。如果推理引擎不支持稀疏计算你费半天劲剪出来的模型存粹是糊弄自己。参数文件确实变小了但推理的时候那堆0还是照样参与矩阵乘法速度一点不变甚至因为元素分布不规则反而更慢。这就是为什么很多工业落地项目更青睐结构化剪枝尤其是通道剪枝。一个卷积核的输出通道被整组删掉后下一个卷积层的输入通道数也相应减少计算矩阵的尺寸直接变小任何推理框架都能受益不需要额外支持。3.3 结构化剪枝的坑通道删了对不上号通道剪枝的实现过程比概念复杂得多。剪完一个卷积层的输出通道意味着下一层卷积的输入通道也要跟着改否则张量维度就对不上了。如果网络还有跳连结构那个被剪的层会影响后续多个分支复杂度指数级上升。我在自己踩坑的时候总结出一个笨办法先选一条主线走向比如从网络的第一层沿着主干往下走一路剪到最后一层确保每一层的输入输出维度都能对上号再加分支。你剪手动改模型的时候心里要时刻一张维度表每剪一个层就更新一次前后层的输入输出维度对不上就立刻暴露出来。现在很多剪枝工具已经能自动处理带分支的网络结构但不要高兴太早。自动处理不等于一定正确剪出来的模型还是要逐层打印shape检查。尤其是含多尺度特征融合的网络通道不匹配的现象非常隐蔽可能要到真正推理时才爆出来一个维度报错。3.4 BN层和剪枝的关系很多人一直没搞懂不少人问剪枝的时候要不要把BN层一起剪掉答案是要但不能瞎剪。大部分卷积网络里每一层卷积后面都跟着BN层和激活函数BN层的缩放因子其实可以当作通道重要性的一个判断依据。有一种经典的剪枝方法就是基于BN层的γ系数γ接近0的通道说明这一通道的输出被压制得很厉害对后续影响不大可以剪掉。这种方法的优点是不需要额外计算什么重要性指标训练完了直接看γ分布就能定剪枝比例。实际操作时要注意不是所有γ接近0的通道都能剪。剪完要重新统计一下BN层的均值和方差因为剩下的通道在分布上已经和原来不一样了。如果不重新统计直接拿原BN参数推理精度会明显掉。很多教程没提这一步导致不少人剪完模型后精度掉一截以为方法不行其实是BN没处理。4. 量化没那么玄PTQ和QAT的实用视角4.1 什么是量化大白话再讲一遍量化说白了就是把权重和激活值从高精度浮点数存成低精度整数。举个例子原来一个权重是0.123456789现在规定它只能取0到255之间的整数于是映射到31。这个过程会丢失一些精度但换来的是模型更小、推理更快尤其在只支持整数运算的硬件上速度提升非常明显。新手最容易误解的是以为量化是训练后才做的事情把精度损失全怪在“量化”头上。实际上量化可以发生在训练之后后训练量化PTQ也可以在训练过程中加入模拟量化的噪声让模型主动适应低精度表示量化感知训练QAT。两者在精度上的差别彻底打破了我“量化就是输一下命令的事”的幻想。4.2 PTQ最快的路径但不是万能的PTQ的思路很直接训练好的模型你拿一些校准数据统计一下权重和激活值的范围然后把32位浮点模型直接变成8位整数模型。好处是快一条命令的事不需要改训练代码。坏处是如果模型对量化敏感精度可能会掉得比较多。灵敏度的核心取决于激活值分布。有些层的激活值分布很整齐集中在0附近量化误差小有些层的激活值分布不均匀有大量离群点量化时为了迁就离群点中间区域的分辨率会被严重压缩精度就崩了。所以PTQ的关键在于选好校准数据和校准方法不要用一个两个样本随便校也不能用训练集整天量校。如果PTQ掉的精度超过你能接受的范围别硬扛该上QAT就上QAT。4.3 QAT把量化误差当成训练的一部分QAT的原理是在训练过程中模拟量化的过程。前向传播时把权重和激活值先量化再反量化回来让模型在训练的时候就见过这种“噪声”于是参数会朝对量化更鲁棒的方向收敛。训练完的模型再转成真正低精度推理时误差就小很多。听起来很美好代价是训练时间变长、代码复杂度变高。而且要特别注意QAT用的训练超参和普通训练是不一样的尤其是学习率通常要更小训练周期也更长否则模型会在量化噪声下震荡。很多框架里已经内置了QAT的支持函数你可以在冻结BN层后加载预训练权重再跑一段QAT微调。这里的“冻结BN”是个关键细节QAT阶段如果BN还在更新推理时使用的统计量是滑动平均值跟训练时实际走的批量统计值会不一致对量化精度影响很大。不少人在这一步翻车精调半天不如别人冻结BN效果好。4.4 哪些层不要量化哪些层一定要量化不是所有层都适合量化。比如网络的输入层直接连着原始图像数据量化误差会通过后续层放大容易掉点一些极其敏感的层比如检测头里输出边框坐标的层有时候得保持浮点。反过来卷积层和全连接层是最值得量化的因为它们的计算量最大。像BN层在推理时实际可以折叠进前面的卷积层量化不需要额外操心。但如果你用的是残差连接加法操作的融合细节在不同推理引擎里表现不一样建议实测别只看理论。入门阶段我可以给一个很土但有效的建议把整个模型量化完先跑一遍验证集记录每层量化前后的输出差异。差异最大的那几层试着保持浮点不量化看看整体精度是否明显提升。用这种方法找出“敏感层”比盲目堆QAT更高效。5. 一套可以照着做的剪枝量化组合流程5.1 完整流程清单照着顺序来我这里给出一套我实际用过的组合流程实验模型是一般的图像分类网络数据集也只是普通的公开分类数据集。整个流程核心是“剪枝在前量化在后”因为量化会放大剪枝带来的结构变化如果顺序反了误差叠加之后很难救。整个过程按顺序应该是选一个预训练模型明确基线指标比如Top-1准确率、模型大小、推理延迟。按某个比例做通道剪枝先按分层策略从低比例开始观察每个层剪枝后的精度变化。对剪完的模型做重训练微调恢复精度记录剪枝后的模型和精度。把剪枝后的模型导出为通用格式比如ONNX。在通用模型基础上做PTQ用校准集确定量化范围先看效果。如果PTQ精度不达标再上QAT微调然后重新量化导出。用最终量化模型在不同设备或推理引擎上做测试验证精度和加速比。这套流程里前两步我建议至少跑两轮。第一轮用一个比较保守的剪枝比例比如剪掉20%摸一下水位第二轮再逐渐加大比例比如40%、50%看精度变化趋势。切忌一上来就剪70%然后发现模型崩得没法看。5.2 微调重训练的参考参数我直接给一组参考值方向比绝对数值重要学习率取原模型训练末期的十分之一大概在1e-4到1e-5之间。训练轮数不需要太多很多情况下5到20个epoch就够了。你可以每轮结束都看一下验证集精度一旦连续几个epoch没提升就可以及时收手。优化器SGD加Momentum往往比Adam更稳剪枝重训练这一阶段Adam有时候还是会偏激进。批量大小跟上一次训练尽量保持一致BN统计量变化太大也会影响恢复速度。有一件事必须提醒重训练的时候要固定住随机种子否则同样的配置你跑两次精度可能差一个点以上长尾实验结果根本没法复现。这个细节在论文里通常不会写但在实际工程里特别重要。5.3 量化后的模型怎么验证量化模型的验证不能只在训练框架里面跑一个数值精度了事。我见过有人在PyTorch里测量化模型有98%的准确率结果部署到端侧了一下直接掉到93%吓得以为量化算法有问题。其实这是因为训练框架里的量化模拟和真实推理引擎的底层实现有微小差异。正确的做法是把量化完的模型导出到目标推理框架在真实设备或模拟环境中重新跑一遍验证集得到的精度才是真正能写进项目报告的数值。这一步千万不要偷懒。另外验证指标除了准确率还要关注延迟和峰值内存。剪枝量化的最终目的是部署不许说准了但跑不动。在真实设备上多测几种输入尺寸记录下不同batch size下的推理时间你才能真正判断题模型是被“剪”好了还是只是被“压”了一堆数字。6. 我在实操中踩过坑总结几条经验6.1 剪枝比例越高越好别被“论文指标”骗了论文里经常展示剪掉80%、90%参数量之后精度还能保持看得人心里直痒痒。实际做项目的时候我非常不建议追求极致剪枝比例。理由很简单论文用的数据集相对干净任务相对纯粹你手里的实际问题数据噪声大、类别不平衡、输入五花八门模型本来就吃紧你剪得越狠容错空间越小一点点分布偏移都可能让线上效果崩掉。我现在的习惯是剪枝以“达到部署要求”为上限目标是把模型塞进目标设备并满足延迟要求而不是“剪到不能再剪”。比如你的硬性要求是模型不超过10MB推理时间不超过30ms那就剪到刚好满足这两条再留一点余量。超出目标多剪的部分带来的精度风险完全没必要。6.2 部署格式转换是最容易白日做梦的一环很多教程直接省略格式转换的细节仿佛写个reshape就能从Pytorch模型导出到ONNX再转到其他推理引擎。但实际踩一下就知道每一步都可能报错。特别是剪枝后的模型如果带有各种自定义层或动态分支标准的转换工具根本认不出来。我的建议是尽早把“模型导出”纳入实验流程而不是最后才做。每一次剪枝实验后都尝试导一次模型并跑一个测试推理一旦发现某个操作不被目标引擎支持立刻决定是改模型还是改实现。越早发现代价越小。此外导出的模型和原模型在数值上可能有细小差异这是正常的。遇到这种情况不用急着怀疑转换出错先跑一个简单的测试样本对比输出差多少。通常小误差在可接受范围以内但如果某个输出的偏差特别大就得回头检查是不是某层没转换对。6.3 精度掉了别急着调算法先查最简单的环节不管剪枝还是量化精度掉了第一反应不要是调算法。我见过太多人花一个星期调剪枝策略最后发现是微调的学习率设太高模型直接跑飞了。还有一次是量化的校准数据采样不均匀导致某层范围统计偏差换一批校准数据就好了。我给自己的排查顺序是先确认基线可复现再确认剪枝/量化操作本身是否正确再确认重训练参数是否合理最后才怀疑算法选择问题。这四个环节里面前面三个错的可能性远远大于最后一个。多数情况下你只是某个超参数设错了或者某个导出选项勾错了而不是你的剪枝方法不先进。6.4 工具链固化之后别忘了记录配置最后这点比较务实一旦你实验出一套效果还不错的剪枝量化配置强烈建议把它记下来包括预训练模型、剪枝比例、重训练学习率、epoch数、校准集大小、量化参数设置全部记录到一个配置文档里。别看现在觉得都记得下个月再让你调一个类似的任务保证忘掉一大半。我现在所有项目都会建一个“实验记录表”每跑一次实验就填一行配置和结果。这个习惯帮我节省了大量重复试错的时间也让别人接手项目的时候能快速进入状态。笔记本上记的不只是参数更是一整套踩坑路径和避坑地图。最后再分享一个感受剪枝和量化入门最难的其实不是原理而是“组合使用”时的工程排错。你要是能在一个小模型上独立走通剪枝到量化的全流程并且定位过两三个报错那之后再接新的部署任务心里基本就有底了。希望这篇大白话能帮你省下一些我当年绕远路的时间。
返回列表