From 9e8fb924d0196970a175d0116a682de4f8491757 Mon Sep 17 00:00:00 2001 From: yuhanshi Date: Wed, 26 Oct 2022 11:02:26 +0800 Subject: [PATCH] docs: add guidelines of neural network runtime Signed-off-by: yuhanshi --- zh-cn/application-dev/napi/Readme-CN.md | 1 + zh-cn/application-dev/napi/figures/02.png | Bin 0 -> 43311 bytes .../napi/neural-network-runtime-guidelines.md | 491 ++++++++++++++++++ 3 files changed, 492 insertions(+) create mode 100644 zh-cn/application-dev/napi/figures/02.png create mode 100644 zh-cn/application-dev/napi/neural-network-runtime-guidelines.md diff --git a/zh-cn/application-dev/napi/Readme-CN.md b/zh-cn/application-dev/napi/Readme-CN.md index 99e16d5ce9..e6626f0373 100644 --- a/zh-cn/application-dev/napi/Readme-CN.md +++ b/zh-cn/application-dev/napi/Readme-CN.md @@ -5,3 +5,4 @@ - [Rawfile开发指导](rawfile-guidelines.md) - [Window开发指导](native-window-guidelines.md) - [使用MindSpore Lite引擎进行模型推理](mindspore-lite-guidelines.md) +- [Neural Network Runtime对接AI推理框架开发指导](neural-network-runtime-guidelines.md) \ No newline at end of file diff --git a/zh-cn/application-dev/napi/figures/02.png b/zh-cn/application-dev/napi/figures/02.png new file mode 100644 index 0000000000000000000000000000000000000000..1e31c1ce7f4e93ed9bd2e8acda0facce7a9a00f7 GIT binary patch literal 43311 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 literal 0 HcmV?d00001 diff --git a/zh-cn/application-dev/napi/neural-network-runtime-guidelines.md b/zh-cn/application-dev/napi/neural-network-runtime-guidelines.md new file mode 100644 index 0000000000..d1db02f381 --- /dev/null +++ b/zh-cn/application-dev/napi/neural-network-runtime-guidelines.md @@ -0,0 +1,491 @@ +# Neural Network Runtime对接AI推理框架开发指导 + +## 场景介绍 + +Neural Network Runtime作为AI推理引擎和加速芯片的桥梁,为AI推理引擎提供精简的Native接口,满足推理引擎通过加速芯片执行端到端推理的需求。 + +本文以图1展示的`Add`单算子模型为例,介绍Neural Network Runtime的开发流程。`Add`算子包含两个输入、一个参数和一个输出,其中的`activation`参数用于指定`Add`算子中激活函数的类型。 + +**图1** Add单算子网络示意图 +!["Add单算子网络示意图"](figures/02.png) + +## 环境准备 + +### 环境要求 + +Neural Network Runtime部件的环境要求如下: + +- 系统版本:OpenHarmony master分支。 +- 开发环境:Ubuntu 18.04及以上。 +- 接入设备:OpenHarmony定义的标准设备,并且系统中内置的硬件加速器驱动,已通过HDI接口对接Neural Network Runtime。 + +由于Neural Network Runtime通过OpenHarmony Native API对外开放,需要通过OpenHarmony的Native开发套件编译Neural Network Runtime应用。在社区的[每日构建](http://ci.openharmony.cn/dailys/dailybuilds)下载对应系统版本的ohos-sdk压缩包,从压缩包中提取对应平台的Native开发套件。以Linux为例,Native开发套件的压缩包命名为`native-linux-{版本号}.zip`。 + +### 环境搭建 + +1. 打开Ubuntu编译服务器的终端。 +2. 把下载好的Native开发套件压缩包拷贝至当前用户根目录下。 +3. 执行以下命令解压Native开发套件的压缩包。 +```shell +unzip native-linux-{版本号}.zip +``` + +解压缩后的内容如下(随版本迭代,目录下的内容可能发生变化,请以最新版本的Native API为准): +```text +native/ +├── build // 交叉编译工具链 +├── build-tools // 编译构建工具 +├── docs +├── llvm +├── nativeapi_syscap_config.json +├── ndk_system_capability.json +├── NOTICE.txt +├── oh-uni-package.json +└── sysroot // Native API头文件和库 +``` +## 接口说明 + +这里给出Neural Network Runtime开发流程中通用的接口,具体请见下列表格。 + +### 结构体 + +| 结构体名称 | 描述 | +| --------- | ---- | +| typedef struct OH_NNModel OH_NNModel | Neural Network Runtime的模型句柄,用于构造模型。 | +| typedef struct OH_NNCompilation OH_NNCompilation | Neural Network Runtime的编译器句柄,用于编译AI模型。 | +| typedef struct OH_NNExecutor OH_NNExecutor | Neural Network Runtime的执行器句柄,用于在指定设备上执行推理计算。 | + +### 模型构造相关接口 + +| 接口名称 | 描述 | +| ------- | --- | +| OH_NNModel_Construct() | 创建OH_NNModel类型的模型实例。 | +| OH_NN_ReturnCode OH_NNModel_AddTensor(OH_NNModel *model, const OH_NN_Tensor *tensor) | 向模型实例中添加张量。 | +| OH_NN_ReturnCode OH_NNModel_SetTensorData(OH_NNModel *model, uint32_t index, const void *dataBuffer, size_t length) | 设置张量的数值。 | +| OH_NN_ReturnCode OH_NNModel_AddOperation(OH_NNModel *model, OH_NN_OperationType op, const OH_NN_UInt32Array *paramIndices, const OH_NN_UInt32Array *inputIndices, const OH_NN_UInt32Array *outputIndices) | 向模型实例中添加算子。 | +| OH_NN_ReturnCode OH_NNModel_SpecifyInputsAndOutputs(OH_NNModel *model, const OH_NN_UInt32Array *inputIndices, const OH_NN_UInt32Array *outputIndices) | 指定模型的输入输出。 | +| OH_NN_ReturnCode OH_NNModel_Finish(OH_NNModel *model) | 完成模型构图。| +| void OH_NNModel_Destroy(OH_NNModel **model) | 释放模型实例。 | + +### 模型编译相关接口 + +| 接口名称 | 描述 | +| ------- | --- | +| OH_NNCompilation *OH_NNCompilation_Construct(const OH_NNModel *model) | 创建OH_NNCompilation类型的编译实例。 | +| OH_NN_ReturnCode OH_NNCompilation_SetDevice(OH_NNCompilation *compilation, size_t deviceID) | 指定模型编译和计算的硬件。 | +| OH_NN_ReturnCode OH_NNCompilation_SetCache(OH_NNCompilation *compilation, const char *cachePath, uint32_t version) | 设置编译后的模型缓存路径和缓存版本。 | +| OH_NN_ReturnCode OH_NNCompilation_Build(OH_NNCompilation *compilation) | 进行模型编译。 | +| void OH_NNCompilation_Destroy(OH_NNCompilation **compilation) | 释放OH_NNCompilation对象。 | + +### 执行推理相关接口 + +| 接口名称 | 描述 | +| ------- | --- | +| OH_NNExecutor *OH_NNExecutor_Construct(OH_NNCompilation *compilation) | 创建OH_NNExecutor类型的执行器实例。 | +| OH_NN_ReturnCode OH_NNExecutor_SetInput(OH_NNExecutor *executor, uint32_t inputIndex, const OH_NN_Tensor *tensor, const void *dataBuffer, size_t length) | 设置模型单个输入的数据。 | +| OH_NN_ReturnCode OH_NNExecutor_SetOutput(OH_NNExecutor *executor, uint32_t outputIndex, void *dataBuffer, size_t length) | 设置模型单个输出的缓冲区。 | +| OH_NN_ReturnCode OH_NNExecutor_Run(OH_NNExecutor *executor) | 执行推理。 | +| void OH_NNExecutor_Destroy(OH_NNExecutor **executor) | 销毁OH_NNExecutor实例,释放实例占用的内存。 | + +### 设备管理相关接口 + +| 接口名称 | 描述 | +| ------- | --- | +| OH_NN_ReturnCode OH_NNDevice_GetAllDevicesID(const size_t **allDevicesID, uint32_t *deviceCount) | 获取对接到 Neural Network Runtime 的硬件ID。 | + + +## 开发步骤 + +Neural Network Runtime的开发流程主要包含**模型构造**、**模型编译**和**推理执行**三个阶段。以下开发步骤以`Add`单算子模型为例,介绍调用Neural Network Runtime接口,开发应用的过程。 + +1. 创建应用样例文件。 + + 首先,创建Neural Network Runtime应用样例的源文件。在项目目录下执行以下命令,创建`nnrt_example/`目录,在目录下创建 `nnrt_example.cpp` 源文件。 + + ```shell + mkdir ~/nnrt_example && cd ~/nnrt_example + touch nnrt_example.cpp + ``` + +2. 导入Neural Network Runtime。 + + 在 `nnrt_example.cpp` 文件的开头添加以下代码,引入Neural Network Runtime模块。 + + ```cpp + #include + #include + #include + + #include "neural_network_runtime/neural_network_runtime.h" + + // 常量,用于指定输入、输出数据的字节长度 + const size_t DATA_LENGTH = 4 * 12; + ``` + +3. 构造模型。 + + 使用Neural Network Runtime接口,构造`Add`单算子样例模型。 + + ```cpp + OH_NN_ReturnCode BuildModel(OH_NNModel** pModel) + { + // 创建模型实例,进行模型构造 + OH_NNModel* model = OH_NNModel_Construct(); + if (model == nullptr) { + std::cout << "Create model failed." << std::endl; + return OH_NN_MEMORY_ERROR; + } + + // 添加Add算子的第一个输入Tensor,类型为float32,张量形状为[1, 2, 2, 3] + int32_t inputDims[4] = {1, 2, 2, 3}; + OH_NN_Tensor input1 = {OH_NN_FLOAT32, 4, inputDims, nullptr, OH_NN_TENSOR}; + OH_NN_ReturnCode ret = OH_NNModel_AddTensor(model, &input1); + if (ret != OH_NN_SUCCESS) { + std::cout << "BuildModel failed, add Tensor of first input failed." << std::endl; + return ret; + } + + // 添加Add算子的第二个输入Tensor,类型为float32,张量形状为[1, 2, 2, 3] + OH_NN_Tensor input2 = {OH_NN_FLOAT32, 4, inputDims, nullptr, OH_NN_TENSOR}; + ret = OH_NNModel_AddTensor(model, &input2); + if (ret != OH_NN_SUCCESS) { + std::cout << "BuildModel failed, add Tensor of second input failed." << std::endl; + return ret; + } + + // 添加Add算子的参数Tensor,该参数Tensor用于指定激活函数的类型,Tensor的数据类型为int8。 + int32_t activationDims = 1; + int8_t activationValue = OH_NN_FUSED_NONE; + OH_NN_Tensor activation = {OH_NN_INT8, 1, &activationDims, nullptr, OH_NN_ADD_ACTIVATIONTYPE}; + ret = OH_NNModel_AddTensor(model, &activation); + if (ret != OH_NN_SUCCESS) { + std::cout << "BuildModel failed, add Tensor of activation failed." << std::endl; + return ret; + } + + // 将激活函数类型设置为OH_NN_FUSED_NONE,表示该算子不添加激活函数。 + ret = OH_NNModel_SetTensorData(model, 2, &activationValue, sizeof(int8_t)); + if (ret != OH_NN_SUCCESS) { + std::cout << "BuildModel failed, set value of activation failed." << std::endl; + return ret; + } + + // 设置Add算子的输出,类型为float32,张量形状为[1, 2, 2, 3] + OH_NN_Tensor output = {OH_NN_FLOAT32, 4, inputDims, nullptr, OH_NN_TENSOR}; + ret = OH_NNModel_AddTensor(model, &output); + if (ret != OH_NN_SUCCESS) { + std::cout << "BuildModel failed, add Tensor of output failed." << std::endl; + return ret; + } + + // 指定Add算子的输入、参数和输出索引 + uint32_t inputIndicesValues[2] = {0, 1}; + uint32_t paramIndicesValues = 2; + uint32_t outputIndicesValues = 3; + OH_NN_UInt32Array paramIndices = {¶mIndicesValues, 1}; + OH_NN_UInt32Array inputIndices = {inputIndicesValues, 2}; + OH_NN_UInt32Array outputIndices = {&outputIndicesValues, 1}; + + // 向模型实例添加Add算子 + ret = OH_NNModel_AddOperation(model, OH_NN_OPS_ADD, ¶mIndices, &inputIndices, &outputIndices); + if (ret != OH_NN_SUCCESS) { + std::cout << "BuildModel failed, add operation failed." << std::endl; + return ret; + } + + // 设置模型实例的输入、输出索引 + ret = OH_NNModel_SpecifyInputsAndOutputs(model, &inputIndices, &outputIndices); + if (ret != OH_NN_SUCCESS) { + std::cout << "BuildModel failed, specify inputs and outputs failed." << std::endl; + return ret; + } + + // 完成模型实例的构建 + ret = OH_NNModel_Finish(model); + if (ret != OH_NN_SUCCESS) { + std::cout << "BuildModel failed, error happened when finishing model construction." << std::endl; + return ret; + } + + *pModel = model; + return OH_NN_SUCCESS; + } + ``` + +4. 查询Neural Network Runtime已经对接的加速芯片。 + + Neural Network Runtime支持通过HDI接口,对接多种加速芯片。在执行模型编译前,需要查询当前设备下,Neural Network Runtime已经对接的加速芯片。每个加速芯片对应唯一的ID值,在编译阶段需要通过设备ID,指定模型编译的芯片。 + ```cpp + void GetAvailableDevices(std::vector& availableDevice) + { + availableDevice.clear(); + + // 获取可用的硬件ID + const size_t* devices = nullptr; + uint32_t deviceCount = 0; + OH_NN_ReturnCode ret = OH_NNDevice_GetAllDevicesID(&devices, &deviceCount); + if (ret != OH_NN_SUCCESS) { + std::cout << "GetAllDevicesID failed, get no available device." << std::endl; + return; + } + + for (uint32_t i = 0; i < deviceCount; i++) { + availableDevice.emplace_back(devices[i]); + } + } + ``` + +5. 在指定的设备上编译模型。 + + Neural Network Runtime使用抽象的模型表达描述AI模型的拓扑结构,在加速芯片上执行前,需要通过Neural Network Runtime提供的编译模块,将抽象的模型表达下发至芯片驱动层,转换成可以直接推理计算的格式。 + ```cpp + OH_NN_ReturnCode CreateCompilation(OH_NNModel* model, const std::vector& availableDevice, OH_NNCompilation** pCompilation) + { + // 创建编译实例,用于将模型传递至底层硬件编译 + OH_NNCompilation* compilation = OH_NNCompilation_Construct(model); + if (compilation == nullptr) { + std::cout << "CreateCompilation failed, error happended when creating compilation." << std::endl; + return OH_NN_MEMORY_ERROR; + } + + // 设置编译的硬件、缓存路径、性能模式、计算优先级、是否开启float16低精度计算等选项 + + // 选择在第一个设备上编译模型 + OH_NN_ReturnCode ret = OH_NNCompilation_SetDevice(compilation, availableDevice[0]); + if (ret != OH_NN_SUCCESS) { + std::cout << "CreateCompilation failed, error happened when setting device." << std::endl; + return ret; + } + + // 将模型编译结果缓存在/data/local/tmp目录下,版本号指定为1 + ret = OH_NNCompilation_SetCache(compilation, "/data/local/tmp", 1); + if (ret != OH_NN_SUCCESS) { + std::cout << "CreateCompilation failed, error happened when setting cache path." << std::endl; + return ret; + } + + // 完成编译设置,进行模型编译 + ret = OH_NNCompilation_Build(compilation); + if (ret != OH_NN_SUCCESS) { + std::cout << "CreateCompilation failed, error happened when building compilation." << std::endl; + return ret; + } + + *pCompilation = compilation; + return OH_NN_SUCCESS; + } + ``` + +6. 创建执行器。 + + 完成模型编译后,需要调用Neural Network Runtime的执行模块,创建推理执行器。执行阶段,设置模型输入、获取模型输出和触发推理计算的操作均围绕执行器完成。 + ```cpp + OH_NNExecutor* CreateExecutor(OH_NNCompilation* compilation) + { + // 创建执行实例 + OH_NNExecutor* executor = OH_NNExecutor_Construct(compilation); + return executor; + } + ``` + +7. 执行推理计算,并打印计算结果。 + + 通过执行模块提供的接口,将推理计算所需要的输入数据传递给执行器,触发执行器完成一次推理计算,获取模型的推理计算结果。 + ```cpp + OH_NN_ReturnCode Run(OH_NNExecutor* executor) + { + // 构造示例数据 + float input1[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + float input2[12] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}; + + int32_t inputDims[4] = {1, 2, 2, 3}; + OH_NN_Tensor inputTensor1 = {OH_NN_FLOAT32, 4, inputDims, nullptr, OH_NN_TENSOR}; + OH_NN_Tensor inputTensor2 = {OH_NN_FLOAT32, 4, inputDims, nullptr, OH_NN_TENSOR}; + + // 设置执行的输入 + + // 设置执行的第一个输入,输入数据由input1指定 + OH_NN_ReturnCode ret = OH_NNExecutor_SetInput(executor, 0, &inputTensor1, input1, DATA_LENGTH); + if (ret != OH_NN_SUCCESS) { + std::cout << "Run failed, error happened when setting first input." << std::endl; + return ret; + } + + // 设置执行的第二个输入,输入数据由input2指定 + ret = OH_NNExecutor_SetInput(executor, 1, &inputTensor2, input2, DATA_LENGTH); + if (ret != OH_NN_SUCCESS) { + std::cout << "Run failed, error happened when setting second input." << std::endl; + return ret; + } + + // 设置输出的数据缓冲区,OH_NNExecutor_Run执行计算后,输出结果将保留在output中 + float output[12]; + ret = OH_NNExecutor_SetOutput(executor, 0, output, DATA_LENGTH); + if (ret != OH_NN_SUCCESS) { + std::cout << "Run failed, error happened when setting output buffer." << std::endl; + return ret; + } + + // 执行计算 + ret = OH_NNExecutor_Run(executor); + if (ret != OH_NN_SUCCESS) { + std::cout << "Run failed, error doing execution." << std::endl; + return ret; + } + + // 打印输出结果 + for (uint32_t i = 0; i < 12; i++) { + std::cout << "Output index: " << i << ", value is: " << output[i] << "." << std::endl; + } + + return OH_NN_SUCCESS; + } + ``` + +8. 构建端到端模型构造-编译-执行流程。 + + 步骤3-步骤7实现了模型的模型构造、编译和执行流程,并封装成4个函数,便于模块化开发。以下示例代码将4个函数串联成完整的Neural Network Runtime开发流程。 + ```cpp + int main() + { + OH_NNModel* model = nullptr; + OH_NNCompilation* compilation = nullptr; + OH_NNExecutor* executor = nullptr; + std::vector availableDevices; + + // 模型构造阶段 + OH_NN_ReturnCode ret = BuildModel(&model); + if (ret != OH_NN_SUCCESS) { + std::cout << "BuildModel failed." << std::endl; + OH_NNModel_Destroy(&model); + return -1; + } + + // 获取可执行的设备 + GetAvailableDevices(availableDevices); + if (availableDevices.empty()) { + std::cout << "No available device." << std::endl; + OH_NNModel_Destroy(&model); + return -1; + } + + // 模型编译阶段 + ret = CreateCompilation(model, availableDevices, &compilation); + if (ret != OH_NN_SUCCESS) { + std::cout << "CreateCompilation failed." << std::endl; + OH_NNModel_Destroy(&model); + OH_NNCompilation_Destroy(&compilation); + return -1; + } + + // 创建模型的推理执行器 + executor = CreateExecutor(compilation); + if (executor == nullptr) { + std::cout << "CreateExecutor failed, no executor is created." << std::endl; + OH_NNModel_Destroy(&model); + OH_NNCompilation_Destroy(&compilation); + return -1; + } + + // 使用上一步创建的执行器,执行单步推理计算 + ret = Run(executor); + if (ret != OH_NN_SUCCESS) { + std::cout << "Run failed." << std::endl; + OH_NNModel_Destroy(&model); + OH_NNCompilation_Destroy(&compilation); + OH_NNExecutor_Destroy(&executor); + return -1; + } + + // 释放申请的资源 + OH_NNModel_Destroy(&model); + OH_NNCompilation_Destroy(&compilation); + OH_NNExecutor_Destroy(&executor); + + return 0; + } + ``` + +## 调测验证 + +1. 准备应用样例的编译配置文件。 + + 新建一个 `CMakeLists.txt` 文件,为开发步骤中的应用样例文件 `nnrt_example.cpp` 添加编译配置。以下提供简单的 `CMakeLists.txt` 示例: + ```text + cmake_minimum_required(VERSION 3.16) + project(nnrt_example C CXX) + + add_executable(nnrt_example + ./nnrt_example.cpp + ) + + target_link_libraries(nnrt_example + neural_network_runtime.z + ) + ``` + +2. 编译应用样例。 + + 执行以下命令,在当前目录下新建build/目录,在build/目录下编译 `nnrt_example.cpp`,得到二进制文件 `nnrt_example`。 + ```shell + mkdir build && cd build + cmake -DCMAKE_TOOLCHAIN_FILE={交叉编译工具链的路径}/build/cmake/ohos.toolchain.cmake -DOHOS_ARCH=arm64-v8a -DOHOS_PLATFORM=OHOS -DOHOS_STL=c++_static .. + make + ``` + +3. 执行以下代码,将样例推送到设备上执行。 + ```shell + # 将编译得到的 `nnrt_example` 推送到设备上,执行样例。 + hdc_std file send ./nnrt_example /data/local/tmp/. + + # 给测试用例可执行文件加上权限。 + hdc_std shell "chmod +x /data/local/tmp/nnrt_example" + + # 执行测试用例 + hdc_std shell "/data/local/tmp/nnrt_example" + ``` + + 如果样例执行正常,应该得到以下输出。 + ```text + Output index: 0, value is: 11.000000. + Output index: 1, value is: 13.000000. + Output index: 2, value is: 15.000000. + Output index: 3, value is: 17.000000. + Output index: 4, value is: 19.000000. + Output index: 5, value is: 21.000000. + Output index: 6, value is: 23.000000. + Output index: 7, value is: 25.000000. + Output index: 8, value is: 27.000000. + Output index: 9, value is: 29.000000. + Output index: 10, value is: 31.000000. + Output index: 11, value is: 33.000000. + ``` + +4. 检查模型缓存(可选)。 + + 如果在调测环境下,Neural Network Runtime对接的HDI服务支持模型缓存功能,执行完 `nnrt_example`, 可以在 `/data/local/tmp` 目录下找到生成的缓存文件。 + + > **说明:** + > + > 模型的IR需要传递到硬件驱动层,由HDI服务将统一的IR图,编译成硬件专用的计算图,编译的过程非常耗时。Neural Network Runtime支持计算图缓存的特性,可以将HDI服务编译生成的计算图,缓存到设备存储中。当下一次在同一个加速芯片上编译同一个模型时,通过指定缓存的路径,Neural Network Runtime可以直接加载缓存文件中的计算图,减少编译消耗的时间。 + + 检查缓存目录下的缓存文件: + ```shell + ls /data/local/tmp + ``` + + 以下为打印结果: + ```text + # 0.nncache cache_info.nncache + ``` + + 如果缓存不再使用,需要手动删除缓存,可以参考以下命令,删除缓存文件。 + ```shell + rm /data/local/tmp/*nncache + ``` + +## 相关实例 + +第三方AI推理框架对接Neural Network Runtime的流程,可以参考以下相关实例: +- [Tensorflow Lite接入NNRt Delegate开发指南](https://gitee.com/openharmony/neural_network_runtime/tree/master/example/deep_learning_framework) + -- GitLab