深入理解浮点转换工具链 yaml 配置文件及其踩坑点

目前在 XJ3 浮点转化工具链中,我们为每一个示例模型都提供了一个 yaml 配置文件。有些初次接触的小伙伴可能对于其中某些配置项并没有很好地理解,从而达不到自己预期的性能或精度指标,甚至直接就踩了坑。

下面我将以 efficientnet_lite0 模型为例,分别为大家解读下性能和精度相关的配置参数。

【性能相关】

1) layer_out_dump

该参数服务于 vec_diff 工具(samples/05_miscellaneous/03_vector_diff),打开后会为每一层卷积添加一个输出节点,如下图所示。该功能主要用于逐层比较浮点、量化、混合模型 featuremap 的相似度。

除了调试工作外,正常情况我们应该默认关闭该参数,否则那么多的输出节点肯定会造成很大的性能下降,带宽压力也急剧上升。

2) output_layout

目前默认情况下,我们是不需要手动配置该参数的(配置为 None 即可),工具链内部会自动解析原始浮点模型,保持其原有的 layout 输出。

不过在我们提供的检测模型示例中,大部分都强制配置成了 NHWC,其目的是为了更好地适配我们的后处理逻辑,以达到更好的整体性能。

所以如果基于我们的检测示例 yaml 进行改写的话,需要关注该参数,如果不需要强制转换应该配置为 None,否则可能会引入额外的 transpose(如果模型为多输出,那么性能损失将更大)。

3) compile_mode

该参数用于选择编译器的优化方法,正常情况下我们会以性能优先,即配置为 ‘latency’;

对于比较重型、featuremap 比较大的模型来说,根据实际情况可能需要配置成 ‘bandwidth’,以降低带宽压力,不过相应的性能指标也会有所降低。

4)debug

该参数开启后会得到更加丰富的仿真信息,可以看到在 html 静态性能文件中多出了 layer 级别的仿真信息,如果关闭该参数则没有。

不过开启该参数,也会使得模型本身包含更多的参数信息,对最终的性能就会造成一定的降低。

5)core_num

该参数主要用于配置模型的运行模式。详细说明可参考我们的 FAQ 文档 《FAQ-2-08:单帧单核,单帧双核两种工作模式解析》

6)optimize_level

该参数用于配置编译器的优化等级,O0 到 O3 优化效果逐步提高,但其编译时间也越来越长。

一般我们可能会基于 O0~O2 等级进行模型转换成功率和精度的快速验证,而正式上板测试性能则应该用 O3 来获取最佳指标。

【精度相关】

1)input_type_rt

该参数为模型本身训练时的数据类型,如果误将 rgb/rgbp 配置成 bgr/bgrp 就会造成一定程度的精度损失(一般 5%~10% 左右),而且问题定位还比较难。所以我们应该首先确认该参数配置是否准确。

2)mean_value scale_value

mean_value 和 scale_value 本身并不难配置,主要的踩坑点是可能会和数据预处理存在重复操作的问题。

如果我们在 yaml 中配置了这两项参数,并且通过 norm_type 参数进行了使能,那么我们在模型编译过程中,就会将其作为预处理节点插入到模型前端,以获得更高效的性能。

此时我们在校准数据集或验证/测试集上,就无需再做归一化了。如果进行了重复操作,则其整个数据分布都不正确,精度自然会受到严重影响。

3)cal_data_dir

校准数据集的选择其实对于后量化方法一直有点玄学。不过常规思路还是选择 20~100张左右覆盖典型场景的数据。

数量过多/过少、未覆盖典型场景、包含无效数据(如全黑全白)等可能会造成一定的精度损失。

4)calibration_type

该参数指定模型的校准方法,我们默认提供 max 和 kl 两种,他们在不同类型的模型上表现不一,大家可以自行测试一下。

5)max_percentile

高阶参数 1,非产品化交付功能,主要用于精度调优。

该参数需搭配 max 校准进行使用,是一种以 max 最大值的分位数来选择量化 threshold 的方法。在某些场景的模型上表现比较好。

6)per_channel

高阶参数 2,非产品化交付功能,主要用于精度调优。

该参数可搭配任意一种量化方法进行使用,如 kl、max、max + max_percentile。是一种逐 channel 选择 threshold 的方法,在某些场景的模型上表现比较好。

你好,请教一下,J3模型量化,在反量化层之前为什么有的头的输出是int32?

如何指定中间节点上报。。

上面说的 “1)input_type_rt”应该是上板运行时模型吧,而input_type_train才是训练时模型?请问一下校准数据生成(自己先做好了resize和通道转置)后是否是对接在上板模型输入上(因为文中提到不做重复的归一化(若上板模型已经添加了归一化的预处理节点)进行校准的(但我也注意到samples分类用例里面好像做校准数据预处理时都有ScaleTransformer(255)即乘以255,这是为何?这对于后续的推理应用开发的预处理有何要求?)?另外对于input_type_rt类型为 rgb/bgr/rgbp/bgrp 的配置,要求输入上板模型的输入是不是一定是int8的,那我这边就需要将opencv读入的bgr uint8图片要减去-128并转为int8处理才行? 谢谢

之前转线下沟通了,这里再回复下哈:

1)input_type_rt 是板端模型在实际应用场景下会拿到的输入数据类型,比较多的是 nv12(摄像头采集后送给模型推理);

2)input_type_train 是模型本身训练时类型,以 rgbg/bgrp 为主

3)在我们的模型示例中,mapper/02_preprocess.sh 脚本提供了对校准数据的预处理,用到的 transformer 逻辑配置在 mapper/data_transformer.py 中,包括 Padding,CenterCrop 这种都可以使用

4)需要注意的是,我们提供的分类示例模型以 skimage.io.imread 读取图片(0~1,RGB),检测示例模型以 opencv.imread 读取图片(0~255,BGR),所以我们提供的 mapper/data_transformer.py 会有些逻辑上的差异,不过最后都是以 u8 保存为二进制文件。此时需要在 transformer 中手动加上 resize 操作,使其适配模型输入大小

5)如果校准数据的预处理仅有 Resize,那个其实可以跳过 mapper/02_preprocess.sh 步骤,在 yaml 里直接配置原始 JPEG 文件夹的路径,并配置 preprocess_on:True,我们会根据模型输入大小和 input_type_rt 自动将原始 JPEG 图像做相应的缩放和颜色空间转换(客户就不需要操心了)

6)如果走 mapper/02_preprocess.sh 流程,那么 yaml 里需要配置刚才保存下来的 u8 二进制文件的目录路径,并配置 preprocess_on:False

7)int8 其实是芯片硬件对于模型输入的要求,不过客户并不需要感知,我们内部会自动去做 -128 的操作。所以对于 input_type_rt 为 rgb/bgr/rgbp/bgrp 的模型来说,可以参考我们 runtime 的 API 接口,我们准备 输入 tensor 的时候,配置的数据类型为 BPU_TYPE_IMG_BGR 这种,这里只有传入 u8 数据即可

8)如果是在 mapper 里运行 python 代码,去评估 quanti.onnx 模型的结果,那么 -128 的操作其实是在这里,以 horizon_x3_tc_1.1.19e/samples/03_classification/cls_inference.py 为例:

其中,

1)1 处我们将 RGB 或 BGR 的数据先转成了 YUV444,layout 为 HWC,这里用户只感知到 u8 类型

2)实际 -128 的操作其实在 2 处的 sess.run 中,这里调用的是 …/python3.6/site-packages/x3_tc_ui/hb_quanti_onnxruntime.py 中的类函数,可以如果配置了 3 处的几种类型,我们就会在 4 处进行 u8 到 int8 的转换

您好,可以详细描述下需求吗?

如上图红框所示

谢谢 我目前跑了两个模型 还可以 精度后续再进一步优化。

您好,对于已经归一化的图像(比如:/255),也要执行-128操作吗

找到了 使用output_nodes参数就行

如果模型以conv结尾,会以int32高精度输出