神经网络运行时部件(NNRt)是跨设备的AI运行时框架,作为端侧推理框架和专用加速芯片的中间桥梁,为端侧推理框架提供了统一的Native接口。
本demo旨在介绍TensorFlow Lite推理框架如何接入NNRt,并在专有芯片上加速推理,接入OpenHarmony社区生态。
本demo根据用户输入参数(模型、标签、模型输入shape、循环浮点推理次数、是否允许动态尺寸推理、以及是否打印结果等)完成标签分类模型推理,用户可通过打印信息观察在不同条件下的模型推理性能、精度和预测类别。
在开发前,开发者需要先了解以下概念,以便更好地理解全文内容:
图1 运作机制
以TensorFlow lite的MobileNetv2模型进行标签分类任务为例,实现调用NNRt API在指定芯片上加速推理,主要有以下三个部分:
图2 开发流程
主要开发步骤包括命令行参数解析、创建NNRt Delegate、TFLite nodes的替换、tensors的内存分配、执行推理、结果查看等,具体如下:
本节主要描述NNRt接入TFLite的TFLite-delegate代理机制,重点对TFLite调用delegate的流程和delegate对接NNRt的方式进行了介绍。 TensorFlow Lite Delegate有两个基类DelegateProvider、TfLiteDelegate,本节主要描述继承这两个基类得到子类NnrtDelegate和NnrtDelegateProvider。
本demo主要文件目录结构如下图:
.
├── CMakeLists.txt
├── delegates
│ └── nnrt_delegate
│ ├── CMakeLists.txt # 生成libnnrt_delegate.so的交叉编译规则文件
│ ├── nnrt_delegate.cpp # NnrtDelegate源文件,对接到NNRt上,使TensorFlow Lite模型能运行在加速芯片上
│ ├── nnrt_delegate.h # NnrtDelegate头文件
│ ├── nnrt_delegate_kernel.cpp # NnrtDelegateKernel源文件,将TensorFlow Lite模型中的operators替换成Nnrt中的operators
│ ├── nnrt_delegate_kernel.h # NnrtDelegateKernel头文件
│ ├── nnrt_delegate_provider.cpp # 用于创建NNrtDelegate
│ ├── nnrt_op_builder.cpp # NnrtOpBuilder源文件,给每个operators设置输入输出tensor和operation属性
│ ├── nnrt_op_builder.h # NnrtOpBuilder头文件
│ ├── nnrt_utils.cpp # 用于辅助创建NnrtDelegate工具方法的源文件
│ ├── nnrt_utils.h # 用于辅助创建NnrtDelegate工具方法的头文件
│ └── tensor_mapping.h # TensorFlow Lite Tensor到Nnrt tensor的转换头文件
├── label_classify
│ ├── CMakeLists.txt # 生成可执行文件label_classify的交叉编译规则文件
│ ├── label_classify.cpp # 生成可执行文件label_classify的源文件
│ └── label_classify.h # 生成可执行文件label_classify的头文件
├── nnrt
│ ├── CMakeLists.txt # 生成libnnrt_implementation.so的交叉编译规则文件
│ ├── nnrt_implementation.cpp # 生成libnnrt_implementation.so的源文件,用于加载NNRt Api
│ └── nnrt_implementation.h # 生成libnnrt_implementation.so的头文件
└── tools
├── bitmap_helpers.cpp # 用于读取输入的bmp格式图片源文件
├── bitmap_helpers.h # 用于读取输入的bmp格式图片头文件
├── get_topn.h # 用于返回推理的top N结果
├── log.h # 日志模块文件
├── utils.cpp # 用于辅助模型推理输入和输出工具方法的源文件
└── utils.h # 用于辅助模型推理输入和输出工具方法的头文件
创建NnrtDelegate类。
NnrtDelegate类定义在nnrt_delegate文件中,用于对接NNRt,使TensorFlow Lite模型能运行在加速芯片上。用户需要实现DoPrepare接口、GetSupportedNodes接口、GetDelegateKernelRegistration接口,详细代码参考链接。主要步骤有以下两点:
获取TensorFlow Lite中能替换的nodes。
TfLiteNode* node = nullptr;
TfLiteRegistration* registration = nullptr;
for (auto nodeIndex : TfLiteIntArrayView(executionPlan)) {
node = nullptr;
registration = nullptr;
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(context, nodeIndex, &node, ®istration));
if (NnrtDelegateKernel::Validate(registration->builtin_code)) {
supportedNodes.emplace_back(nodeIndex);
} else {
TFLITE_LOG_PROD(TFLITE_LOG_WARNING,
"[NNRT-DELEGATE] Get unsupportted node: %d.", registration->builtin_code);
}
}
注册的Delegate kernel,初始化TfLiteRegistration的init,prepare,invoke成员函数指针,指向NnrtDelegateKernel的Init,Prepare和run函数方法。
nnrtDelegateKernel.init = [](TfLiteContext* context, const char* buffer, size_t length) -> void* {
if (buffer == nullptr) {
return nullptr;
}
const TfLiteDelegateParams* params = reinterpret_cast<const TfLiteDelegateParams*>(buffer);
auto* delegateData = static_cast<Data*>(params->delegate->data_);
NnrtDelegateKernel* state = new (std::nothrow) NnrtDelegateKernel(delegateData->nnrt);
if (state == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to create NnrtDelegateKernel instance.");
return state;
}
TfLiteStatus status = state->Init(context, params);
if (status != kTfLiteOk) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to init NnrtDelegateKernel.");
delete state;
state = nullptr;
}
return state;
};
nnrtDelegateKernel.prepare = [](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
if (node == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to prepare delegate kernels, the node is nullptr.");
return kTfLiteError;
}
NnrtDelegateKernel* state = reinterpret_cast<NnrtDelegateKernel*>(node->user_data);
return state->Prepare(context, node);
};
nnrtDelegateKernel.invoke = [](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
if (node == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to invoke delegate kernels, the node is nullptr.");
return kTfLiteError;
}
NnrtDelegateKernel* state = reinterpret_cast<NnrtDelegateKernel*>(node->user_data);
return state->Invoke(context, node);
};
创建NnrtDelegateProvider。
NnrtDelegateProvider定义在nnrt_delegate_provider文件中,用于创建NNrtDelegate,完成与TFLite的对接。主要步骤有以下两点:
注册NnrtDelegateProvider
REGISTER_DELEGATE_PROVIDER(NnrtDelegateProvider);
创建CreateTfLiteDelegate主要有以下几步
NnrtDelegate::Options options;
const auto* nnrtImpl = NnrtImplementation();
if (!nnrtImpl->nnrtExists) {
TFLITE_LOG(WARN) << "NNRT acceleration is unsupported on this platform.";
return delegate;
}
Interpreter::TfLiteDelegatePtr TfLiteDelegatePtr(new (std::nothrow) NnrtDelegate(nnrtImpl, options),
[](TfLiteDelegate* delegate) { delete reinterpret_cast<NnrtDelegate*>(delegate); });
label_classify.cpp中加载NnrtDelegate,并完成node的替换。
interpreter->ModifyGraphWithDelegate(std::move(delegate.delegate))
编译生成Tensorflow Lite库及其依赖库。
请参考Tensorflow Lite交叉编译指南,同时在tensorflow/lite/CMakeLists.txt
中增加以下内容:
list(APPEND TFLITE_EXTERNAL_DELEGATE_SRC
${TFLITE_SOURCE_DIR}/tools/delegates/delegate_provider.cc
# ${TFLITE_SOURCE_DIR}/tools/delegates/external_delegate_provider.cc
${TFLITE_SOURCE_DIR}/tools/tool_params.cc
${TFLITE_SOURCE_DIR}/tools/command_line_flags.cc
)
target_link_libraries(tensorflow-lite
PUBLIC
Eigen3::Eigen
NEON_2_SSE
absl::flags
absl::hash
absl::status
absl::strings
absl::synchronization
absl::variant
farmhash
fft2d_fftsg2d
flatbuffers
gemmlowp
ruy
${CMAKE_DL_LIBS}
${TFLITE_TARGET_DEPENDENCIES}
)
编译生成NNRt库libneural_network_runtime.z.so。
请参考编译指导,编译命令如下:
./build.sh --product-name rk3568 –ccache --jobs=16 --build-target=neural_network_runtime
用cmake编译北向demo。
TensorFlow Lite头文件和依赖库配置。
将TensorFlow Lite头文件和编译生成的TensorFlow Lite库,分别放在deep_learning_framework/lib_3rd_nnrt_tflite/include/tensorflow/lite/
和deep_learning_framework/lib_3rd_nnrt_tflite/com/arm64-v8a/lib/
下。
交叉编译工具配置。
在社区的每日构建下载对应系统版本的ohos-sdk压缩包,从压缩包中提取对应平台的Native开发套件;指定ohos的cmake, ohos.toolchain.cmake路径,在foundation/ai/neural_network_runtime/example/cmake_build/build_ohos_tflite.sh
中替换以下两行:
./tool_chain/native/build-tools/cmake/bin/cmake \
-DCMAKE_TOOLCHAIN_FILE=./tool_chain/native/cmake_build/cmake/ohos.toolchain.cmake \
修改交叉编译文件。
进入foundation/ai/neural_network_runtime/example/cmake_build
,执行以下修改:
如果需要在arm32架构的CPU上运行:
# 修改```tflite/CMakeLists.txt```
```text
set(CMAKE_CXX_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2 -march=armv7-a")
```
# 执行编译命令
```shell
bash build_ohos_tflite.sh armeabi-v7a
```
如果需要在arm64架构的CPU上运行:
# 修改```tflite/CMakeLists.txt```
```text
set(CMAKE_CXX_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2 -march=armv8-a")
```
# 执行编译命令
```shell
bash build_ohos_tflite.sh arm64-v8a
```
创建目录
在example/deep_learning_framework/
目录下创建lib和output两个文件夹:
mkdir lib output
执行链接命令
进入foundation/ai/neural_network_runtime/example/cmake_build
,执行链接命令:
make
结果查看
北向demo成功编译完成后会在deep_learning_framework/lib
生成libnnrt_delegate.so和libnnrt_implementation.so,在deep_learning_framework/output
下生成label_classify可执行文件,目录结构体如下所示:
deep_learning_framework
├── lib
│ ├── libnnrt_delegate.so # 生成的TensorFlow Lite nnrt delegate库
│ └── libnnrt_implementation.so # 生成的nnrt在TensorFlow Lite中接口实现库
└── output
└── label_classify # 生成的可执行文件
在开发板上运行北向demo。
推送文件至开发板
将步骤1生成的libnnrt_implementation.so、libnnrt_delegate.so和可执行文件label_classify,libneural_network_runtime.z.so、tensorflow-lite.so及其依赖的库、mobilenetv2.tflite模型、标签labels.txt、测试图片grace_hopper.bmp推送到开发板上:
# 假设上述待推送文件均放在push_files/文件夹下
hdc_std file send push_files/ /data/demo/
执行demo
进入开发板,执行demo前需要添加环境变量,文件执行权限等:
# 进入开发板
hdc_std shell
# 进入推送文件目录,并增加可执行文件权限
cd /data/demo
chmod +x ./label_classify
# 添加环境变量
export LD_LIBRARY_PATH=/data/demo:$LD_LIBRARY_PATH
# 执行demo,-m tflite模型, -i 测试图片, -l 数据标签, -a 1表示使用nnrt, 0表示不使用nnrt推理,-z 1 表示打印输出张量大小的结果
./label_classify -m mobilenetv2.tflite -i grace_hopper.bmp -l labels.txt -a 1 -z 1
结果查看
demo成功执行后,可以看到以下运行结果:
INFO: invoked, average time: 194.972 ms
INFO: 0.536433: 653 653:military uniform
INFO: 0.102077: 835 835:suit, suit of clothes
INFO: 0.0398081: 466 466:bulletproof vest
INFO: 0.0251576: 907 907:Windsor tie
INFO: 0.0150422: 440 440:bearskin, busby, shako
完整demo可以参考社区实现Demo实例。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。