zl程序教程

您现在的位置是:首页 >  后端

当前栏目

PyTorch模型静态量化、保存、加载int8量化模型

静态PyTorch 模型 加载 保存 量化 INT8
2023-06-13 09:15:18 时间

1、什么是模型量化?

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

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

2、模型量化有什么好处?

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

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

3、量化的对象是什么?

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

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

4、量化的位数

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

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

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

5、量化的分类

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

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

6、模型量化的形式有哪些?

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

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

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

7、模型量化的过程

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

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)以及数据映射方式,让原来的浮点值映射到整型值后,尽量保持准确率不下降(或者在可控范围)。

8、模型的量化粒度

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

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

8、量化的挑战有哪些?

1、多后端难点

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

2、硬件黑盒难点

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

3、量化误差分析难点

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

9、模型量化方法简介

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

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

常用算法 adaround 就是这种

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

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

PyTorch模型训练完毕后静态量化、保存、加载int8量化模型

1. PyTorch模型量化方法

Pytorch模型量化方法介绍有很多可以参考的,这里推荐两篇文章写的很详细可以给大家一个大致的参考

Pytorch的量化

https://zhuanlan.zhihu.com/p/299108528

官方量化文档

https://pytorch.org/docs/stable/quantization.html#common-errors

Pytorch的量化大致分为三种:模型训练完毕后动态量化、模型训练完毕后静态量化、模型训练中开启量化,本文从一个工程项目(Pose Estimation)给大家介绍模型训练后静态量化的过程。

具体量化知识可以从推荐的两篇文章中学习。

2. 量化过程准备工作。

代码运行环境:PyTorch1.9.0, Python3.6.4.

1.数据集下载(在做静态量化时需要对数据集进行推理获取数据的分布特点、定标),用MSCOCO的验证集,选100张左右MSCOCO_val2017

http://images.cocodataset.org/zips/val2017.zip

2.Pytorch模型文件可以从这里下载Pose_Model提取密码:s7qh.

https://pan.baidu.com/s/1nvml9pB

3.量化代码下载Pytorch_Model_Quantization

https://github.com/Laicheng0830/Pytorch_Model_Quantization

代码下载后如上图,把下载的MSCOC数据集选100张放在data目录,把下载的模型文件coco_pose_iter_440000.pth.tar放在models目录。

pth_to_int.py是对Pytorch的float32模型转成int8模型。

evaluate_model.py里加载int8模型进行推理。

3. 模型静态量化

模型静态量化主要代码如下,读取float32模型,然后转成int8模型保存为openpose_vgg_quant.pth。完整代码可以从pth_to_int.py文件中看到。具体每一步做什么工作在注释中详细说明了。

4. 量化模型加载进行推理

注意:量化后模型的forward代码稍有改动,需要在模型输入前后安插量化和解量化。如下示例:

量化和解量化在pose_estimation.py文件34-86行可以看到.

加载int8模型不能和之前加载float32模型一样,需要将模型通过prepare() , convert()操作转成量化模型,然后load_state_dict加载进模型。

5. 性能

下图为量化后结果,整体来说损失不大。其中模型大小200M->50M,模型运行时间5.7s->3.4s。整体来说,模型大小压缩为原来的1/4, 模型运行时间减少20%左右