模型适配过程
下面以ResNet50为例说明模型适配操作步骤。ResNet50为Vision类型模型,只需要开发用于适配benchmark工具的预处理脚本和后处理脚本即可,无需适配模型推理。此外,ResNet50模型适配模型后处理和精度统计,采用一个脚本实现。
前提条件
- 获取ResNet50模型
获取地址:https://github.com/KaimingHe/deep-residual-networks
说明:从该地址下载模型网络文件prototxt和权重文件caffemodel。
- 获取测试集
获取地址:http://www.image-net.org/challenges/LSVRC/2012/
说明:从该地址下载2012年的图像测试集,并利用scripts目录下get_info.py脚本将图像测试集生成模型推理所需的输入数据集ImageNet2012_bin.list。输入数据集的格式为“图像编号 图像路径 宽 高”,示例如下:
1 dataset/000001.bin 224 224 2 dataset/000002.bin 224 224 ...
操作步骤
- 适配模型预处理。模型预处理支持以下两种方法:
- 方法1:采用工具中scripts目录下模型预处理脚本将原始图像做预处理,并保存成bin格式文件,然后拷贝到NPU的Device端,构造成模型输入数据结构。
- 方法2:读取JPEG图像,通过DVPP做图像预处理,并拷贝到NPU的Device端,构造成模型输入数据结构。
本例采用方法1进行模型预处理,即对图像依次进行固定宽高比缩放、居中裁剪和减均值,并保存成bin格式文件。
- 固定宽高比缩放。
在模型输入大小基础上,对图像先放大后缩小,做固定宽高比例缩放。如下所示:
def resize_with_aspectratio(img, out_height, out_width, scale=87.5, inter_pol=cv2.INTER_LINEAR): height, width, _ = img.shape new_height = int(100. * out_height / scale) new_width = int(100. * out_width / scale) if height > width: w = new_width h = int(new_height * height / width) else: h = new_height w = int(new_width * width / height) img = cv2.resize(img, (w, h), interpolation=inter_pol) return im
- 居中裁剪。
def center_crop(img, out_height, out_width): height, width, _ = img.shape left = int((width - out_width) / 2) right = int((width + out_width) / 2) top = int((height - out_height) / 2) bottom = int((height + out_height) / 2) img = img[top:bottom, left:right] return img
- 减均值。
对图像三个颜色通道减去对应的平均值。
means = np.array([103.94, 116.78, 126.68], dtype=np.float16)
- 模型前处理完整步骤如下:
import numpy as np import os import cv2 import sys def preprocess_resnet50(img, need_transpose=True, precision="fp16"): output_height = 224 output_width = 224 cv2_interpol = cv2.INTER_AREA img = resize_with_aspectratio(img, output_height, output_width, inter_pol=cv2_interpol) img = center_crop(img, output_height, output_width) if precision == "fp32": img = np.asarray(img, dtype='float32') if precision == "fp16": img = np.asarray(img, dtype='float16') means = np.array([103.94, 116.78, 126.68], dtype=np.float16) img -= means if need_transpose: img = img.transpose([2, 0, 1]) return img def resnet50(input_path, output_path): img = cv2.imread(input_path) h, w, _ = img.shape img_name = input_path.split('/')[-1] bin_name = img_name.split('.')[0] + ".bin" output_fl = os.path.join(output_path, bin_name) if os.path.exists(output_fl): pass else: imgdst = preprocess_resnet50(img) imgdst.tofile(output_fl) if __name__ == "__main__": pathSrcImgFD = sys.argv[1] pathDstBinFD = sys.argv[2] images = os.listdir(pathSrcImgFD) for image_name in images: if not image_name.endswith(".jpeg"): continue print("start to process image {}....".format(image_name)) path_image = os.path.join(pathSrcImgFD, image_name) resnet50(path_image, pathDstBinFD)
- 适配模型推理。ResNet50模型推理,benchmark工具已经适配支持,无需再适配。推理方法如下:
- 将开源caffe模型转换为ATC模型。1中已将图像处理为float16类型,模型输入类型同样指定为float16类型。模型转换方法详细请参见《CANN 开发辅助工具指南 (推理)》中的“ATC工具使用指导”章节。模型转换命令如下:
atc --model=./ResNet50_bs8.prototxt --weight=./ResNet-50-model.caffemodel --framework=0 --input_shape=data:8,3,224,224 --input_fp16_nodes=data --soc_version=Ascend310 --input_format=NCHW --output_type=FP32 --output=./ResNet50_bs8_ip16op32
- 使用benchmark工具进行模型推理。推理过程详细请参见Vision类型。
模型推理命令如下:
./benchmark -model_type=vision -batch_size=8 -device_id=0 -om_path=./ResNet50_bs8_ip16op32.om -input_width=224 -input_height=224 -input_text_path=./dataset/ImageNet2012_bin.list -useDvpp=false
- 将开源caffe模型转换为ATC模型。1中已将图像处理为float16类型,模型输入类型同样指定为float16类型。模型转换方法详细请参见《CANN 开发辅助工具指南 (推理)》中的“ATC工具使用指导”章节。模型转换命令如下:
- 适配模型后处理与精度统计。
ResNet50模型用于图像分类,该模型最后一层为softmax层,softmax层输出所有类别的预测概率值。模型后处理与精度统计的主要步骤如下,具体代码可以从benchmark工具包scripts目录中的vision_metric.py脚本获取。执行benchmark工具保存模型输出层信息,再执行vision_metric.py脚本解析模型输出层信息,并根据解析出来的信息统计精度。
- 读取数据集真实标签。
def cre_groundtruth_dict_fromtxt(gtfile_path): """ :param filename: file contains the imagename and label number :return: dictionary key imagename, value is label number """ img_gt_dict = {} with open(gtfile_path, 'r')as f: for line in f.readlines(): temp = line.strip().split(" ") imgName = temp[0].split(".")[0] imgLab = temp[1] img_gt_dict[imgName] = imgLab return img_gt_dict
- 加载数据预测结果,并按照预测概率从高到低排序。
for tfile_name in os.listdir(prediction_file_path): count += 1 temp = tfile_name.split('.')[0] index = temp.rfind('_') img_name = temp[:index] filepath = os.path.join(prediction_file_path, tfile_name) ret = load_statistical_predict_result(filepath) prediction = ret[0] n_labels = ret[1] sort_index = np.argsort(-prediction)
- 逐个比较预测结果与真实标签是否一致,统计模型分类精度,输出准确率。
gt = img_gt_dict[img_name] if (n_labels == 1000): realLabel = int(gt) elif (n_labels == 1001): realLabel = int(gt) + 1 else: realLabel = int(gt) resCnt = min(len(sort_index), topn) for i in range(resCnt): if (str(realLabel) == str(sort_index[i])): count_hit[i] += 1 break accuracy = np.cumsum(count_hit) / count
- 精度统计完整步骤如下:
if __name__ == '__main__': start = time.time() try: # txt file path folder_davinci_target = sys.argv[1] # annotation files path, "dataset/HiAIAnnotations/" annotation_file_path = sys.argv[2] # the path to store the results json path result_json_path = sys.argv[3] # result json file name json_file_name = sys.argv[4] except IndexError: print("Stopped!") exit(1) if not (os.path.exists(folder_davinci_target)): print("target file folder does not exist.") if not (os.path.exists(annotation_file_path)): print("Ground truth file does not exist.") if not (os.path.exists(result_json_path)): print("Result folder doesn't exist.") img_label_dict = cre_groundtruth_dict(annotation_file_path) create_visualization_statistical_result(folder_davinci_target, result_json_path, json_file_name, img_label_dict, topn=5) elapsed = (time.time() - start) print("Time used:", elapsed)
- 读取数据集真实标签。