模型量化-有校准数据训练后量化

本文首先简单介绍有校准数据训练后量化,然后说明产出量化模型、量化模型预测,最后给出一个使用示例。 如果想快速上手,大家可以先参考使用示例,再查看详细使用方法。

1 简介

有校准数据训练后量化,使用少量校准数据计算量化因子,可以快速得到量化模型。使用该量化模型进行预测,可以减少计算量、降低计算内存、减小模型大小。

有校准数据训练后量化中,有两种计算量化因子的方法,非饱和量化方法和饱和量化方法。非饱和量化方法计算整个Tensor的绝对值最大值abs_max,将其映射为127。饱和量化方法使用KL散度计算一个合适的阈值T (0<T<mab_max),将其映射为127。一般而言,待量化Op的权重采用非饱和量化方法,待量化Op的激活(输入和输出)采用饱和量化方法 。

使用条件:

  • 有训练好的预测模型
  • 有少量校准数据,比如100~500张图片

使用步骤:

  • 产出量化模型:使用PaddlePaddle或者PaddleSlim调用有校准数据训练后量化接口,产出量化模型
  • 量化模型预测:使用PaddleLite加载量化模型进行预测推理

优点:

  • 减小计算量、降低计算内存、减小模型大小
  • 不需要大量训练数据
  • 快速产出量化模型,简单易用

缺点:

  • 对少部分的模型,尤其是计算量小、精简的模型,量化后精度可能会受到影响

2 产出量化模型

大家可以使用PaddlePaddle或者PaddleSlim调用有校准数据训练后量化接口,得到量化模型。本文主要介绍使用PaddlePaddle产出量化模型,使用PaddleSlim可以参考文档

2.1 安装PaddlePaddle

参考PaddlePaddle官网,安装PaddlePaddle CPU/GPU 1.7版本。

2.2 准备模型和校准数据

准备已经训练好的FP32预测模型,即 save_inference_model() 保存的模型。 准备校准数据集,校准数据集应该是测试集/训练集中随机挑选的一部分,量化因子才会更加准确。对常见的视觉模型,建议校准数据的数量为100~500张图片。

2.3 配置校准数据生成器

有校准数据训练后量化内部使用异步数据读取的方式读取校准数据,大家只需要根据模型的输入,配置读取数据的sample_generator。sample_generator是Python生成器,必须每次返回单个样本数据,会用作DataLoader.set_sample_generator()的数据源。 建议参考异步数据读取文档和本文示例,学习如何配置校准数据生成器。

2.4 调用有校准数据训练后量化

对于调用有校准数据训练后量化,首先给出一个例子,让大家有个直观了解。

  1. import paddle.fluid as fluid
  2. from paddle.fluid.contrib.slim.quantization import PostTrainingQuantization
  3. exe = fluid.Executor(fluid.CPUPlace())
  4. model_dir = path/to/fp32_model_params
  5. # set model_filename as None when the filename is __model__,
  6. # otherwise set it as the real filename
  7. model_filename = None
  8. # set params_filename as None when all parameters were saved in
  9. # separate files, otherwise set it as the real filename
  10. params_filename = None
  11. save_model_path = path/to/save_model_path
  12. # prepare the sample generator according to the model, and the
  13. # sample generator must return a sample every time. The reference
  14. # document: https://www.paddlepaddle.org.cn/documentation/docs/zh
  15. # /user_guides/howto/prepare_data/use_py_reader.html
  16. sample_generator = your_sample_generator
  17. batch_size = 10
  18. batch_nums = 10
  19. algo = "KL"
  20. quantizable_op_type = ["conv2d", "depthwise_conv2d", "mul"]
  21. ptq = PostTrainingQuantization(
  22. executor=exe,
  23. sample_generator=sample_generator,
  24. model_dir=model_dir,
  25. model_filename=model_filename,
  26. params_filename=params_filename,
  27. batch_size=batch_size,
  28. batch_nums=batch_nums,
  29. algo=algo,
  30. quantizable_op_type=quantizable_op_type)
  31. ptq.quantize()
  32. ptq.save_quantized_model(save_model_path)

对于调用有校准数据训练后量化,以下对接口进行详细介绍。

  1. class PostTrainingQuantization(
  2. executor=None,
  3. scope=None,
  4. model_dir=None,
  5. model_filename=None,
  6. params_filename=None,
  7. sample_generator=None,
  8. batch_size=10,
  9. batch_nums=None,
  10. algo="KL",
  11. quantizable_op_type=["conv2d", "depthwise_conv2d", "mul"],
  12. is_full_quantize=False,
  13. weight_bits=8,
  14. activation_bits=8,
  15. is_use_cache_file=False,
  16. cache_dir="./temp_post_training"):

调用上述api,传入必要的参数。参数说明如下:

  • executor(fluid.Executor):执行模型的executor,可以指定在cpu或者gpu上执行。
  • scope(fluid.Scope, optional):模型运行时使用的scope,默认为None,则会使用global_scope()。行首有optional,说明用户可以不设置该输入参数,直接使用默认值,下同。
  • model_dir(str):待量化模型的路径,其中保存模型文件和权重文件。
  • model_filename(str, optional):待量化模型的模型文件名,如果模型文件名不是__model__,则需要使用model_filename设置模型文件名。
  • params_filename(str, optional):待量化模型的权重文件名,如果所有权重保存成一个文件,则需要使用params_filename设置权重文件名。
  • sample_generator(Python Generator):配置的校准数据生成器。
  • batch_size(int, optional):一次读取校准数据的数量。
  • batch_nums(int, optional):读取校准数据的次数。如果设置为None,则从sample_generator中读取所有校准数据进行训练后量化;如果设置为非None,则从sample_generator中读取batch_size*batch_nums个校准数据。
  • algo(str, optional):计算待量化激活Tensor的量化因子的方法。设置为KL,则使用饱和量化方法,设置为direct,则使用非饱和量化方法。默认为KL
  • quantizable_op_type(list[str], optional): 需要量化的op类型,默认是["conv2d", "depthwise_conv2d", "mul"],列表中的值可以是任意支持量化的op类型。
  • is_full_quantize(bool, optional):是否进行全量化。设置为True,则对模型中所有支持量化的op进行量化;设置为False,则只对quantizable_op_type 中op类型进行量化。目前支持的量化类型如下:’conv2d’, ‘depthwise_conv2d’, ‘mul’, “pool2d”, “elementwise_add”, “concat”, “softmax”, “argmax”, “transpose”, “equal”, “gather”, “greater_equal”, “greater_than”, “less_equal”, “less_than”, “mean”, “not_equal”, “reshape”, “reshape2”, “bilinear_interp”, “nearest_interp”, “trilinear_interp”, “slice”, “squeeze”, “elementwise_sub”。
  • weight_bits(int, optional):权重量化的比特数,可以设置为1~16。PaddleLite目前仅支持加载权重量化为8bit的量化模型。
  • activation_bits(int, optional): 激活量化的比特数,可以设置为1~16。PaddleLite目前仅支持加载激活量化为8bit的量化模型。
  • is_use_cache_file(bool, optional):是否使用缓存文件。如果设置为True,训练后量化过程中的采样数据会保存到磁盘文件中;如果设置为False,所有采样数据会保存到内存中。当待量化的模型很大或者校准数据数量很大,建议设置is_use_cache_file为True。默认为False。
  • cache_dir(str, optional):当is_use_cache_file等于True,会将采样数据保存到该文件中。量化完成后,该文件中的临时文件会自动删除。
  1. PostTrainingQuantization.quantize()

调用上述接口开始训练后量化。根据校准数据数量、模型的大小和量化op类型不同,训练后量化需要的时间也不一样。比如使用ImageNet2012数据集中100图片对MobileNetV1进行训练后量化,花费大概1分钟。

  1. PostTrainingQuantization.save_quantized_model(save_model_path)

调用上述接口保存训练后量化模型,其中save_model_path为保存的路径。

训练后量化支持部分量化功能:

  • 方法1:设置quantizable_op_type,则只会对quantizable_op_type中的Op类型进行量化,模型中其他Op类型保持不量化。
  • 方法2:构建网络的时候,将不需要量化的特定Op定义在 skip_quant 的name_scope中,则可以跳过特定Op的量化,示例如下。
  1. with fluid.name_scope('skip_quant'):
  2. pool = fluid.layers.pool2d(input=hidden, pool_size=2, pool_type='avg', pool_stride=2)
  3. # 不对pool2d进行量化

3 量化模型预测

首先,使用PaddleLite提供的模型转换工具(model_optimize_tool)将量化模型转换成移动端预测的模型,然后加载转换后的模型进行预测部署。

3.1 模型转换

参考模型转换准备模型转换工具,建议从Release页面下载。

参考模型转换使用模型转换工具。注意opt命令的输入参数—prefer_int8_kernel必须设置为true,其他参数按照实际情况参考文档设置。比如在安卓手机ARM端进行预测,模型转换的命令为:

  1. ./opt --model_dir=./mobilenet_v1_quant \
  2. --optimize_out_type=naive_buffer \
  3. --optimize_out=mobilenet_v1_quant_opt \
  4. --valid_targets=arm \
  5. --prefer_int8_kernel=true

3.2 量化模型预测

和FP32模型一样,转换后的量化模型可以在Android/IOS APP中加载预测,建议参考C++ DemoJava DemoAndroid/IOS Demo

4 使用示例

4.1 产出量化模型

参考本文 “2.1 安装PaddlePaddle” 安装PaddlePaddle。

下载打包文件,解压到本地。

  1. wget https://paddle-inference-dist.cdn.bcebos.com/PaddleLite/quantization_demo/post_training_quantization_withdata.tgz
  2. tar zxvf post_training_quantization_withdata.tgz
  3. cd post_training_quantization_withdata

执行下面的命令,自动下载预测模型(mobilenetv1_fp32_model)和校准数据集,然后调用有校准数据训练后方法产出量化模型。

  1. sh run_post_training_quanzation.sh

量化模型保存在mobilenetv1_int8_model文件夹中。

4.2 量化模型预测

下载测试文件(benchmark_bin)或者参考Benchmark测试方法编译测试文件。

将mobilenetv1_fp32_model、mobilenetv1_int8_model和benchmark_bin文件都保存到手机上。

  1. adb push mobilenetv1_fp32_model /data/local/tmp
  2. adb push mobilenetv1_int8_model /data/local/tmp
  3. chmod 777 benchmark_bin
  4. adb push benchmark_bin /data/local/tmp

测试量化模型和原始模型的性能,依次执行下面命令:

  1. ./benchmark_bin --is_quantized_model=true --run_model_optimize=true --result_filename=res.txt --warmup=10 --repeats=30 --model_dir=mobilenetv1_int8_model/
  2. ./benchmark_bin --is_quantized_model=true --run_model_optimize=true --result_filename=res.txt --warmup=10 --repeats=30 --model_dir=mobilenetv1_fp32_model/
  3. cat res.txt

在res.txt文件中可以看到INT8量化模型和FP32原始模型的速度。 举例来说,在骁龙855手机、单线程的情况下测试mobilenetv1,INT8量化模型的计算时间是14.52ms,FP32原始模型的计算时间是31.7ms。