Important


配合 D-Robotics 算法工具链的模型完整开发过程,需要经过 浮点模型准备、 模型验证、 模型转换、 性能评估 和 精度评估 共五个重要阶段,如下图:

image.png

浮点模型准备

支持 onnx@opset11caffe1.0 的模型

一些 pytorch 有的算子而 onnx@opset11 没有的算子,D-Robotics 算法工具链提供了导出脚本可以将其从 PyTorch 算子导出到 D-Robotics 自定义的 onnx OP 中。

模型验证

使用方法

  hb_mapper checker --model-type ${model_type} \
                    --march ${march} \
                    --proto ${proto} \
                    --model ${caffe_model/onnx_model} \
                    --input-shape ${input_node} ${input_shape} \
                    --output ${output}
 
# 多个输入的情况下,多次指定 --input-shape 即可

结果解读

  ==============================================
  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
  ... 

每行含 Node、ON、Subgraph 和 Type 四列,分别为节点名称、执行节点计算的硬件、节点所属子图和节点映射到的 D-Robotics 算子名称。

官方对 yolov5 举的示例中,会要求把最终输出的 anchor 解算去掉,并且用 permute 操作把输出格式弄成 NHWC

示例使用的 yolov5 官方的模型 v2.0 和 v7.0 都是 5 维,不支持

convert 成 NHWC 是方便后续后处理的 anchor 解算。可以保持 NCHW 也没关系,但是解算过程就会比较复杂。

模型转换

需要 100 份左右校准数据

准备校准数据集

preprocess_on 参数可以比较方便的把数据处理成模型需要的类型、格式、大小,但是官方不建议开启,会影响量化的效果。

因为这个参数开启后,前处理的过程是固定的,但实际上不同模型前处理可能不太一样。

举一个实际的例子,使用 ImageNet 训练的用于分类的原始浮点模型:

  • 它只有一个输入节点,输入信息描述如下:
    • 输入类型:BGR
    • 输入 layout:NCHW
    • 输入尺寸:1x3x224x224
  • 使用验证集做模型推理(inference)时的数据预处理如下:
    1. 图像长宽等比 scale,短边缩放到 256。
    2. center_crop 方法获取 224x224 大小图像。
    3. 按通道减 mean。
    4. 数据乘以 scale 系数。

量化编译镜像里面安装了 horizon_tc_ui 包,有常用的 transformer 操作,具体有哪些参考:Transformer使用说明

借助各类 transformer 来实现预处理过程,将数据处理成上面描述的样子:

  # 本示例使用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)

Important

需要您特别注意的是,transformers 中并没有体现减 mean 和乘 scale 的处理
mean 和 scale 操作已经融合到了模型中,请参考下文 norm_type/mean_value/scale_value 配置

怎么解读上面这段话呢?

减均值除方差的操作会融合到模型的最前面去做,所以这段预处理部分就不需要做这个操作了。

使用 hf_mapper makertbin 工具转换模型

hb_mapper makertbin --config ${config_file}  \
                    --model-type  ${model_type}

模型转换过程通过 yaml 配置控制,分成四个大的参数组:

  • model_parameters
  • input_parameters
  • calibration_parameters
  • compiler_parameters

参数存在多个值的情况下,形式为: param_name: 'param_value1; param_value2; param_value3'

进阶的几个参数说明:

  • model_parameters.node_info: 可以配置 计算 op 的硬件、输入数据类型、输出数据类型。
    • node_info 在指定输入/输出类型为 int16 的时候:==在您配置了某个 op 输入/输出数据类型为 int16 后,模型转换内部会自动进行 op 输入输出上下文(context)int16 配置的更新和检查。 例如,当配置 op_1 输入/输出数据类型为 int16 时,实际上潜在同时指定了 op_1 的上/下一个 op 需要支持以 int16 计算。 对于不支持的场景,模型转换工具会打印 log 提示该 int16 配置组合暂时不被支持并回退到 int8 计算。==
  • input_parameters.norm_type/mean_value/scale_value: 用来设置预处理减均值除方差的过程,如果有配置的话,会生成一个 HzPreprocess 算子插入到输入节点后面做归一化操作。
  • calibration_parameters.calibration_type: 取值范围是 default mix kl max load skip 。==使用 skip 可以先用随机数校准,适合对模型结构进行验证。==
  • calibration_parameters.cal_data_dir: 如果 dir 有后缀如 _f32 可以不需要指定 cal_data_type。但是建议显式指定 cal_data_type
  • calibration_parameters.run_on_cpu: deprecated
  • calibration_parameters.run_on_bpu: deprecated
  • ==compiler_parameters.core_num: 默认是 1,X5 也不支持多核,所以不用配置。==
  • compiler_parameters.debug: 分析每一层算子的计算量、计算耗时、数据搬运耗时等

在您配置了某个 op 输入/输出数据类型为 int16 后,模型转换内部会自动进行 op 输入输出 上下文(context)int16 配置的更新和检查。 例如,当配置 op_1 输入/输出数据类型为 int16 时,实际上==潜在同时指定了 op_1 的上/下一个 op 需要支持以 int16 计算==。 对于不支持的场景,模型转换工具会打印 log 提示该 int16 配置组合暂时不被支持并回退到 int8 计算。

转换内部过程解析

模型转换重点在解决 输入数据处理 和 模型优化编译 两个问题


输入数据处理:

相关的配置参数为:

  • input_type_train
  • input_layout_train
  • input_type_rt
  • input_layout_rt

通过配置这一系列参数后,模型的输入会变成这样一个过程:

image.png

  1. Data format Conversion:这一步根据上述配置的四个参数,将 layout 和 type 为 rt 的边缘设备上的数据,转为 layout 和 type 为 train 的数据格式和类型
  2. Data preprocessing:这一步做减均值除方差,之后开始模型的计算。

通过这样的配置,用户可以少做很多输入数据处理的相关工作,而且厂商能够利用硬件来加速这个输入数据的处理过程。

具体如何配置:

  • type 支持的配置对:
input_type_train \ input_type_rtnv12yuv444rgbbgrgrayfeaturemap
yuv444YYNNNN
rgbYYYYNN
bgrYYYYNN
grayNNNNYN
featuremapNNNNNY
  • layout NCHW 还是 NHWC 没有要求。只需要根据实际情况设置即可:

    • 第一是 input_layout_train 必须与原始模型的数据排布一致
    • 第二是在处理器上准备好与 input_layout_rt 一致排布的数据

    正确的数据排布是顺利解析数据的基础。


模型优化编译:

模型优化编译 完成了模型解析、模型优化、模型校准与量化、模型编译几个重要阶段,其内部工作过程如下图所示:

image.png


模型解析阶段:(产出 original_float_model.onnx)

在原始浮点模型上会根据转换配置 yaml 文件中的配置参数决定是否加入数据预处理节点,此阶段产出一个 original_float_model.onnx。 这个 ONNX 模型计算精度仍然是 float32,但在输入部分加入了一个数据预处理节点。这个预处理节点会完成 input_type_rtinput_type_train 的完整转换。

需要注意的是,这个转换过程会利用硬件资源。实际上 onnx 模型并不包含硬件转换的部分,所以其实 onnx 的输入是一个中间态的数据。如下所示,onnx 其实是从中间态这个输入开始计算的。


input_type_rt —— 「硬件」—— 中间态 —— 「转换」 —— input_type_train


自然地,每种 input_type_rt 都有其对应的中间态:

input_type_rtnv12yuv444rgbbgrgrayfeaturemap
中间态yuv444_128yuv444_128RGB_128BGR_128GRAY_128featuremap

解释:

  • yuv444_128 是 yuv444 数据减去 128 结果,每个数值采用 int8 表示。
  • RGB_128 是 RGB 数据减去 128 的结果,每个数值采用 int8 表示。
  • BGR_128 是 BGR 数据减去 128 的结果,每个数值采用 int8 表示。
  • GRAY_128 是 gray 数据减去 128 的结果,每个数值采用 int8 表示。
  • featuremap 是一个四维张量数据,每个数值采用 float32 表示。

模型优化阶段:(产出 optimized_float_model.onnx)

计算精度仍然是 fp32,输入的要求和 original_float_model.onnx 一样


模型校准与量化阶段:(产出 quantized_model.onnx)

计算精度是 int8。==输入的要求目前存疑。==


模型编译阶段:(产出 ***.bin)


性能评估

BPU-Model-Performance-Evaluation

精度评估

BPU-Model-Accuracy-Evaluation