PTQ原理及步骤详解
简介
模型转换是指将原始浮点模型转换为D-Robotics 混合异构模型的过程。原始浮点模型(文中部分地方也称为浮点模型)是指您通过TensorFlow/PyTorch等DL框架训练得到的可用模型,这个模型的计算精度为float32;混合异构模型是一种适合在D-Robotics 处理器上运 行的模型格式。 本章节将反复使用到这两种模型名词,为避免理解歧义,请先理解这个概念再阅读下文。
配合D-Robotics 算法工具链的模型完整开发过程,需要经过 浮点模型准备、 模型验证、 模型转换、 性能评估 和 精度评估 共五个重要阶段,如下图:
浮点模型准备 本阶段用来确保原始浮点模型的格式为D-Robotics 模型转换工具支持的格式,原始浮点模型来自于您通过TensorFlow/PyTorch等DL框架训练得到可用模型。具体的浮点模型要求与建议,请阅读浮点模型准备章节内容。
模型验证 本阶段用来校验原始浮点模型是否满足D-Robotics 算法工具链的要求。D-Robotics 提供 hb_mapper checker
检查工具来完成浮点模型的检查。具体使用方法,请阅读验证模型 章节内容。
模型转换 本阶段用来完成浮点模型到D-Robotics 混合异构模型的转换,经过这个阶段,您将得到一个可以在D-Robotics 处理器上运行的模型。D-Robotics 提供 hb_mapper makertbin
转换工具来完成模型优化、量化和编译等关键步骤。具体使用方法,请阅读模型转换章节内容。
性能评估 本阶段主要用于测评D-Robotics 混合异构模型的推理性能情况,D-Robotics 提供了模型性能评估的工具,您可以使用这些工具验证模型性能是否达到应用要求。具体使用说明,请阅读 模型性能分析与调优章节内容。
精度评估 本阶段主要用于测评D-Robotics 混合异构模型的推理精度情况,D-Robotics 提供了模型精度评估的工具。具体使用说明,请阅读模型精度分析与调优章节内容。
模型准备
基于公开DL框架训练得到的浮点模型是D-Robotics 模型转换工具的输入,目前转换工具支持的DL框架如下:
框架 | Caffe | PyTorch | TensorFlow | MXNet | PaddlePaddle |
---|---|---|---|---|---|
D-Robotics 工具链 | 支持 | 支持(转ONNX) | 支持(转ONNX) | 支持(转ONNX) | 支持(转ONNX) |
以上框架中, Caffe框架导出的caffemodel是直接支持的,PyTorch、TensorFlow和MXNet等DL框架通过转换到ONNX格式间接支持。
对于不同框架到ONNX的转换,目前都有对应的标准化方案,参考如下:
-
Pytorch2Onnx:PytTorch官方API支持直接将模型导出为ONNX模型,参考链接: https://pytorch.org/tutorials/advanced/super_resolution_with_onnxruntime.html。
-
Tensorflow2Onnx:基于ONNX社区的onnx/tensorflow-onnx 进行转换,参考链接: https://github.com/onnx/tensorflow-onnx。
-
MXNet2Onnx:MXNet官方API支持直接将模型导出为ONNX模型 ,参考链接: https://github.com/dotnet/machinelearning/blob/master/test/Microsoft.ML.Tests/OnnxConversionTest.cs。
-
更多框架的ONNX转换支持,参考链接: https://github.com/onnx/tutorials#converting-to-onnx-format。
关于Pytorch、PaddlePaddle、TensorFlow2框架的模型,我们也提供了如何导出ONNX及模型可视化的教程,请参考:
-
浮点模型中所使用的算子需要符合D-Robotics 算法工具链的算子约束条件,具体请阅读 模型算子支持列表 章节进行查询。
-
目前转换工具仅支持输出个数小于或等于32的模型进行转换。
-
支持
caffe 1.0
版本的caffe浮点模型和ir_version≤7
,opset=10
、opset=11
版本的onnx浮点模型量化成D-Robotics 支持的定点模型, onnx模型的ir_version与onnx版本的对应关系请参考onnx官方文档 ; -
模型输入维度只支持
固定4维
输入NCHW或NHWC(N维度只能为1),例如:1x3x224x224或1x224x224x3, 不支持动态维度及非4维输入; -
浮点模型中不要包含有
后处理算子
,例如:nms算子。
模型验证
模型正式转换前,请先使用 hb_mapper checker
工具进行模型验证,确保其符合D-Robotics 处理器的支持约 束。
建议参考使用D-Robotics 模型转换 horizon_model_convert_sample
示例包中的caffe、onnx等示例模型的脚本方法: 01_check_X3.sh
或 01_check_Ultra.sh
。
使用 hb_mapper checker
工具验证模型
hb_mapper checker 工具的使用方式如下:
hb_mapper checker --model-type ${model_type} \
--march ${march} \
--proto ${proto} \
--model ${caffe_model/onnx_model} \
--input-shape ${input_node} ${input_shape} \
--output ${output}
hb_mapper checker 参数解释:
--model-type
用于指定检查输入的模型类型,目前只支持设置 caffe
或者 onnx
。
--march
用于指定需要适配的D-Robotics 处理器类型,可设置值为 bernoulli2
和 bayes
;RDK X3设置为 bernoulli2
,RDK Ultra设置为 bayes
,RDK X5设置为 bayes-e
。
--proto
此参数仅在 model-type
指定 caffe
时有效,取值为Caffe模型的prototxt文件名称。
--model
在 model-type
被指定为 caffe
时,取值为Caffe模型的caffemodel文件名称。
在 model-type
被指定为 onnx
时,取值为ONNX模型文件名称。
--input-shape
可选参数,明确指定模型的输入shape。
取值为 {input_name} {NxHxWxC/NxCxHxW}
,input_name
与shape之间以空格分隔。
例如模型输入名称为 data1
,输入shape 为 [1,224,224,3]
,
则配置应该为 --input-shape data1 1x224x224x3
。
如果此处配置shape与模型内shape信息不一致,以此处配置为准。
注意一个 --input-shape
只接受一个name和shape组合,如果您的模型有多个输入节点,
在命令中多次配置 --input-shape
参数即可。
--output参数已经废弃,log信息默认存储于 hb_mapper_checker.log
中。
检查异常处理
如果模型检查步骤异常终止或者出现报错信息,则说明模型验证不通过,请根据终端打印或在当前路径下生成的 hb_mapper_checker.log
日志文件确认报错信息和修改建议。
例如:以下配置中含不可识别算子类型 Accuracy
:
layer {
name: "data"
type: "Input"
top: "data"
input_param { shape: { dim: 1 dim: 3 dim: 224 dim: 224 } }
}
layer {
name: "Convolution1"
type: "Convolution"
bottom: "data"
top: "Convolution1"
convolution_param {
num_output: 128
bias_term: false
pad: 0
kernel_size: 1
group: 1
stride: 1
weight_filler {
type: "msra"
}
}
}
layer {
name: "accuracy"
type: "Accuracy"
bottom: "Convolution3"
top: "accuracy"
include {
phase: TEST
}
}
使用 hb_mapper checker
检查这个模型,您会在 hb_mapper_checker.log
中得到如下信息:
ValueError: Not support layer name=accuracy type=Accuracy
- 如果模型检查步骤异常终止或者出现报错信息,则说明模型验证不通过,请根据终端打印或在当前路径下生成的
hb_mapper_checker.log
日志文件确认报错信息和修改建议,错误信息可以在 模型量化错误及解决方法 章节来查找错误的解决方法,若以上步骤仍不能排除问题,请联系D-Robotics 技术支持团队或在D-Robotics 官方技术社区提出您的问题,我们将在24小时内给您提供支持。
检查结果解读
如果不存在ERROR,则顺利通过校验。 hb_mapper checker
工具将直接输出如下信息:
==============================================
Node ON Subgraph Type
----------
conv1 BPU id(0) HzSQuantizedConv
conv2_1/dw BPU id(0) HzSQuantizedConv
conv2_1/sep BPU id(0) HzSQuantizedConv
conv2_2/dw BPU id(0) HzSQuantizedConv
conv2_2/sep BPU id(0) HzSQuantizedConv
conv3_1/dw BPU id(0) HzSQuantizedConv
conv3_1/sep BPU id(0) HzSQuantizedConv
...
结果中每行都代表一个模型节点的check情况,每行含Node、ON、Subgraph和Type四列,分别为节点名称、执行节点计算的硬件、节点所属子图和节点映射到的D-Robotics 算子名称。 如果模型在网络结构中出现了CPU计算的算子,hb_mapper checker工具将把这个算子前后连续在BPU计算的部分拆分为两个Subgraph(子图)。
检查结果的调优指导
在最理想的情况下,模型网络结构中的算子都应该在BPU上运行,也就是只有一个子图。 如果出现了CPU算子导致拆分多个子图, hb_mapper checker
工具会给出导致CPU算子出现的具体原因,以下给出了分别在RDK X3 和 RDK Ultra上示例模型验证的情况;
- 以下在 RDK X3 上运行的Caffe模型出现了Reshape + Pow + Reshape 的结构, 从 RDK X3 的算子约束列表中我们可以看到, Reshape 算子目前为在CPU上运行的算子, 而Pow的shape也是非4维的,不符合X3 BPU算子约束条件。
因此模型最终检查结果也会出现分段情况, 如下:
2022-05-25 15:16:14,667 INFO The converted model node information:
====================================================================================
Node ON Subgraph Type
-------------
conv68 BPU id(0) HzSQuantizedConv
sigmoid16 BPU id(0) HzLut
axpy_prod16 BPU id(0) HzSQuantizedMul
UNIT_CONV_FOR_eltwise_layer16_add_1 BPU id(0) HzSQuantizedConv
prelu49 BPU id(0) HzPRelu
fc1 BPU id(0) HzSQuantizedConv
fc1_reshape_0 CPU -- Reshape
fc_output/square CPU -- Pow
fc_output/sum_pre_reshape CPU -- Reshape
fc_output/sum BPU id(1) HzSQuantizedConv
fc_output/sum_reshape_0 CPU -- Reshape
fc_output/sqrt CPU -- Pow
fc_output/expand_pre_reshape CPU -- Reshape
fc_output/expand BPU id(2) HzSQuantizedConv
fc1_reshape_1 CPU -- Reshape
fc_output/expand_reshape_0 CPU -- Reshape
fc_output/op CPU -- Mul
- 以下在 RDK Ultra 上运行的ONNX模型出现了Mul + Add + Mul的结构,从 RDK Ultra 的算子约束列表中我们可以看到,Mul和Add算子在五维上是支持BPU运行的,但前提要符合Ultra BPU算子约束条件,不然就会回退到CPU计算。
因此模型最终检查结果也会出现分段情况,如下:
====================================================================================
Node ON Subgraph Type
-------------------------------------------------------------------------------------
Reshape_199 BPU id(0) Reshape
Transpose_200 BPU id(0) Transpose
Sigmoid_201 BPU id(0) HzLut
Split_202 BPU id(0) Split
Mul_204 CPU -- Mul
Add_206 CPU -- Add
Mul_208 CPU -- Mul
Mul_210 CPU -- Mul
Pow_211 BPU id(1) HzLut
Mul_213 CPU -- Mul
Concat_214 CPU -- Concat
Reshape_215 CPU -- Reshape
Conv_216 BPU id(0) HzSQuantizedConv
Reshape_217 BPU id(0) Reshape
Transpose_218 BPU id(0) Transpose
Sigmoid_219 BPU id(0) HzLut
Split_220 BPU id(0) Split
Mul_222 CPU -- Mul
Add_224 CPU -- Add
Mul_226 CPU -- Mul
Mul_228 CPU -- Mul
Pow_229 BPU id(2) HzLut
Mul_231 CPU -- Mul
Concat_232 CPU -- Concat
Reshape_233 CPU -- Reshape
Conv_234 BPU id(0) HzSQuantizedConv
Reshape_235 BPU id(0) Reshape
Transpose_236 BPU id(0) Transpose
Sigmoid_237 BPU id(0) HzLut
Split_238 BPU id(0) Split
Mul_240 CPU -- Mul
Add_242 CPU -- Add
Mul_244 CPU -- Mul
Mul_246 CPU -- Mul
Pow_247 BPU id(3) HzLut
Mul_249 CPU -- Mul
Concat_250 CPU -- Concat
Reshape_251 CPU -- Reshape
根据 hb_mapper checker 给出的提示,一般来说算子运行在BPU上会有更好的性能表现,这里可以将pow、reshape 这类CPU算子从模型中移除,将对应算子的功能放入后处理中计算,从而减少子图数量。
当然,多个子图也不会影响整个转换流程,但会较大程度地影响模型性能,建议尽量调整模型算子到BPU上执行,可参考D-Robotics 处理器算子支持列表中的BPU算子支持列表来做同功能的算子替换或者将模型中的CPU算子移到模型推理的前、后处理中去做CPU计算。
模型转换
模型转换阶段会完成浮点模型到D-Robotics 混合异构模型的转换,经过这个阶段,您将得到一个可以在D-Robotics 处理器上运行的模型。 在进行转换之前,请确保已经顺利通过了上文的验证模型过程。
模型转换使用 hb_mapper makertbin
工具完成,转换期间会完成模型优化和校准量化等重要过程,校准需要依照模型预处理要求准备校准数据。
为了方便您全面了解模型转换,本节将依次介绍校准数据准备、转换工具使用、转换内部过程解读、转换结果解读和转换产出物解读等内容。
准备校准数据
在进行模型转换时,校准阶段会需要 100份左右 标定样本输入,每一份样本都是一个独立的数据文件。 为了确保转换后模型的精度效果,我们希望这些校准样本来自于您训练模型使用的 训练集或验证集 ,不要使用非常少见的异常样本,例如 纯色图片、不含任何检测或分类目标的图片等。
转换配置文件中的 preprocess_on
参数,该参数启用和关闭状态下分别对应了两种不同的预处理样本要求。
(有关参数的详细配置可参考下文校准参数组中相关说明)
preprocess_on
关闭状态下,您需要把取自训练集/验证集的样本做与模型推理(inference)前一样的前处理,
处理完后的校准样本会与原始模型具备一样的数据类型( input_type_train
)、尺寸( input_shape
)和
layout( input_layout_train
),对于featuremap输入的模型,您可以通过 numpy.tofile
命令将数据保存为float32格式的二进制文件,
工具链校准时会基于 numpy.fromfile
命令进行读取。
例如,使用ImageNet训练的用于分类的原始浮点模型,它只有一个输入节点,输入信息描述如下:
- 输入类型:
BGR
- 输入layout:
NCHW
- 输入尺寸:
1x3x224x224
使用验证集做模型推理(inference)时的数据预处理如下:
- 图像长宽等比scale,短边缩放到256。
center_crop
方法获取224x224大小图像。- 按通道减mean。
- 数据乘以scale系数。
针对上述举例模型的样本处理代码如下:
为避免过长代码篇幅,各种简单transformer实现代码未贴出,具体使用请参考transformer使用方法 章节内容。
建议参考使用D-Robotics 模型转换 horizon_model_convert_sample
示例包中的caffe、onnx等示例模型的预处理步骤方法: 02_preprocess.sh
和 preprocess.py
。
# 本示例使用skimage,如果是opencv会有所区别
# 需要您特别注意的是,transformers中并没有体现减mean和乘scale的处理
# mean和scale操作已经融合到了模型中,请参考下文norm_type/mean_value/scale_value配置
def data_transformer():
transformers = [
# 长宽等比scale,短边缩放至256
ShortSideResizeTransformer(short_size=256),
# CenterCrop获取224x224图像
CenterCropTransformer(crop_size=224),
# skimage读取结果为NHWC排布,转换为模型需要的NCHW
HWC2CHWTransformer(),
# skimage读取结果通道顺序为RGB,转换为模型需要的BGR
RGB2BGRTransformer(),
# skimage读取数值范围为[0.0,1.0],调整为模型需要的数值范围
ScaleTransformer(scale_value=255)
]
return transformers
# src_image 标定集中的原图片
# dst_file 存放最终标定样 本数据的文件名称
def convert_image(src_image, dst_file, transformers):
image = skimage.img_as_float(skimage.io.imread(src_file))
for trans in transformers:
image = trans(image)
# 模型指定的input_type_train BGR数值类型是UINT8
image = image.astype(np.uint8)
# 二进制存储标定样本到数据文件
image.tofile(dst_file)
if __name__ == '__main__':
# 此处表示原始标定图片集合,伪代码
src_images = ['ILSVRC2012_val_00000001.JPEG',...]
# 此处表示最终标定文件名称(后缀名不限制),伪代码
# calibration_data_bgr_f32是您在配置文件中指定的cal_data_dir
dst_files = ['./calibration_data_bgr_f32/ILSVRC2012_val_00000001.bgr',...]
transformers = data_transformer()
for src_image, dst_file in zip(src_images, dst_files):
convert_image(src_image, dst_file, transformers)
preprocess_on
启用状态下,标定样本使用skimage支持读取的图片格式文件即可。
转换工具读取这些图片后,会将其缩放到模型输入节点要求的尺寸大小,以此结果作为校准的输入。
这样的操作会简单,但是对于量化的精度没有保障,因此我们强烈建议您使用关闭 preprocess_on
的方式。
请注意,yaml文件中input_shape参数作用为指定原始浮点模型的输入数据尺寸。若为动态输入模型则可通过这个参数设置转换后的输入大小,而校准数据的shape大小应与input_shape保持一致。
例如:若原始浮点模型输入节点shape为?x3x224x224(“?”号代表占位符,即该模型第一维为动态输入), 转换配置文件中设置input_shape: 8x3x224x224,则用户需要准备的每份校准数据大小为 8x3x224x224。 (请知悉,此类输入shape第一维不等于1的模型,不支持通过input_batch参数修改模型batch信息。)
使用 hb_mapper makertbin 工具转换模型
hb_mapper makertbin提供两种模式,开启 fast-perf
模式和不开启 fast-perf
模式。
fast-perf
模式开启后,会在转换过程中生成可以在板端运行最高性能的bin模型,工具内部主要进行以下操作:
-
将BPU可执行算子尽可能运行在BPU上(若使用
RDK Ultra
和RDK X5
则可以通过yaml文件中node_info参数指定在BPU上运行的算子,RDK X3
是自动优化,无法通过yaml配置文件指定算子)。 -
删除模型首尾不可删除的CPU算子,包括:Quantize/Dequantize、Transpose、Cast、Reshape等。
-
以性能最高的O3优化等级编译模型。
建议参考使用D-Robotics 模型转换 horizon_model_convert_sample
示例包中的caffe、onnx等示例模型的脚本方法: 03_build_X3.sh
或 03_build_Ultra.sh
。
hb_mapper makertbin命令使用方式如下:
不开启 fast-perf
模式:
hb_mapper makertbin --config ${config_file} \
--model-type ${model_type}
开启 fast-perf
模式:
hb_mapper makertbin --fast-perf --model ${caffe_model/onnx_model} --model-type ${model_type} \
--proto ${caffe_proto} \
--march ${march}
hb_mapper makertbin参数解释:
--help
显示帮助信息并退出。
-c, --config
模型编译的配置文件,为yaml格式,文件名使用.yaml后缀,完整的配置文件模板参考如下章节内容。
--model-type
用于指定转换输入的模型类型,目前支持设置 caffe
或者 onnx
。
--fast-perf
开启fast-perf模式,该模式开启后,会在转换过程中生成可以在板端运行最高性能的bin模型,方便您用于后续的模型性能评测。
如您开启了fast-perf模式,还需要进行如下配置:
--model
Caffe或ONNX浮点模型文件。
--proto
用于指定Caffe模型prototxt文件。
--march
BPU的微架构。若使用 RDK X3
则设置为 bernoulli2
,若使用 RDK Ultra
则设置为 bayes
,若使用 RDK X5
则设置为 bayes-e
。
-
RDK X3 yaml配置文件
,可直接使用RDK X3 Caffe模型量化yaml文件模板 和RDK X3 ONNX模型量化yaml文件模板模板文件进行填写。 -
RDK Ultra yaml配置文件
,可直接使用RDK Ultra Caffe模型量化yaml文件模板 和RDK Ultra ONNX模型量化yaml文件模板模板文件进行填写。 -
RDK X5 yaml配置文件
,可直接使用RDK X5 Caffe模型量化yaml文件模板 和RDK X5 ONNX模型量化yaml文件模板模板文件进行填写。 -
若 hb_mapper makertbin 步骤异常终止或者出现报错信息,则说明模型转换失败,请根据终端打印或在当前路径下生成的
hb_mapper_makertbin.log
日志文件确认报错信息和修改建议,错误信息可以在 模型量化错误及解决方法章节来查找错误的解决方法,若以上步骤仍不能排除问题,请联系D-Robotics 技术支持团队或在D-Robotics 官方技术社区提出您的问题,我们将在24小时内给您提供支持。