MindSpore初级教程-9.推理

推理

本节是初级教程的最后一节,为了更好地适配不同推理设备,因此推理分为 1)昇腾AI处理器推理和 2)移动设备推理。

昇腾AI处理器推理

1 概述

昇腾(Ascend)AI处理器是面向边缘场景的高能效高集成度AI处理器。可以实现图像、视频等多种数据分析与推理计算,可广泛用于智能监控、机器人、无人机、视频服务器等场景。本节我们来学习如何在昇腾AI处理器上使用MindSpore执行推理。

2 推理代码介绍

首先创建目录放置推理代码工程,model目录用于存放上述导出的MindIR模型文件,test_data目录用于存放待分类的图片,推理代码工程目录结构如下:

└─主目录
    ├── CMakeLists.txt                    // 构建脚本
    ├── README.md                         // 使用说明
    ├── main.cc                           // 主函数
    ├── model
    │   └── resnet50_imagenet.mindir      // MindIR模型文件
    └── test_data
        ├── ILSVRC2012_val_00002138.JPEG  // 输入样本图片1
        ├── ILSVRC2012_val_00003014.JPEG  // 输入样本图片2
        ├── ...                           // 输入样本图片n

引用mindsporemindspore::dataset的名字空间。

namespace ms = mindspore;
namespace ds = mindspore::dataset;

初始化环境,指定推理使用的硬件平台,设置DeviceID。

  • Ascend 910为kDeviceTypeAscend910
  • Ascend 310为kDeviceTypeAscend310

这里设置硬件为Ascend 910,DeviceID为0,示例代码如下:

ms::GlobalContext::SetGlobalDeviceTarget(ms::kDeviceTypeAscend910);
ms::GlobalContext::SetGlobalDeviceID(0);

加载模型文件:

// 加载MindIR模型
auto graph =ms::Serialization::LoadModel(resnet_file, ms::ModelType::kMindIR);
// 用图构建模型
ms::Model resnet50((ms::GraphCell(graph)));
ms::Status ret = resnet50.Build({});

获取模型所需输入信息:

std::vector<ms::MSTensor> model_inputs = resnet50.GetInputs();

加载图片文件:

// Readfile是读取图像的函数
ms::MSTensor ReadFile(const std::string &file);
auto image = ReadFile(image_file);

图片预处理:

// 使用MindData提供的CPU算子进行图片预处理

// 将输入编码成RGB格式
std::shared_ptr<ds::TensorTransform> decode(new ds::vision::Decode());
// 把图片缩放到指定大小
std::shared_ptr<ds::TensorTransform> resize(new ds::vision::Resize({256}));
// 归一化输入
std::shared_ptr<ds::TensorTransform> normalize(new ds::vision::Normalize(
    {0.485 * 255, 0.456 * 255, 0.406 * 255}, {0.229 * 255, 0.224 * 255, 0.225 * 255}));
// 裁剪输入图片
std::shared_ptr<ds::TensorTransform> center_crop(new ds::vision::CenterCrop({224, 224}));
// 将shape (H, W, C)变换成shape (C, H, W)
std::shared_ptr<ds::TensorTransform> hwc2chw(new ds::vision::HWC2CHW());

// 定义一个MindData数据预处理函数
ds::Execute preprocessor({decode, resize, normalize, center_crop, hwc2chw});

// 调用数据预处理函数获取处理后的图像
ret = preprocessor(image, &image);

执行推理:

// 创建输出vector
std::vector<ms::MSTensor> outputs;
// 创建输入vector
std::vector<ms::MSTensor> inputs;
inputs.emplace_back(model_inputs[0].Name(), model_inputs[0].DataType(), model_inputs[0].Shape(),
                    image.Data().get(), image.DataSize());
// 调用Model的Predict函数进行推理
ret = resnet50.Predict(inputs, &outputs);

获取推理结果:

// 输出概率最大值
std::cout << "Image: " << image_file << " infer result: " << GetMax(outputs[0]) << std::endl;

3 构建脚本

接下来构建脚本用于构建用户程序,由于MindSpore使用旧版的C++ ABI,因此用户程序需与MindSpore一致,否则编译链接会失败。

add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)
set(CMAKE_CXX_STANDARD 17)

为编译器添加头文件搜索路径:

option(MINDSPORE_PATH "mindspore install path" "")
include_directories(${MINDSPORE_PATH})
include_directories(${MINDSPORE_PATH}/include)

在MindSpore中查找所需动态库:

find_library(MS_LIB libmindspore.so ${MINDSPORE_PATH}/lib)
file(GLOB_RECURSE MD_LIB ${MINDSPORE_PATH}/_c_dataengine*)

使用指定的源文件生成目标可执行文件,并为目标文件链接MindSpore库:

add_executable(resnet50_sample main.cc)
target_link_libraries(resnet50_sample ${MS_LIB} ${MD_LIB})
详细样例请参考:  https://gitee.com/mindspore/docs/blob/master/tutorials/tutorial_code/ascend910_resnet50_preprocess_sample/CMakeLists.txt

4 编译推理代码

接下来编译推理的代码,首先要进入工程目录ascend910_resnet50_preprocess_sample,设置如下环境变量:

如果是Ascend 310设备,则进入工程目录 ascend310_resnet50_preprocess_sample,以下代码均用Ascend 910为例。
# 控制log的打印级别. 0-DEBUG, 1-INFO, 2-WARNING, 3-ERROR, 默认是WARNING级别.
export GLOG_v=2

# 选择Conda环境
LOCAL_ASCEND=/usr/local/Ascend # 运行包的根目录

# 运行包依赖的lib库
export LD_LIBRARY_PATH=${LOCAL_ASCEND}/ascend-toolkit/latest/fwkacllib/lib64:${LOCAL_ASCEND}/driver/lib64/common:${LOCAL_ASCEND}/driver/lib64/driver:${LOCAL_ASCEND}/opp/op_impl/built-in/ai_core/tbe/op_tiling:${LD_LIBRARY_PATH}

# MindSpore依赖的lib库
export LD_LIBRARY_PATH=`pip3 show mindspore-ascend | grep Location | awk '{print $2"/mindspore/lib"}' | xargs realpath`:${LD_LIBRARY_PATH}

# 配置必要的环境变量
export TBE_IMPL_PATH=${LOCAL_ASCEND}/ascend-toolkit/latest/opp/op_impl/built-in/ai_core/tbe            # TBE算子的路径
export ASCEND_OPP_PATH=${LOCAL_ASCEND}/ascend-toolkit/latest/opp                                       # OPP路径
export PATH=${LOCAL_ASCEND}/ascend-toolkit/latest/fwkacllib/ccec_compiler/bin/:${PATH}                 # TBE算子编译工具的路径
export PYTHONPATH=${TBE_IMPL_PATH}:${PYTHONPATH}                                                       # TBE依赖的Python库

执行cmake命令,其中pip3需要按照实际情况修改:

cmake . -DMINDSPORE_PATH=`pip3 show mindspore-ascend | grep Location | awk '{print $2"/mindspore"}' | xargs realpath`

再执行make命令编译即可。

make

编译完成后,在ascend910_resnet50_preprocess_sample下会生成可执行main文件。

5 执行推理并查看结果

以上操作完成之后,我们可以开始学习如何用执行推理。

首先,登录Ascend 910环境,创建model目录放置MindIR文件resnet50_imagenet.mindir,例如/home/HwHiAiUser/mindspore_sample/ascend910_resnet50_preprocess_sample/model。 创建test_data目录放置图片,例如/home/HwHiAiUser/mindspore_sample/ascend910_resnet50_preprocess_sample/test_data。 就可以开始执行推理了:

./resnet50_sample

执行后,会对test_data目录下放置的所有图片进行推理,比如放置了2张ImageNet2012验证集中label为0的图片,可以看到推理结果如下。

Image: ./test_data/ILSVRC2012_val_00002138.JPEG infer result: 0
Image: ./test_data/ILSVRC2012_val_00003014.JPEG infer result: 0

移动设备推理

1 MindSpore Lite 概述

MindSpore Lite可完成在手机等端侧设备中的模型推理过程。当前支持Android、Ubuntu-x64、Windows-x64操作系统下的模型推理,支持端侧ARM CPU、ARM GPU、NPU多种硬件平台,支持C++、Java两种API。

下面通过C++ API演示端侧推理的基本流程。Demo通过随机生成的数据作为输入数据,执行MobileNetV2模型的推理,打印获得输出数据。

2 模型转换

模型在用于端侧推理之前需要先进行格式的转换。当前,MindSpore Lite支持MindSpore、TensorFlow Lite、Caffe和ONNX 4类AI框架。

下面以MindSpore训练得到的mobilenetv2.mindir模型为例,说明Demo中所使用的mobilenetv2.ms模型是如何生成的。

本小节展开说明了转换的操作过程,仅实现Demo运行可跳过本小节。
本小节仅针对Demo所用模型,详细的转换工具使用说明请参考官网 推理模型转换章节。
  • 转换工具下载

根据所使用的操作系统,下载转换工具的压缩包并解压至本地目录,获得converter工具,并配置环境变量。

  • 转换工具使用
  • Linux使用说明
    进入converter_lite可执行文件所在的目录,将下载的mobilenetv2.mindir模型放入同一路径下,在电脑终端中输入命令完成转换:
    cpp ./converter_lite --fmk=MINDIR --modelFile=mobilenetv2.mindir --outputFile=mobilenetv2
  • Windows使用说明
    进入converter_lite可执行文件所在的目录,将下载的mobilenetv2.mindir模型放入同一路径下,在电脑终端中输入命令完成转换:
    cpp call converter_lite --fmk=MINDIR --modelFile=mobilenetv2.mindir --outputFile=mobilenetv2
  • 参数说明
    在执行命令的过程中设置了三个参数,--fmk代表输入模型的原始格式,这里设置为MINDIR,即MindSpore框架训练模型的导出格式;--modelFile指输入模型的路径;--outputFile设定了模型的输出路径,这里自动将转换后的模型添加了.ms后缀。

3 构建环境与运行

Linux系统构建与运行

  • 编译构建

mindspore/lite/examples/quick_start_cpp目录下执行build脚本,将能够自动下载相关文件并编译Demo。

bash bash build.sh

  • 执行推理

编译构建后,进入mindspore/lite/examples/quick_start_cpp/build目录,并执行以下命令,体验MindSpore Lite推理MobileNetV2模型。

bash ./mindspore_quick_start_cpp ../model/mobilenetv2.ms

执行完成后将能得到如下结果,打印输出Tensor的名称、输出Tensor的大小,输出Tensor的数量以及前50个数据:

shell tensor name is:Default/head-MobileNetV2Head/Softmax-op204 tensor size is:4000 tensor elements num is:1000 output data is:5.26823e-05 0.00049752 0.000296722 0.000377607 0.000177048 .......

Windows系统构建与运行

  • 编译构建
  • 库下载:请手动下载硬件平台为CPU、操作系统为Windows-x64的MindSpore Lite模型推理框架mindspore-lite-{version}-win-x64.zip,将解压后inference/lib目录下的libmindspore-lite.a拷贝到mindspore/lite/examples/quick_start_cpp/lib目录、inference/include目录拷贝到mindspore/lite/examples/quick_start_cpp/include目录。
  • 模型下载:请手动下载相关模型文件mobilenetv2.ms,并将其拷贝到mindspore/lite/examples/quick_start_cpp/model目录。
可选择使用模型转换小节所获得的mobilenetv2.ms模型文件。
  • 编译:在mindspore/lite/examples/quick_start_cpp目录下执行build脚本,将能够自动下载相关文件并编译Demo。

bash call build.bat

  • 执行推理

编译构建后,进入mindspore/lite/examples/quick_start_cpp/build目录,并执行以下命令,体验MindSpore Lite推理MobileNetV2模型。

bash call ./mindspore_quick_start_cpp.exe ../model/mobilenetv2.ms

执行完成后将能得到如下结果,打印输出Tensor的名称、输出Tensor的大小,输出Tensor的数量以及前50个数据:

shell tensor name is:Default/head-MobileNetV2Head/Softmax-op204 tensor size is:4000 tensor elements num is:1000 output data is:5.26823e-05 0.00049752 0.000296722 0.000377607 0.000177048 .......

4 推理代码解析

下面分析Demo源代码中的推理流程,显示C++ API的具体使用方法。

模型加载

首先从文件系统中读取MindSpore Lite模型,并通过mindspore::lite::Model::Import函数导入模型进行解析。

// 读模型文件
size_t size = 0;
char *model_buf = ReadFile(model_path, &size);
if (model_buf == nullptr) {
  std::cerr << "Read model file failed." << std::endl;
  return RET_ERROR;
}
// 加载模型
auto model = mindspore::lite::Model::Import(model_buf, size);
delete[](model_buf);
if (model == nullptr) {
  std::cerr << "Import model file failed." << std::endl;
  return RET_ERROR;
}

模型编译

模型编译主要包括创建配置上下文、创建会话、图编译等步骤。

mindspore::session::LiteSession *Compile(mindspore::lite::Model *model) {
  // 初始化上下文
  auto context = std::make_shared<mindspore::lite::Context>();
  if (context == nullptr) {
    std::cerr << "New context failed while." << std::endl;
    return nullptr;
  }

  // 创建session.
  mindspore::session::LiteSession *session = mindspore::session::LiteSession::CreateSession(context.get());
  if (session == nullptr) {
    std::cerr << "CreateSession failed while running." << std::endl;
    return nullptr;
  }

  // 图编译
  auto ret = session->CompileGraph(model);
  if (ret != mindspore::lite::RET_OK) {
    delete session;
    std::cerr << "Compile failed while running." << std::endl;
    return nullptr;
  }

  // 注意:如果使用 model->Free(),模型将不能再次被编译
  if (model != nullptr) {
    model->Free();
  }
  return session;
}

模型推理

模型推理主要包括输入数据、执行推理、获得输出等步骤,其中本示例中的输入数据是通过随机数据构造生成,最后将执行推理后的输出结果打印出来。

int Run(mindspore::session::LiteSession *session) {
  auto inputs = session->GetInputs();
  auto ret = GenerateInputDataWithRandom(inputs);
  if (ret != mindspore::lite::RET_OK) {
    std::cerr << "Generate Random Input Data failed." << std::endl;
    return ret;
  }

  ret = session->RunGraph();
  if (ret != mindspore::lite::RET_OK) {
    std::cerr << "Inference error " << ret << std::endl;
    return ret;
  }

  auto out_tensors = session->GetOutputs();
  for (auto tensor : out_tensors) {
    std::cout << "tensor name is:" << tensor.first << " tensor size is:" << tensor.second->Size()
              << " tensor elements num is:" << tensor.second->ElementsNum() << std::endl;
    auto out_data = reinterpret_cast<float *>(tensor.second->MutableData());
    std::cout << "output data is:";
    for (int i = 0; i < tensor.second->ElementsNum() && i <= 50; i++) {
      std::cout << out_data[i] << " ";
    }
    std::cout << std::endl;
  }
  return mindspore::lite::RET_OK;
}

内存释放

无需使用MindSpore Lite推理框架时,需要释放已经创建的LiteSessionModel

// 删除模型缓存
delete model;
// 删除session缓存
delete session;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值