深度学习近些年取得了长足的发展,在很多领域都展现出了非常强大的性能。不可避免的,模型网络参数量正变得越来越大、计算复杂度越来越高,同时对硬件资源的消耗也更高。导致算法模型很难部署在计算资源有限的硬件平台上。工程师们只能望模型兴叹。
如何能在保持原始模型性能的同时,减少网络参数规模并降低计算复杂度呢?模型的压缩和加速就是围绕这两个方向展开研究的。模型压缩目的是减少网络参数量;模型加速的目标则是降低计算复杂度。

模型压缩和加速可以通过多种方式来实现。
首先是针对网络结构本身进行优化改进,常见的如使用3x3的小卷积核取代大卷积核;average-pooling取代了full-connection layersmobilenets中使用depth-wise convolution替代传统的卷积方式等。
除了根据人工经验和数学模型设计的特有算子结构外,还包括模型剪枝、量化、蒸馏方法。剪枝方法去除神经网络结构中不重要的部分;量化方法则是调整网络结构中的权重或激活值的表达精度;蒸馏方法则是把复杂网络结构的知识转移到较小网络结构上。

其次是在推理框架上做优化,成熟的轻量化框架如tensorrt/tf-lite/ncnn/mnn等,这些框架能够实现编译优化、缓存优化、算子优化、稀疏存储与计算、加速指令集应用等。能显著的提升模型推理速度。

最后是在硬件层级,硬件厂商通常会为特定深度学习任务或者框架做针对性设计优化,使得模型在匹配的硬件平台上获得最大的加速效果。目前主流的硬件平台有gpufpgaasic等。gpu通用性好,计算能力强,但是功耗较大,主要用在云端训练和推理;而asic这种定制化程度较高的芯片通用性较低,在专属任务上性能较高,主要用在前端计算设备上。tpunpu属于asic的范畴。

本篇综述,主要介绍模型的剪枝和量化原理。


1. 剪枝流程


深度神经网络结构中,有着大量的冗余参数。在推理过程中,仅有少部分的权值参与有效的计算,并对推理结果产生主要影响。剪枝方法通过把网络结构中冗余的权值、节点或层去掉,减少网络的规模,降低计算复杂度。让模型在推理效果、推理速度上达到平衡。

我们可以形象化的把模型剪枝想象为园艺师修剪盆栽。那么修剪的流程是什么呢?首先园艺师获得一个枝叶茂盛的盆栽(原模型);然后园艺师判断哪些枝叶对外观美感影响不大(重要性判断),并剪掉这些不重要枝叶(参数设置为0)。


类似的,模型剪枝的主要流程如下:
1)训练一个性能较好的原始模型。原模型网络参数量较大,推理速度较慢。
2)判断原模型中参数的重要程度。
3)去除重要程度较低的参数。
4)在训练集上微调,尽量避免由于网络结构变化而出现的性能下降。
5)判断模型大小、推理速度、效果等是否满足要求,不满足则继续剪枝。


2. 剪枝类型

. 模型剪枝

从剪枝的基本操作单元来看,可以分为“非结构化剪枝(unstructured pruning)”和“结构化剪枝(structured pruning”两大类。
“非结构化剪枝”主要通过对权重矩阵中的单个或整行、整列的权重值进行修剪。修剪后的新权重矩阵会变成稀疏矩阵(被修剪的值会设置为0)。因而除非硬件平台和计算库能够支持高效的稀疏矩阵计算,否则剪枝后的模型是无法获得真正的性能提升的。
“结构化剪枝”的基本修剪单元是滤波器或权重矩阵的一个或多个channel。由于结构化剪枝没有改变权重矩阵本身的稀疏程度,现有的计算平台和框架都可以实现很好的支持。

2.1 非结构化剪枝


2.1.1 权值剪枝


剪掉神经元节点之间的不重要的连接。相当于把权重矩阵中的单个权重值设置为0。一般的,会对权重矩阵中所有的数值按照大小排序,把排在后面的一定比例的值设为0即可。

 

. 权值剪枝的矩阵表现


2.1.2 神经元剪枝

把权重矩阵中某个神经元节点去掉,则和神经元相连接的突触也要全部去除。相当于同时去除权重矩阵中的某一行和列。如何判断神经元节点的重要程度呢?可以通过计算神经元对应的行和列的权重值的平方和的根的大小进行排序,把排序在后面一定比例的神经元节点去掉。

. 神经元剪枝的矩阵表现



2.2 结构化剪枝


2.2.1 filter-wise

一个卷积核被剪枝,那么其前一个feature map和下一个feature map 都会发生相应的变化

图. filter-wise剪枝

以上图为例,在第i层卷积中,其中第25个卷积核被剪掉(卷积核数量变少,每个卷积核的shape不变);当i-1层的featruemap经过第i层卷积矩阵卷积后得到的第i层feature map,其中的第25个channel也相应的被去除。为了匹配第i层featuremap通道维度产生的变化,第i 1层的卷积中的每个卷积核的第25个channel的权重被去除(卷积核数量不变,但每个卷积的shape发生改变)。

为方便观察,把上图中每层卷积核排列成卷积核矩阵的形状,如下图kernel matrix。通过这种形式,我们继续探讨两种剪枝形式。


◎ 单层中卷积核剪枝

. 剪掉一个卷积核,相关的feature层也同时被剪掉

如上图所示,kernel matrix 中的ni表示第i层feature map的通道深度;ni 1表示第i 1层feature map的通道深度。kernelmatrix中每个卷积核的尺寸为k x k
从第i个卷积层剪掉n个卷积核的算法过程如下:
1)计算每个卷积核权重绝对值之和
(2)根据的值大小排序。
(3)将最小的n个卷积核及对应的feature map剪掉。下一个卷积层中相关的卷积核也要移除。
(4)生成了第i层和第i 1层新的权重矩阵,剩余的权重参数被复制到新模型中。

◎ 跨多层剪枝
在上图中的i 1层的卷积矩阵中,由于i 1层的featuremap减少一个通道,导致该卷积层中相关的卷积核(一行)被剪枝;如果同时要对该层卷积矩阵中某一列进行剪枝该,则这层卷积矩阵既被上一层的feature map影响,又能影响下一层feature map。这种跨多层的剪枝应该如何确定需要剪枝的卷积核呢?
方法1:分别独立的做每一次剪枝,既黄色的卷积核每次都会参与计算权重绝对值之和。
方法2:采用贪心策略,已经被剪枝过的卷积核(黄色)不在参与下一次的剪枝。实验显示贪心策略的剪枝方式更好一些。



. 跨多层剪枝时,计算权重值和的方式


2.2.2 channel-wise



. 利用bn放缩因子来修剪channel

《learning efficient convolutional networks through network slimming》为例。
conv-layer的每个channel的重要程度可以和batchnorm层关联起来,如果某个channel后的batchnorm层中对应的scaling factor足够小,就说明该channel的重要程度低,可以被忽略。如上图中橙色的两个通道被剪枝。
batchnorm的公式如下所示:
其中:表示channel scaling factors
为了增加的稀疏程度,方便对channel进行剪枝,训练时需要对每个batchnorm层的scaling factor增加l1的约束。

channel-wise和filter-wise既有区别,也有联系。两者使用的剪枝评判方法不同,但最终都会体现在对卷积核或卷积核中某些layer的剪枝。



2.2.3 shape-wise


filter-wise剪枝是对完整的卷积核(kxkxc)进行剪枝;channel-wise是对所有卷积核中相同layer进行剪枝。shape-wise的剪枝颗粒度相对而言则更小一些。剪枝对象是所有卷积核中相同位置的部分权重的剪枝。如下图所示可以看到三种剪枝的区别。

. 几种剪枝方式的图示

由于每个卷积核中不重要的权重值的位置并不相同,这种剪枝方式可能会导致模型网络丢掉有效的信息。

2.2.4 stripe-wise pruning (swp)


这种方法是腾讯优图发表的一篇论文《pruning filter in filter》,从另外一个维度对权重矩阵中的卷积核进行剪枝:针对每个卷积核的depth维度整体剪枝。但是不同卷积核剪枝的位置和数量不相同。
如下图所示,每个3x3xc的卷积核,被重组为91x1xcstripe,然后对这些stripes做剪枝。

. stripe-wise剪枝,卷积核被重组


. strip-wise剪枝示意图

论文通过在训练过程中引入fsfilter skeleton)模块,学习得到卷积核中每个stripe的重要程度。训练结束后每个卷积核就会得到一个特有权重的fs。并根据fs的权重值,确定要对哪些stripes进行剪枝。如下图:


. fs模块自动学习每个stripe的重要性

相对于filter-wise剪枝有两个优势:首先,剪枝的操作对象数量变为原来的kxk倍(k为卷积核尺寸),这意味着可以获得更高的剪枝比率。其次在一些数据集上,即使不经过fine tuning,剪枝后的性能依然很好,这是由于fs模块为每个卷积核学到了最佳的形状。而filter-wise剪枝这破坏了原来最佳的网络结构,必须要经过finetuning才会有好的表现。

3. 剪枝评判标准


在前面的几种剪枝方法中,我们可以看到最重要的一步是确定对哪些部分进行剪枝,这就涉及到评判标准的问题。
最常使用的是贪心法,按照大小顺序排列,然后按照一定的比例去除。比如在非结构化剪枝中,比较权值本身的大小;或者结构化剪枝中比较卷积核的权重绝对值之和的大小等。
在使用贪心法时,通常会使用一些策略以便于能更有效的剪枝:训练时在loss中加入l1正则化,使得权重稀疏化;在结构化剪枝时,使用group lasso来得到稀疏权重。当然l1正则化也可以使用在结构化剪枝中,如前面提到的channel-wise剪枝。

4. sparsity ratio / pruning rate


剪枝的评判标准确定了要对那些要素剪枝;稀疏率则确定了需要剪枝的比率。根据是否是预设固定的,可以分为预定义稀疏率和自适应稀疏率。
如果预定义的稀疏率用于所有的层,称之为全局稀疏率;如果不同的层有不同的预设稀疏率,则每层的稀疏率称为局部稀疏率。全局稀疏率设置方便,但是颗粒度较粗,没有考虑到不同的层对于剪枝的敏感程度。虽然为每层单独设置局部稀疏率较为繁琐,但能取得更好的效果。
自适应稀疏率通常要有剪枝算法自动得到每层的值。如前面提到的stripe-wisefs模块,就是在训练过程中获取每个fs的稀疏率。

5. fine tuning


由于剪枝直接改变了原模型最佳的网络参数/结构,往往对精度影响很大。需要通过finetuning 把损失的精度恢复回来。通常情况下,剪枝和fine tuning会交替进行,避免一次性孤独剪枝导致精度难以恢复。


百度百科中是这样描述量化的:“量化在数字信号处理领域,是指将信号的连续取值(或者大量可能的离散取值)近似为有限多个(或较少的)离散值的过程”。
把深度网络模型中高比特的权值和特征值用更低比特来表示的方法称为模型量化。当量化后的的数值是2的幂次倍的时候,如1248bit等,量化也可以称为定点化。量化后的值成为定点值。
在深度模型训练和推理过程中,我们最常使用的是32bit浮点型精度。但是高比特意味着模型的体积更大,推理速度更慢,硬件资源的消耗更多。这对于部署在计算资源和存储资源有限的边缘设备上是很不友好的。通过使用更低比特的精度,在尽量保持原模型效果的同时,获得尺寸更小、推理速度更快、硬件资源占用更少的模型是目前研究的重点方向。

1. 量化基本原理


模型量化方法本质上是函数映射。根据映射函数是否线性可以把量化分为线性量化和非线性量化。

1.1 线性量化

    线性量化是目前最常用的量化方法,尤其是在工业界应用比较成熟的8比特量化方案采用的都是线性量化。
模型量化建立了高精度的浮点数值和量化后低精度的定点数值之间的数据映射。浮点转换为定点的公式如下:

定点转换为浮点的公式如下所示:
其中r表示输入的浮点数据,q表示量化之后的定点数据,z表示偏移量,或零点/最小值对应的量化数值,s表示缩放系数。s的求解方式如下:
其中 分别表示输入浮点数据中的最大值和最小值, 分别表示量化后最大定点值和最小定点值。

1.2 非线性量化

“非线性”映射函数多种多样,通常需要根据不同场景的权值输入分布特点,研究使用何种映射方式。
非线性映射最显著的一个特点是,可以把不同重要程度的权值,映射到不同的量化范围。假如,权值输入主要分布在[]中,其余区间的权值虽然比例较少,但也不能忽略,则可以通过非线性函数,把[]区间中的权值映射到更大的量化后区间内,增加训练过程对于主要权值分布的敏感性。
在模型量化早期,会使用聚类方法,把权值聚为若干个类别,然后每个类别量化为同一个定值。这也一种非常典型的非线性量化方式。


2. 量化方法


2.1 聚类量化

典型代表如mit韩松教授的 deep compression 论文,这篇论文中综合使用了剪枝、量化、编码等技术实现模型的轻量化。其中,量化这一部分的基本操作如图中所示:

. 聚类量化示意

如上图所示,在这个4x4的权重矩阵中,所有权值的大小都在[-1.08, 2.12]之间,如果以-1012这几个离散整数作为聚类中心,然后对权重矩阵中所有的权值聚类,并把同一类中的所有权值四舍五入为聚类中心的值即可。同一类的权值梯度相加做为量化后聚类终中心的梯度用于更新权重。图中相同颜色的权值表示属于同一个聚类组。
作者采用k-means聚类方法,把原始的m个权重 w={w1,w2...,wm} 量化为k个聚类中心 c={c1,c2...,cm}。聚类算法最小化组内误差,目标函数如下所示:
k-means 初始聚类中心的选择非常关键。常见的初始化方法三种:均匀初始化、随机初始化和按密度初始化。论文中证明最好使用均匀初始化,均匀初始化的方法是统计权值的最小值和最大值,并把数值区间平分为k份,以每一份的分界点处的权值作为聚类的中心。

2.2 对数量化


对数量化后的定点值是2的幂次倍,既两个邻近定点数值之间是在以2为底的对数域上均匀分布的。如量化定点值为,在对数域上的分布为-1-2-3。这种量化特性使得模型在推理时可以通过移位运算来实现快速的计算。
下图展示了对数量化的一种方式,权重矩阵中权值量化到2的幂次的定点值上。与聚类量化方法不同的是,对每层的权重采用分批量化,而不是一次量化整个权重。这样分批量化的方式能够让部分权重保持高精度,更方便的进行训练优化。


. 对数量化并逐步量化过程


2.3 二值量化(1bit)


二值量化,也称作1bit量化。是目前压缩率最高的量化方法。有两种方法实现权重的二值化。一种是使用符号函数,如下面公式:

另一种是以一定概率随机量化,如下面公式:
其中,概率的计算方式如下:
为了保持精度,权重在前向传播和反向传播计算的时候进行二值化,但是在进行参数更新的时候使用浮点类型。
虽然二值量化的效率非常高,但是在模型精度损失非常大,落地比较困难。


2.4 8bit量化


8bit量化是目前在工程上应用最为成熟的方案。该方案非常好的平衡了推理速度和精度损失之间的矛盾。google的tensorflow和nvidiatensorrt等框架都提供了对8bit量化的支持。google采用的是非对称量化方案,而nvidia采用的是对称量化方案。
对称量化和非对称量化都属于线性量化。常见的线性量化过程可以用下面的公式来表示示:
r=round(s(q−z))
r表示的是原始的float32数值;
z表示的是float32数值的偏移量,在很多地方又叫zero point;
s表示的是float32的缩放因子,在很多地方又叫scale;
q表示的是量化后的一个整数值。

2.4.1 对称量化


. 对称量化

如上图所示,对称量化将输入数据映射到[-128,127]的范围内。但是这种方法隐藏着一个问题:如果输入数据中存在偏离正常分布较远的离群点,则会导致比较大的量化精度损失;因此在实际使用时,通常会选择介于127和max(|xf|)之间的一个阈值t对输入数据进行截断,以避免离群点对量化精度额影响。既量化的输入区间从[-max(|xf|), max(|xf|)]变为[-|t|, |t|]
对称量化需要保证原始的输入数据中的零点通过映射公式后仍然对应[-128,127]区间的零点。在上面的公式中,当 z=0,且 q=0时恰好有r=0。
当z=0,s的取值可以使用如下的公式表示。
2.4.2 非对称量化

. 非对称量化

  如上图所示,非对称量化将输入数据映射到[0,255]的范围内。在非对称量化中,我们可以取z=min(xf),s的取值可以使用如下的公式:


z值是区分对称量化和非对称量化的重要特点。当z为零时,代表的是对称量化;z不为零,代表的是非对称量化。



2.4.3 量化步骤

在了解了线性量化的两种方案之后,我们来看一下如何具体执行量化:
1)选择合适的量化方法,确定选用对称量化或者非对称量化;
2)统计输入数据的数值区间[min_value, max_value]
3)根据量化方法,以及输入区间计算量化参数:零点值z和缩放系数s
4)根据转换公式,对输入的float32精度的数据转换为int8精度的数据。
5)可以尝试不同的sz,验证量化后的模型效果。


2.5训练后量化和训练感知量化


量化过程不可避免的会带来模型精度的损失,为了能够尽量保持原模型的精度,通常会对量化后的模型做fine tuning,或者进行重新训练。这种方式称作“训练感知量化”。
如果模型量化的精度能够满足使用要求,则会忽略掉finetuning和重训过程,这种简单直接的量化方式称作“训练后量化”。
训练感知量化能够获得更高的量化后模型精度,但是量化过程较为繁琐;训练后量化过程简单快速,但是模型精度损失会较大。尤其是规模较小的模型,有时可能会导致无法使用。

2.5.1 训练感知量化(quantization aware training

. qat流程

qat的量化方法如下:
(1)训练一个高比特的浮点模型。
(2)确定模型网络中需要量化的部分,并在网络相应位置插入伪量化的ops。

(3)在模拟量化过程(simulated quantization,伪量化)的模式下迭代训练。

(4)存储量化参数,生成并优化低比特的量化推理模型。
(5)使用量化模型执行推理。

在训练过程中引入量化操作会面临一个问题,量化后的数值是离散的,在反向传播时无法进行求导。因此引入了伪量化模块。如下图所示。

图. 伪量化训练过程

在上图中的模型网络中,有两个部分添加了伪量化的ops。一是权重矩阵weights在进入卷积之前先完成量化;另一个是激活函数的输出部分做量化。这种操作能够模拟量化产生的误差,并在训练过程中影响到权值的更新。
google在量化白皮说中对量化的模块有明确的定义:

其中x就是量化模块前的输入值,是原平滑连续区间的浮点型数值;在经过量化后,又转到了浮点型数值,只不过新的浮点区间并不是平滑连续的。这种伪量化(simulated quantization)方法能够有效减轻梯度传播中的量化累积误差。尤其是对参数规模较小的模型。

下图展示了google 在量化训练时采用的伪量化方法,让网络的优化更加简单。


. 伪量化的精度转换

因为模拟量化方程在各个位置的导数都为0,不能求反向传播时的梯度,在实际的工程中会使用一种近似方程,定义如下公式。称为直通滤波器(ste,straight through estimator)
    如下图所示,在前向传播时会对输入权值做量化,但是在反向传播时梯度值会直接通过ste


. 直通滤波器ste

2.5.2 训练后量化(post training quantization)


图.pqt

相较于训练感知量化,训练后量化流程更为简单。主要流程如下:
1)准备数据集,用于对量化模型进行量化参数校正。
2)以训练好的高精度模型为基准,使用校正数据集对其量化。
3)统计权重和激活值的数值范围,确定量化参数。
4)使用量化参数对模型进行量化。

◎权重量化和激活量化
训练后量化为甚会需要calibration data呢?如果不使用会有什么影响。要了解数据集的作用,我们要先了解量化的两种方式:权重量化和权重激活量化。
权重量化是指仅对模型中的weights进行量化。由于网络权重在训练结束后都是确定的值,因而通过对每一层权值的统计就可以得到量化参数。这时是不需要calibaration data参与的。既data free
但是如果还需要对网络中的激活值进行量化,既权重的卷积输出(也可能是经过激活函数后的输出)。由于feature的数值范围需要动态的获取,因而就需要calibration data作为数据输入,通过前向推理产生的各层的feature统计量化参数。

◎ 量化参数的更新
对激活值量化时,需要根据输入的calibration data动态的统计量化参数。通常会采用一些策略来确定更有效的量化参数:
(1)统计每个batch的量化参数,通过指数平滑方法更新参数值。
(2)统计量化参数时,需要去除偏离数据正常分布较远的离群点,以免造成大的量化误差。
(3)使用kl散度评估模型量化产生的信息损失,选择kl散度最小时的量化参数量化模型。如tensorrt使用该方法度量int8的信息损失。

3. 量化总结


◎ 模型量化:
1)选择训练后量化方法时,可以先尝试使用权重的对称逐通道量化。当精度出现出现损失时,需要考虑fine tune
2)使用训练感知量化能够进一步减小量化模型和浮点型模型的精度差距。采用8bit量化时可以将精度损失控制在5%以内,甚至对所有层做4bit量化后也能有同样的精度表现。

性能表现:
1)在cpu上,8bit量化推理能够获得2到3倍的速度提升。在专为低精度向量计算优化过的特定处理器上,如支持hvx的骁龙dsp,和原浮点型模型推理速度相比能够加速10倍。
2)使用均匀量化(线性量化)可以在保持精度不变的情况下缩小4倍的模型大小。使用非均匀量化(非线性量化)技术则能够实现更高的压缩比,比如k-means聚类量化。

模型结构和量化的关系:
1)模型大小和压缩率之间存在明确的折中关系。规模越大的模型对量化误差的容忍程度越高。
2)对于某一个模型结构,可以在特征数量(权重激活的数量)和量化之间进行折中,使用越多的特征数量参与量化,则卷积核可以相应支持更低的比特位宽。
3)训练过程中不约束激活函数的输出范围,而是直接对输出进行量化,这样能进一步提升的精度。从一些实验结果看出,使用relu做为激活函数要优于relu6。
模型的压缩和加速是目前很火的研究方向,新技术算法的也在不断地出现。目的是获取体积更轻巧、精度更高、速度更快的轻量化模型。
本文重点总结了剪枝、量化方法的技术原理。并分别介绍了两种轻量化方案的若干典型方法。希望大家可以对剪枝量化有个初步的了解。
  • https://jinzhuojun.blog.csdn.net/article/details/100621397

  • https://cs.nju.edu.cn/wujx/paper/pruning_survey_mla21.pdf

  • https://blog.csdn.net/weixin_49457347/article/details/117110458

  • https://zhuanlan.zhihu.com/p/138059904

  • https://blog.csdn.net/wspba/article/details/75675554

  • http://fjdu.github.io/machine/learning/2016/07/07/quantize-neural-networks-with-tensorflow.html

  • https://zhuanlan.zhihu.com/p/45496826

  • https://zhuanlan.zhihu.com/p/361957385

  • https://zhuanlan.zhihu.com/p/374374300

  • https://blog.csdn.net/wzz18191171661/article/details/103332338

  • https://zhuanlan.zhihu.com/p/58182172

  • https://on-demand.gputechconf.com/gtc/2017/presentation/s7310-8-bit-inference-with-tensorrt.pdf

  • https://developer.download.nvidia.cn/video/gputechconf/gtc/2020/presentations/s21664-toward-int8-inference-deploying-quantization-aware-trained-networks-using-tensorrt.pdf

  • deep compression: compressing deep neural networks with pruning, trained quantization and huffman coding

  • learning structured sparsity in deep neural networks

  • learning effificient convolutional networks through network slimming

  • accelerating convolutional neural networks by group-wise 2d-fifilter pruning

  • pruning filters for efficient convnets

  • quantizing deep convolutional networks for effificient inference: a whitepaper

  • data-free quantization through weight equalization and bias correction

  • pruning filter in filter