项目简介
本项目应用PaddleSlim对YOLOv3进行量化训练,从而对模型进行压缩,提高其在端上运行的性能。 在量化训练的过程中加入了MAP的计算,实时的评估训练过程中的效果。
PaddleSlim简介
PaddleSlim是一个模型压缩工具库,包含模型剪裁、定点量化、知识蒸馏、超参搜索和模型结构搜索等一系列模型压缩策略。
对于业务用户,PaddleSlim提供完整的模型压缩解决方案,可用于图像分类、检测、分割等各种类型的视觉场景。 同时也在持续探索NLP领域模型的压缩方案。另外,PaddleSlim提供且在不断完善各种压缩策略在经典开源任务的benchmark, 以便业务用户参考。
对于模型压缩算法研究者或开发者,PaddleSlim提供各种压缩策略的底层辅助接口,方便用户复现、调研和使用最新论文方法。 PaddleSlim会从底层能力、技术咨询合作和业务场景等角度支持开发者进行模型压缩策略相关的创新工作。
功能
-
模型剪裁
- 卷积通道均匀剪裁
- 基于敏感度的卷积通道剪裁
- 基于进化算法的自动剪裁
-
定点量化
- 在线量化训练(training aware)
- 离线量化(post training)
-
知识蒸馏
- 支持单进程知识蒸馏
- 支持多进程分布式知识蒸馏
-
神经网络结构自动搜索(NAS)
- 支持基于进化算法的轻量神经网络结构自动搜索
- 支持One-Shot网络结构自动搜索
- 支持 FLOPS / 硬件延时约束
- 支持多平台模型延时评估
- 支持用户自定义搜索算法和搜索空间
本项目主要展示PaddleSlim在目标检测模型YOLOv3上的量化训练。
基于PaddleSlim量化训练原理:
1.1 背景
近年来,定点量化使用更少的比特数(如8-bit、3-bit、2-bit等)表示神经网络的权重和激活已被验证是有效的。定点量化的优点包括低内存带宽、低功耗、低计算资源占用以及低模型存储需求等。
表1: 不同类型操作的开销对比
由表1可知,低精度定点数操作的硬件面积大小及能耗比高精度浮点数要少几个数量级。 使用定点量化可带来4倍的模型压缩、4倍的内存带宽提升,以及更高效的cache利用(很多硬件设备,内存访问是主要能耗)。除此之外,计算速度也会更快(通常具有2x-3x的性能提升)。由表2可知,在很多场景下,定点量化操作对精度并不会造成损失。另外,定点量化对神经网络于嵌入式设备上的推断来说是极其重要的。
表2:模型量化前后精度对比
目前,学术界主要将量化分为两大类:Post Training Quantization
和Quantization Aware Training
。Post Training Quantization
是指使用KL散度、滑动平均等方法确定量化参数且不需要重新训练的定点量化方法。Quantization Aware Training
是在训练过程中对量化进行建模以确定量化参数,它与Post Training Quantization
模式相比可以提供更高的预测精度。
1.2 量化原理
1.2.1 量化方式
目前,存在着许多方法可以将浮点数量化成定点数。例如:
r=min(max(x,a),b)r = min(max(x, a), b) r=min(max(x,a),b)
s=b−an−1s = \frac{b - a}{n - 1} s=n−1b−a
q=⌊r−as⌉q = \left \lfloor \frac{r - a}{s} \right \rceil q=⌊sr−a⌉
式中,xxx是待量化的浮点值,[a,b][a, b][a,b]是量化范围,aaa是待量化浮点数中的最小值, bbb 是待量化浮点数中的最大值。⌊⌉\left \lfloor \right \rceil⌊⌉ 表示将结果四舍五入到最近的整数。如果量化级别为kkk,则nnn为2k2^k2k。例如,若kkk为8,则nnn为256。qqq是量化得到的整数。 PaddleSlim框架中选择的量化方法为最大绝对值量化(max-abs
),具体描述如下:
M=max(abs(x))M = max(abs(x)) M=max(abs(x))
q=⌊xM∗(n−1)⌉q = \left \lfloor \frac{x}{M} * (n - 1) \right \rceil q=⌊Mx∗(n−1)⌉
式中,xxx是待被量化的浮点值,MMM是待量化浮点数中的绝对值最大值。⌊⌉\left \lfloor \right \rceil⌊⌉表示将结果四舍五入到最近的整数。对于8bit量化,PaddleSlim采用int8_t
,即n=27=128n=2^7=128n=27=128。qqq是量化得到的整数。 无论是min-max量化
还是max-abs量化
,他们都可以表示为如下形式: q=scale∗r+bq = scale * r + bq=scale∗r+b 其中min-max
和max-abs
被称为量化参数或者量化比例或者量化范围。
1.2.2 量化训练
1.2.2.1 前向传播
前向传播过程采用模拟量化的方式,具体描述如下:
图1:基于模拟量化训练的前向过程
由图1可知,基于模拟量化训练的前向过程可被描述为以下四个部分:
- 输入和权重均被量化成8-bit整数。
- 在8-bit整数上执行矩阵乘法或者卷积操作。
- 反量化矩阵乘法或者卷积操作的输出结果为32-bit浮点型数据。
- 在32-bit浮点型数据上执行偏置加法操作。此处,偏置并未被量化。 对于通用矩阵乘法(
GEMM
),输入XXX和权重WWW的量化操作可被表述为如下过程:
Xq=⌊XXm∗(n−1)⌉X_q = \left \lfloor \frac{X}{X_m} * (n - 1) \right \rceil Xq=⌊XmX∗(n−1)⌉
Wq=⌊WWm∗(n−1)⌉W_q = \left \lfloor \frac{W}{W_m} * (n - 1) \right \rceil Wq=⌊WmW∗(n−1)⌉
执行通用矩阵乘法:
Yq=Xq∗WqY_q = X_q * W_q Yq=Xq∗Wq
对量化乘积结果YqYqYq进行反量化:
Ydq=Yq(n−1)∗(n−1)∗Xm∗Wm=Xq∗Wq(n−1)∗(n−1)∗Xm∗Wm=(Xqn−1∗Xm)∗(Wqn−1∗Wm)Y_{dq} = \frac{Y_q}{(n - 1) * (n - 1)} * X_m * W_m = \frac{X_q * W_q}{(n - 1) * (n - 1)} * X_m * W_m = (\frac{X_q}{n - 1} * X_m) * (\frac{W_q}{n - 1} * W_m) Ydq=(n−1)∗(n−1)Yq∗Xm∗Wm=(n−1)∗(n−1)Xq∗Wq∗Xm∗Wm=(n−1Xq∗Xm)∗(n−1Wq∗Wm)
上述公式表明反量化操作可以被移动到GEMM
之前,即先对XqXqXq和WqWqWq执行反量化操作再做GEMM
操作。因此,前向传播的工作流亦可表示为如下方式:
图2:基于模拟量化训练前向过程的等价工作流
训练过程中,PaddleSlim使用图2中所示的等价工作流。在设计中,量化Pass在IrGraph中插入量化op和反量化op。因为在连续的量化、反量化操作之后输入仍然为32-bit浮点型数据。因此,PaddleSlim量化训练框架所采用的量化方式被称为模拟量化。
1.2.2.2 反向传播
由图3可知,权重更新所需的梯度值可以由量化后的权重和量化后的激活求得。反向传播过程中的所有输入和输出均为32-bit浮点型数据。注意,梯度更新操作需要在原始权重上进行,即计算出的梯度将被加到原始权重上而非量化后或反量化后的权重上。
图3:基于模拟量化训练的反向传播和权重更新过程
因此,量化Pass也会改变相应反向算子的某些输入。
1.2.2.3 确定量化比例系数
存在着两种策略可以计算求取量化比例系数,即动态策略和静态策略。动态策略会在每次迭代过程中计算量化比例系数的值。静态策略则对不同的输入采用相同的量化比例系数。 对于权重而言,在训练过程中采用动态策略。换句话说,在每次迭代过程中量化比例系数均会被重新计算得到直至训练过程结束。 对于激活而言,可以选择动态策略也可以选择静态策略。若选择使用静态策略,则量化比例系数会在训练过程中被评估求得,且在推断过程中被使用(不同的输入均保持不变)。静态策略中的量化比例系数可于训练过程中通过如下三种方式进行评估:
-
在一个窗口中计算激活最大绝对值的平均值。
-
在一个窗口中计算激活最大绝对值的最大值。
-
在一个窗口中计算激活最大绝对值的滑动平均值,计算公式如下:
Vt=(1−k)∗V+k∗Vt−1Vt = (1 - k) * V + k * V_{t-1} Vt=(1−k)∗V+k∗Vt−1
式中,VVV 是当前batch的最大绝对值, VtVtVt是滑动平均值。kkk是一个因子,例如其值可取为0.9。
1.2.4 训练后量化
训练后量化是基于采样数据,采用KL散度等方法计算量化比例因子的方法。相比量化训练,训练后量化不需要重新训练,可以快速得到量化模型。
训练后量化的目标是求取量化比例因子,主要有两种方法:非饱和量化方法 ( No Saturation) 和饱和量化方法 (Saturation)。非饱和量化方法计算FP32类型Tensor中绝对值的最大值abs_max
,将其映射为127,则量化比例因子等于abs_max/127
。饱和量化方法使用KL散度计算一个合适的阈值T
(0<T<mab_max
),将其映射为127,则量化比例因子等于T/127
。一般而言,对于待量化op的权重Tensor,采用非饱和量化方法,对于待量化op的激活Tensor(包括输入和输出),采用饱和量化方法 。
训练后量化的实现步骤如下:
- 加载预训练的FP32模型,配置
DataLoader
; - 读取样本数据,执行模型的前向推理,保存待量化op激活Tensor的数值;
- 基于激活Tensor的采样数据,使用饱和量化方法计算它的量化比例因子;
- 模型权重Tensor数据一直保持不变,使用非饱和方法计算它每个通道的绝对值最大值,作为每个通道的量化比例因子;
- 将FP32模型转成INT8模型,进行保存。
参考链接
YOLOv3
论文原文:https://arxiv.org/pdf/1804.02767v1.pdf
参考链接:https://blog.csdn.net/litt1e/article/details/88907542
PaddleSlim
代码结构
code/
├── model
├── pretrained_model 预训练模型
├── YOLOv3.py 以darknet53为主干网络的yolov3(原版yolov3)
├── reader.py 数据读取脚本
├── train.py 量化训练脚本
├── infer.py 预测脚本
├── config.py 配置参数脚本
├── compress.yaml 量化训练参数配置脚本
├── train_list.txt 训练数据列表
├── val_list.txt 验证数据列表
├── label_list label列表
量化训练参数配置脚本说明:compress.yaml
version: 1.0
strategies:
quantization_strategy:
class: 'QuantizationStrategy'
start_epoch: 0
end_epoch: 4
float_model_save_path: './output/yolov3/float'
mobile_model_save_path: './output/yolov3/mobile'
int8_model_save_path: './output/yolov3/int8'
weight_bits: 8
activation_bits: 8
weight_quantize_type: 'abs_max'
activation_quantize_type: 'abs_max'
save_in_nodes: ['img', 'image_shape']
save_out_nodes: ['multiclass_nms.tmp_0']
compressor:
epoch: 5
checkpoint_path: './checkpoints/yolov3/'
strategies:
- quantization_strategy
可配置的参数:
- class: 量化策略的类名称,目前仅支持QuantizationStrategy
- start_epoch: 在start_epoch开始之前,量化训练策略会往train_program和eval_program插入量化operators和反量化operators. 从start_epoch开始,进入量化训练阶段。
- end_epoch: 在end_epoch结束之后,会保存用户指定格式的模型。注意:end_epoch之后并不会停止量化训练,而是继续训练到compressor.epoch为止。
- float_model_save_path: 保存float数据格式模型的路径。模型weight的实际大小在int8可表示范围内,但是是以float格式存储的。如果设置为None, 则不存储float格式的模型。默认为None.
- int8_model_save_path: 保存int8数据格式模型的路径。如果设置为None, 则不存储int8格式的模型。默认为None.
- mobile_model_save_path: 保存兼容paddle-mobile框架的模型的路径。如果设置为None, 则不存储mobile格式的模型。默认为None.
- weight_bits: 量化weight的bit数,bias不会被量化。
- activation_bits: 量化activation的bit数。
- weight_quantize_type: 对于weight的量化方式,目前支持'abs_max', 'channel_wise_abs_max'.
- activation_quantize_type: 对activation的量化方法,目前可选abs_max或range_abs_max。abs_max意为在训练的每个step和inference阶段动态的计算量化范围。range_abs_max意为在训练阶段计算出一个静态的范围,并将其用于inference阶段。
- save_in_nodes: variable名称列表。在保存量化后模型的时候,需要根据save_in_nodes对eval programg 网络进行前向遍历剪枝。默认为eval_feed_list内指定的variable的名称列表。
- save_out_nodes: varibale名称列表。在保存量化后模型的时候,需要根据save_out_nodes对eval programg 网络进行回溯剪枝。默认为eval_fetch_list内指定的variable的名称列表。
数据集
本项目采用Pascal VOC数据集,数据集具体介绍可以参考https://blog.csdn.net/u013832707/article/details/80060327
本项目根据实际需要,将VOC数据集的Annotation文件做了进一步整理,写入在code/train_list.txt, code/val_list.txt方便reader调用
#解压数据集,并将voc数据集的训练和验证文件拷贝到相应位置
!cd data/data4379/ && unzip -qo pascalvoc.zip
!cd data/data4379/pascalvoc/ && rm label_list
!cp code/train_list.txt data/data4379/pascalvoc/
!cp code/val_list.txt data/data4379/pascalvoc/
!cp code/label_list data/data4379/pascalvoc/
#模型训练
!cd code && python train.py
#模型预测
!cd code && python infer.py
######检测结果可视化
%matplotlib inline
import os
import matplotlib.pyplot as plt
import numpy as np
import cv2
if os.path.exists('code/result.jpg'):
detect_img= cv2.imread('code/result.jpg')
plt.imshow(detect_img)
plt.show()
W0323 16:50:33.122778 298 device_context.cc:236] Please NOTE: device: 0, CUDA Capability: 70, Driver API Version: 9.2, Runtime API Version: 9.0 W0323 16:50:33.126850 298 device_context.cc:244] device: 0, cuDNN Version: 7.3. predict cost time:44.95 ms 检测到目标 检测结果保存为result.jpg infer one picture cost 44.952392578125 ms
使用AI Studio一键上手实践项目吧:https://aistudio.baidu.com/aistudio/projectdetail/316757