6.3.3. 校准数据准备

注解

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

在进行模型转换时,校准阶段会需要20~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时的数据预处理步骤如下:

  1. 图像长宽等比scale,短边缩放到256。

  2. center_crop 方法获取224x224大小图像。

  3. 转换输入layout为模型所需的 NCHW

  4. 转换色彩空间为模型所需的 BGR

  5. 归一化处理。

依照 preprocess_on 关闭状态下的样本文件制作原则, 针对上述举例模型的样本处理代码如下(为避免过长代码篇幅,各种简单transformer实现代码未贴出,transformer使用方法可参考 图片处理transformer说明 ):

# 本示例使用skimage,如果是opencv会有所区别
# 需要您特别注意的是,transformers中并没有体现减mean和乘scale的处理
# mean和scale操作已经融合到了模型中,参考前文norm_type/mean_values/scale_values配置
import skimage
import skimage.io
import numpy as np
from horizon_tc_ui.data.transformer import (CenterCropTransformer,
                                            HWC2CHWTransformer,
                                            RGB2BGRTransformer,
                                            ScaleTransformer,
                                            ShortSideResizeTransformer)
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_image)).astype(np.float32)]
    for trans in transformers:
        image = trans(image)
    # 模型指定的input_type_train BGR数值类型是UINT8
    image = image[0].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信息。)