7. 量化与量化调优
神经网络在大规模部署时候,往往对吞吐量也就是推理时间有较高要求,硬件也专门对低比特计算进行了优化,其算力更加突出。所以以尽量高的精度进行低比特量化就显得尤为重要。 但是要保持高精度和高吞吐率,网络往往需要以混合精度方式运行,即大部分算子以低比特定点计算,少部分以浮点进行计算。如何决定哪些算子使用浮点往往与网络和网络权重有直接关系,需要根据网络特点来选择。
TPU-MLIR所采用的混合精度方式为搜索网络中不适于低比特量化的层生成 quantize_table
,用以在 model_deploy
阶段指定这些层采用较高比特的量化方式。本章会对当前TPU-MLIR已有的 quantize_table
自动生成工具使用方式进行介绍。
7.1. 1. run_qtable
本节以检测网络 yolov3 tiny
网络模型为例, 介绍如何使用run_qtable进行混精度。
本节需要安装tpu_mlir。
7.1.1. 安装tpu-mlir
$ pip install tpu_mlir[all]
# or
$ pip install tpu_mlir-*-py3-none-any.whl[all]
7.1.2. 准备工作目录
请从Github的 Assets 处下载 tpu-mlir-resource.tar
并解压,解压后将文件夹重命名为 tpu_mlir_resource
:
$ tar -xvf tpu-mlir-resource.tar
$ mv regression/ tpu-mlir-resource/
建立 yolov3_tiny
目录, 并把模型文件和图片文件都放入 yolov3_tiny
目录中。
操作如下:
1 $ mkdir yolov3_tiny && cd yolov3_tiny
2 $ wget https://media.githubusercontent.com/media/onnx/models/main/validated/vision/object_detection_segmentation/tiny-yolov3/model/tiny-yolov3-11.onnx
3 $ cp -rf tpu_mlir_resource/dataset/COCO2017 .
4 $ mkdir workspace && cd workspace
注意如果 tiny-yolov3-11.onnx
用wget下载失败, 请用其他方式下载后放到 yolov3_tiny
目录。
7.1.3. 验证原始模型
detect_yolov3
是已经写好的验证命令, 可以用来对 yolov3_tiny
网络进行验证。执行过程如下:
$ detect_yolov3 \
--model ../tiny-yolov3-11.onnx \
--input ../COCO2017/000000366711.jpg \
--output yolov3_onnx.jpg
执行完后打印检测到的结果如下:
person:60.7%
orange:77.5%
并得到图片 yolov3_onnx.jpg
, 如下 ( yolov3_tiny ONNX执行效果 ):
7.1.4. 转成INT8对称量化模型
如前面章节介绍的转模型方法, 这里不做参数说明, 只有操作过程。
7.1.4.1. 第一步: 转成F32 mlir
$ model_transform \
--model_name yolov3_tiny \
--model_def ../tiny-yolov3-11.onnx \
--input_shapes [[1,3,416,416]] \
--scale 0.0039216,0.0039216,0.0039216 \
--pixel_format rgb \
--keep_aspect_ratio \
--pad_value 128 \
--output_names=convolution_output1,convolution_output \
--mlir yolov3_tiny.mlir
7.1.4.2. 第二步: 生成calibartion table
$ run_calibration yolov3_tiny.mlir \
--dataset ../COCO2017 \
--input_num 100 \
-o yolov3_cali_table
7.1.4.3. 第三步: 转对称量化模型
$ model_deploy \
--mlir yolov3_tiny.mlir \
--quantize INT8 \
--calibration_table yolov3_cali_table \
--processor bm1684x \
--model yolov3_int8.bmodel
7.1.4.4. 第四步: 验证模型
$ detect_yolov3 \
--model yolov3_int8.bmodel \
--input ../COCO2017/000000366711.jpg \
--output yolov3_int8.jpg
执行完后有如下打印信息,表示检测到一个目标:
orange:72.9%
得到图片 yolov3_int8.jpg
, 如下 ( yolov3_tiny int8对称量化执行效果 ):
可以看出int8对称量化模型相对原始模型, 在这张图上效果不佳,只检测到一个目标。
7.1.5. 转成混精度量化模型
在转int8对称量化模型的基础上, 执行如下步骤。
7.1.5.1. 第一步: 生成混精度量化表
使用 run_qtable
生成混精度量化表, 相关参数说明如下:
参数名 |
必选? |
说明 |
---|---|---|
无 |
是 |
指定mlir文件 |
dataset |
否 |
指定输入样本的目录, 该路径放对应的图片, 或npz, 或npy |
data_list |
否 |
指定样本列表, 与dataset必须二选一 |
calibration_table |
是 |
输入校准表 |
processor |
是 |
指定模型将要用到的平台, 支持bm1690, bm1688, bm1684x, bm1684, cv186x, cv183x, cv182x, cv181x, cv180x |
fp_type |
否 |
指定混精度使用的float类型, 支持auto,F16,F32,BF16,默认为auto,表示由程序内部自动选择 |
input_num |
否 |
指定输入样本数量, 默认用10个 |
expected_cos |
否 |
指定期望网络最终输出层的最小cos值,一般默认为0.99即可,越小时可能会设置更多层为浮点计算 |
min_layer_cos |
否 |
指定期望每层输出cos的最小值,低于该值会尝试设置浮点计算, 一般默认为0.99即可 |
debug_cmd |
否 |
指定调试命令字符串,开发使用, 默认为空 |
o |
是 |
输出混精度量化表 |
global_compare_layers |
否 |
指定用于替换最终输出层的层,并用于全局比较,例如: |
fp_type |
否 |
指定混合精度的浮点类型 |
loss_table |
否 |
指定保存所有被量化成浮点类型的层的损失值的文件名,默认为 |
本例中采用默认10张图片校准, 需要首先安装 Graphviz 工具:
$ sudo apt-get install graphviz
然后执行如下命令(对于CV18xx系列的处理器,将processor设置为对应的名称即可):
$ run_qtable yolov3_tiny.mlir \
--dataset ../COCO2017 \
--calibration_table yolov3_cali_table \
--processor bm1684x \
--min_layer_cos 0.999 \
--expected_cos 0.9999 \
-o yolov3_qtable
若 --min_layer_cos
使用默认的0.99,程序会检测到原始int8模型已满足0.99的cos,从而直接不再搜索。执行完后最后输出如下打印:
int8 outputs_cos:0.999115 old
mix model outputs_cos:0.999517
Output mix quantization table to yolov3_qtable
total time:44 second
上面int8 outputs_cos表示int8模型原本网络输出和fp32的cos相似度,mix model outputs_cos表示部分层使用混精度后网络输出的cos相似度,total time表示搜索时间为44秒,
另外,生成的混精度量化表 yolov3_qtable
, 内容如下:
# op_name quantize_mode
model_1/leaky_re_lu_2/LeakyRelu:0_pooling0_MaxPool F16
convolution_output10_Conv F16
model_1/leaky_re_lu_3/LeakyRelu:0_LeakyRelu F16
model_1/leaky_re_lu_3/LeakyRelu:0_pooling0_MaxPool F16
model_1/leaky_re_lu_4/LeakyRelu:0_LeakyRelu F16
model_1/leaky_re_lu_4/LeakyRelu:0_pooling0_MaxPool F16
model_1/leaky_re_lu_5/LeakyRelu:0_LeakyRelu F16
model_1/leaky_re_lu_5/LeakyRelu:0_pooling0_MaxPool F16
model_1/concatenate_1/concat:0_Concat F16
该表中, 第一列表示相应的layer, 第二列表示类型, 支持的类型有F32/F16/BF16/INT8。
另外同时也会生成一个loss表文件 full_loss_table.txt
, 内容如下:
1# platform: bm1684x mix_mode: F16
2###
3No.0 : Layer: model_1/leaky_re_lu_3/LeakyRelu:0_LeakyRelu Cos: 0.994022
4No.1 : Layer: model_1/leaky_re_lu_5/LeakyRelu:0_LeakyRelu Cos: 0.997445
5No.2 : Layer: model_1/leaky_re_lu_2/LeakyRelu:0_LeakyRelu Cos: 0.997487
6No.3 : Layer: model_1/leaky_re_lu_4/LeakyRelu:0_LeakyRelu Cos: 0.997978
7No.4 : Layer: model_1/leaky_re_lu_2/LeakyRelu:0_pooling0_MaxPool Cos: 0.998159
8No.5 : Layer: convolution_output11_Conv Cos: 0.998307
9No.6 : Layer: model_1/leaky_re_lu_1/LeakyRelu:0_LeakyRelu Cos: 0.999249
10No.7 : Layer: convolution_output9_Conv Cos: 0.999292
11No.8 : Layer: convolution_output8_Conv Cos: 0.999427
12No.9 : Layer: model_1/leaky_re_lu_1/LeakyRelu:0_pooling0_MaxPool Cos: 0.999580
13No.10 : Layer: convolution_output12_Conv Cos: 1.000004
该表按cos从小到大顺利排列, 表示该层的前驱Layer根据各自的cos已换成相应的浮点模式后, 该层计算得到的cos, 若该cos仍小于前面min_layer_cos参数,则会将该层及直接后继层设置为浮点计算。
run_qtable
会在每次设置某相邻2层为浮点计算后,接续计算整个网络的输出cos,若该cos大于指定的expected_cos,则退出搜素。因此,若设置更大的expected_cos,会尝试将更多层设为浮点计算
7.1.5.2. 第二步: 生成混精度量化模型
$ model_deploy \
--mlir yolov3_tiny.mlir \
--quantize INT8 \
--quantize_table yolov3_qtable \
--calibration_table yolov3_cali_table \
--processor bm1684x \
--model yolov3_mix.bmodel
7.1.5.3. 第三步: 验证混精度模型
$ detect_yolov3 \
--model yolov3_mix.bmodel \
--input ../COCO2017/000000366711.jpg \
--output yolov3_mix.jpg
执行完后打印结果为:
person:63.9%
orange:72.9%
得到图片yolov3_mix.jpg, 如下 ( yolov3_tiny 混精度对称量化执行效果 ):
可以看出混精度后, 检测结果更接近原始模型的结果。
需要说明的是,除了使用run_qtable生成量化表外,也可根据模型中每一层的相似度对比结果,自行设置量化表中需要做混精度量化的OP的名称和量化类型。
7.2. 2. run_sensitive_layer
本节以检测网络 mobilenet-v2
网络模型为例, 介绍如何使用敏感层搜索。
本节需要安装tpu_mlir。
7.2.1. 安装tpu-mlir
$ pip install tpu_mlir[all]
# or
$ pip install tpu_mlir-*-py3-none-any.whl[all]
7.2.2. 准备工作目录
请从Github的 Assets 处下载 tpu-mlir-resource.tar
并解压,解压后将文件夹重命名为 tpu_mlir_resource
:
$ tar -xvf tpu-mlir-resource.tar
$ mv regression/ tpu-mlir-resource/
建立 mobilenet-v2
目录, 并把模型文件和图片文件都放入 mobilenet-v2
目录中。
操作如下:
1 $ mkdir mobilenet-v2 && cd mobilenet-v2
2 $ wget https://github.com/sophgo/tpu-mlir/releases/download/v1.4-beta.0/mobilenet_v2.pt
3 $ cp -rf tpu_mlir_resource/dataset/ILSVRC2012 .
4 $ mkdir workspace && cd workspace
7.2.3. 测试Float和INT8对称量化模型分类效果
如前面章节介绍的转模型方法, 这里不做参数说明, 只有操作过程。
7.2.3.1. 第一步: 转成FP32 mlir
$ model_transform \
--model_name mobilenet_v2 \
--model_def ../mobilenet_v2.pt \
--input_shapes [[1,3,224,224]] \
--resize_dims 256,256 \
--mean 123.675,116.28,103.53 \
--scale 0.0171,0.0175,0.0174 \
--pixel_format rgb \
--mlir mobilenet_v2.mlir
7.2.3.2. 第二步: 生成calibartion table
$ run_calibration mobilenet_v2.mlir \
--dataset ../ILSVRC2012 \
--input_num 100 \
-o mobilenet_v2_cali_table
7.2.3.3. 第三步: 转FP32 bmodel
$ model_deploy \
--mlir mobilenet_v2.mlir \
--quantize F32 \
--processor bm1684 \
--model mobilenet_v2_bm1684_f32.bmodel
7.2.3.4. 第四步: 转对称量化模型
$ model_deploy \
--mlir mobilenet_v2.mlir \
--quantize INT8 \
--processor bm1684 \
--calibration_table mobilenet_v2_cali_table \
--model mobilenet_v2_bm1684_int8_sym.bmodel
7.2.3.5. 第五步: 验证FP32模型和INT8对称量化模型
classify_mobilenet_v2是已经写好的验证程序,可以用来对mobilenet_v2网络进行验证。执行过程如下,FP32模型:
$ classify_mobilenet_v2 \
--model_def mobilenet_v2_bm1684_f32.bmodel \
--input ../ILSVRC2012/n01440764_9572.JPEG \
--output mobilenet_v2_fp32_bmodel.JPEG \
--category_file ../ILSVRC2012/synset_words.txt
在输出结果图片上可以看到如下分类信息,正确结果tench排在第一名:
Top-5
n01440764 tench, Tinca tinca
n02536864 coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch
n02422106 hartebeest
n02749479 assault rifle, assault gun
n02916936 bulletproof vest
INT8对称量化模型:
$ classify_mobilenet_v2 \
--model_def mobilenet_v2_bm1684_int8_sym.bmodel \
--input ../ILSVRC2012/n01440764_9572.JPEG \
--output mobilenet_v2_INT8_sym_bmodel.JPEG \
--category_file ../ILSVRC2012/synset_words.txt
在输出结果图片上可以看到如下分类信息,正确结果tench排在第一名:
Top-5
n01440764 tench, Tinca tinca
n02749479 assault 日file, assau
n02536864 coho, cohoe, coho
n02916936 bulletproof vest
n04336792 stretcher
7.2.4. 转成混精度量化模型
在转int8对称量化模型的基础上, 执行如下步骤。
7.2.4.1. 第一步: 进行敏感层搜索
使用 run_sensitive_layer
搜索损失较大的layer,注意尽量使用bad cases进行敏感层搜索,相关参数说明如下:
参数名 |
必选? |
说明 |
---|---|---|
无 |
是 |
指定mlir文件 |
dataset |
否 |
指定输入样本的目录, 该路径放对应的图片, 或npz, 或npy |
data_list |
否 |
指定样本列表, 与dataset必须二选一 |
calibration_table |
是 |
输入校准表 |
processor |
是 |
指定模型将要用到的平台, 支持bm1690, bm1688, bm1684x, bm1684, cv186x, cv183x, cv182x, cv181x, cv180x |
fp_type |
否 |
指定混精度使用的float类型, 支持auto,F16,F32,BF16,默认为auto,表示由程序内部自动选择 |
input_num |
否 |
指定用于量化的输入样本数量, 默认用10个 |
inference_num |
否 |
指定用于推理的输入样本数量, 默认用10个 |
max_float_layers |
否 |
指定用于生成qtable的op数量, 默认用5个 |
tune_list |
否 |
指定用于调整threshold的样本路径 |
tune_num |
否 |
指定用于调整threshold的样本数量,默认为5 |
histogram_bin_num |
否 |
指定用于kld方法中使用的bin数量,默认为2048 |
post_process |
否 |
用户自定义后处理文件路径, 默认为空 |
expected_cos |
否 |
指定期望网络最终输出层的最小cos值,一般默认为0.99即可,越小时可能会设置更多层为浮点计算 |
debug_cmd |
否 |
指定调试命令字符串,开发使用, 默认为空 |
o |
是 |
输出混精度量化表 |
global_compare_layers |
否 |
指定用于替换最终输出层的层,并用于全局比较,例如: |
fp_type |
否 |
指定混合精度的浮点类型 |
敏感层搜索支持用户自定义的后处理方法 post_process_func.py
,可以放在当前工程目录下,也可以放在其他位置,如果放在其他位置需要在 post_process
中指明文件的完整路径。后处理方法函数名称需要定义为 PostProcess
,输入数据为网络的输出,输出数据为后处理结果。创建 post_process_func.py
文件,其示例内容如下:
def PostProcess(data):
print("in post process")
return data
本例中采用100张图片做量化, 30张图片做推理,执行命令如下:
$ run_sensitive_layer mobilenet_v2.mlir \
--dataset ../ILSVRC2012 \
--input_num 100 \
--inference_num 30 \
--calibration_table mobilenet_v2_cali_table \
--processor bm1684 \
--post_process post_process_func.py \
-o mobilenet_v2_qtable
执行完后最后输出如下打印:
the layer input3.1 is 0 sensitive layer, loss is 0.008808857469573828, type is top.Conv
the layer input11.1 is 1 sensitive layer, loss is 0.0016958347875666302, type is top.Conv
the layer input128.1 is 2 sensitive layer, loss is 0.0015641432811860367, type is top.Conv
the layer input130.1 is 3 sensitive layer, loss is 0.0014325751094084183, type is top.Scale
the layer input127.1 is 4 sensitive layer, loss is 0.0011817314259702227, type is top.Add
the layer input13.1 is 5 sensitive layer, loss is 0.001018420214596527, type is top.Scale
the layer 787 is 6 sensitive layer, loss is 0.0008603856180608993, type is top.Scale
the layer input2.1 is 7 sensitive layer, loss is 0.0007558935451825732, type is top.Scale
the layer input119.1 is 8 sensitive layer, loss is 0.000727441637624282, type is top.Add
the layer input0.1 is 9 sensitive layer, loss is 0.0007138056757098887, type is top.Conv
the layer input110.1 is 10 sensitive layer, loss is 0.000662179506136229, type is top.Conv
......
run result:
int8 outputs_cos:0.978803 old
mix model outputs_cos:0.989258
Output mix quantization table to mobilenet_v2_qtable
total time:402.15848112106323
success sensitive layer search
上面int8 outputs_cos表示int8模型原本网络输出和fp32的cos相似度,mix model outputs_cos表示前五个敏感层使用混精度后网络输出的cos相似度,total time表示搜索时间为402秒,
另外,生成的混精度量化表 mobilenet_v2_qtable
, 内容如下:
# op_name quantize_mode
input3.1 F32
input11.1 F32
input128.1 F32
input130.1 F32
input127.1 F32
该表中, 第一列表示相应的layer, 第二列表示类型, 支持的类型有F32/F16/BF16/INT8。
与此同时,也会生成一个log日志文件 SensitiveLayerSearch
, 内容如下:
1INFO:root:start to handle layer: input3.1, type: top.Conv
2INFO:root:adjust layer input3.1 th, with method MAX, and threshlod 5.5119305
3INFO:root:run int8 mode: mobilenet_v2.mlir
4INFO:root:outputs_cos_los = 0.014830573787862011
5INFO:root:adjust layer input3.1 th, with method Percentile9999, and threshlod 4.1202815
6INFO:root:run int8 mode: mobilenet_v2.mlir
7INFO:root:outputs_cos_los = 0.011843443367980822
8INFO:root:adjust layer input3.1 th, with method KL, and threshlod 2.6186381997094728
9INFO:root:run int8 mode: mobilenet_v2.mlir
10INFO:root:outputs_cos_los = 0.008808857469573828
11INFO:root:layer input3.1, layer type is top.Conv, best_th = 2.6186381997094728, best_method = KL, best_cos_loss = 0.008808857469573828
日志文件记录了每个Op在不同量化方法(MAX/Percentile9999/KL)下得到的threshold,同 时给出了在只对该Op使用对应threshold做int8计算后的混精度模型与原始float模型输出的相似度的loss(1-余弦相似度)。 此外,日志还包含了屏幕端输出的每个op的loss信息以及最后的混精度模型与原始float模型的余弦相似度。 用户可以使用程序输出的qtable,也可以根据loss信息对qtable进行修改,然后生成混精度模型。 在敏感层搜索结束后,最优的threshold会被更新到一个新的量化表new_cali_table.txt, 该量化表存储在当前工程目录下,在生成混精度模型时需要调用新量化表。 在本例中,根据输出的loss信息,观察到input3.1的loss比其他op高很多,可以在qtable中只设置input3.1为FP32。
7.2.4.2. 第二步: 生成混精度量化模型
$ model_deploy \
--mlir mobilenet_v2.mlir \
--quantize INT8 \
--processor bm1684 \
--calibration_table new_cali_table.txt \
--quantize_table mobilenet_v2_qtable \
--model mobilenet_v2_bm1684_mix.bmodel
7.2.4.3. 第三步: 验证混精度模型
$ classify_mobilenet_v2 \
--model_def mobilenet_v2_bm1684_mix.bmodel \
--input ../ILSVRC2012/n01440764_9572.JPEG \
--output mobilenet_v2_INT8_sym_bmodel.JPEG \
--category_file ../ILSVRC2012/synset_words.txt
在输出结果图片上可以看到如下分类信息,可以看出混精度后, 正确结果tench排到了第一名。
Top-5
n01440764 tench, Tinca tinca
n02749479 assault rifle, assault gun
n02916936 bulletproof vest
n02536864 coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch
n04090263 rifle
7.3. 3. fp_forward
对于特定网络,部分层由于数据分布差异大,量化成INT8会大幅降低模型精度,使用局部不量化功能,可以一键将部分层之前、之后、之间添加到混精度表中,在生成混精度模型时,这部分层将不被量化。
7.3.1. 使用方法
本节将沿用第三章提到的yolov5s网络的例子,介绍如何使用局部不量化功能,快速生成混精度模型。
生成FP32和INT8模型的过程与第三章相同,下面仅介绍精度测试方案与混精度流程。
对于yolo系列模型来说,最后三个卷积层由于数据分布差异较大,常常手动添加混精度表以提升精度。使用局部不量化功能,从 model_transform
生成的Top层 mlir文件搜索到对应的层。快速添加混精度表。
$ fp_forward \
yolov5s.mlir \
--quantize INT8 \
--processor bm1684x \
--fpfwd_outputs 474_Conv,326_Conv,622_Conv\
-o yolov5s_qtable
点开yolov5s_qtable可以看见相关层都被加入到qtable中。
生成混精度模型
$ model_deploy \
--mlir yolov5s.mlir \
--quantize INT8 \
--calibration_table yolov5s_cali_table \
--quantize_table yolov5s_qtable\
--processor bm1684x \
--test_input yolov5s_in_f32.npz \
--test_reference yolov5s_top_outputs.npz \
--tolerance 0.85,0.45 \
--model yolov5s_1684x_mix.bmodel
验证FP32模型和混精度模型的精度 model-zoo中有对目标检测模型进行精度验证的程序yolo,可以在mlir.config.yaml中使用harness字段调用yolo:
相关字段修改如下
$ dataset:
imagedir: $(coco2017_val_set)
anno: $(coco2017_anno)/instances_val2017.json
harness:
type: yolo
args:
- name: FP32
bmodel: $(workdir)/$(name)_bm1684_f32.bmodel
- name: INT8
bmodel: $(workdir)/$(name)_bm1684_int8_sym.bmodel
- name: mix
bmodel: $(workdir)/$(name)_bm1684_mix.bmodel
切换到model-zoo顶层目录,使用tpu_perf.precision_benchmark进行精度测试,命令如下:
$ python3 -m tpu_perf.precision_benchmark yolov5s_path --mlir --target BM1684X --devices 0
执行完后,精度测试的结果存放在output/yolo.csv中:
FP32模型mAP为: 37.14%
INT8模型mAP为: 34.70%
混精度模型mAP为: 36.18%
在yolov5以外的检测模型上,使用混精度的方式常会有更明显的效果。
7.3.2. 参数说明
参数名 |
必选? |
说明 |
---|---|---|
无 |
是 |
指定mlir文件 |
processor |
是 |
指定模型将要用到的平台,支持bm1690, bm1688, bm1684x, bm1684, cv186x, cv183x, cv182x, cv181x, cv180x |
fpfwd_inputs |
否 |
指定层(包含本层)之前不执行量化,多输入用 |
fpfwd_outputs |
否 |
指定层(包含本层)之后不执行量化,多输入用 |
fpfwd_blocks |
否 |
指定起点和终点之间的层不执行量化,起点和终点之间用 |
fp_type |
否 |
指定混精度使用的float类型, 支持auto,F16,F32,BF16,默认为auto,表示由程序内部自动选择 |
o |
是 |
输出混精度量化表 |