AscendCL简介

AscendCL 主要接口调用流程
AscendCL

使用MindStudio实现基于ResNet-50的分类应用

gitee中下载工程文件,该样例基于Caffe ResNet-50网络(单输入、单Batch)实现图片分类的功能。

在该样例中:

  1. 先使用样例提供的脚本transferPic.py,将2张*.jpg图片都转换为*.bin格式,同时将图片从1024*683的分辨率缩放为224*224。
  2. 加载离线模型om文件,对2张图片进行同步推理,分别得到推理结果,再对推理结果进行处理,输出top5置信度的类别标识。

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
├── data
│ ├── dog1_1024_683.jpg //测试数据,需要从链接下载
│ ├── dog2_1024_683.jpg //测试数据,需要从链接下载

├── inc
│ ├── model_process.h //声明模型处理相关函数的头文件
│ ├── sample_process.h //声明资源初始化/销毁相关函数的头文件
│ ├── utils.h //声明公共函数(例如:文件读取函数)的头文件

├── script
│ ├── transferPic.py //将*.jpg转换为*.bin,同时将图片从1024*683的分辨率缩放为224*224

├── src
│ ├── acl.json //系统初始化的配置文件
│ ├── CMakeLists.txt //编译脚本
│ ├── main.cpp //主函数,图片分类功能的实现文件
│ ├── model_process.cpp //模型处理相关函数的实现文件
│ ├── sample_process.cpp //资源初始化/销毁相关函数的实现文件
│ ├── utils.cpp //公共函数(例如:文件读取函数)的实现文件

├── .project //工程信息文件,包含工程类型、工程描述、运行目标设备类型等
├── CMakeLists.txt //编译脚本,调用src目录下的CMakeLists文件

获取模型文件和权重文件

首先需要根据README中的下载链接获得ResNet-50的模型文件和权重文件,由于是基于Caffe训练得到的,所以文件分别为resnet50.prototxtresnet50.caffemodel

模型转换

MindStudio提供可视化模型转换窗口Ascend -> Model Converter,并选择模型文件,如下图,将生成模型保存至工程根目录下的model文件夹中。
model converter

自动生成ATC命令
atc

转换输出
output

配置构建

构建 -> Edit Configurations,Toolchain选择远程ECS。构建注意 工程目录 不能存在中文字符
build

编译结束
target

配置运行

运行 -> 编辑配置,配置目录。
out

图片转换

模型读取的是BIN格式图片,因此需要将JPEG图片转为图片。
script下的transferPic.py移动到data下,运行转换,将生成的BIN文件同步到远程服务器。

运行

run

代码分析

transferPic.py

该脚本主要是读取.jpg格式图片,缩放到256*256大小再转为numpy格式后中心裁剪到224*224,色域由RGB转为BGR,数值中心化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
input_image = Image.open(input_path)
input_image = input_image.resize((256, 256))
# hwc
img = np.array(input_image)
height = img.shape[0]
width = img.shape[1]
h_off = int((height-224)/2)
w_off = int((width-224)/2)
crop_img = img[h_off:height-h_off, w_off:width-w_off, :]
# rgb to bgr
img = crop_img[:, :, ::-1]
shape = img.shape
img = img.astype("float16")
img[:, :, 0] -= 104
img[:, :, 1] -= 117
img[:, :, 2] -= 123
img = img.reshape([1] + list(shape))
result = img.transpose([0, 3, 1, 2])
output_name = input_path.split('.')[0] + ".bin"
result.tofile(output_name)

main.cpp

主函数入口很简单,主要创建sampleProcess对象,完成初始化并调用。

1
2
3
SampleProcess sampleProcess;
Result ret = sampleProcess.InitResource();
ret = sampleProcess.Process();

sampleProcess

  • InitResource
    实现ACL初始化,设置device,创建context、stream,设置rum mode

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const char *aclConfigPath = "../src/acl.json";
    aclError ret = aclInit(aclConfigPath);
    ret = aclrtSetDevice(deviceId_);

    ret = aclrtCreateContext(&context_, deviceId_);
    ret = aclrtCreateStream(&stream_);

    aclrtRunMode runMode;
    ret = aclrtGetRunMode(&runMode);
    g_isDevice = (runMode == ACL_DEVICE);
  • Process
    该方法主要是创建ModelProcess对象,加载模型、创建模型描述信息、创建模型输入和输出、运行模型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ModelProcess modelProcess;
    const char* omModelPath = "../model/resnet50.om";
    Result ret = modelProcess.LoadModel(omModelPath);
    ret = modelProcess.CreateModelDesc();

    ret = modelProcess.GetInputSizeByIndex(0, devBufferSize); // 获取输入尺寸
    aclError aclRet = aclrtMalloc(&picDevBuffer, devBufferSize, ACL_MEM_MALLOC_NORMAL_ONLY); // 分配内存


    ret = modelProcess.CreateInput(picDevBuffer, devBufferSize);
    ret = modelProcess.CreateOutput();
    ret = modelProcess.Execute();

    <!-- 销毁对象和内存 -->

model_process

  • LoadModel
    查询模型大小 -> 申请内存(模型和权重) -> 内存加载模型

  • CreateModelDesc
    创建Desc -> 获取Desc

  • GetInputSizeByIndex
    获取模型输入大小,

  • CreateInput
    获取输入大小 -> 创建Dataset -> 创建Databuffer -> 添加Databuffer到Dataset

  • CreateOutput
    创建Dataset -> 获取输出大小 -> 创建Databuffer -> 添加Databuffer到Dataset

  • Execute
    执行aclmdlExecute

  • OutputModelResult
    后处理(输出置信度)
    获取输出 -> 从DEVICE拷贝到HOST -> 输出置信度与索引