HarmonyOS:Neural Network Runtime 对接 AI 推理框架开发指导

2023-12-19

场景介绍

Neural Network Runtime 作为 AI 推理引擎和加速芯片的桥梁,为 AI 推理引擎提供精简的 Native 接口,满足推理引擎通过加速芯片执行端到端推理的需求。

本文以图 1 展示的 Add 单算子模型为例,介绍 Neural Network Runtime 的开发流程。Add 算子包含两个输入、一个参数和一个输出,其中的 activation 参数用于指定 Add 算子中激活函数的类型。

图 1 Add 单算子网络示意图

环境准备

环境要求

Neural Network Runtime 部件的环境要求如下:

● 开发环境:Ubuntu 18.04 及以上。

● 接入设备:HarmonyOS 定义的标准设备,并且系统中内置的硬件加速器驱动,已通过 HDI 接口对接 Neural Network Runtime。

由于 Neural Network Runtime 通过 Native API 对外开放,需要通过 Native 开发套件编译 Neural Network Runtime 应用。

环境搭建

1.  打开 Ubuntu 编译服务器的终端。

2.  把下载好的 Native 开发套件压缩包拷贝至当前用户根目录下。

3.  执行以下命令解压 Native 开发套件的压缩包。

unzip native-linux-{版本号}.zip

解压缩后的内容如下(随版本迭代,目录下的内容可能发生变化,请以最新版本的 Native API 为准):

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 开发流程中通用的接口,具体请见下列表格。

结构体

模型构造相关接口

模型编译相关接口

执行推理相关接口

设备管理相关接口

开发步骤

Neural Network Runtime 的开发流程主要包含模型构造、模型编译和推理执行三个阶段。以下开发步骤以 Add 单算子模型为例,介绍调用 Neural Network Runtime 接口,开发应用的过程。

1.  创建应用样例文件。

首先,创建 Neural Network Runtime 应用样例的源文件。在项目目录下执行以下命令,创建 nnrt_example/目录,在目录下创建 nnrt_example.cpp 源文件。

mkdir ~/nnrt_example && cd ~/nnrt_exampletouch nnrt_example.cpp

2.  导入 Neural Network Runtime。

在 nnrt_example.cpp 文件的开头添加以下代码,引入 Neural Network Runtime 模块。

#include <cstdint>#include <iostream>#include <vector>
#include "neural_network_runtime/neural_network_runtime.h"
// 常量,用于指定输入、输出数据的字节长度const size_t DATA_LENGTH = 4 * 12;

3.  构造模型。

使用 Neural Network Runtime 接口,构造 Add 单算子样例模型。

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 = {&paramIndicesValues, 1};
    OH_NN_UInt32Array inputIndices = {inputIndicesValues, 2};
    OH_NN_UInt32Array outputIndices = {&outputIndicesValues, 1};

    // 向模型实例添加Add算子
    ret = OH_NNModel_AddOperation(model, OH_NN_OPS_ADD, &paramIndices, &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,指定模型编译的芯片。

void GetAvailableDevices(std::vector<size_t>& 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 提供的编译模块,将抽象的模型表达下发至芯片驱动层,转换成可以直接推理计算的格式。

OH_NN_ReturnCode CreateCompilation(OH_NNModel* model, const std::vector<size_t>& availableDevice, OH_NNCompilation** pCompilation)
{
    // 创建编译实例,用于将模型传递至底层硬件编译
    OH_NNCompilation* compilation = OH_NNCompilation_Construct(model);
    if (compilation == nullptr) {
        std::cout << "CreateCompilation failed, error happened 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 的执行模块,创建推理执行器。执行阶段,设置模型输入、获取模型输出和触发推理计算的操作均围绕执行器完成。

OH_NNExecutor* CreateExecutor(OH_NNCompilation* compilation){    // 创建执行实例    OH_NNExecutor* executor = OH_NNExecutor_Construct(compilation);    return executor;}

7.  执行推理计算,并打印计算结果。

通过执行模块提供的接口,将推理计算所需要的输入数据传递给执行器,触发执行器完成一次推理计算,获取模型的推理计算结果。

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 开发流程。

int main(){    OH_NNModel* model = nullptr;    OH_NNCompilation* compilation = nullptr;    OH_NNExecutor* executor = nullptr;    std::vector<size_t> 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 示例:

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。

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.  执行以下代码,将样例推送到设备上执行。

# 将编译得到的 `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"


如果样例执行正常,应该得到以下输出。

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 可以直接加载缓存文件中的计算图,减少编译消耗的时间。

检查缓存目录下的缓存文件:

ls /data/local/tmp

以下为打印结果:

# 0.nncache  cache_info.nncache

如果缓存不再使用,需要手动删除缓存,可以参考以下命令,删除缓存文件。

rm /data/local/tmp/*nncache
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

HarmonyOS:Neural Network Runtime 对接 AI 推理框架开发指导 的相关文章

随机推荐

  • 探索技术备忘录的未来技术趋势与发展方向

    技术备忘录作为一种记录和分享技术知识和经验的工具 已经在许多领域得到广泛应用 然而 随着技术的不断发展和创新 技术备忘录也需要不断适应和演进 本文将探索技术备忘录的未来技术趋势与发展方向 展望其在未来的应用和发展前景 首先 技术备忘录将更加
  • 聚观早报 |苹果自研Wi-Fi 7芯片;新农业品牌“一米八”亮相

    聚观365 12月19日消息 苹果自研Wi Fi 7芯片 新农业品牌 一米八 亮相 李斌亲自直播测试蔚来电池 努比亚Z60 Ultra星空典藏版亮相 董宇辉升为东方甄选高级合伙人 苹果自研Wi Fi 7芯片 据外媒报道 外界普遍认为 在与高
  • Databend 开源周报第 124 期

    Databend 是一款现代云数仓 专为弹性和高效设计 为您的大规模分析需求保驾护航 自由且开源 即刻体验云服务 https app databend cn What s On In Databend 探索 Databend 本周新进展 遇
  • 【手写数字识别】BP神经网络单个或连续手写数字识别系统【含GUI Matlab源码 2296期】

    博主简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 Matlab项目合作可私信 个人主页 海神之光 代码获取方式 海神之光Matlab王者学习之路 代码获取方式 座右铭 行百里者 半于九十 更多Matlab仿真内容点击 Matl
  • 如何把视频转文字?快把这些方法收好

    听说你想找一个好用的视频转文字提取软件 我这边正好有一波精选推荐 毕竟 谁不喜欢将视频中的内容转化为文字 以此方便查阅和编辑呢 让我来点亮你的转文字技能吧 跟我一起探索各种视频转文字提取软件 让你轻松将视频中的对话 演讲或访谈等内容转换为文
  • 11月25日,RPA 学习天地基于UiPath产品公开课,圆满结束,帮助学员掌握RPA能力!

    11月25日 RPA学习天地在UiPath产品的公开课上 成功地帮助学员们掌握了RPA Robotic Process Automation 的能力 这堂课程通过深入浅出的讲解 让学员们了解到了RPA的原理和应用场景 以及如何利用UiPat
  • 演讲技巧大公开:如何打造一场引人入胜的演讲?

    演讲技巧大公开 如何打造一场引人入胜的演讲 在当今社会 演讲已成为一项至关重要的技能 无论是商务会议 学术研讨会 还是日常生活中的交流 演讲都起着重要的作用 然而 要打造一场引人入胜 令人难忘的演讲并不容易 下面 我们将分享几个实用的技巧
  • vue最强table vxe-table 虚拟滚动列表 前端导出

    vxe table 是一个功能强大的 Vue 表格组件 它支持虚拟滚动列表作为其核心功能之一 下面是 vxe table 的虚拟滚动列表功能的使用场景和优势 使用场景 大数据量展示 当表格需要展示大量数据时 传统的表格渲染方式可能会导致页面
  • MySQL安装

    MySQL安装 MySQL在MAC下安装 下载 brew install mysql mysql server 在support files下 启动服务 mysql server start windows启动命令 net start my
  • 【手写数字识别】BP神经网络手写数字识别【含Matlab源码 3133期】

    博主简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 Matlab项目合作可私信 个人主页 海神之光 代码获取方式 海神之光Matlab王者学习之路 代码获取方式 座右铭 行百里者 半于九十 更多Matlab仿真内容点击 Matl
  • feign 配置替换为okhttp

    OpenFeign默认使用的是Spring Cloud自带的HttpURLConnection进行HTTP请求 不支持对请求和响应进行压缩 所以 要么关闭压缩 要么替换为okhttp支持压缩 一种推荐方式 直接配置替换为okhttp 并且直
  • 【手写数字识别】CNN网络手写数字识别分类【含Matlab源码 1286期】

    博主简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 Matlab项目合作可私信 个人主页 海神之光 代码获取方式 海神之光Matlab王者学习之路 代码获取方式 座右铭 行百里者 半于九十 更多Matlab仿真内容点击 Matl
  • DBeaver Ultimate for Mac/win:掌握数据库的终极利器,助您高效管理数据!

    在当今数字化时代 数据管理变得越来越重要 而作为一款功能强大的数据库管理工具 DBeaver Ultimate 简称DBU 助您轻松应对各种复杂的数据管理任务 无论您是数据库管理员 开发人员还是数据分析师 DBU都能为您提供全面的解决方案
  • java连接oracle出现ORA-12505错误

    问题 sqlplus可以连接 但java连接报错 ORA 12505 ORA 12505 TNS listener does not currently know of SID given in connect descr 解析 原因 数据
  • 新华发电:以财务共享建设为抓手, 构建世界一流财务管理体系

    作者 杨军 王俊锋 刘永伟 供职于新华水力发电有限公司 来源 新理财 公司理财 杂志2023年11月刊 编辑 滕娟 新华发电将财务共享建设作为推动财务数智化 建设世界一流财务管理体系的突破口 以构建具有企业自身特色的财务管理理论和财务管理模
  • 控费用,数字化支出管理让大型企业营销费用管理精细化

    在当今竞争激烈的市场环境中 企业营销费用的合理支出与管理显得尤为重要 然而 营销费用的支出不断增加及管理不善常常成为企业的一大挑战 01 企业营销费用支出的机遇及挑战 企业营销费用主要包括广告费 促销费 市场调研费 营销人员薪酬等 大部分企
  • thinkphp+vue教学互动课程资源网站

    软件工程课程建设通篇文章的撰写基础是实际的应用需要 然后在架构系统之前全面复习大学所修习的相关知识以及网络提供的技术应用教程 以软件工程课程建设的实际应用需要出发 架构系统来改善现软件工程课程建设工作流程繁琐等问题 不仅如此以操作者的角度来
  • 【分享】5种方法将Excel设置为“只读”

    将Excel表格设置以 只读方式 打开 可以提醒或者防止表格被随意改动 今天小编来分享一下将Excel设置为 只读 的5种方法 方法一 通过 保护工作簿 设置 首先 打开Excel表格依次点击菜单选项卡 文件 信息 保护工作簿 始终以只读方
  • Thinkphp+vue中小学编程教育培训学习网页端软件设计与实现7r1ci

    要内容 研究方法和思路 总体安排和进度 包括阶段性工作内容及完成日期 1 主要内容 本课题主要注重少儿编程教育平台的设计与实现 主要分为以下三方面 1 前台功能需求 基础功能 课程功能 互动功能 2 后台功能需求 课程管理功能 机构管理功能
  • HarmonyOS:Neural Network Runtime 对接 AI 推理框架开发指导

    场景介绍 Neural Network Runtime 作为 AI 推理引擎和加速芯片的桥梁 为 AI 推理引擎提供精简的 Native 接口 满足推理引擎通过加速芯片执行端到端推理的需求 本文以图 1 展示的 Add 单算子模型为例 介绍