4.1.1.8. 模型精度分析与调优

基于几十或上百张校准数据实现浮点模型到定点模型转换的后量化方式,不可避免地会存在一定的精度损失。 但经过大量实际生产经验验证,如果能筛选出最优的量化参数组合,地瓜机器人的转换工具在大部分情况下,都可以将精度损失保持在 1%以内。

本节先介绍了如何正确地进行模型精度分析,如果通过评估发现不及预期,则可以参考 精度调优 小节的内容尝试调优, 实在无法解决可寻求地瓜机器人的技术支持。

4.1.1.8.1. 模型精度分析

在进入到此部分介绍前,我们希望您已经了解如何对一个模型进行精度评测。本节介绍的内容是如何使用模型转换的产出物进行推理。

前文提到模型成功转换的产出物包括以下四个部分:

  • *_original_float_model.onnx

  • *_optimized_float_model.onnx

  • *_calibrated_model.onnx

  • *_quantized_model.onnx

  • *.bin

虽然最后的 bin 模型才是将部署到计算平台的模型,考虑到方便在 Ubuntu 开发机上完成精度评测, 我们提供了*_quantized_model.onnx 完成这个精度评测的过程。 quantized 模型已经完成了量化,与最后的 bin 模型具有一致的精度效果。 使用地瓜机器人开发库加载 ONNX 模型推理的基本流程如下所示,这份示意代码不仅适用于 quantized 模型, 对 original 和 optimized 模型同样适用,根据不同模型的输入类型和 layout 要求准备数据即可。

import numpy as np
# 加载地瓜机器人依赖库
from horizon_tc_ui import HB_ONNXRuntime

# 准备模型运行的输入
input_data = np.load("input.npy")
# 加载模型文件
sess = HB_ONNXRuntime(model_file = "***_quantized_model.onnx")
# 获取模型输入&输出节点信息
input_names = sess.input_names
output_names = sess.output_names
# 准备输入数据,这里我们假设此模型只有1个输入
input_info = {input_names[0]: input_data}
# 开始模型推理,推理的返回值是一个list,依次与output_names指定名称一一对应
output = sess.run(output_names, input_info)

  """
  Modification  history:
    OE 1.3 ~ 1.6
        outputs = sess.run(output_names, feed_dict, input_type_rt=None, float_offset=0)
        outputs = sess.run_feature(output_names, feed_dict, {input_name: "featuremap"}, float_offset=0)
    OE 1.7
        outputs = sess.run(output_names, feed_dict, input_type_rt=None, float_offset=None, input_offset=128)
        outputs = sess.run_feature(output_names, feed_dict, {input_name: "featuremap"}, float_offset=0)
    OE 1.8 ~ 1.9
        outputs = sess.run(output_names, feed_dict, input_offset=128)
        outputs = sess.run_feature(output_names, feed_dict, input_offset=128)

    note: OE 1.5 后架构上的调整,如果更新 OE 需要重新编译模型
  """

此外,输入数据准备过程是最容易出现误操作的部分。 较于您设计&训练原始浮点模型的精度验证过程,我们需要您在数据预处理后将推理输入数据进一步调整, 这些调整主要是数据格式(RGB、NV12 等)、数据精度(int8、float32 等)和数据排布(NCHW 或 NHWC)。 至于具体怎么调整,这个是由您在模型转换时设置的 input_type_traininput_layout_traininput_type_rtinput_layout_rt 四个参数共同决定的,其详细规则请参考 转换内部过程解读 部分的介绍。

举个例子,有一个使用 ImageNet 训练的用于分类的原始浮点模型,它只有一个输入节点。 这个节点接受 BGR 顺序的三通道图片,输入数据排布为 NCHW。原始浮点模型设计&训练阶段,验证集推理前做的数据预处理如下:

  1. 图像长宽等比 scale,短边缩放到 256。

  2. center_crop 方法获取 224x224 大小图像。

  3. 按通道减 mean。

  4. 数据乘以 scale 系数。

使用地瓜机器人转换这个原始浮点模型时, input_type_train 设置 bgrinput_layout_train 设置 NCHWinput_type_rt 设置 bgrinput_layout_rt 设置 NHWC

根据 转换内部过程解读 部分介绍的规则, *_quantized_model.onnx 接受的输入应该为 bgr_128、NCHW 排布。 对应到前文的示例代码, your_custom_data_prepare 部分提供的数据处理应该一个这样的过程:

# 本示例使用skimage,如果是opencv会有所区别
# 需要您特别注意的是,transformers中并没有体现减mean和乘scale的处理
# mean和scale操作已经融合到了模型中,参考前文norm_type/mean_values/scale_values配置
def your_custom_data_prepare_sample(image_file):
  # skimage读取图片,已经是NHWC排布
  image = skimage.img_as_float(skimage.io.imread(image_file))
  # 长宽等比scale,短边缩放至256
  image = ShortSideResize(image, short_size=256)
  # CenterCrop获取224x224图像
  image = CenterCrop(image, crop_size=224)
  # skimage读取结果通道顺序为RGB,转换为bgr_128需要的BGR顺序
  image = RGB2BGR(image)
  # 如果原模型是 NCHW 输入(input_type_rt为nv12除外)
  if layout == "NCHW":
    image = HWC2CHW(image)
  # skimage读取数值范围为[0.0,1.0],调整为bgr需要的数值范围
  image = image * 255
  # bgr_128是bgr减去128
  image = image - 128
  #bgr_128使用int8
  image = image.astype(np.int8)

  return image

4.1.1.8.2. 精度调优

基于前文的精度分析工作,如果确定模型的量化精度不符合预期,则主要可分为以下两种情况进行解决:

1.精度有较明显损失(损失大于 4%)。

这种问题往往是由于 yaml 配置不当,校验数据集不均衡等导致的,可以根据我们接下来提供的建议逐一排查。

2.精度损失较小(1.5%~3%)。

排除 1 导致的精度问题后,如果仍然出现精度有小幅度损失,往往是由于模型自身的敏感性导致,可以使用我们提供的精度调优工具进行调优。

3.在尝试 1 和 2 后,如果精度仍无法满足预期,可以尝试使用我们提供的精度 debug 工具进行进一步尝试。

整体精度问题解决流程示意如下图:

../../../../_images/accuracy_problem.png

一、 精度有明显损失(4%以上)

通常情况下,明显的精度损失往往是由于各种配置不当引起的,我们建议您依次从 pipeline、模型转换配置和一致性三个方面检查。

pipeline 检查

pipeline 是指您完成数据准备、模型推理、后处理、精度评测 Metric 的全过程。 在以往的实际问题跟进经验中,我们发现这些部分在原始浮点模型训练阶段中有变动,却没有及时更新到模型转换的精度验证过程来是比较常见的情况。

模型转换配置检查

  • input_type_rtinput_type_train 该参数用来区分转后混合异构模型与原始浮点模型需要的数据格式,需要认真检查是否符合预期,尤其是 BGR 和 RGB 通道顺序是否正确。

  • norm_typemean_valuesscale_values 等参数是否配置正确。通过转换配置可以直接在模型中插入 mean 和 scale 操作节点, 需要确认是否对校验/测试图片进行了重复的 mean 和 scale 操作。重复预处理是错误的易发区。

数据处理一致性检查

  • skimage.readopencv.imread 是两种常用图片读取方法,这两种方法在输出的范围和格式上都有所区别。 使用 skimage 的图片读取,得到的是 RGB 通道顺序,取值范围为 0~1,数值类型为 float;而使用 opencv,得到的是 BGR 通道顺序,取值范围为 0~255,数据类型为 uint8。

  • 在校准数据准备阶段、给应用程序准备应用样本时,我们常使用 numpy 的 tofile 序列化数据。这种方式不会保存 shape 和类型信息, 在加载时都需要手动指定,需要您确保这些文件的序列化和反序列化过程的数据类型、数据尺寸和数据排布等信息都是一致的。

  • 推荐您在地瓜机器人工具链使用过程中,依然使用原始浮点模型训练验证阶段依赖的数据处理库。 对于鲁棒性较差的模型,不同库实现的功能 resize、crop 等典型功能都可能引起扰动,进而影响模型精度。

  • 校验图片集是否合理设置。校准图片集数量应该在百张左右,同时最好可以覆盖到数据分布的各种场合,例如在多任务或多分类时,校验图片集可以覆盖到各个预测分支或者各个类别。 同时避免偏离数据分布的异常图片(过曝光等)。

  • 使用 *_original_float_model.onnx 再验证一遍精度,正常情况下,这个模型的精度应该是与原始浮点模型精度保持小数点后三到五位对齐。 如果验证发现不满足这种对齐程度,则表明您的数据处理需要再仔细检查。

二、 较小精度损失提升

一般情况下,为降低模型精度调优的难度,我们建议您在转换配置中使用的是自动参数搜索功能。 如果发现自动搜索的精度结果仍与预期有一定的差距,较于原始浮点模型的精度损失在 1.5%到 3%范围左右。 可以分别尝试使用以下建议提高精度:

  • 尝试在配置转换中手动指定 calibration_type ,可以先选择 mix ,如果最终精度仍不符合预期,再尝试 kl / max

  • 尝试在配置转换中启用 per_channel

  • calibration_type 设定为 max 时, 同时配置 max_percentile 参数分别为 0.999990.999950.99990.99950.999 进行尝试。

三、 精度 debug 工具

在尝试了一和二中提供的方法后,如果您的精度仍无法满足预期,为了方便您定位问题,我们提供了精度 debug 工具用于协助您定位问题。 该工具能够协助您对校准模型进行节点粒度的量化误差分析,快速定位出现精度异常的节点。 工具的详细介绍及使用方法您可参考 精度 debug 工具 章节。

根据以往的实际生产经验,以上策略已经可以应对各种实际问题。 如果经过以上尝试仍然未能解决您的问题,欢迎在 地瓜机器人官方技术社区 发帖与我们取得联系, 我们将根据您的具体问题提供更具针对性的指导建议。

4.1.1.8.3. 使用 QAT 量化感知训练方案进一步提升模型精度

如果通过上述分析,并没有发现任何配置上的问题,但是精度仍不能满足要求,则可能是 PTQ 本身的限制。 这时候我们可以改用 QAT 的方式来对模型进行量化。

本小节内容对 QAT 方案进行详细介绍:

  • 首先,关于量化 介绍量化的概念和两种量化方法;

  • 其次,关于模型转换 介绍地瓜机器人模型转换、原始浮点模型和混合异构模型的概念;

  • 接着,在理解了以上一些概念后,关于模型量化编译流程 一小节内容,让您理解地瓜机器人 PTQ 和 QAT 方案的关系,便于您可以在不同情况下选择更合适的模型处理方案;

  • 最后,QAT 模型量化编译 则是为您引入通过 QAT 方案完成量化模型编译的介绍。

4.1.1.8.3.1. 关于量化

目前在 GPU 上训练的模型大部分都是浮点模型,即参数使用的是 float 类型存储。 地瓜机器人 BPU 架构的计算平台使用的是 int8 的计算精度(业内计算平台的通用精度),能运行定点量化模型。 那么 从训练出的浮点精度转为定点模型的过程,我们叫做量化。

量化方法有两种,分别为

  • 后量化(post training quantization,PTQ)

    先训练浮点模型,然后使用校准图片计算量化参数,将浮点模型转为量化模型。 该方法简单、快捷,但将浮点模型直接转为量化模型难免会有一些量化损失。

    注解

    关于 PTQ 模型的量化和编译流程, 模型量化与编译 已为您做出了详细介绍。

  • 量化感知训练(quantization aware training,QAT)

    在浮点训练的时候,就先对浮点模型结构进行干预,增加量化误差,使得模型能够感知到量化带来的损失。 该方法需要用户在全量训练集上重新训练,能有效地降低量化部署的量化误差。 一些社区框架都提供 QAT 方案,例如 pytorch 的 eager mode 方案、pytorch 的 fx graph 方案、tf-lite 量化方案等。

    注解

    QAT 训练与浮点训练的关系

    QAT 训练是一种 finetune 方法,最好是在浮点结果已经拟合的情况下,再用 QAT 方法提升量化精度。 即用户的训练分为了两个步骤,先训练浮点模型,将模型精度提升到满意的指标;再通过 QAT 训练,提升量化精度。

    为了让模型更好的感知到量化误差,QAT 训练需要使用全量的训练数据集。 训练轮数和模型难度相关,大约是原来的浮点训练的 1/10。 因为是在浮点模型上 finetune,所以 QAT 训练的学习率尽量和浮点模型的最后几个 epoch 一致。

4.1.1.8.3.2. 关于模型转换

模型转换是指将原始浮点模型转换为地瓜机器人混合异构模型的过程。 其中会包括模型前处理节点修改、原始模型图优化、模型量化和上板模型编译等过程。

原始浮点模型 (文中部分地方也称为浮点模型)是指您通过 TensorFlow/PyTorch 等等 DL 框架训练得到的可用模型,这个模型的计算精度为 float32; 目前我们的 QAT 方案中,训练工具基于 Pytorch 开发,因此只支持 Pytorch 格式的模型。 PTQ 方案只支持 Caffe&ONNX 模型格式,因此对于 TensorFlow/PyTorch 等格式的模型,需要先通过转换到 ONNX 模型后,才能够被地瓜机器人的工具进行量化&编译。

混合异构模型 是一种适合在地瓜机器人计算平台上运行的模型格式,之所以被称为异构模型是因为它能够支持模型同时在 ARM CPU 和 BPU 上执行。 由于在 BPU 上的运算速度会远大于 CPU 上的速度,因此会尽可能的将算子放在 BPU 上运算。 对于 BPU 上暂时不支持的算子,则会放在 CPU 上进行运算。

4.1.1.8.3.3. 关于模型量化编译流程

正常的模型量化编译流程如下图所示:

../../../../_images/qat_compile_flow.png

小技巧

由于 PTQ 方式的使用代价小,因此推荐用户首先尝试该方法进行模型量化编译。 若尝试并调优后的模型精度依然无法满足要求,则可以再改为尝试 QAT 方案。

4.1.1.8.3.4. QAT 模型量化编译介绍

Horizon Plugin Pytorch 参考了 PyTorch 官方的量化接口和思路,Plugin 采用的是 Quantization Aware Training(QAT)方案, 因此建议用户先阅读 PyTorch 官方文档 中和 QAT 相关部分。

更详细的关于 Horizon Plugin Pytorch 的介绍,您可以参考 量化感知训练(QAT) 章节。