我们知道,一定程度上,网络越深,参数越多,模型也会越复杂,但其最终效果也越好,而模型压缩算法是旨在将一个庞大而复杂的大模型转化为一个精简的小模型。
之所以必须做模型压缩,是因为嵌入式设备的算力和内存有限,经过压缩后的模型方才能部署到嵌入式设备上。
模型压缩问题的定义可以从 3 角度出发:
模型压缩的收益:
FLOPs),降低延迟(Latency)GPU/NPU 计算利用率公式定义模型压缩问题: \(\underset{Policy_i}{min} {Model\_Size(Policy_i)}\)
模型压缩问题的约束: \(accuracy(Policy_i) >= accuracy\_sla\)
按照压缩过程对网络结构的破坏程度,《解析卷积神经网络》一书中将模型压缩技术分为“前端压缩”和“后端压缩”两部分:
知识蒸馏、轻量级网络(紧凑的模型结构设计)以及滤波器(filter)层面的剪枝(结构化剪枝)等;低秩近似、未加限制的剪枝(非结构化剪枝/稀疏)、参数量化以及二值网络等,目标在于尽可能减少模型大小,会对原始网络结构造成极大程度的改造。总结:前端压缩几乎不改变原有网络结构(仅仅只是在原模型基础上减少了网络的层数或者滤波器个数),后端压缩对网络结构有不可逆的大幅度改变,造成原有深度学习库、甚至硬件设备不兼容改变之后的网络。其维护成本很高。
工业界主流的模型压缩方法有:知识蒸馏(Knowledge Distillation,KD)轻量化模型架构(也叫紧凑的模型设计)、剪枝(Pruning)、量化(Quantization)。各个模型压缩方法总结如下:
| 模型压缩方法 | 描述 | 涉及的网络层 | 示例 |
|---|---|---|---|
| 知识蒸馏 | 属于迁移学习的一种,主要思想是将学习能力强的复杂教师模型中的“知识”迁移到简单的学生模型中。 | 卷积和全连接层 | 经典KD论文,属于蒸 "logits"方法,将Teacher Network输出的soft label作为标签来训练Student Network。必须重新训练模型。 |
| 轻量化模型架构 | 轻量级网络的核心是在尽量保持精度的前提下,从体积和速度两方面对网络进行轻量化改造。 | 卷积层/卷积模块 | Mobilenet 提出深度可分离卷积; shufflenetv2 论文 提出的四个高效网络设计的实用指导思想; RepVGG 提出重参数化思想。 都需要重新设计 backbone 和和重新训练模型。 |
| 剪枝 | 将权重低于阈值的连接都从网络中删除。 | 卷积层和全连接层 | 韩松2016年Deep Compression属于开山之作,剪枝步骤:正常训练,删除网络中权重低于阈值的连接层,重新训练。需要重新训练模型。 |
| 量化 | 指将神经网络的浮点算法转换为定点算法 | 卷积、全连接、激活、BN层等 | TensoRT框架中的基于 KL 散度方法的INT8量化策略是主流技术。PTQ 训练后量化方法不需要重新训练模型。 |
知识蒸馏(knowledge distillation),其实也属于迁移学习(transfer learning)的一种,通俗理解就是训练一个大模型(teacher 模型)和一个小模型(student 模型),将庞大而复杂的大模型学习到的知识,通过一定技术手段迁移到精简的小模型上,从而使小模型能够获得与大模型相近的性能。也可说让小模型去拟合大模型,从而让小模型学到与大模型相似的函数映射。使其保持其快速的计算速度前提下,同时拥有复杂模型的性能,达到模型压缩的目的。
知识蒸馏的关键在于监督特征的设计,这个领域的开篇之作-Distilling the Knowledge in a Neural Network 使用 Soft Target 所提供的类间相似性作为依据去指导小模型训练(软标签蒸馏 KD)。后续工作也有使用大模型的中间层特征图或 attention map(features KD 方法)作为监督特征,对小模型进行指导训练。这个领域的开篇之作-Distilling the Knowledge in a Neural Network,是属于软标签 KD 方法,后面还出现了 features KD 的论文。
以经典的知识蒸馏实验为例,我们先训练好一个 teacher 网络,然后将 teacher 的网络的输出结果 \(q\) 作为 student 网络的目标,训练 student 网络,使得 student 网络的结果 \(p\) 接近 \(q\) ,因此,student 网络的损失函数为 \(L = CE(y,p)+\alpha CE(q,p)\)。这里 CE 是交叉熵(Cross Entropy),\(y\) 是真实标签的 onehot 编码,\(q\) 是 teacher 网络的输出结果,\(p\) 是 student 网络的输出结果。
但是,直接使用 teacher 网络的 softmax 的输出结果 \(q\),可能不大合适。因为,一个网络训练好之后,对于正确的答案会有一个很高的置信度而错误答案的置信度会很小。例如,在 MNIST 数据中,对于某个 2 的输入,对于 2 的预测概率会很高,而对于 2 类似的数字,例如 3 和 7 的预测概率为 \(10^-6\) 和 \(10^-9\)。这样的话,teacher 网络学到数据的相似信息(例如数字 2 和 3,7 很类似)很难传达给 student 网络,因为它们的概率值接近0。因此,论文提出了 softmax-T(软标签计算公式),公式如下所示:
这里 \(q_i\) 是 \(student\) 网络学习的对象(soft targets),\(z_i\) 是 teacher 模型 softmax 前一层的输出 logit。如果将 \(T\) 取 1,上述公式等同于 softmax,根据 logit 输出各个类别的概率。如果 \(T\) 接近于 0,则最大的值会越近 1,其它值会接近 0,近似于 onehot 编码。
所以,可以知道 student 模型最终的损失函数由两部分组成:
这两个损失函数的重要程度可通过一定的权重进行调节,在实际应用中,T 的取值会影响最终的结果,一般而言,较大的 T 能够获得较高的准确度,T(蒸馏温度参数) 属于知识蒸馏模型训练超参数的一种。T 是一个可调节的超参数、T 值越大、概率分布越软(论文中的描述),曲线便越平滑,相当于在迁移学习的过程中添加了扰动,从而使得学生网络在借鉴学习的时候更有效、泛化能力更强,这其实就是一种抑制过拟合的策略。
知识蒸馏算法整体的框架图如图下所示。

图片来源 https://intellabs.github.io/distiller/knowledge_distillation.html。
轻量级网络的核心是在尽量保持精度的前提下,从体积和速度两方面对网络进行轻量化改造。关于如何手动设计轻量级网络的研究,目前还没有广泛通用的准则,只有一些指导思想,和针对不同芯片平台(不同芯片架构)的一些设计总结,建议大家从经典论文中吸取指导思想和建议,然后自己实际做各个硬件平台的部署和模型性能测试。
对于卷积神经网络,典型的工作有 Mobilenet 系列网络、ShuffleNet 系列网络、RepVGG、CSPNet、VoVNet 等论文。轻量级网络论文解析可阅读 github 专栏-轻量级网络。
GPU、移动端 ARM CPU、端侧 NPU 芯片等);目前已知影响 CNN 模型推理性能的因素包括: 算子计算量 FLOPs(参数量 Params)、卷积 block 的内存访问代价(访存带宽)、网络并行度等。但相同硬件平台、相同网络架构条件下, FLOPs 加速比与推理时间加速比成正比。metric(例如速度 speed),而不是间接 metric(例如 FLOPs)。FLOPs 低不等于 latency 低,尤其是在有加速功能的硬体 (GPU、DSP 与 TPU)上不成立,得结合具硬件架构具体分析。CNN 模型,即使是 FLOPs 相同,但其 MAC 也可能差异巨大。Depthwise 卷积操作对于流水线型 CPU、ARM 等移动设备更友好,对于并行计算能力强的 GPU 和具有加速功能的硬件(专用硬件设计-NPU 芯片)上比较没有效率。Depthwise 卷积算子实际上是使用了大量的低 FLOPs、高数据读写量的操作。因为这些具有高数据读写量的操作,再加上多数时候 GPU 芯片算力的瓶颈在于访存带宽,使得模型把大量的时间浪费在了从显存中读写数据上,从而导致 GPU 的算力没有得到“充分利用”。结论来源知乎文章-FLOPs与模型推理速度和论文 G-GhostNet。channel 数为 16 的倍数比较有利高效计算。如海思 351x 系列芯片,当输入通道为 4 倍数和输出通道数为 16 倍数时,时间加速比会近似等于 FLOPs 加速比,有利于提供 NNIE 硬件计算利用率。(来源海思 351X 芯片文档和 MobileDets 论文)channel 数的情况下 (如网路的前几层),在有加速功能的硬件使用普通 convolution 通常会比 separable convolution 有效率。(来源 MobileDets 论文)MAC、G2-分组数太多的卷积会增加 MAC、G3-网络碎片化会降低并行度、G4-逐元素的操作不可忽视。GPU 芯片上 \(3\times 3\) 卷积非常快,其计算密度(理论运算量除以所用时间)可达 \(1\times 1\) 和 \(5\times 5\) 卷积的四倍。(来源 RepVGG 论文)DenseNet 的密集连接带来的高内存访问成本和能耗问题入手,如 VoVNet 网络,其由 OSA(One-Shot Aggregation,一次聚合)模块组成。在阅读和理解经典的轻量级网络 mobilenet 系列、MobileDets、shufflenet 系列、cspnet、vovnet、repvgg 等论文的基础上,做了以下总结:
cpu 硬件,考虑 mobilenetv1(深度可分离卷机架构-低 FLOPs)、低 FLOPs 和 低MAC的shuffletnetv2(channel_shuffle 算子在推理框架上可能不支持)asic 硬件设备-npu 芯片(地平线 x3/x4 等、海思 3519、安霸cv22 等),分类、目标检测问题考虑 cspnet 网络(减少重复梯度信息)、repvgg2(即 RepOptimizer: vgg 型直连架构、部署简单)gpu 硬件-t4 芯片,考虑 repvgg 网络(类 vgg 卷积架构-高并行度有利于发挥 gpu 算力、单路架构省显存/内存,问题: INT8 PTQ 掉点严重)MobileNet block (深度可分离卷积 block, depthwise separable convolution block)在有加速功能的硬件(专用硬件设计-NPU 芯片)上比较没有效率。
这个结论在 CSPNet 和 MobileDets 论文中都有提到。
除非芯片厂商做了定制优化来提高深度可分离卷积 block 的计算效率,比如地平线机器人 x3 芯片对深度可分离卷积 block 做了定制优化。
下表是 MobileNetv2 和 ResNet50 在一些常见 NPU 芯片平台上做的性能测试结果。

以上,均是看了轻量级网络论文总结出来的一些不同硬件平台部署轻量级模型的经验,实际结果还需要自己手动运行测试。
模型剪枝(model pruning)也叫模型稀疏化(model sparsity)。
深度学习模型中一般存在着大量冗余的参数,将权重矩阵中相对“不重要”的权值剔除(即置为 0),可达到降低计算资源消耗和提高实时性的效果,而对应的技术则被称为模型剪枝。

来源论文 Han et al. Learning both Weights and Connections for Efficient Neural Networks, NIPS 2015。
上图是典型的三段式剪枝算法 pipeline,主要是 3 个步骤:
以上三个步骤反复迭代进行,直到模型精度达到目标,则停止训练。
模型剪枝算法根据粒度的不同,可以粗分为细粒度剪枝和粗粒度剪枝,如下所示:
Channel Pruning),对整个卷积核组进行剪枝,会造成推理过程中输出特征通道数的改变,滤波器剪枝的工作是目前研究最多的。按照剪枝是否规则,剪枝算法也可分为:
2016 年的论文。与非结构化剪枝相比,结构化剪枝通常通常会牺牲模型的准确率和压缩比。结构化稀疏对非零权值的位置进行了限制,在剪枝过程中会将一些数值较大的权值剪枝,从而影响模型准确率。 “非规则”的剪枝则契合了神经网络模型中不同大小权值的随机分布,这对深度学习模型的准确度至关重要。展开来讲就是:

综上所述,深度神经网络的权值稀疏应该在模型有效性和计算高效性之间做权衡。
目前,有一种趋势是在软硬件上都支持稀疏张量,因此未来非结构化剪枝可能会变得更流行。
相比于剪枝操作,参数量化则是一种常用的后端压缩技术。所谓量化,其实可以等同于低精度(Low precision)运算概念,常规模型精度一般使用 FP32(32 位浮点数,单精度)存储模型权重参数,低精度则表示使用 INT8、FP16 等权重数值格式。
模型量化(Model Quantization,也叫网络量化)过程分为两部分:将模型的单精度参数(一般 FP32-32位浮点参数)转化为低精度参数(一般 INT8-8 位定点参数),以及模型推理过程中的浮点运算转化为定点运算,这个需要推理框架支持。
模型量化技术可以降低模型的存储空间、内存占用和计算资源需求,从而提高模型的推理速度,也是为了更好的适配移动端/端侧 NPU 加速器。简单总结就是,模型变小了,速度变快了,支持的场景更多了。
最后,现在工业界主流的思路就是模型训练使用高精度-FP32 参数模型,模型推理使用低精度-INT8 参数模型: 将模型从 FP32 转换为 INT8(即量化算术过程),以及使用 INT8 进行推理。
在实践中将浮点模型转为量化模型的方法有以下三种方法:
data free:不使用校准集,传统的方法直接将浮点参数转化成量化数,使用上非常简单,但是一般会带来很大的精度损失,但是高通最新的论文 DFQ 不使用校准集也得到了很高的精度。calibration:基于校准集方案,通过输入少量真实数据进行统计分析。很多芯片厂商都提供这样的功能,如 tensorRT、高通、海思、地平线、寒武纪finetune:基于训练 finetune 的方案,将量化误差在训练时仿真建模,调整权重使其更适合量化。好处是能带来更大的精度提升,缺点是要修改模型训练代码,开发周期较长。按照量化阶段的不同,量化方法分为以下两种:
PTQ(训练后量化、离线量化);QAT(训练时量化,伪量化,在线量化)。目前已知的加快推理速度概率较大的量化方法主要有:
ristretto 几种。在 nvdia gpu,x86、arm 和 部分 AI 芯片平台上,均支持 8bit 的计算,效率提升从 1 倍到 16 倍不等,其中 tensor core 甚至支持 4bit计算,这也是非常有潜力的方向。线性量化引入的额外量化/反量化计算都是标准的向量操作,因此也可以使用 SIMD 进行加速,带来的额外计算耗时不大。nvdia gpu,x86、arm 三大平台上没有实现对数量化的加速库,但是目前已知海思 351X 系列芯片上使用了对数量化。4 个因素:计算量 FLOPs、内存访问代价 MAC、计算并行度、硬件平台架构与特性(算力、GPU 内存带宽)。我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah