×

签到

分享到微信

打开微信,使用扫一扫进入页面后,点击右上角菜单,

点击“发送给朋友”或“分享到朋友圈”完成分享

寒武纪MLU离线模型使用教程 leyi2023-08-03 16:25:59 回复 查看 经验交流
寒武纪MLU离线模型使用教程
分享到:

知乎链接:寒武纪MLU离线模型使用教程 - 知乎 (zhihu.com)


首先,目前关于MLU相关的开发资料,主要集中在寒武纪官网开发者社区,可以查看相关的在线实验以及用户手册进行学习。MLU支持PyTorch、Caffe、Tensorflow等框架的模型移植,官网上有不少具体的在线实验,可以实践学习,其中可能出现一些小问题也都好解决。今天这篇文章主要聚焦于,获得一个移植模型的离线文件后,如何使用它。

环境准备:

  1. Linux系统

  2. 安装好MLU相关驱动,并且部署好CNRT。(具体安装方法见官网的手册,可用cnmon命令查看MLU的使用情况;CNRT头文件位于 /usr/local/neuware/include 目录下,动态链接库位于 /usr/local/neuware/lib64 或 /usr/local/neuware/lib 目录下。)

  3. 已经移植好的模型文件model.cambriconmodel.cambricon_twins(用cat ./model.cambricon_twins可以查看离线模型的具体信息)

  4. CMake(用于编译)

  5. glog(可选,用于日志记录)

编写CMakeLists.txt:

cmake_minimum_required(VERSION 3.10 FATAL_ERROR)project(demoproj VERSION 1.0)# 添加cnrtinclude_directories(/usr/local/neuware/include) _directories("/usr/local/neuware/lib64") _libraries(cnrt)# 添加gloginclude_directories(/usr/include) _directories("/usr/lib/x86_64-linux-gnu") _libraries(glog)add_executable(democnrt main.cpp)target_ _libraries(democnrt glog)set_property(TARGET democnrt PROPERTY CXX_STANDARD 14)

编写main.cpp文件

引入相关的头文件,

#include <sys/time.h>#include "glog/logging.h"#include <cnrt.h>#include <fstream>#include <iostream>#include <sstream>#include <string>#include <vector>

int main(int argc, char* argv[])中,首先初始化glog,能够在终端显示日志信息。

google::InitGoogleLogging(argv[0]);
FLAGS_alsologtostderr = 1;

随后初始化device,这里假定有两个MLU device,跟CUDA类似,这里设置它们的id即可,主要设置目前所用的device。

// Initializes device// Assuming there are multiple MLU devicescnrtInit(0);int curr_device_id = 0, ctx_device_id = 1;cnrtDev_t dev;cnrtGetDeviceHandle(&dev, curr_device_id);cnrtSetCurrentDevice(dev);

随后加载已有的离线模型,这里的model是指cnrt模型,也就是离线的.cambricon文件,这里的function就是指模型中的网络,是通过储存在.cambricon中的名称来区分的,可以通过查看离线模型信息来获得function的名称。

// Loads model and extracts function// Assuming the model is an immutable model filecnrtModel_t model;cnrtFunction_t function;std::string fname = "/your/path/to/model.cambricon";std::string symbol = "your_func_name";LOG(INFO) << "load file: " << fname;cnrtLoadModel(&model, fname.c_str());cnrtCreateFunction(&function);cnrtExtractFunction(&function, model, symbol.c_str());

随后创建一个CNRT context用于运行model,然后创建cnrtQueue储存运行的事件,创建cnrtNotifier用于记录信息,这些都是标准步骤,除了deviceId需要根据情况改变,其他基本都是标准。

// Creates CNRT Context and sets device configurationcnrtRuntimeContext_t ctx;cnrtCreateRuntimeContext(&ctx, function, NULL);cnrtSetRuntimeContextDeviceId(ctx, ctx_device_id);// Initailizes CNRT ContextcnrtInitRuntimeContext(ctx, NULL);// Creates queue and notifier from CNRT Context as the following two methods// which you can choose one to achievecnrtQueue_t queue;cnrtNotifier_t notifier_start = NULL, notifier_end = NULL;// By CNRT Context functionscnrtRuntimeContextCreateQueue(ctx, &queue);cnrtRuntimeContextCreateNotifier(ctx, &notifier_start);cnrtRuntimeContextCreateNotifier(ctx, &notifier_end);// create start_event and end_eventcnrtCreateQueue(&queue);cnrtCreateNotifier(&notifier_start);cnrtCreateNotifier(&notifier_end);

接下来,需要处理输入和输出,先获取输入、输出数据的大小和类型,分配对应内存空间,并且从输入文件中读取输入数据,放在CPU上的array中。

int input_num, output_num;int64_t *input_size_array, *output_size_array;// Data sizecnrtGetInputDataSize(&input_size_array, &input_num, function);cnrtGetOutputDataSize(&output_size_array, &output_num, function);cnrtDataType_t* input_data_type = nullptr;cnrtDataType_t* output_data_type = nullptr;// Data TypecnrtGetInputDataType(&input_data_type, &input_num, function);cnrtGetOutputDataType(&output_data_type, &output_num, function);// input array and output arrayvoid** input_cpu_ptr_array = reinterpret_cast<void**>(malloc(sizeof(void*) * input_num));void** output_cpu_ptr_array = reinterpret_cast<void**>(malloc(sizeof(void*) * output_num));for (int i = 0; i < input_num; i++){
    /* process your data to input_cpu_ptr_array */
    int ip = input_size_array[i] / cnrtDataTypeSize(input_data_type[i]);
    auto databuf = reinterpret_cast<float*>(malloc(sizeof(float) * ip));
    /* read your data to databuf */
    input_cpu_ptr_array[i] = reinterpret_cast<void*>(databuf);}for (int i = 0; i < output_num; i++){
    int op = output_size_array[i] / cnrtDataTypeSize(output_data_type[i]);
    float* outcpu = reinterpret_cast<float*>(malloc(op * sizeof(float)));
    output_cpu_ptr_array[i] = reinterpret_cast<void*>(outcpu);}// if data's dimension not match, should process them uniform

接下来,需要在MLU上为输入和输出分配空间,并把输入从CPU上复制到GPU上。

void **input_mlu_ptr_array = (void **)malloc(sizeof(void *) * input_num);void **output_mlu_ptr_array = (void **)malloc(sizeof(void *) * output_num);for (int i = 0; i < input_num; i++) {
    cnrtMalloc(&(input_mlu_ptr_array[i]), input_size_array[i]);
    cnrtMemcpy(input_mlu_ptr_array[i], input_cpu_ptr_array_reshape[i], input_size_array[i],
               CNRT_MEM_TRANS_DIR_HOST2DEV);}for (int i = 0; i < output_num; i++) {
    cnrtMalloc(&(output_mlu_ptr_array[i]), output_size_array[i]);}LOG(INFO) << "cnrtMalloc input and output ";

把MLU上的输入和输出放到一起

// Prepares parameters for cnrtInvokeRuntimeContext// input_mlu_ptr_array and output_mlu_ptr_array are device addresses// of all input and output tensorvoid **param = (void **)malloc(sizeof(void *) * (input_num + output_num));for (int i = 0; i < input_num; ++i) {param[i] = input_mlu_ptr_array[i];}for (int j = 0; j < output_num; ++j) {param[input_num + j] = output_mlu_ptr_array[j];}

接下来,运行CNRT Context,得到model的输出,这一部分也是标准步骤。

// Invokes CNRT Context and gets time of invoking time as the following two methodsfloat *ptv = (float *)malloc(sizeof(float));cnrtPlaceNotifier(notifier_start, queue);CNRT_CHECK(cnrtInvokeRuntimeContext(ctx, param, queue, NULL));cnrtPlaceNotifier(notifier_end, queue);cnrtSyncQueue(queue);cnrtNotifierDuration(notifier_start, notifier_end, ptv);LOG(INFO) << "Invoking CNRT Context time is: " << *ptv;

然后把MLU上的输出复制到CPU上,再进行后续处理(输出prediction或者写入文件等)

void** final_output = reinterpret_cast<void**>(malloc(sizeof(void*) * output_num));for (int i = 0; i < output_num; i++){
    cnrtMemcpy(output_cpu_ptr_array[i], 
                output_mlu_ptr_array[i], 
                output_size_array[i],
                CNRT_MEM_TRANS_DIR_DEV2HOST);}// process output_cpu_ptr_array to final_output

最后释放掉对应资源即可。

for (int i = 0; i < input_num; i++) {
    cnrtFree(input_mlu_ptr_array[i]);}for (int i = 0; i < output_num; i++) {
    cnrtFree(output_mlu_ptr_array[i]);}cnrtDestroyQueue(queue);cnrtDestroyNotifier(&notifier_start);cnrtDestroyNotifier(&notifier_end);cnrtDestroyRuntimeContext(ctx);cnrtDestroyFunction(function);cnrtUnloadModel(model);cnrtDestroy();free(param);

至此,就跑通了一个MLU的离线模型,总结下来,一般开发者主要要做的就是1.对输入数据进行处理,保证交给离线模型的数据是正确的。2.对输出数据进行处理,把网络输出按照需求进行对应加工。


版权所有 © 2024 寒武纪 Cambricon.com 备案/许可证号:京ICP备17003415号-1
关闭