关于奇亿娱乐 奇亿注册
咨询热线:

020-88888888

联系我们CONTACT

奇亿娱乐-奇亿注册登录站
邮箱:youweb@admin.com
手机:13899999999
电话:020-88888888
地址:广东省广州市番禺经济开发区

奇亿新闻

当前位置: 主页 > 奇亿新闻

应该怎么学习神经网络模型压缩、加速优化?

发布时间:2024-04-15 12:00 点击量:

应该怎么学习神经网络模型压缩、加速优化?

目前是看了三本深度学习入门书,不知道具体接下来怎么学习,看了两三篇综述,了解到大概有剪枝、量化等。该去哪里系统的学习它们呢?





随着深度学习的发展,神经网络被广泛应用于各种领域,模型性能的提高同时也引入了巨大的参数量和计算量。模型量化是一种将浮点计算转成低比特定点计算的技术,可以有效的降低模型计算强度、参数大小和内存消耗,但往往带来巨大的精度损失。尤其是在极低比特(<4bit)、二值网络(1bit)、甚至将梯度进行量化时,带来的精度挑战更大。

这篇文章比较详细,所以下面这个图是这篇文章的一个整体目录。当然啦,除了非常多的文字,这篇文章塞了59个公式,涉及到量化在推理和训练的内容。虽然可能看得很辛苦,但是也希望可以多多支持ZOMI酱哈。打公式不容易,发现错误欢迎评论留言指正。


与FP32类型相比,FP16、INT8、INT4的低精度类型所占用空间更小,因此对应的存储空间和传输时间都可以大幅下降。以手机为例,为了提供更人性和智能的服务,现在越来越多的OS和APP集成了深度学习的功能,自然需要包含大量的模型及权重文件。以经典的AlexNet为例,原始权重文件的大小已经超过了200MB,而最近出现的新模型正在往结构更复杂、参数更多的方向发展。显然,低精度类型的空间受益还是很明显的。低比特的计算性能也更高,INT8相对比FP32的加速比可达到3倍甚至更高,功耗上也对应有所减少。

模型量化即以较低的推理精度损失将连续取值(或者大量可能的离散取值)的浮点型模型权重或流经模型的张量数据定点近似(通常为int8)为有限多个(或较少的)离散值的过程,它是以更少位数的数据类型用于近似表示32位有限范围浮点型数据的过程,而模型的输入输出依然是浮点型,从而达到减少模型尺寸大小、减少模型内存消耗及加快模型推理速度等目标。

首先量化会损失精度,这相当于给网络引入了噪声,但是神经网络一般对噪声是不太敏感的,只要控制好量化的程度,对高级任务精度影响可以做到很小。

其次,传统的卷积操作都是使用FP32浮点,浮点运算时需要很多时间周期来完成,但是如果我们将权重参数和激活在输入各个层之前量化到INT8,位数少了乘法操作少了,而且此时做的卷积操作都是整型的乘加运算,比浮点快很多,运算结束后再将结果乘上scale_factor变回FP32,这整个过程就比传统卷积方式快很多。

提前从体系结构的考量角度思考量化带来的另一个好处是节能和芯片面积,怎么理解呢?每个数使用了更少的位数,做运算时需要搬运的数据量少了,减少了访存开销(节能),同时所需的乘法器数目也减少(减少芯片面积)。

一边而言,量化方案主要分为两种:在线量化(On Quantization)和离线量化(Off Quantization),在线量化指的是感知训练量化(Aware Quantization),离线量化指的是训练后量化(Post Quantization)。训练量化根据名字的意思很好理解,其实就是在网络模型训练阶段采用量化方案进行量化。训练后量化中的量化方案跟训练并不相关,主要是在模型离线工具(模型转换工具的时候)采用量化方案进行量化。

  1. 感知量化训练(Aware Quantization)

实际上无论是Tensorflow、MindSpore、Pytroch的量化感知训练是一种伪量化的过程,它是在可识别的某些操作内嵌入伪量化节点(fake quantization op),用以统计训练时流经该节点数据的最大最小值,便于在使用端测转换工具(推理转换工具)的时候,转换成端侧需要的格式时进行量化使用。

目的是减少精度损失,其参与模型训练的前向推理过程令模型获得量化损失的差值,但梯度更新需要在浮点下进行,因而其并不参与反向传播过程。

某些操作无法添加伪量化节点,这时候就需要人为的去统计某些操作的最大最小值,但如果统计不准那么将会带来较大的精度损失,因而需要较谨慎检查哪些操作无法添加伪量化节点。

值得注意的是,伪量化节点的意义在于统计流经数据的最大最小值,并参与前向传播,让损失函数的值增大,优化器感知到这个损失值得增加,并进行持续性地反向传播学习,进一步提高因为伪量化操作而引起的精度下降,从而提升精确度。

值得注意的是,训练时候的原理与在端测推理的时候,其工作原理并不一致。

2. 训练后动态量化(Post Dynamic Quantization)

训练后动态量化是针对已训练好的模型来说的,针对大部分已训练,未做任何量化处理的模型来说均可用此方法进行模型量化。

其工作比较简单,在端测转换工具的时候,对网络模型的权重进行统计其每一层卷积的layer或者channel的最大值和最小值,然后通过量化公式对数据进行byte转换。这样得到的权重参数比以前小1/4。推理的时候,在内存初始化的时候对网络模型中的权重进行反量化操作变成float进行正常的推理。

3. 训练后校正量化(Post Calibration Quantization)

训练后静态量化,同时也称为校正量化或者数据集量化。其原理是对于Mindspore在端测低比特推理的时候(Inference),需要生成一个校准表来量化模型。这个量化校准表的生成需要输入有代表性的数据集,对于分类任务建议输入五百张到一千张有代表性的图片,最好每个类都要包括(即数据无偏)。

一般而言,无论per channel还是per layer量化方案,对于weight权重的量化使用对称量化,对于activate激活的量化使用非对称量化。

其原因是对于权重而言,其数据的分布为对称的如或,因此采用对称量化可以进一步有效降低计算量,使得数据分布更加符合其真实的分布;activate激活在网络中通常使用ReLU和ReLU6等作为网络模型的激活函数,其数据分布集中在或者,如果采用对称方案会加剧数据的离散程度,另外一个原理是会造成数据的浪费,如[-128,0]之间并不存储任何数据,只有[0,127]有数据分布。

下面以int8作为量化的标准,介绍一下两种量化算法的主要细节。

  1. 对称量化symmetry

对称的量化算法原始浮点精度数据与量化后INT8数据的转换如下:

float=scale ×int

其中,scale默认是float32浮点数,为了能够表示正数和负数,int采用signed int8的数值类型。通过将float32原始高精度数据转换到signed int8数据的操作如下,其中round为取整函数,量化算法需要确定的数值即为常数scale:

int=round(1/scale)

由于在神经网络的结构以层(Layer)来进行划分的,因此对权值和数据的量化可以层Layer为单位来进行,对每层Layer的参数和数据分别进行不同scale的量化。

对权值和数据的量化可以归结为寻找scale的过程,由于int8为有符号数,要保证正负数值表示范围的对称性,因此对所有数据首先进行取绝对值的操作,使待量化数据的范围变换为,再来确定scale,其scale正确的计算公式为:

scale=(2max?(|x_{min}|,x_{max}))/(Q_{min}-Q_{max})

另外对于offset的计算公式为:

offset=0

确定了scale之后,itn8数据对应的表示范围为 [-128×scale,127×scale] ,量化操作即为对待量化数据以 [-128×scale,127×scale] 进行饱和式截断,即超过范围的数据饱和到边界值,然后进行第一条公式所示量化计算即可。

2. 非对称asymmetric

非对称的量化算法与对称的量化算法,其主要区别在于数据转换的方式不同,如下,同样需要确定scale与offset这两个常数:

float=scale×(uint+offset)

确定后通过原始float32高精度数据计算得到uint8数据的转换即为如下公式所示:

uint8=round(float/scale)-offset

其中,scale是float32浮点数,uint为unsigned INT8定点数,offset是int32定点数。其表示的数据范围为[ scale×offset,scale×(255+offset) ]。若待量化数据的取值范围为 [x_{min}, x_{max}] ,则scale的计算公式如下:

scale=(x_{max}-x_{min})/(Q_{max}-Q_{min})

offset的计算方式如下:

offset=Q_{min}-round(x_{min}/scale)

对于权值和数据的量化,都采用上述公式的方案进行量化, x_{min}x_{max} 为待量化参数的最小值和最大值, Q_{max}Q_{min} 为:

Q_{max}=2^n,Q_{min}=0

3. 量化初始化

在一般的端测推理的时候,由于网络模型的输入并没有伪量化节点记录输入tensor的最大值和最小值,因为无法求得量化参数scale和offset,这个时候推理框架许可要根据输入数据的标准差deviation和均值mean进行计算,得到对应的量化参数。

其中标准差Standard Deviation的计算公式为下:

standard.deviation=Q_{max}/(float_{max}-float_{min})

其对应的scale为:

scale=1/(standard.deviation)

Offset的计算为:

offset=mean

值得注意的是,由于在其他端测推理框架(如tflite)需要手工输入标准差deviation和均值mean,容易导致对数据分析、数据输入的错误和框架的割裂,因此感知量化训练的时候会对模型的输入和输出加上fakeQuant伪量化节点,记录数据min和max,从而求得Mindspore端测用到input tensor的scale和offset。

上面讲解了基本的量化公式和量化的方法,下面来详细展开感知量化训练(Aware Quantization)模型中插入伪量化节点fake quant来模拟量化引入的误差。端测推理的时候折叠fake quant节点中的属性到tensor中,在端测推理的过程中直接使用tensor中带有的量化属性参数。

  1. 伪量化节点

为量化节点(Fake Quant)的意义在于:

1)找到输入数据的分布,即找到min和max值;

2)模拟量化到低比特操作的时候的精度损失,把该损失作用到网络模型中,传递给损失函数,让优化器去在训练过程中对该损失值进行优化。

a. 基本原理

低比特量化模型运行时,需要确定scale与offset两个量化参数:

float=scale*(uint8 + offset)

其中,是float32浮点数,uint8为unsigned int定点数,offset是int32定点数。其表示的数据范围为:

[scale×offset,scale×(2^n+offset)]

若待量化数据的取值范围为[a,b],则scale和offset的计算方式如下:

scale(a,b,n)=(a-b)/2^n

offset=round(a / S(a,b,n))

对于权值和数据的量化,都采用上述公式的方案进行量化,min和max为待量化参数的最小值和最大值。

前面呢,曾经提到一般而言,权重数据是对称,为了可以进一步减少数据的计算,较少额外的存储数据,因此推荐使用对称的量化算法。但是在对mobieNetV2,mobileNetV3的实际测试开发的过程中,发现数据大部分是有偏的,例如mobileNetV2中:

此时权重对应的数据并不是以0为中心点对称,featture.child8.conv.child3.weight的权重数据分布范围是,为了更好地拟合更多的数据,对更多的数据具有更好的泛化性,因此统一使用了非对称的量化算法。

b. 正向传播

为了求得网络模型tensor数据精确的Min和Max值,因此在模型训练的时候插入伪量化节点来模拟引入的误差,得到数据的分布。对于每一个算子,量化参数通过下面的方式得到:

clamp(x,x_{min},x_{max})?min?(max?(x,x_{min}),x_{max})

Fake quant节点的具体计算公式为:

q(x;x_{min},x_{max},n)=[(clamp(x;x_{min},x_{max})-x_{min})/s(x_{min},x_{max},n) ]×s(x_{min},x_{max},n)+x_{min}

为了更好地表示上面公式的作用,正向传播的时候fake quant节点对数据进行了模拟量化规约的过程,如下图所示:

首先对数据根据min和max值进行截断,然后把float数据转换成为int数据,之后再把int数据重新转换成为float数据,该操作会丢失掉float1与float2转换之间的精度差。对于量化感知训练,通过模拟量化操作时候对数据进行规约操作,所以网络模型中每一个tensor数据可以根据下面公式进行封装成为op,得到:

x_{out}=SimQuant(x)

c. 反向传播

按照正向传播的公式,如果方向传播的时候对其求导数会导致权重为0,因此反向传播的时候相当于一个直接估算器:

δ_{out}=δ_{in},I_{(x∈S)}∈S:x:x_{min}≤x≤x_{max}

最终反向传播的时候fake quant节点对数据进行了截断式处理,如下图所示:

d. 更新Min和Max

FakeQuant伪量化节点主要是根据找到的min和max值进行伪量化操作,更新min和max分别为running和moving,跟batch normal算子一样,这里不进行过多的讲述,具体在Mindspore框架中会自动根据网络模型的结构进行自动插入不同的形式,具体用户不感知。

running minimum的计算公式为:

x_{min}=min?(X), where, x_{min}=None

x_{min}=min?(x_{min},min?(X)), where, otherwise

Running maximum的计算公式为:

x_{max}=max?(X), where, x_{max}=None

x_{max}=min?(x_{max},max?(X)), where, otherwise

Moving minimum的计算公式为:

x_{min}=min?(X), where, x_{min}=None

(1-c) x_{min}+c min(X), where, otherwise

Moving maximum的计算公式为:

x_{max}=max?(X), where, x_{max}=None

(1-c) x_{max}+c max(X), where, otherwise

初步实施方案中构图比较复杂的主要集中在如何拆分BN层,本节将会对拆分的原理进行细化,落实确定到每一个算子上面,包括每个算子的具体计算公式,控制原理。

a. BN Fold原理

基本卷积操作为:

y_{cout}=w\\cdot x+b

经过BN层处理后:

y_{bn}=BN(y_{cout})

拆分上式得到:

y_{bn}=γ?\\frac{(y_{cout}-E[x])}{\\sqrt{(var[x]+?)}}+β

y_{bn}=γ? \\frac{(wx+b-E[x])}{\\sqrt{var[x]+?}}+β

定义:

\\widehat{w}=\\frac{\\gamma}{\\sqrt{\\operatorname{var}[x]+\\epsilon}}\\cdot w

\\hat{b}=\\frac{\\gamma}{\\sqrt{\\operatorname{var}[x]+\\epsilon}}\\cdot(b-E[x])+\\beta

因此conv+batch normal两个算子在推理的时候,合起来融合为一个conv算子:

y_{b n}=\\operatorname{BN}\\left(y_{c o u t}\\right)=B N(w \\cdot x+b)=\\widehat{w}\\cdot x+\\widehat{b}

网络模型中经常使用BN层对数据进行规约,有效降低上下层之间的数据依赖性。然而大部分在推理框架当中都会对BN层和卷积操作进行融合,为了更好地模拟在推理的时候的算子融合操作,这里对BN层进行folding处理。下面左边图是(a)普通训练时模型,右边图是(b) 融合后推理时模型。

下面图中左边是BN折叠的BN folding训练模型,右边是BN folding感知量化训练模型。

b. Correction数据矫正

  • 贝塞尔矫正(Bessel Correction)

在计算方差的时候,论文并没有提及贝塞尔校正,因为贝塞尔校正是通用的统计学方法,用于校正方差。

具体对应于样本标准差为:

\\sigma_{n}^{2}=\\frac{\\sum(x-\\mu)^{2}}{n}, \	ext{ where }\\mu=\\frac{1}{n}\\sum x

贝塞尔校正的样本标准差为:

\\sigma^{2}=\\frac{\\sum(x-\\mu)^{2}}{n-1}=\\frac{\\sum x^{2}}{n-1}-\\frac{\\left(\\sum x\\right)^{2}}{n(n-1)}=\\frac{n}{n-1}\\sigma_{n}{ }^{2}

因此对应贝塞尔因子为:

c=\\frac{n}{n-1}

  • Batch Normal矫正

由于每次更新batch参数的delta的时候,都是使用当前batch的统计数据,这会使得权重参数连带着一起被当前的batch影响,为了避免当前权重数据w的抖动,加入correction的校正因子将权重缩放到长期batch统计数据中。

w=\\frac{\\sigma_{B}}{\\sigma}\	imes \\frac{\\gamma W}{\\sigma_{B}}, \\quad c=\\frac{\\sigma_{B}}{\\sigma}

模型开始训练的时候会在fold conv卷积的输出除以correction因子,这样的操作是为了让模型直接去学习正常的BN操作(根据当前的batch进行学习),在学习到一定程度之后,冻结当前的BN不再更新全局delta,然后fold conv卷积的操作中直接作用,并同时修改bias,作为correction_2更新到ADD算子中。

Frozen前:

y_{\	ext{correction }}=y / c

Frozen后:

\\mathrm{y}_{\	ext{correction }}=y

\\mathrm{b}_{\	ext{correction }}=\\gamma\\left(\\frac{\\mu_{B}}{\\sigma_{B}}-\\frac{\\mu}{\\sigma}\\right)

加上correction能够有效地抑制在后期阶段weight的数据波动。

c. BN FOLD训练图

  • 算子

组合模式ConvD+BN+ReLU拆分子图并加入FakeQuant算子,一共拆分为为以下几个算子:Conv2D、BatchNormFold、CorrectionMul、MulFold、FakeQuantWithMinMaxPreChannel、ConvMul、CorrectionAdd、AddFold、ReLU、FakeQuantWithMinMax。

1) Conv2D (in: x, weight; out: y):普通卷积,用于计算BatchNormFold的入参:

y=w * x

2) BatchNormFold ( \	ext{ in: }\\mathrm{w}, \\sigma_{B}, \\sigma_{n}; \	ext{ out: }\\mathrm{w} ):FoldBN,用于计算当前 \\mu_{B}\\sigma_{B} ,并使用CMA算法更新 \\sigma_{n+1}\\mu_{n+1} ,运行时使用 \\sigma_{n}\\mu_{n} 。该算子输出 x_{b n}=\\gamma\\left(\\frac{x-\\mu_{B}}{\\sigma_{B}}\\right)+\\beta 并不作用于网络,因此该算子实际计算公式为更新 \\mu_{B}, \\sigma_{B}, \\mu_{n}, \\mu_{n}

\\mathrm{x}\\rightarrow \\mu_{B}, \\sigma_{B}

\\mathrm{x}, \\sigma_{n-1}, \\mu_{n-1}\\rightarrow \\sigma_{n}, \\mu_{n}

3) CorrectionMul ( \	ext{ in: }\\mathrm{w}, \\sigma_{B}, \\sigma_{n}; \	ext{ out: }\\mathrm{w} ):对w乘以校正因子。由于每次更新batch参数的 \\sigma_{B} 的时候,都是使用当前batch的统计数据,这会使得权重参数连带着一起被当前的batch影响,为了避免当前权重数据w的抖动,加入correction校正因子将权重缩放到长期batch统计数据中:

w_{\	ext{correction }}=w \\cdot c, \	ext{ where: }c=\\frac{\\sigma_{B}}{\\sigma}

4) MulFold ( \	ext{ in: }\\mathrm{w}, \\sigma_{B}, \\gamma, \	ext{ out: }\\mathrm{w} ):根据batch mean参数对w进行规约,模拟融合操作:

w=w_{\	ext{correction }}* \\frac{\\gamma}{\\sigma_{B}}

5) FakeQuantWithMinMaxPreChannel (in: w, delay; out: self):控制流delay,训练模型中插入伪量化节点fake quant来模拟量化引入的误差,推荐权重按PerChannel模式。

w=\	ext{ if delay ? SimQuantPreChannel }(w): w

6) Conv2D (in: w, x, out:x):实际作用的卷积:

x=w * x

7) ConvMul ( \	ext{ in: }\\mathrm{x}, \\sigma_{B}, \\sigma, \	ext{ out: }x ):控制流freeze,模型开始训练的时候会在fold conv卷积的输出除以correction校正因子,这样的操作是为了让模型直接去学习正常的BN操作(根据当前的batch进行学习);在学习到一定程度之后,冻结当前的BN不再更新全局。

x=\	ext{ if freeze }? x+\\gamma\\left(\\frac{\\mu_{B}}{\\sigma_{B}}-\\frac{\\mu}{\\sigma}\\right): x

8) CorrectionAdd ( \	ext{ in: x, gamma, }\\mu_{B}, \\sigma_{B}, \\mu, \\sigma, \	ext{ out: }\\mathrm{x} ):控制流freeze,在学习到一定程度之后冻结当前BN,校正因子修正bias偏置。

x=\	ext{ if freeze }? x+\\gamma\\left(\\frac{\\mu_{B}}{\\sigma_{B}}-\\frac{\\mu}{\\sigma}\\right): x

9) AddFold (\	ext{ in:x, }\\beta, \\gamma, \\mu_{B}, \\sigma_{B}, \	ext{ out: }x ):作用于bias偏置,BN融合后剩余参数作用于bias偏置:

x=x+\\beta-\\gamma \\frac{\\mu_{B}}{\\sigma_{B}}

10) ReLU (in:x, out: x):实际作用的激活函数;

11) FakeQuantWithMinMax(in:x, min, max, delay; out: x):控制流delay,训练模型中插入伪量化节点fake quant来模拟量化引入的误差,并用指数移动平均算法更新min和max:

\\min=\\mathrm{EMA}(\\min ), \\max=\\mathrm{EMA}(\\max )

x=\	ext{ if delay }? \\operatorname{SimQuant}(x): x

  • 正向图

下面左边为原始设计bn折叠训练图,右边是实际Mindspore执行经过优化融合的训练图。

图中主要注意的点为BN_Fold_1和BN_Fold_2,其中BN_Fold_1的作用有两个:拆分融合的BN层对weight的影响,和对权重数据进行校正;BN_Fold_2的作用有两个:拆分融合的BN层对bias的作用影响,和对bias数据进行校正。

d. BN FOLD推理图

  • 算子

组合模式ConvD+BN+ReLU拆分子图并加入FakeQuant算子,一共拆分为以下个算子:Conv2D、BatchNormFold、MulFold、FakeQuantWithMinMaxPreChannel、AddFold。

1) Conv2D (in: x, weight; out: y):普通卷积,用于计算BatchNormFold的入参:

y=w * x

2) BatchNormFold ( \	ext{ in: x, }\\sigma_{n-1}, \\mu_{n-1}, \	ext{ out: }\\mu_{B}, \\sigma_{B}, \\sigma_{n}, \\mu_{n} ):开根号作用:

x \\rightarrow \\sigma, \\mu

3) MulFold (\	ext{ in:w, }\\sigma, \\gamma, \	ext{ out: }w ):根据BN参数对w进行规约,模拟融合操作:

w=w * \\frac{\\gamma}{\\sigma}

4) FakeQuantWithMinMaxPreChannel (in: w, delay; out: self):训练模型中插入伪量化节点fake quant来模拟量化引入的误差,推荐权重按PerChannel模式。

w=\\operatorname{SimQuantPreChannel}(w)

5) Conv2D (in: w, x, out:x):实际作用的卷积:

x=w*x

6) AddFold ( \\operatorname{in}: \\mathrm{x}, \\beta, \\gamma, \\mu, \\sigma, \	ext{ out: }\\mathrm{x} ):作用于bias偏置,BN融合后剩余参数作用于bias偏置:

x=x+\\beta-\\gamma \\frac{\\mu}{\\sigma}

  • 验证图示例

正向推理验证图,上面的所有公式都会化为这个图的实际计算方式。

训练后静态量化(Post Calibration Quantization),同时也称为校正量化或者数据集量化。其原理是对于Mindspore在端测低比特推理的时候(Inference),需要生成一个校准表来量化模型。其核心是取得数据值的量化参数表。

权值在进行推理加速时均已确定,因此不需要对权值进行校准。如果使用对称量化方案,max使用权值的绝对值的最大值,对于非对称量化算法,max和min使用权值的最大值和最小值。

根据算法验证结果,对于Convolution,每个卷积核按照per channel的方式采用一组独立的量化系数(scale和offset),量化后推理精度较高。因此,Convolution权值的量化根据卷积核数量分组进行,计算得到的scale和offset的数量与卷积核数量相同。Dense的权值使用一组scale和offset。

数据量化是对每个要量化的Operation的输入数据进行统计,每个Operation计算出最优的一组scale和offset。

数据是推理计算的中间结果,其数据的范围与输入高度相关,需要使用一组参考输入作为激励,得到每个Operation的输入数据用于确定量化max和min。数据的范围与输入相关,为了使确定的min和max在网络接收不同输入时有更好的鲁棒性,因此提出基于统计分布确定min和max的方案。

该方案的思路为最小化量化后数据的统计分布与原始高精度数据的统计分布差异性,操作流程如下:

  1. 使用直方图统计的方式得到原始float32数据的直方图统计分布;
  2. 在给定的min和max搜索空间中选取若干个和分别对待量化数据进行量化,分别得到量化后的数据;
  3. 使用同样的直方图统计的方式得到n个的直方图统计分布;
  4. 分别计算中每个与的统计分布差异性,找到差异性最低的一个对应的min和max作为确定的量化数值。

在上述操作中,涉及的超参数包括进行直方图统计时选取的直方图bin个数、min和max的搜索空间、统计分布差异性的指标。

对于直方图bin个数,该参数直接反应了直方图统计特征的分布数个数,由于数据经过量化后会集中到256个离散的点上,因此bin的个数不宜过大,否则绝大多数的bin都没有数值。

max的搜索空间可以通过search_start_scale,search_end_scale与search_step来确定。

  • search_start_scale为搜索空间起始点与search_value的比值。
  • search_end_scale为搜索空间结束点与search_value的比值。

search_step为搜索空间中每次搜索的值与seach_value的比值步进值。以max_candidate=100,search_start_scale=0.8,search_end_scale=1.2,search_step=0.01为例,对称量化算法下,其定义的max搜索空间为从100*0.8=80到100*1.2=120的范围,每次步进100*0.01=1,一共41个d_max搜索值;非对称量化算法下,搜索0.8*(max–min) ~ 1.2*(max–min),确定最好的一个系数。

继续举多一个例子, search_start_scale=0.3,search_step=0.01,search_end_scale==1.7,bin=150。需要在0.3*max~1.7*max这个范围中找一个最好的max,搜索步长是0.01max,因此需要搜索(1.7-0.3)/0.01 + 1=141个max数值。直方统计的bin个数也可以设置,假设当前是150。对于算法方案一,将0-2*max的数据分为150段,统计数据落在每段中的频率,使用数据的绝对值统计,对于算法方案二,在0.3*(max – min)~1.7*(max - min)这个范围中找一个最好的ratio,将0-2*(max - min)的数据分为150段,统计数据落在每段中的频率,使用data – min的值来统计频率。141个数值都要统计,因此一个量化算子需要存储141*150个数值。多个batch情况下对frequancy进行累加。

统计分布差异性的指标为计算两个长度为n直方图、分布之间的信息差异度,选取的指标包括如下三种:

1) Kullback-Leibler Divergence(KL散度)计算方式如下:

D_{K L}\\left(P_{f}\\| Q_{q}\\right)=\\sum_{i=1}^{N}P(i) * \\log _{2}\\frac{P_{f}(i)}{Q_{q}(i)}

2) Symmetric Kullback-Leibler Divergence(对称KL散度):对于 P_{f}Q_{q} 两个分布, P_{f} 相对 Q_{q}Q_{q} 相对 P_{f} 的KL Divergence是不同的,Symmetric KL Divergence的计算方式为 P_{f} 相对 Q_{q}Q_{q} 相对的KL Divergence的均值,计算方式如下:

S Y M_{K L}=\\frac{1}{2}*\\left(D_{K L}\\left(P_{f}\\| Q_{q}\\right)+D_{K L}\\left(Q_{q}\\| P_{f}\\right)\\right)

SYM_{K L}=\\frac{1}{2}*\\left(\\sum_{i=1}^{n}P_{f}(i) * \\log _{2}\\frac{P_{f}(i)}{Q_{q}(i)}+\\sum_{i=1}^{n}Q_{q}(i) * \\log _{2}\\frac{Q_{q}(i)}{P_{f}(i)}\\right)

3) Jensen-Shannon Divergence(JS散度):首先生成一个新的分布M,为与的均值,JS Divergence为相对M的KL Divergence与相对M的KL Divergence的均值, 计算方式如下:

M=\\frac{1}{2}*\\left(P_{f}+Q_{q}\\right)

D_{J S}=\\frac{1}{2}*\\left(D_{K L}\\left(P_{f}\\| M\\right)+D_{K L}\\left(Q_{q}\\| M\\right)\\right)

=\\frac{1}{2}*\\left(\\sum_{i=1}^{n}P(i) * \\log _{2}\\frac{P_{f}(i)}{M(i)}+\\sum_{i=1}^{n}Q(i) * \\log _{2}\\frac{Q_{q}(i)}{M(i)}\\right)

a. Calibration功能

离线Calibration需要完成以下功能:

  1. 对算子的输入数据量化校准,计算出数据的最优scale和offset;
  2. 将算子的权值量化为INT8,计算出权值的scale和offset。
  3. 将算子的bias量化为INT32。
  4. 由于算子输入数据在推理过程中才能产生,因此离线Calibration还要实现inference功能。与普通inference过程的不同之处在于对每个需要量化的op的输入数据和权值数据进行了量化和反量化过程。

b. Calibration过程

Calibration过程可以分为4步:

  1. 遍历graph中的node,对需要量化的算子的权重进行量化。
  2. 第一次inference,搜索需要量化的算子的输入数据的max和min。
  3. 第二次inference,进行数据的直方统计。
  4. 计算分布差异性能指标(见4.2),根据指标选择最好的min和max,然后计算出数据的scale和offset,根据数据和权值的scale,进行bias量化。

值得注意的是,min不一定是所有数据batch中的最小值,max也不一定是所有batch中的最大值,与min_percentile和max percentile的值相关。

假设每个batch处理10张图片,共100个batch,某个op的1张图片输入数据是1000个数,max_percentile=0.99999,数据总量是10*100*1000=1000000个数。1000000*0.99999=999990。把所有输入数据从大到小排序,max取第10个数。因此需要缓存10个数,对第一个batch的输入1000个数进行从大到小排序,取前10大的数进行缓存,第二个batch的输入1000个数进行从大到小排序,取前10个数,与缓存的10个数共20个数进行排序,取前10大的数,以此类推,最终获得所有batch数据中前10大的数。

Inference有两种方式,第一种是数据不做量化进行推理,第二种是数据量化后进行推理。数据量化是指算子的当前batch输入数据使用当前batch的max和min进行量化,将数据量化为INT8,再由INT8转回FP32进行inference计算。

第二次inference,进行数据的直方统计。根据第2步已经根据max_percentile找到需要的max的值。

最后,计算分布差异指标,根据指标选择最好的min和max,然后计算出数据的scale和offset,根据数据和权值的scale,进行bias量化。

c. 搜索空间

影响搜索空间的参数可以分为两类:一类是增加搜索组合的数量的可以称之为配置参数,一类是影响单个模型结果的称之为超参数(不增加结果数量)。

端侧量化推理的结构方式主要由3种,分别是下图的(a) Fp32输入Fp32输出、(b) Fp32输入int8输出、(c) int8输入int32输出。

INT8卷积示意图,里面混合里三种不同的模式,因为不同的卷积通过不同的方式进行拼接。

使用INT8进行inference时,由于数据是实时的,因此数据需要在线量化,量化的流程如图所示。数据量化涉及Quantize,Dequantize和Requantize等3种操作。

将float32数据量化为int8。离线转换工具转换的过程之前,计算出数据量化需要的scale和offset,如果采用对称量化算法,则根据公式4进行数据量化,如果采用非对称量化算法,则根据本文公式6进行数据量化。

INT8相乘、加之后的结果用INT32格式存储,如果下一Operation需要float32格式数据作为输入,则通过Dequantize反量化操作将INT32数据反量化为float32。

Dequantize反量化推导过程如下:

y=x \\cdot w

=x_{\	ext{scale }}\\cdot\\left(x_{\	ext{int }}+x_{\	ext{offset }}\\right) \\cdot w_{\	ext{scale }}\\cdot\\left(w_{\	ext{int }}+w_{\	ext{offset }}\\right)

=\\left(x_{\	ext{scale }}* w_{\	ext{scale }}\\right) \\cdot\\left(x_{\	ext{int }}+x_{\	ext{offset }}\\right) \\cdot\\left(w_{\	ext{int }}+w_{\	ext{offset }}\\right)

=\\left(x_{\	ext{scale }}\\cdot w_{\	ext{scale }}\\right) \\cdot\\left(x_{i n t}\\cdot w_{i n t}+x_{i n t}x_{o f f s e t}+w_{i n t}x_{o f f s e t}+w_{o f f s e t}x_{o f f s e t}\\right)

\\begin{aligned}=&\\left(x_{\	ext{scale }}\\cdot w_{\	ext{scale }}\\right) \\cdot\\left(I N T 32_{\	ext{result }}+x_{\	ext{int }}x_{\	ext{offset }}+w_{\	ext{int }}x_{\	ext{offset }}+w_{\	ext{offset }}x_{\	ext{offset }}\\right) \\end{aligned}

\\approx\\left(x_{\	ext{scale }}\\cdot w_{\	ext{scale }}\\right) \\cdot \	ext{ INT32 }_{\	ext{result }}

因此反量化需要input和weight的scale。值得注意的是,后面实际的公式约减了 x_{i n t}x_{o f f s e t}w_{i n t}x_{o f f s e t}w_{o f f s e t}x_{o f f s e t} ,是因为在实际网络的运行和测试过程结果表明,发现这两个参数对网络模型的精度并不影响,其作用相当于bias add,因此故作减约。

INT8乘加之后的结果用INT32格式存储,如果下一层需要INT8格式数据作为输入,则通过Requantize重量化操作将INT32数据重量化为INT8。

重量化推导过程如下:

y=x \\cdot w

=x_{\	ext{scale }}\\cdot\\left(x_{\	ext{int }}+x_{\	ext{offset }}\\right) \\cdot w_{\	ext{scale }}\\cdot\\left(w_{\	ext{int }}+w_{\	ext{of fset }}\\right)

=\\left(x_{\	ext{scale }}\\cdot w_{\	ext{scale }}\\right) \\cdot\\left(x_{\	ext{int }}+x_{\	ext{offset }}\\right) \\cdot\\left(w_{\	ext{int }}+w_{\	ext{offset }}\\right)

=\\left(x_{\	ext{scale }}\\cdot w_{\	ext{scale }}\\right) * \	ext{ INT32_result }

其中y为下一个节点的输入,即  y=x_{next}

y_{\	ext{int }}=y_{\	ext{scale }}*\\left(y_{\	ext{int }}+y_{\	ext{of fset }}\\right)

有:

x_{n \	ext{ ext int }}=\\left(x_{\	ext{scale }}\\cdot w_{\	ext{scale }}/ x_{\	ext{next scale }}\\right) \\cdot \	ext{ INT32 }_{\	ext{result }}-x_{\	ext{next offset }}

因此重量化需要本Operation输入input和weight的scale,以及下一Operation的input输入数据的scale和offset。

计算公式如下:

\\begin{aligned}y &=w \\cdot x+b \\\\ &=w_{\	ext{scale }}\\cdot\\left(w_{\	ext{int }}+w_{\	ext{offset }}\\right) * x_{\	ext{scale }}\\cdot\\left(x_{\	ext{int }}+x_{\	ext{offset }}\\right)+w_{\	ext{scale }}\\cdot x_{\	ext{scale }}\\cdot x_{\	ext{bias }}\\end{aligned}

其中,P为与w矩阵相同大小的全1矩阵,Q为与x矩阵相同大小的全1矩阵

\\begin{array}{rlr}=w_{\	ext{scale }}& \\cdot x_{\	ext{scale }}\\cdot\\left(\\left(w_{\	ext{int }}+w_{\	ext{offset }}\\right) *\\left(x_{\	ext{int }}+x_{\	ext{offset }}\\right)+\\right. & \\left.x_{\	ext{bias }}\\right) \\\\=w_{\	ext{scale }}& * x_{\	ext{scale }}*\\left(w_{\	ext{int }}* x_{\	ext{int }}\\right. & --\\mathrm{c}1 \\\\ & +x_{\	ext{int }}* w_{\	ext{offset }}& --\\mathrm{c}2 \\\\ & \\left.+w_{\	ext{int }}* x_{\	ext{offset }}+w_{\	ext{offset }}x_{\	ext{offset }}+x_{\	ext{bias }}\\right) & --\\mathrm{c}3 \\end{array}

其中, w_{\	ext{int }} 是量化后INT8权值, x_{\	ext{int }} 是量化后INT8数据, w_{bias} 是量化后INT32的bias。

c3项可以在离线时计算好,作为新的bias参数传入。c1和c2项需要在线计算。Conv2d的INT8计算根据输入是float32还是int8决定是否需要插入Quantize OP,根据输出是float32还是int8决定输出是插入Dequantize OP还是Requantize OP。

好的模型结构是深度学习成功的关键因素之一,不仅是非常重要的学术研究方向,在工业界实践中也是模型是否能上线的关键。对各类底层深度学习模型设计和优化技术理解的深度是决定我们能否在深度学习项目中游刃有余的关键,而模型的部署则是项目的最后一环。为了让大家能够系统性掌握相关内容,为以后做研究以及就业打下基础,我们平台输出了海量与模型有关的内容,包括公众号文章,书籍,以及4门对应的视频课程,本次来汇总一下我们的4门相关视频课程,内容层层递进,从新手到进阶,一键全部掌握!

第1重境界:模型分析

首先是第1重境界,想要掌握模型,必须先懂你的模型!

深度学习模型使用的门槛虽然低,但模型参数多,网络结构复杂,模型结构的设计以及训练过程中超参数的调试,都非常依赖于经验。

结果不好,是数据的问题还是模型的问题,往往分析起来比较困难。如果是数据问题,那到底是什么问题?如果只凭经验,没有很科学的分析工具,仍然会有盲人摸象的感觉。因此为了能够更好地进行研究和实践,对模型进行相关的分析是非常重要的。

为了帮助初学者深入掌握模型分析相关知识,有三AI推出了《深度学习之模型分析》系列课程,为大家深入掌握深度学习模型分析相关方法以及实践打下基础。

本课程内容包括模型分析的几个重要方向的理论与实践(基于Pytorch),已有时长大概3个小时,下面是当前课程的大纲脑图:

(1) 模型可视化:涵盖了深度学习模型可视化的几个重要且实用的方向,如模型结构可视化、模型权重可视化、模型特征可视化,激活热图可视化,后续还会增加其他可视化技术等,每一部分内容都是理论+实践。我们会非常详细地讲解算法中的细节,帮助彻底消化算法原理;

(2) 复杂度分析。重点讲解FLOPs,MAC等模型复杂度指标的理论计算,以及若干自动化统计工具的使用。

(3) 速度分析。重点讲解Pytorch模型速度分析API以及相关插件的使用。

课程的更多详情,请大家点击下面的图片阅读:

【视频课】先搞懂你用的模型,深度学习模型分析课程来了!

订阅方法如下:

第2重境界:模型设计

然后是第2重境界,开始针对自己的任务选择与设计模型!

模型的设计是整个深度学习领域的通用基础,不管是从头设计自己的神经网络,还是对已有模型进行优化获得更高的任务指标,更快的运行速度,获得更小的模型体积,都需要掌握各类网络的设计技巧,是深度学习算法工程师进阶的难点及必学点!!!

为了让大家能够更加系统更加深入的掌握模型设计相关的内容,有三AI推出了《深度学习之模型设计-理论实践篇》系列课程,为大家深入掌握深度学习模型设计相关方法以及实践打下基础。

本课程内容已经包含有网络深度与模型性能、网络宽度与模型性能、注意力机制、轻量化网络、安卓部署5个部分,总共时长超过20个小时:

理论部分内容如下,包括:

(1) 网络深度与模型性能主要包含:经典浅层卷积网络的设计、网络深度对分类模型的影响;

(2) 网络宽度与模型性能主要包含:经典模型的宽度设计思想、网络宽度对模型性能影响;

(3) 注意力机制主要包含:空间注意力模型、通道注意力模型、 混合注意力模型、项目实战;

(4) 轻量化网络理论主要包含:Xception网络、MobileNet V1和V2网络、 ShuffleNet V1和V2网络、SqueezeNet网络;

实践部分内容如下,包括:

(1) 基于MobileNet的网络深度设计分析及实战;

(2) 基于XceptionNet的网络宽度设计分析及实战;

(3) 基于SeNet的人种分类实战;

(4) 基于Pytorch的安卓端模型部署。

课程的更多详情,请大家点击下面的图片阅读:

【视频课】超30个小时模型设计理论,4大Pytorch案例实战与安卓部署,长期更新中

订阅方法如下:

第3重境界:模型优化

接下来是第3重境界,对模型进行优化改进,使其能够满足自己业务上线的需求!

模型优化是模型能够在各类嵌入式平台使用的关键技术,包括紧凑模型设计,模型剪枝,模型量化,模型蒸馏,自动化模型设计等内容。

为了让大家能够更加系统更加深入的掌握模型优化相关的内容,有三AI推出了《深度学习之模型优化-理论实践篇》系列课程,为大家深入掌握深度学习模型优化相关方法以及实践打下基础。

本课程内容已经涵盖了深度学习模型优化的核心领域,包括模型剪枝、模型量化、知识蒸馏,超过10个小时,既有足够的宽度,也具备有足够的深度。我们会非常详细地讲解算法中的细节,帮助彻底消化算法原理;

下图是已有课程的大纲脑图:

课程的更多详情,请大家点击下面的图片阅读:

【视频课】超10小时模型优化技术,模型剪枝+模型量化+知识蒸馏理论与实践!

订阅方法如下:

同时与本课程相关的还有有三AI知识星球中的各类模型优化模块,形式是相关论文的图文解读。

关于知识星球的介绍可以参考。

【杂谈】有三AI知识星球指导手册出炉!和公众号相比又有哪些内容?

【总结】超1000页有三AI文档资源领取方法汇总!

第4重境界:模型部署

最后是第4重境界,对模型进行实际部署,才是我们的终极目标!

深度学习模型必须要经过部署到实际的生产环境中,才能产生真正的应用价值。对于公司而言,无需为AI模型部署云端服务器,能够大幅度降低公司的运营和服务成本;对于用户而言,用户的数据,如视频、图片、声音、文字等,无需上传至云端服务器,可在本地移动端进行计算,用户的隐私得到有效的保护。

为了让大家能够更加系统深入的掌握模型部署相关的内容,有三AI推出了《深度学习之模型部署》系列课程,让有经验有实力的讲师带你系统深入学习,掌握模型部署流程。

本次课程中一共已经包含了5个平台和框架案例,分别为通用NCNN部署,Tengine在EAIDK嵌入式平台上的部署,TensorRT在服务器端的模型优化与部署,微信小程序的前后端完整部署,MNN在Android手机端部署,后续还会增加其他硬件平台与部署框架。

课程大纲如下:

课程的更多详情,请大家点击下面的图片阅读:

【视频课】快速掌握5大模型部署框架(NCNN+MNN+Tengine+TensortRT+微信小程序)!

订阅方法如下:

课程讲师

以上课程讲师为言有三与有三AI团队的一些负责人,具体负责内容大家可以在课程的详细介绍中获取详情。

言有三简介如下:

龙鹏,笔名言有三,技术社区《有三AI》创始人。先后就读于华中科技大学(2008-2012),中国科学院半导体研究所神经网络实验室(2012-2015),先后就职于奇虎360人工智能研究院(2015.7-2017.5),陌陌科技深度学习实验室(2017.5-2019.3),深度学习算法专家,阿里云MVP,华为云MVP。

拥有超过7年的计算机视觉从业经验,拥有丰富的传统图像算法和深度学习计算机视觉项目经验,著有书籍《深度学习之图像识别:核心技术与案例实战》(机械工业出版社2019.4),《深度学习之模型设计:核心算法与案例实践》(电子工业出版社2020.6),《深度学习之人脸图像处理:核心算法与案例实战》(机械工业出版社2020.7),《深度学习之摄影图像处理:核心算法与案例精粹》(人民邮电出版社2021.4),拥有10余项发明技术专利与学术论文。

擅长领域:Caffe,Tensorflow,Pytorch等主流深度学习平台。神经网络与深度学习理论,深度学习模型设计与优化,计算机视觉的基础领域,AI美学,2D与3D人脸算法,生成对抗网络GAN等领域。

模型设计课程负责人简介如下:

大学老师,审稿人,有三AI线上与线下课讲师。

项目负责人简介如下:

一个时刻担心秃头的大龄程序员。大厂8年工作经验,专注CV领域模型优化、模型部署。有三AI研发组负责人,擅长代码实战。

全部都想要学习

如果你想要一次性获取全部模型相关的课程,那么推荐你加入有三AI秋季划-模型优化组,除了以上4门课程之外,还可以额外获得:

(1) 言有三本人一对一答疑。

(2) 深度学习之模型设计赠书。

(3) 有三AI知识星球。

(4) 有三AI研发组、内容组、运营组的权限。

(5) 《深度学习之数据使用》,《深度学习之Pytorch入门实战》课程。

《深度学习之数据使用:理论实践篇》的内容包括数据的获取,数据的整理,数据的标注,数据增强,数据的分析等领域,覆盖了深度学习中数据使用的各个方向,大纲如下:

课程的完整介绍,请大家点击下图阅读:

【视频课】深度学习入门必修,子欲学算法,必先搞数据!

《深度学习之Pytorch入门实践篇》的内容包含PyTorch简介、PyTorch环境配置、张量简介、PyTorch中的层结构及初始化、PyTorch中的损失函数、PyTorch中的优化器、PyTorch中的数据读取、PyTorch中的模型加载与保存、基于PyTorch的垃圾图像分类等内容,目前总课时超过6个小时。

课程采用理论加Pytorch实战的方式进行讲解,大纲如下:

有三AI-CV秋季划模型优化组的详情介绍请阅读:

【CV秋季划】模型算法与落地很重要,如何循序渐进地学习好?

订阅方式如下:

这些年来,深度学习在众多领域亮眼的表现使其成为了如今机器学习的主流方向,但其巨大的计算量仍为人诟病。近几年,业界涌现出越来越多基于深度神经网络的端侧上的智能应用。而对于端上产品而言,深度神经网络巨大计算量是非常大的挑战。为了弥补端侧智能应用的算力需求与端侧的算力能力的鸿沟,近几年来模型压缩成为了业界的热点之一。而模型量化属于非常实用的模型压缩技术,并且当前已经在工业界发展非常成熟。

模型量化所处位置

为了保证较高的精度,大部分的科学运算都是采用浮点型进行计算,常见的是32位浮点型和64位浮点型,即float32和double64。然而推理没有反向传播,网络中存在很多不重要的参数,或者并不需要太细的精度来表示它们。

所以,模型量化就是将训练好的深度神经网络的权值,激活值等从高精度转化成低精度的操作过程,例如将32位浮点数转化成8位整型数int8,同时我们期望转换后的模型准确率与转化前相近。

  1. 减少内存和存储占用。量化对模型的『瘦身』效果可谓立杆见影,它所带来的好处是两方面的:其一,减少memory footprint。我们知道很多时候推理性能的瓶颈不在于计算而在于访存,在这种情况下提高计算密度会对耗时有显著的优化效果;其二,节省存储空间,减少应用的size,同时便于软件的升级更新。
  2. 降低功耗。我们知道功耗主要来自计算和访存两部分。一方面 ,以乘加运算为例,8位整型与32浮点相比能耗可有数量级的差异。另一方面,访存是耗电大户。假设原本只能放在DRAM中的模型经过量化能放入SRAM,那不仅提升性能,而且减少了能耗,简直完美。
  3. 提升计算速度。很多处理器整数计算指令都要比相应的浮点计算高效。以CPU为例,浮点运算指令的latency平均会长于对应的整数运算指令。

因此,在工业界对模型量化有非常强烈的需求。

一般来说,会对模型的哪些数据进行量化。主要有以下三个。实际中可能是量化其中的多个甚至全部。

  • weight(权重):weight的量化是最常规也是最常见的。量化weight可达到减少模型大小内存和占用空间。
  • activation(激活函数输出):实际中activation往往是占内存使用的大头,因此量化activation不仅可以大大减少内存占用。更重要的是,结合weight的量化可以充分利用整数计算获得性能提升。
  • gradient(梯度)相对上面两者略微小众一些,因为主要用于训练。它主要作用是在分布式计算中减少通信开销,单机训练时也可以减少backward时的开销。

大部分的科学运算都是采用浮点型进行计算,常见的是32位浮点型和64位浮点型,即float32和double64。

对于量化的位数,可以有很多种选择。大体可分为几类:

  • 16位
  • 8位 最常见也相对成熟。各种主流框架和硬件都支持。
  • 8位以下目前而言学界相对玩得多些,工业界有少量支持,但还没有太成熟。8位以下主要是4,2和1位(因为位数为2的幂次性能会更好,也更容易实现)。如果精度低至1位,也就是二值化,那可以用位运算进行计算。这对处理器而言是很友好的。

量化主要分为离线量化:(Post Training Quantization,PTQ ) 和量化感知训练(Quantization Aware Training,QAT)。高通在 2019 年的一篇paper里,为生产量化模型定义了4 种等级:一般我们用的多的就是level2 和level3。

  • Level 1:无数据离线量 无需数据,不需要反向传播,一个 API 调用完成量化模型生产
  • Level 2:有数据离线量化 需要数据,不需要反向传播,数据用于校准 BN,或者统计激活值分布,用于降低讲话?误差
  • Level 3:量化感知训练 需要数据,需要反向传播。通过训练和微调使量化模型达到可接受的精度,一般需要完整的训练过程和超参数调整
  • Level 4:修改网络结构的量化感知训练 需要数据,需要反向传播,同时调整网络结构。需要明显更多的训练时间和细致的超参数调整

如下图所示,量化可分为均匀量化和非均匀量量化。

  • 均匀量化:
    • 二值化 用1个bit位进行量化
    • 线性量化(对称、非对称、Ristretto)
  • 非均匀量化
    • 对数量化
    • 其他

如下图所示,量化还可分为对称量化和非对称量化。量化前后0点的值不变的称为对称量化。但在实际过程中,量化的对象分布式不均匀的,不一定在0点值两边对称,所下图右侧所示,量化前后0点的值不同的称为非对称量化。

上面的介绍可知,量化其实就是将训练好的深度神经网络的权值,激活值等从高精度转化成低精度的操作过程,并保证精度不下降的过程。如何从高精度转到低精度呢?在定点与浮点等数据之间建立一种数据映射关系,将信号的连续取值 近似为 有限多个离散值,并使得以较小的精度损失代价获得了较好的收益。这个映射过程一般用下面的公式来表示:

Q=round(scale factor * clip(x,α,β))+ zero point

这个公式中:x 代表需要量化的数,也就是量化对象,是个浮点型数据。Q代表量化后的数,是个整型数据。公式涉及到3个操作,round操作,clip操作和 scale factor 选取。以及需要确定的值α,β,是clip操作的上下界,称为clipping value

zero point 代表的是原值域中的0在量化后的值。在weight或activation中会有不少0(比如padding,或者经过ReLU),因此我们在量化时需要让实数0在量化后可以被精确地表示。

round操作:其实就是一种映射关系,决定如何将原来的浮点值按照一定的规律映射到整型值上。举个例子,我们可以选用四舍五入「假设5.4 则取值为5,5.5 则取值为6」的原则,也可以选用最近左顶点「5.4 和 5.5 都取值为5」或者最近右顶点原则等。

clip操作:其实就是切片操作,如何来选择这个量化对象的范围。为什么要选这个范围呢,因为量化到n位数后,可以用来表示量化后的整型值就是固定的,只有 2^N 个,这么有限的数据,怎么才能更好去映射原来的浮点值分布呢?这个范围选的太大了(按照原来的最大最小值来选,如下图所示),此时如果在头尾的浮点值只有零星的几个,而且距离其他值非常远,(如果此时是均匀量化)那么这个对于图中 α-α,β-β的离散值可能就被浪费了,这样浮点值到整型值的映射后导致的误差可能就会很大。这个取值是门艺术,大了浪费比特位,小了把就把太多有用信息“切”掉。

所以当前不同的量化算法和优化策略往往是寻找一个恰当的[α,β],使得 clip 和 round 操作导致的误差较小。

scale factor:是表述浮点数和整数之间的比例关系【不同的量化形式取不同的值】,如果是线性均匀量化,那么

总结下,量化的过程就是选取合适量化参数(如scaling factor,zero point,clipping value)以及数据映射方式,让原来的浮点值映射到整型值后,尽量保持准确率不下降(或者在可控范围)。

根据不同的模型量化参数选择范围,可将模型量化的粒度分为:

  • 通道量化(Per-axis/per-channel):对tensor的单个轴有单独的量化参数,如per-channel就是weight的每个channel使用单独的量化参数。通常情况下,per-channel 因为量化的粒度更细致,量化参数的自由度更高,往往更优于 per-tensor 的量化精度。
  • 层量化(Per-tensor/per-layer):每个tensor有单独的量化参数。对于卷积或全连接层这些的话这也就意味着每层独立的量化参数。
  • Global:即整个网络使用相同的量化参数。一般来说,对于8位量化,全局量化参数影响不明显,但到更低精度,就会对准确率有较大影响。

1、多后端难点

不同芯片后端的量化算法实现具有不同的特点。对于不同的硬件,用户需要针对硬件研究不同的量化方案。这同时也会导致量化方案的硬件泛化 性受到限制。 所谓硬件泛化性,主要是指量化模型在泛硬件平台的迁移能力。

2、硬件黑盒难点

模型的量化部署往往伴随着复杂的模型精度修复工作,特别是在第三方硬件下,需要解决如何与第三方硬件对齐的问题。 但在实际服务过程中,硬件的比特级对齐方案又是部署服务的重中之重。可以通过提供量化算法的模拟环境,使得精度修复算法能够在硬件上复现。常见的硬件差异主要源于非计算算子(Concat, Eltwise)的量化处理、累加器的重采样和取整方式的不同。

3、量化误差分析难点

量化在计算过程中会引入误差,导致网络部署精度相对于 FP32 会损失部分表达。 在实际业务中,部署的一大挑战在于,如何去保证量化模型的精度,通过降低误差,以保证模型速度和精度的收益平衡。

1、如何找到一个更好的数据映射方式

量化最简单粗暴的做法的是rounding。将浮点转为定点的rounding方法:一种是Round-to-nearest,即就近取整;另一种是Stochastic rounding,即以一定概率(与向下取整点的距离成正比)确定向上还是向下取整。

常用算法 adaround 就是这种

2、给定一个训练好的量化模型,如何找到最优的量化超参数

解决问题就是:给定一个 Tensor,寻找一个恰当的[α,β],使得 clip 和 round 操作导致的误差较小。

  • ACIQ方法
  • 基于MSE来找最优的clipping value
  • PACT(PArameterized Clipping acTivation)方法
  • LSQ(Learned Step Size Quantization)等

3、如何让量化目标对象的分布变得更适合量化

神经网络加速优化可以分为软件层面和硬件层面,软件层面要求在不损失或损失精度较小的情况下,设计或者转换硬件友好的、易于部署的算法模型,硬件层面要求在满足吞吐率、延迟、功耗等约束下正确的完成推理任务。


在模型设计阶段,要充分了解网络模型的参数量和计算量。参数量影响执行推理任务时硬件所需的内存、带宽等,计算量影响吞吐率、延迟等。设计网络模型时要充分考虑待部署的硬件的能力,有一些经典的轻量化网络如MobileNetShuffleNet、NasNet,可以去了解它们的思想。

  • MobileNet V1将卷积结构替换为深度可分离卷积,普通卷积被分解为深度卷积(depthwise convolution)和逐点卷积(pointwise convolution),大幅度降低参数量和计算量。
  • MobileNet V2加入残差设计来解决过深的网络可能出现的梯度消失问题,还有对深度可分离卷积构建块的其他改进。
  • ShuffleNet借鉴了MobileNet的思想,引进了channel shuffle、pointwise group convolutions和depthwise separable convolution等。
  • NasNet使用神经架构搜索的方法自动搜索网络结构,借鉴这种思想以精度、参数量、计算量、硬件平台为约束,可以搜索出能高效在目标平台运行同时满足正确率的网络结构。

在神经网络中存在大量冗余连接,去除这些冗余后,网络的精度并不会有明显下降。在CNN中,剪枝通常可以分为不规则剪枝和规则剪枝。不规则剪枝是指没有任何规律的,也很难在硬件层面利用这种稀疏性。规则剪枝搭配定制的硬件,可以在压缩网络到原来的30%甚至10%的情况下,达到差不多的精度。

规则剪枝通常有好几种粒度:

  • filter,直接减去整个滤波器,性能损失可能很大
  • 按行剪枝
  • 按列剪枝
  • 按块剪枝

同时为了保证正确率和负载均衡,要在剪枝的稀疏度与正确率间做均衡。

通常在训练时,为了保持较高的性能和扩大表示范围,神经网络一般采用单精度浮点数(Single Precision Floating-Point Number)表示网络中的数值,包括权重、输入和计算中间结果等。一般的计算能力较强的硬件设备具备浮点运算单元,可以较好的支持这种表示方式。但是在部署到终端设备时,通常不具备这样的条件,硬件平台可能只支持整数计算或对整数计算有优于浮点计算的性能。从必要性上讲,训练时复杂的、高精度的模型是必要的,因为需要在参数优化过程中捕捉微小的梯度变化值,但在推理时并不一定需要一直保持高精度。因此可以将浮点的参数值转换为整数值计算,通常在float32类型下训练,在int8下推理。量化是一种模型压缩的常用方法,具有以下优点:

(1)减少存储占用。显而易见的,若将float32类型的参数全部量化为int8类型,占用内存空间会变为原来的1/4。但是一般激活(Activation)值仍需具有大位宽表示范围,例如int32,否则极易造成计算结果溢出(Overflow)。

(2)功耗降低。功耗受计算过程和存储访问的影响,降低位宽后减少了运算次数,也使得每次存储数据量显著降低,可以降低功耗。

(3)提升吞吐率。在int8下计算速度大大快于float32,同样的访存带宽下存取时间也变少,可以极大提升吞吐率。

量化机制也会引入一些缺点,量化的过程不可避免会造成误差,进而影响神经网络的精度。在合适的量化方法下,这样的误差相比带来的性能提升是可接受的。

推荐下面两篇综述,很好的概括了各种量化方式的场景和适用性:第一篇是高通研究院的,第二篇是伯克利的。

M. Nagel, M. Fournarakis, R. A. Amjad, Y. Bondarenko, M. van Baalen, and T. Blankevoort, “A White Paper on Neural Network Quantization.” arXiv, Jun. 15, 2021. Accessed: Jul. 19, 2022.[Online]. Available: arxiv.org/abs/2106.0829

A. Gholami, S. Kim, Z. Dong, Z. Yao, M. W. Mahoney, and K. Keutzer, “A Survey of Quantization Methods for Efficient Neural Network Inference.” arXiv, Jun. 21, 2021. Accessed: Jun. 24, 2022.[Online]. Available: arxiv.org/abs/2103.1363

由于移动设备的算力和存储受限,在这些设备中部署DNN模型是一个巨大的挑战。为了解决这个问题,Bucilua等人(2006)首次提出了模型压缩,将信息从大模型或集成模型转移到训练的小模型中,并且不会显著导致精度下降。从大模型中学习小模型的方法后来作为知识蒸馏正式推广。

? 在知识蒸馏中,小型学生模型通常由大型教师模型监督。其主要思想是:学生模型模仿教师模型,并获得近似甚至更优越的性能表现。关键问题是如何将大教师模型转移到小的学生模型上。知识蒸馏系统基本由三个关键部分组成:知识、蒸馏算法、教师-学生结构。

关于知识蒸馏更详细的介绍,可以看下这篇文章

守夜人:知识蒸馏(一)概述

为了使上层深度学习框架中的神经网络模型编译到底层不同硬件后端更加容易,深度学习编译器被提出,其架构一般如图所示。现存较流行的深度学习编译器有TVM、Glow、XLA、TC和nGraph。TVM是由陈天奇主导的开源的深度学习编译器,后四个是由企业如Facebook、Intel、Google主持研发的。在开发语言上,以上编译器都是由Python和C++混合开发,由Python提供前端接口灵活性,C++提供高性能操作。在深度学习框架支持方面,TVM支持TensorFlow、Keras、PyTorch、Caffe2、MxNet等常见的框架。后几种编译器只支持有限的二到三个框架,XLA只提供了TensorFlow的接口。在自动调优方面,TVM具有基于模板的AutoTVM调优器和不依赖模板的Ansor两种调优器。nGraph和Glow不支持自动调优技术。在训练支持上,TVM目前只支持神经网络推理过程,不支持训练,而其他几种编译器都支持训练过程。在后端设备支持上,TVM支持CPU、GPU和FPGA,NGraph和XLA的支持设备范围与TVM类似,而TC只支持Nvidia GPU。在对FPGA的支持上,TVM提供了对应的硬件栈VTA生成自定义参数化的FPGA配置。

在CPU上运行神经网络比较少见,因为CPU适合执行控制流任务,CPU有精心设计的分支预测器,相比之下,它的计算单元就显得孱弱。但是CPU的分布范围广,一台设备也许没有GPU和NPU,但一定有一个充当CPU的硬件。

守夜人:AI编译器中常见CPU运行优化--以TVM为例

得益于Nvidia的GPGPU和CUDA的推广,我们可以方便的使用pytorch调用GPU进行高效训练。Nvidia有CuDnn、CuSparse、CuBlas等多个库加速DNN,在部署时也有TensorRT框架。如果想学习在GPU端加速模型,可以从CUDA编程和TensorRT部署入手,这方面有详细的官方文档。

相比于GPU,FPGA的功耗更低;相比于ASIC,FPGA的成本低,包括时间成本和试错的经济成本,且可以很方便的重构,探索不同硬件配置的影响。

FPGA的特点是可以高度定制化,包括计算的数值精度,与量化对应;稀疏数据流模式,与剪枝对应;如果是基于指令的设计,还可以对接深度学习编译器,在不重构硬件的情况下运行多种神经网络模型,如VTA加速器对接的TVM。

神经网络的计算是逐层计算的,在计算一层时通常存在大量的数据复用机会,基于数据重用的形式,有这几种数据流:

  • weight stationary
  • output stationary
  • input stationary
  • row stationary

FPGA擅长整数和定点数运算,相同功能使用不同硬件资源实现也会影响性能。为了快速进行开发,XIlinx推出了Vivado HLS(最新版叫Vitis HLS,高层次综合),以受限的C++语言描述算法,并经过编译器生成verilog代码。具体介绍可以看这篇文章:

守夜人:HLS(一)Vivado高层次综合概述

在FPGA上实现DNN时,可以先从单个算子开始,这里介绍了在FPGA实现softmax算子的几种方式,通过数学变换、查找表、近似等方法,使得非线性的算子可以高效的运行。

守夜人:硬件友好的高效softmax函数实现调研与分析

至于完整的加速器,可以学习VTA的实现。TVM-VTA实现了一个端到端的软硬件加速栈。

守夜人:VTA专题内容(一):VTA(Versatile Tensor Accelerator)介绍守夜人:VTA专题内容(二) 第三方改进调研

除了VTA,还有其他的一些CNN加速库。这些库大都包含基于HLS的算子模板库,能根据输入的模型定制生成加速器框架。

守夜人:基于HLS(High-level synthesis)的开源CNN加速库调研


关于作者本人:

目前研究生在读,研究方向是AI模型加速,主要为FPGA加速器设计,也会关注软件栈部分如剪枝、量化、深度学习编译器等,实现软硬件结合的优化。欢迎关注我的几个专栏,会分享自己平时看到的好论文,自己阅读论文所做的总结等。希望和大家一起交流学术~

一切都从四年前开始上CENG5030, 三年前开始上CMSC5743 两门课说起:

经过了三四年的改进,添加了几个主题(与时俱进嘛),拿掉了几个主题(主要是一些过时的编译器,和纯粹的training内容),大部分的slides手敲了公式(还在不断改slides过程中)。现在的知识图谱如下:

知识图谱分成两个分支,左边分支对应着“模型压缩”,右边的分支对应着“加速优化”。

在模型层面,对应着算法工程师的任务。在准确度满足指标的前提下,可以适当对模型做化简

Mo1 Pruning: 在模型中引入更多的0,更大的稀疏性(训练中,或者训练后)。

Mo1 Pruning slides

Mo2 Decomposition: 将模型中的某一层分解为低秩(low-rank)的两层

Mo2 Decomposition slides

Mo3 Quantization: 不用64-bit来表示一个数,用更少的bit(比方说8bit, 16bit)

Mo3 Quantization slides

Mo4 BNN (binarized neural network): 更激进的用1-bit or 2-bit来表示模型中的每个参数

Mo4 BNN slides

Mo5 KD (knowledge distillation): 知识蒸馏,从大模型迁移知识去小模型(部署的时候上小模型)

Mo5 KD slides

Mo6 NAS (network architecture search): 自动的网络架构搜索,希望能搜到一个又快又准的网络

Mo6 NAS slides

在部署层面,对应着软件工程师的任务。这个时候算法团队已经把模型给过来了,在输出能跟模型层对齐的基础上,怎么在infernece时候跑的更快。

Im1 GEMM (general matrix multiplication): im2col 和 MEC 两种操作,怎样提高访存友好性

Im1 GEMM slides

Im2 Direct-Conv: 直译过来就是“直接搞卷积”,但为了性能考虑了 loop-reordering, unrolling, tiling

Im2 Direct-Conv slides

Im3 Winograd: 矩阵乘可以用Strassen 和 Winograd两种方法,减少需要的乘法次数

Im3 Winograd slides

Im4 Sparse Conv: 如果kernel matrix是(随机)稀疏的,我们该如何加速

Im3 Sparse-Conv slides

Im5 CUDA: 手撸CUDA优化某些算子(这个是好大的topic,我也在不断学习中就简单介绍一下吧)

Im5 CUDA slides

Im6 TVM: 部署层面,实际的优化最后都会集成到一个编译器中。介绍一下TVM,MLIR也要看看

Im6 TVM slides

有空我会把每个topic 单独开文章聊聊。毕竟我都手敲公式这么费劲了,而且上了3年从两眼一抹黑到总算在我心中串成体系了(终于呀终于)。




我其实一直在CUHK教计算机组成原理(computer architecture)和嵌入式系统(embedded system),如果是EDA相关的课我也很非常熟悉。

时间回到2019年,我需要上一门研究生的课,EDA相关的课已经有Evan老师教了,剩下也没几门可以选了,我就稀里糊涂的上了这门CENG5030: energy efficient computing (低功耗计算)。

当时想着无论是算法层,架构层,EDA层,低功耗都是永恒的优化主题吧,就上这门课吧。

2019年这门课覆盖了比较正统的低功耗计算主题,包括:

  • 设计时候的静态功耗设置 (static voltage assignment): slides
  • 运行时候的动态功耗/频率调整 (dynamic voltage/frequency scheduling, DVFS): slides
  • 体系结构中的power gating: slides
  • 近似计算 (approximate computing): slides

上了一年觉得同学们对这个没啥兴趣,一是很多都是比较经典(老旧)的题目,不算科研热点。另一个,题目的实际性也有一些问题。

比方说,随着CPU功耗的不断压缩,在电路设计中,已经不提供两个供电网络(不像2000年那会);而且也不提供两套threshold voltage

再比方说,approximate computing一直也在找实际的应用场景;quantization出来后,approximate computing的适用范围进一步减小。

所以呢,2020年开始,咬咬牙扔掉了传统的低功耗计算主题,只看深度网络中的低功耗,我也是悄默默的给课程名字加了注释:

Energy Efficient (Deep Neural Network) Computing LOL

这也算是“一课两表”吧:同一门课,学校的解释,和我上课时候的解释,各有定义

平台注册入口