1 问题介绍
使用PTQ模型转换方案时,在准备校准数据过程中不知从何下手,直接给一张jpg图片,遇到cannot reshape array of size xxx into shape (x, xxx, xxx)的问题该如何解决?
报错截图2 解决方案
以ResNet18在imagenet数据集上的预处理为例,介绍最基本的校准数据准备过程。
2.1 pytorch数据预处理伪代码
import torchvision.transforms as transforms
def Data():
image.convert(RGB)
data_transform = transforms.Compose(
image,
[transforms.Resize(224),
# ToTensor():数据归一化 + 图像从HWC变为CHW
transforms.ToTensor(),
# Imagenet数据集上,图片 RGB 的均值、标准差,注意顺序
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
2.2 文件准备
[root@7660ab0db525 resnet18]# tree -L 1
.
├── 03_build.sh
├── origin_image # 放了10张jpg图片
├── preprocess.py # 校准数据准备脚本
├── resnet18_baseline_config.yaml # 模型转换时的yaml配置文件
└── resnet18_baseline.onnx
2.3 准备校准数据
准备校准数据的代码放在了preprocess.py中,其具体内容如下:
import cv2
import os
import numpy as np
## ------------------------------------------------------------#
# src_dir:原始jpg图片
# dst_dir:处理后的图片存放的路径
# pic_ext:处理后的图片后缀名(影响不大,只是为了说明它的通道顺序)
## ------------------------------------------------------------#
src_dir = './origin_image'
dst_dir = './image_converted_rgb_f32' # yaml文件中cal_data_dir参数配置成这个路径即可
pic_ext = '.rgb'
if not os.path.exists(dst_dir):
os.mkdir(dst_dir)
## ---------------------------------------#
# 一次只操作一张图片
## ---------------------------------------#
for src_name in sorted(os.listdir(src_dir)):
## -----------------------------#
# 把图片路径拼出来
## -----------------------------#
src_file = os.path.join(src_dir, src_name)
img = cv2.imread(src_file)
# ---------------------------------------#
# 常规操作是:先转成RGB,再减均值,除标准差
# 到底要不要转成rgb,主要看,模型训练时用的是啥,
# 毕竟在后面yaml配置中,这两个参数都行
# ---------------------------------------#
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
## -----------------------------#
## opencv实现图片resize
## -----------------------------#
img = cv2.resize(np.array(img), (224, 224), interpolation=cv2.INTER_CUBIC)
## ------------------------------------------------------------------#
# 用的是Pytorch框架,totensor中会实现从HWC,变为CHW,故需要这一步。
## ------------------------------------------------------------------#
img = img.transpose(2, 0, 1)
# ---------------------------------------------------------------------#
# 这儿保存控制校准数据的类型,需要和yaml中的cal_data_type参数对应。
# 若不对应,会造成报错:
# ERROR cannot reshape array of size xxx into shape (x,xxx,xxx)
# ---------------------------------------------------------------------#
img = img.astype(np.float32)
# img = img.astype(np.uint8)
## -----------------------------------------------------#
# PC端网络训练时,数据需要归一化,为何在这儿不做?
# 答:模型转换时,需要的图像输入type为uint,范围是0~255,归一化操作会集成
# 在yaml文件中mean和scale中,故不要归一化。
## -----------------------------------------------------#
# img /= 255.0
## --------------------------------------------------------------#
# PC端网络训练时,数据需要减均值,除标准差,为何在这儿不做?
# 答:为了和yaml中data_mean_and_scale下的mean_value与scale_value
# 参数配合,在yaml文件中设置即可
## --------------------------------------------------------------#
# img -= [0.485, 0.456, 0.406]
# img /= [0.229, 0.224, 0.225]
# -----------------------------------------------------#
# os.path.basename:返回最后的 文件名
# 例如:os.path.basename("./src/1.jpg"),返回:1.jpg
# -----------------------------------------------------#
filename = os.path.basename(src_file)
# print(src_file)
# -----------------------------------------------------#
# os.path.splitext: 把图片名和图片扩展名分开,
# 例如:1.jpg,short_name=1, ext=.jpg
# -----------------------------------------------------#
short_name, ext = os.path.splitext(filename)
# ---------------------------------------#
# 新的图片名
# ---------------------------------------#
pic_name = os.path.join(dst_dir, short_name + pic_ext)
img.tofile(pic_name)
print("write:%s" % pic_name)
注意:校准数据处理过程中,除了归一化、减均值、除标准差三个操作之外,其余部分均应和训练时数据预处理保持一致。
归一化、减均值、除标准差三个操作可以通过在模型中插入预处理节点进行加速实现,如何配置实现这样的功能在2.4节模型转换中详细介绍。
运行preprocess.py脚本:
[root@7660ab0db525 resnet18]# python3 preprocess.py
write:./image_converted_rgb_f32/ILSVRC2012_val_00000001.rgb
write:./image_converted_rgb_f32/ILSVRC2012_val_00000002.rgb
write:./image_converted_rgb_f32/ILSVRC2012_val_00000003.rgb
write:./image_converted_rgb_f32/ILSVRC2012_val_00000004.rgb
write:./image_converted_rgb_f32/ILSVRC2012_val_00000005.rgb
write:./image_converted_rgb_f32/ILSVRC2012_val_00000006.rgb
write:./image_converted_rgb_f32/ILSVRC2012_val_00000007.rgb
write:./image_converted_rgb_f32/ILSVRC2012_val_00000008.rgb
write:./image_converted_rgb_f32/ILSVRC2012_val_00000009.rgb
2.4 模型转换
归一化、减均值、除标准差三个操作整合到yaml参数配置中,通过norm_type、mean_value、scale_value三个参数实现,下面结合本节示例对yaml中这三个参数的用法进行详细的介绍。
mean和scale的换算逻辑如下:
data=(input/255−mean)×(1/std)=(input−255mean)×(1/255std)data=(input/255-mean)×(1/std)=(input-255mean)×(1/255std)data=(input/255−mean)×(1/std)=(input−255mean)×(1/255std)
255mean就是yaml中的mean_value,1/(255std)就是yaml中的scale_value,故上式可以写成:
data=(input−meanvalue)×scalevaluedata=(input-meanvalue)×scalevaluedata=(input−meanvalue)×scalevalue
# ------------------------------------------------#
# 网络输入的预处理方法,主要有以下几种:
# no_preprocess 不做任何操作
# data_mean 减去通道均值mean_value
# data_scale 对图像像素乘以data_scale系数
# data_mean_and_scale 减去通道均值后再乘以scale系数
# 注意:
# 此处不只是减均值,除标准差,而是通过mean和scale
# 两个参数实现归一化、减均值、除标准差三个功能
# ------------------------------------------------#
norm_type: 'data_mean_and_scale'
# ---------------------------------------------------#
# 图像减去的均值, 如果是通道均值,value之间必须用空格分隔
# 注意:
# 此处的均值需要根据输入数据的范围确定,
# 例如ImageNet的R通道,应该是: 123.68 = 0.485x255
# ---------------------------------------------------#
mean_value: 123.68 116.28 103.53
# -------------------------------------------------------#
# 图像预处理缩放比例,如果是通道缩放比例,value之间必须用空格分隔
# 注意:
# 此处,scale是乘,以前的标准差是除,需要根据输入数据的范围确定
# 例如ImageNet的R通道,应该是: 0.017 = 1 / (0.229x255)
# -------------------------------------------------------#
scale_value: 0.017 0.018 0.017
结合2.3节中生成的校准数据路径,配置yaml文件参数:cal_data_dir: ‘./image_converted_rgb_f32’,执行03_build.sh脚本即可完成模型转换。
[root@7660ab0db525 resnet18]# sh 03_build.sh
cd $(dirname $0) || exit
config_file="./resnet18_baseline_config.yaml"
model_type="onnx"
# build model
hb_mapper makertbin --config ${config_file} \
--model-type ${model_type}
2022-11-16 10:30:05,057 INFO Start hb_mapper....
...
2022-11-16 10:30:38,935 INFO Convert to runtime bin file sucessfully!
2022-11-16 10:30:38,935 INFO End Model Convert

