6.3.6. 模型精度分析

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

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

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

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

  • *_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)

此外,输入数据准备过程是最容易出现误操作的部分。 较于您设计&训练原始浮点模型的精度验证过程,我们需要您在数据预处理后将推理输入数据进一步调整, 这些调整主要是数据格式(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