6.1. PTQ转换原理及流程

6.1.1. 概览

模型从训练到转换再到在开发板运行的过程如下图所示:

../../../_images/workflow.png

模型训练,指使用TensorFlow、PyTorch、Caffe等公开深度学习框架得到可用模型的过程,通过训练得到的可用模型将作为模型转换阶段的输入。 工具链本身不会提供训练相关的库或工具,具体支持的公开学习框架可以参考 浮点模型准备 章节中的说明。

模型转换,本阶段以模型训练得到的浮点模型为输入,通过模型结构优化、模型校准量化等重要步骤, 将浮点模型转换为可以在地平线计算平台高效运行的混合异构模型。 具体使用请参考 模型量化和编译 章节中的说明。

嵌入式应用开发,工具链支持了在X86仿真环境和真实嵌入式环境的应用开发能力,在不方便使用开发板的情况下, 您在仿真环境完成程序的调试和计算结果的验证。 为了降低仿真验证的代价,工具链提供的仿真库接口与嵌入式接口完全一致,只是采用了不同的编译配置。 具体使用方式请参考 嵌入式应用开发指导 章节中的说明。

自定义算子开发,本阶段是一个可选阶段,主要是解决模型存在工具链不支持算子的情况,如果您没有此种情况,可以直接忽略本阶段的处理。 具体使用方式请参考 自定义算子开发 章节中的说明。

下面为您详细介绍PTQ转换的流程与步骤相关内容。

6.1.2. PTQ转换流程说明

模型转换是指将原始浮点模型转换为地平线混合异构模型的过程。

  • 原始浮点模型(文中部分地方也称为浮点模型)是指您通过TensorFlow/PyTorch等等DL框架训练得到的可用模型,这个模型的计算精度为float32。

  • 混合异构模型是一种适合在地平线计算平台上运行的模型格式。

后文将反复使用到这两种模型名词,为避免理解歧义,请先理解这个概念再阅读下文。

配合地平线工具链的模型完整开发过程,需要经过 浮点模型准备模型验证模型转换性能评估精度评估 共五个重要阶段,如下图所示。

../../../_images/model_conversion_flowchart.png

浮点模型准备 阶段的产出是输入到模型转换工具的浮点模型, 这些模型一般都是基于公开DL训练框架得到的, 需要您注意的是将模型导出为地平线工具支持的格式。 具体要求与建议请参考 浮点模型准备 章节。

模型验证 阶段用来确保算法模型是符合计算平台要求的。 地平线提供了指定工具完成此阶段检查,对于不符合要求的情况, 检查工具会明确给出不符合要求的具体算子信息,方便您结合算子约束的说明将模型调整过来。 具体使用请参考 验证模型 章节。

模型转换 阶段将完成浮点模型到地平线混合异构模型的转换。 为了模型能在地平线计算平台上高效运行,地平线转换工具内部会完成模型优化、量化和编译等关键步骤, 地平线的量化方法经过了长期的技术与生产验证,在大部分典型模型上可以保证精度损失小于1%。 具体使用请参考 校准数据准备模型量化与编译 章节。

性能评估 阶段提供了系列评估模型性能的工具。 在应用部署前,您可以使用这些工具验证模型性能是否达到应用要求。 对于部分性能不及预期的情况,也可以参考地平线提供的性能优化建议进行调优。 具体评估请参考 模型性能分析 章节。

精度评估 阶段提供了系列评估模型精度的工具。 大部分情况下,地平线转换后模型可以保持与原始浮点模型基本一致的精度效果, 在应用部署前,您可以使用地平线工具验证模型的精度是否符合预期。 对于部分精度不及预期的情况,也可以参考地平线提供的精度优化建议进行调优。 具体评估请参考 模型精度分析 章节。

注意

  • 通常在模型转换后就已经得到了可以上板的模型, 但是为了确保您得到的模型性能和精度都是符合应用要求的, 地平线强烈建议每次转换后都完成后续的性能评估与精度评估步骤。

  • 模型转换过程会生成onnx模型,该模型均为中间产物,只是便于验证模型精度情况, 因此不保证其在版本间的兼容性。若使用示例中的评测脚本对onnx模型单张或在测试集上进行评测时, 请使用当前版本工具生成的onnx模型进行操作。

6.1.3. 模型转换过程详解

那么如何使用浮点模型转换工具将Caffe、TensorFlow、PyTorch等开源框架训练好的浮点模型转换成地平线硬件上支持的定点模型呢?

一般情况下,从官网下载或者自己训练得到的模型,其阈值、权重等均为浮点数(float32),每个数占用4个字节。 但在嵌入式端运行时,若能将其内部数值从浮点数转化为定点数(int8),则每个数只占用1个字节,因此计算量可以大大减少。 因此,若能在不损失或损失较小的情况下将浮点模型转换为定点模型,则其性能会有显著提升。

模型的转换通常可分为以下几个步骤:

1.检查待转换的模型中是否包含不支持的OP类型。

2.准备好20-100张校准使用的图片,用于转换阶段的校准操作。

3.使用浮点模型转换工具将原始浮点模型转换为定点模型。

4.对转换后的模型进行性能和精度的评估,确保转换后的定点模型精度与之前相差不大。

5.在模拟器/开发板上运行模型,对模型的性能和精度进行验证。

6.1.3.1. 模型检查(hb_mapper checker)

注解

如果本过程您需在示例文件夹内进行,那么您需要先执行文件夹中的 00_init.sh 脚本以获取对应的原始模型和数据集。

模型在从浮点转换为定点模型之前,我们首先需要通过工具 hb_mapper checker 子命令检查一下,看看该模型是否包含不能在地平线硬件上运行的OP。 如果存在,则会在该阶段提示不认识该OP, hb_mapper checker 子命令使用方式可参考 模型检查命令 一节内容。

若过程中存在不支持OP,则会出现如下提示:

ERROR HorizonRT not support these cpu operators: {不支持的OP名称}

小技巧

  • 有关地平线硬件可支持的OP信息,请参见 工具链算子支持约束列表 章节内容。

  • 若无不支持OP,则会输出模型转换后的各OP的列表,以及各OP被划分在CPU还是BPU上执行。若验证结束且无报错,即可继续进行后续步骤。参见如下:

hb_mapper checker --model-type caffe --proto mobilenet_deploy.prototxt \
--model mobilenet.caffemodel --march bayes
2021-01-08 17:33:53,117 INFO Start hb_mapper....
2021-01-08 17:33:53,118 INFO hb_mapper version 1.1.42
...
fc7          BPU  id(0)     HzSQuantizedConv
prob         CPU  --        Softmax
2021-01-08 17:33:53,329 INFO [Fri Jan  8 17:33:53 2021] End to Horizon NN Model Convert.
2021-01-08 17:33:53,332 INFO model deps info empty
2021-01-08 17:33:53,351 INFO End model checking....

小技巧

若存在不支持OP,可咨询地平线技术人员相关OP开发计划,或者参考 自定义算子开发 章节,通过 自定义算子(Custom OP) 功能添加该OP。

6.1.3.2. 准备校准图片

注解

如果本过程您需在示例文件夹内进行,那么您需要先执行文件夹中的 00_init.sh 脚本以获取对应的原始模型和数据集。

在进行模型转换时,校准阶段需要20-100张图片输入进行校准操作。 由于模型的输入类型及layout的不同,输入的图片格式可以多种多样。 该阶段既可以输入原始图片(*.jpg等), 也可以输入处理过的,满足模型输入要求的图片。 您可以直接从模型训练时的数据集中获取相应的校准图片,也可以自行处理图片生成校准数据集。

推荐您自行对校准图片进行前处理,将图片的通道(BGR/RGB),数据排布(NHWC/NCHW),图像大小及填充(Resize&Padding)等操作调整好后, 设置yaml文件(mobilenet_config.yaml) 中的 preprocess_on 参数为 False,那么工具则会通过二进制文件的方式将图片读入后送入校准阶段。

以MobileNet为例,则需要进行如下transformer的操作:

transformers = [
      ShortSideResizeTransformer(short_size=256),   # 短边Pad到256, 保持长宽比
      CenterCropTransformer(crop_size=224),         # 图像中心抠224*224的图像
      HWC2CHWTransformer(),                         # 数据排布从NHWC转换到NCHW
      RGB2BGRTransformer(data_format="CHW"),        # 颜色通道从RGB转换为BGR
      ScaleTransformer(scale_value=255),            # 数据范围从 0-1 转为 0-255
  ]

小技巧

若模型训练时是bgr/rgb色彩空间的,则校准阶段传入工具的图片数据需要是bgr/rgb色彩空间的, 工具内部会自动完成从bgr/rgb到yuv444/gray等的色彩转换。

例如:示例中的MobileNet模型实际输入设置为nv12,而02_preprocess.sh脚本转换结束后是bgr颜色空间的, 剩余的从bgr到nv12的转换是由工具内部自动补全的。

6.1.3.3. 模型转换(hb_mapper makertbin)

当通过上述 hb_mapper checker 子命令,确定了模型能够被转换成功后, 接下来可以使用 hb_mapper makertbin 子命令来将浮点模型转换为一个可以在地平线硬件上运行的定点模型。

该命令需要传入待转换模型的模型类型( caffe / onnx )以及一个包含转换要求的配置文件(*.yaml)。 具体的配置文件设置及各参数的含义,请参考 配置文件模板配置文件具体参数信息 章节的介绍。

模型转换过程结束后,还会将原始模型与转换后的定点模型的相似程度打印在log中, 可根据 Cosine Similarity 字段来判断模型转换前后的相似度。 如下方所示,模型转换后的 Cosine Similarity 非常接近1, 因此模型转换后的模型表现会与转换前的浮点模型非常相近。

2023-03-01 20:26:50,149 INFO Start hb_mapper....
......
2023-03-01 20:27:21,427 INFO [Wed Mar  1 20:27:21 2023] End to compile the model with march bayes.
2023-03-01 20:27:21,429 INFO The converted model node information:
======================================================================================================================
Node                    ON   Subgraph  Type                           Cosine Similarity  Threshold   In/Out DataType
----------------------------------------------------------------------------------------------------------------------
HZ_PREPROCESS_FOR_data  BPU  id(0)     HzSQuantizedPreprocess         0.999988           127.000000  int8/int8
conv1                   BPU  id(0)     HzSQuantizedConv               0.999922           2.937425    int8/int8
conv2_1/dw              BPU  id(0)     HzSQuantizedConv               0.999378           2.040827    int8/int8
conv2_1/sep             BPU  id(0)     HzSQuantizedConv               0.996680           4.486579    int8/int8
conv2_2/dw              BPU  id(0)     HzSQuantizedConv               0.997340           3.545496    int8/int8
conv2_2/sep             BPU  id(0)     HzSQuantizedConv               0.996384           2.791299    int8/int8
conv3_1/dw              BPU  id(0)     HzSQuantizedConv               0.994165           1.417208    int8/int8
conv3_1/sep             BPU  id(0)     HzSQuantizedConv               0.985451           2.188753    int8/int8
conv3_2/dw              BPU  id(0)     HzSQuantizedConv               0.994921           1.822225    int8/int8
conv3_2/sep             BPU  id(0)     HzSQuantizedConv               0.994251           1.841765    int8/int8
conv4_1/dw              BPU  id(0)     HzSQuantizedConv               0.988263           1.043535    int8/int8
conv4_1/sep             BPU  id(0)     HzSQuantizedConv               0.990294           1.736999    int8/int8
conv4_2/dw              BPU  id(0)     HzSQuantizedConv               0.992460           0.990603    int8/int8
conv4_2/sep             BPU  id(0)     HzSQuantizedConv               0.993468           1.574677    int8/int8
conv5_1/dw              BPU  id(0)     HzSQuantizedConv               0.988949           0.823123    int8/int8
conv5_1/sep             BPU  id(0)     HzSQuantizedConv               0.990803           1.265912    int8/int8
conv5_2/dw              BPU  id(0)     HzSQuantizedConv               0.990202           0.772344    int8/int8
conv5_2/sep             BPU  id(0)     HzSQuantizedConv               0.983443           1.530479    int8/int8
conv5_3/dw              BPU  id(0)     HzSQuantizedConv               0.986502           0.783812    int8/int8
conv5_3/sep             BPU  id(0)     HzSQuantizedConv               0.977642           1.927324    int8/int8
conv5_4/dw              BPU  id(0)     HzSQuantizedConv               0.982337           0.996043    int8/int8
conv5_4/sep             BPU  id(0)     HzSQuantizedConv               0.962062           2.167391    int8/int8
conv5_5/dw              BPU  id(0)     HzSQuantizedConv               0.978872           1.923361    int8/int8
conv5_5/sep             BPU  id(0)     HzSQuantizedConv               0.960184           3.578415    int8/int8
conv5_6/dw              BPU  id(0)     HzSQuantizedConv               0.980317           2.463874    int8/int8
conv5_6/sep             BPU  id(0)     HzSQuantizedConv               0.981055           4.124151    int8/int8
conv6/dw                BPU  id(0)     HzSQuantizedConv               0.998241           0.667692    int8/int8
conv6/sep               BPU  id(0)     HzSQuantizedConv               0.985220           0.983833    int8/int8
pool6                   BPU  id(0)     HzSQuantizedGlobalAveragePool  0.993602           11.415899   int8/int8
fc7                     BPU  id(0)     HzSQuantizedConv               0.995105           5.843800    int8/int32
prob                    CPU  --        Softmax                        0.985517           --          float/float
2023-03-01 20:27:21,430 INFO The quantify model output:
=======================================================================
Node  Cosine Similarity  L1 Distance  L2 Distance  Chebyshev Distance
-----------------------------------------------------------------------
prob  0.985517           0.000385     0.000203     0.185123
2023-03-01 20:27:21,432 INFO [Wed Mar  1 20:27:21 2023] End to Horizon NN Model Convert.

注解

该相似度为校准图片中的第一张的相似度情况,并不完全代表模型转换前后的精度。

在模型转换成功后,会在生成的文件夹 (默认为 model_output ) 中生成如下文件:

  • 静态性能评估文件(可读性更好): \*_subgraph_0.html

  • 静态性能评估文件: \*_subgraph_0.json

  • 原始浮点模型: \*_original_float_model.onnx

  • 优化后的浮点模型: \*_optimized_float_model.onnx

  • 校准模型:: \*_calibrated_model.onnx

  • 定点模型: \*_quantized_model.onnx

  • 上板使用的混合模型: \*.bin

这几个模型文件是模型转换的几个关键阶段产出的文件,并会在后续阶段使用到。

注解

  • 可调用03_classification/01_mobilenet/mapper/03_build.sh脚本, 来体验 hb_mapper makertbin 子命令的使用效果。

  • 如果希望了解更多模型转换的工作流程,请阅读 模型量化与编译 一节内容。

6.1.3.4. 单张图片的模型推理

在运行浮点模型转换之后,得到了定点模型,还需对其自身的正确性进行验证。

您需要对模型的输入/输出结构比较了解,并能够正确地对模型输入图片做前处理以及模型输出的后处理解析,并自行编写模型运行脚本。 在此过程中可参照交付包中 03_classification/01_mobilenet/mapper/04_inference.sh 中的示例代码。代码逻辑如下:

import numpy as np
# 加载地平线依赖库
from horizon_tc_ui import HB_ONNXRuntime
from postprocess import postprocess

# 准备模型运行的输入,此处`input.npy`为处理好的数据
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)
# 后处理
top_five_label_probs = postprocess(output)

使用该脚本后,便可通过输入单张图片验证其自身的正确性。 该脚本的输入为一张斑马的图片,在经过前处理将图片数据从rgb处理到input_type_rt的中间类型(无需-128)后(由于示例mobilenet的 input_type_rt 是nv12,对应的中间类型为yuv444_128, 因此infer_transformers过程需要完成rgb–>nv12–>yuv444,关于中间类型的介绍可参考 转换过程内部解读 ), 通过 HB_ONNXRuntime 命令传入模型进行推理,推理后进行后处理,最后打印出其最可能的5个种类。

运行后的输出如下所示,最可能的类别是 label: 340

I0108 18:11:47.398328 140427646048000 cls_inference.py:89] The input picture is classified to be:
label 340: prob 0.97
label 292: prob 0.02
label 282: prob 0.00
label 83: prob 0.00

label 的类别使用的是ImageNet的类别,也可以在 01_common/test_data/classes.txt 中查到, 340 正是对应着斑马,因此该图片推理正确。

6.1.3.5. 模型性能验证

模型在开发板上的运行帧率也是一个很重要的性能指标,未免去开发者架设开发板环境的麻烦, 我们可以直接使用 hb_perf 子命令对转换出来的模型进行性能分析。

在MobileNetv1示例中,运行命令 hb_perf mobilenetv1_224x224_nv12.bin 后,即可在 hb_perf_result/mobilenetv1_224x224_nv12/ 目录下找到模型分析文件 mobilenetv1_224x224_nv12.html

[horizon@gpu-dev model_output]$ hb_perf mobilenetv1_224x224_nv12.bin
2023-08-30 19:40:21,305 INFO log will be stored in horizon/.../mobilenetv1/hb_perf.log
2023-08-30 19:40:21,322 INFO Start hb_perf....
2023-08-30 19:40:21,323 INFO hb_perf version 1.21.0
2023-08-30 19:40:21,375 INFO ********* mobilenetv1_224x224_nv12 perf **********
......
2023-08-30 19:40:21,875 INFO file stored at : horizon/.../mobilenetv1_224x224_nv12/mobilenetv1_224x224_nv12.html

mobilenetv1_224x224_nv12.html 文件中,我们可以看到模型整体的各项性能数据。 当模型分为多段时,还会对每一段在BPU上运行的部分单独有性能分析报告。

../../../_images/hb_mapper_perf_2.png

上图中各项性能分别为:

  • Model Name: 模型名称。

  • BPU Model Latency(ms):模型整体耗时(单位为ms)。

  • Total DDR (loaded+stored) bytes per frame(MB per frame):模型整体BPU部分数据加载和存储所占用的DDR总量(单位为MB/frame)。

  • Loaded Bytes per Frame:模型运行每帧读取数据量。

  • Stored Bytes per Frame:模型运行每帧存储数据量。

注解

上述 BPU Model Latency(ms) 是指模型中在BPU上执行部分的耗时,如果模型中有CPU上执行的部分,则该部分耗时并未被计算在内。

6.1.3.6. 模型精度验证

针对模型的精度验证,我们提供了脚本对模型转换后的精度进行评测。 需要编写代码使模型能够循环推理图片,并将结果与标准结果进行比较,得到精度结果。

因为精度评测时,需要对图片进行 前处理,对模型数据进行 后处理,所以我们提供了一个示例Python脚本。 其原理与单张推理一致,但需要在整个数据集上面运行。 使用该脚本后,便可通过读取数据集,对模型的输出结果进行评判,并输出评测结果。 运行该脚本耗费时间较长,但可以通过设置 PARALLEL_PROCESS_NUM 环境变量来设置运行评测的线程数量。

在执行结束后得到的输出如下所示:

===REPORT-START{MAPPER-EVAL}===
0.7011
===REPORT-END{MAPPER-EVAL}===

可以看到转换后的定点模型精度为 0.7011

注解

  • 在不同的系统下,由于依赖库版本不同,转换得到的模型精度可能会略有差别。

  • 同时由于版本更迭,得到的定点模型精度可能也会略有差别。

  • 如果在使用模型转换工具后得到的定点模型有精度损失的话,您可以参考 模型精度验证及调优建议 来进行问题查究。

6.1.3.7. [参考]支持的校准方法

目前我们支持了以下的校准方法:

1.default

default 是一个自动搜索的策略,会尝试从系列校准量化参数中获得一个相对效果较好的组合。

2.mix

mix 是一个集成多种校准方法的搜索策略,能够自动确定量化敏感节点,并在节点粒度上从不同的校准方法中挑选出最佳方法, 最终构建一个融合了多种校准方法优势的组合校准方式。

3.KL

KL校准方法是借鉴了 TensorRT提出的解决方案 , 使用KL熵值来遍历每个量化层的数据分布,通过寻找最低的KL熵值,来确定阈值。 这种方法会导致较多的数据饱和和更小的数据量化粒度,在一些数据分布比较集中的模型中拥有着比max校准方法更好的效果。

4.max

max校准方法是在校准过程中,自动选择量化层中的最大值作为阈值。 这种方法会导致数据量化粒度较大,但也会带来比KL方法更少的饱和点数量,适用于那些数据分布比较离散的神经网络模型。

5.load

使用 QAT 导出的模型时,需使用此参数。

6.skip

若您只想尝试对模型性能进行验证,但对精度没有要求,则可以尝试 skip 方式进行校准。 该方式会使用max+内部生成的随机校准数据进行校准,不需要您准备校准数据,比较适合初次尝试对模型结构进行验证。

注意

需要您注意的是,使用skip方式时,因该方式使用max+内部生成的随机校准数据进行校准,故得到的模型不可用于精度验证。

6.1.3.8. [参考]OP列表

若想了解更多关于当前工具链支持的算子列表及约束条件,请参见 工具链算子支持约束列表 章节内容。