• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..17-Mar-2025-

cmake_build/H17-Mar-2025-5230

tflite/H17-Mar-2025-4,1683,049

CMakeLists.txtH A D17-Mar-20251 KiB3023

README_zh.mdH A D17-Mar-202517 KiB339273

README_zh.md

1# TensorFlow Lite接入NNRt Delegate开发指南
2
3## 概述
4神经网络运行时部件(NNRt)是跨设备的AI运行时框架,作为端侧推理框架和专用加速芯片的中间桥梁,为端侧推理框架提供了统一的Native接口。
5
6本demo旨在介绍TensorFlow Lite推理框架如何接入NNRt,并在专有芯片上加速推理,接入OpenHarmony社区生态。
7
8本demo根据用户输入参数(模型、标签、模型输入shape、循环浮点推理次数、是否允许动态尺寸推理、以及是否打印结果等)完成标签分类模型推理,用户可通过打印信息观察在不同条件下的模型推理性能、精度和预测类别。
9
10## 基本概念
11在开发前,开发者需要先了解以下概念,以便更好地理解全文内容:
12- NNRt:Neural Network Runtime,神经网络运行时,是本指导主要介绍的部件。
13- OHOS:OpenHarmony Operating System,OpenHarmony操作系统。
14
15## 约束与限制
161. 系统版本:OpenHarmony master分支。
172. 开发环境:Ubuntu 18.04及以上。
183. 接入设备:OpenHarmony定义的标准设备。
194. 其他开发依赖:
20获取TensorFlow Lite头文件,[获取链接](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite)21    - tensorflow-lite.so及其依赖库(参考[编译指导](#调测命令)编译生成),目前完成在tensorflow lite 2.6版本上的测试。
22    - NNRt动态库libneural_network_runtime.z.so,参考[编译指导](https://gitee.com/openharmony-sig/neural_network_runtime/blob/master/README_zh.md)编译生成。
23    - mobilenetv2的Tensorflow lite模型,[获取链接](https://storage.googleapis.com/mobilenet_v2/checkpoints/mobilenet_v2_1.4_224.tgz)24    - 标签文件labels.txt,可从[压缩包](https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_1.0_224_frozen.tgz)中解压得到。
25    - 测试图片grace_hopper.bmp,[获取链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/examples/label_image/testdata/grace_hopper.bmp)26
27## 运作机制
28
29**图1** 运作机制
30
31![运作机制图](../../figures/Principle.png)
32
33以TensorFlow lite的MobileNetv2模型进行标签分类任务为例,实现调用NNRt API在指定芯片上加速推理,主要有以下三个部分:
341. 通过TFLite delegate机制,创建NNRt Delegate,并接入TFLite的Hardware Accelerator中。
352. 将TensorFlow lite模型中能在NNRt上运行的op kernels,替换成NNRt Delegate kernels。
363. 推理过程中,被替换成NNRt Delegate Kernel的op kernel会调用Neural Network Runtime中的API在指定芯片上完成模型的构图、编译和执行。
37
38## 开发流程
39
40**图2** 开发流程
41
42![开发流程图](../../figures/Flowchart.png)
43
44主要开发步骤包括命令行参数解析、创建NNRt Delegate、TFLite nodes的替换、tensors的内存分配、执行推理、结果查看等,具体如下:
451. 解析运行demoe的命令,初始化模型推理所需参数和创建NNRt Delegate的options。
462. 调用Tensorflow lite的BuildFromFile接口完成Tensorflow lite模型的构建。
473. 根据demo的运行命令解析模型是否需要动态输入,如果需要,则调用ResizeInputTensor接口更改模型输入大小。
484. 创建DelegateProviders,并调用DelegateProviders的CreateAllRankedDelegates接口创建NnrtDelegate;创建NnrtDelegate过程中,通过dlopen打开NNRt的动态库来加载NNRt API。
495. 调用ModifyGraphWithDelegate接口完成Node替换,其中该步分四个步骤:
50    - 初始化NnrtDelegate。
51    - 判断TFLite图中各node是否支持在NnrtDelegate上运行,返回支持的node集合。
52    - 调用TFLiteRegistration注册NnrtDelegate,并初始化init,prepare,invoke成员函数指针,指向NnrtDelegateKernel的Init,Prepare和run函数方法。
53    - 替换TensorFlow Delegate的node为已注册的NNrt delegate kernel,并调用Init完成构图过程。
546. 用户调用AllocateTensors,完成tensors内存分配和图编译。其中,支持在NNRtDelegate上运行的node会调用NnrtDelegateKernel的prepare接口完成编译,不支持的会调用tflite operation kernels的prepare编译。
557. 导入输入数据并调用Invoke完成图执行。
568. 结果输出。
57
58
59## 开发步骤
60本节主要描述NNRt接入TFLite的TFLite-delegate代理机制,重点对TFLite调用delegate的流程和delegate对接NNRt的方式进行了介绍。
61TensorFlow Lite Delegate有两个基类DelegateProvider、TfLiteDelegate,本节主要描述继承这两个基类得到子类NnrtDelegate和NnrtDelegateProvider。
62
63本demo主要文件目录结构如下图:
64```text
65.
66├── CMakeLists.txt
67├── delegates
68│   └── nnrt_delegate
69│       ├── CMakeLists.txt                     # 生成libnnrt_delegate.so的交叉编译规则文件
70│       ├── nnrt_delegate.cpp                  # NnrtDelegate源文件,对接到NNRt上,使TensorFlow Lite模型能运行在加速芯片上
71│       ├── nnrt_delegate.h                    # NnrtDelegate头文件
72│       ├── nnrt_delegate_kernel.cpp           # NnrtDelegateKernel源文件,将TensorFlow Lite模型中的operators替换成Nnrt中的operators
73│       ├── nnrt_delegate_kernel.h             # NnrtDelegateKernel头文件
74│       ├── nnrt_delegate_provider.cpp         # 用于创建NNrtDelegate
75│       ├── nnrt_op_builder.cpp                # NnrtOpBuilder源文件,给每个operators设置输入输出tensor和operation属性
76│       ├── nnrt_op_builder.h                  # NnrtOpBuilder头文件
77│       ├── nnrt_utils.cpp                     # 用于辅助创建NnrtDelegate工具方法的源文件
78│       ├── nnrt_utils.h                       # 用于辅助创建NnrtDelegate工具方法的头文件
79│       └── tensor_mapping.h                   # TensorFlow Lite Tensor到Nnrt tensor的转换头文件
80├── label_classify
81│   ├── CMakeLists.txt                         # 生成可执行文件label_classify的交叉编译规则文件
82│   ├── label_classify.cpp                     # 生成可执行文件label_classify的源文件
83│   └── label_classify.h                       # 生成可执行文件label_classify的头文件
84├── nnrt
85│   ├── CMakeLists.txt                         # 生成libnnrt_implementation.so的交叉编译规则文件
86│   ├── nnrt_implementation.cpp                # 生成libnnrt_implementation.so的源文件,用于加载NNRt Api
87│   └── nnrt_implementation.h                  # 生成libnnrt_implementation.so的头文件
88└── tools
89    ├── bitmap_helpers.cpp                     # 用于读取输入的bmp格式图片源文件
90    ├── bitmap_helpers.h                       # 用于读取输入的bmp格式图片头文件
91    ├── get_topn.h                             # 用于返回推理的top N结果
92    ├── log.h                                  # 日志模块文件
93    ├── utils.cpp                              # 用于辅助模型推理输入和输出工具方法的源文件
94    └── utils.h                                # 用于辅助模型推理输入和输出工具方法的头文件
95```
961. 创建NnrtDelegate类。
97
98    NnrtDelegate类定义在nnrt_delegate文件中,用于对接NNRt,使TensorFlow Lite模型能运行在加速芯片上。用户需要实现DoPrepare接口、GetSupportedNodes接口、GetDelegateKernelRegistration接口,详细代码参考[链接](https://gitee.com/openharmony-sig/neural_network_runtime/blob/master/example/deep_learning_framework/tflite/delegates/nnrt_delegate/nnrt_delegate.cpp)。主要步骤有以下两点:
99    - 获取TensorFlow Lite中能替换的nodes。
100        ```cpp
101        TfLiteNode* node = nullptr;
102        TfLiteRegistration* registration = nullptr;
103        for (auto nodeIndex : TfLiteIntArrayView(executionPlan)) {
104            node = nullptr;
105            registration = nullptr;
106            TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(context, nodeIndex, &node, &registration));
107            if (NnrtDelegateKernel::Validate(registration->builtin_code)) {
108                supportedNodes.emplace_back(nodeIndex);
109            } else {
110                TFLITE_LOG_PROD(TFLITE_LOG_WARNING,
111                    "[NNRT-DELEGATE] Get unsupportted node: %d.", registration->builtin_code);
112            }
113        }
114        ```
115
116    - 注册的Delegate kernel,初始化TfLiteRegistration的init,prepare,invoke成员函数指针,指向NnrtDelegateKernel的Init,Prepare和run函数方法。
117        ```cpp
118        nnrtDelegateKernel.init = [](TfLiteContext* context, const char* buffer, size_t length) -> void* {
119            if (buffer == nullptr) {
120                return nullptr;
121            }
122
123            const TfLiteDelegateParams* params = reinterpret_cast<const TfLiteDelegateParams*>(buffer);
124            auto* delegateData = static_cast<Data*>(params->delegate->data_);
125            NnrtDelegateKernel* state = new (std::nothrow) NnrtDelegateKernel(delegateData->nnrt);
126            if (state == nullptr) {
127                TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to create NnrtDelegateKernel instance.");
128                return state;
129            }
130
131            TfLiteStatus status = state->Init(context, params);
132            if (status != kTfLiteOk) {
133                TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to init NnrtDelegateKernel.");
134                delete state;
135                state = nullptr;
136            }
137            return state;
138        };
139
140        nnrtDelegateKernel.prepare = [](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
141            if (node == nullptr) {
142                TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to prepare delegate kernels, the node is nullptr.");
143                return kTfLiteError;
144            }
145
146            NnrtDelegateKernel* state = reinterpret_cast<NnrtDelegateKernel*>(node->user_data);
147            return state->Prepare(context, node);
148        };
149
150        nnrtDelegateKernel.invoke = [](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
151            if (node == nullptr) {
152                TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to invoke delegate kernels, the node is nullptr.");
153                return kTfLiteError;
154            }
155
156            NnrtDelegateKernel* state = reinterpret_cast<NnrtDelegateKernel*>(node->user_data);
157            return state->Invoke(context, node);
158        };
159        ```
160
1612. 创建NnrtDelegateProvider。
162
163    NnrtDelegateProvider定义在nnrt_delegate_provider文件中,用于创建NNrtDelegate,完成与TFLite的对接。主要步骤有以下两点:
164    - 注册NnrtDelegateProvider
165      ```cpp
166        REGISTER_DELEGATE_PROVIDER(NnrtDelegateProvider);
167      ```
168
169    - 创建CreateTfLiteDelegate主要有以下几步
170      ```cpp
171      NnrtDelegate::Options options;
172
173      const auto* nnrtImpl = NnrtImplementation();
174      if (!nnrtImpl->nnrtExists) {
175          TFLITE_LOG(WARN) << "NNRT acceleration is unsupported on this platform.";
176          return delegate;
177      }
178
179      Interpreter::TfLiteDelegatePtr TfLiteDelegatePtr(new (std::nothrow) NnrtDelegate(nnrtImpl, options),
180          [](TfLiteDelegate* delegate) { delete reinterpret_cast<NnrtDelegate*>(delegate); });
181      ```
182
1833. label_classify.cpp中加载NnrtDelegate,并完成node的替换。
184    ```cpp
185    interpreter->ModifyGraphWithDelegate(std::move(delegate.delegate))
186    ```
187
188
189## 调测命令
1901. 编译生成Tensorflow Lite库及其依赖库。
191
192    请参考[Tensorflow Lite交叉编译指南](https://www.tensorflow.org/lite/guide/build_cmake_arm),同时在```tensorflow/lite/CMakeLists.txt```中增加以下内容:
193    ```text
194    list(APPEND TFLITE_EXTERNAL_DELEGATE_SRC
195        ${TFLITE_SOURCE_DIR}/tools/delegates/delegate_provider.cc
196        # ${TFLITE_SOURCE_DIR}/tools/delegates/external_delegate_provider.cc
197        ${TFLITE_SOURCE_DIR}/tools/tool_params.cc
198        ${TFLITE_SOURCE_DIR}/tools/command_line_flags.cc
199    )
200    ```
201    ```text
202    target_link_libraries(tensorflow-lite
203      PUBLIC
204        Eigen3::Eigen
205        NEON_2_SSE
206        absl::flags
207        absl::hash
208        absl::status
209        absl::strings
210        absl::synchronization
211        absl::variant
212        farmhash
213        fft2d_fftsg2d
214        flatbuffers
215        gemmlowp
216        ruy
217        ${CMAKE_DL_LIBS}
218        ${TFLITE_TARGET_DEPENDENCIES}
219    )
220    ```
221
2222. 编译生成NNRt库libneural_network_runtime.z.so223
224    请参考[编译指导](https://gitee.com/openharmony-sig/neural_network_runtime/blob/master/README_zh.md),编译命令如下:
225    ```shell
226    ./build.sh --product-name rk3568 –ccache --jobs=16 --build-target=neural_network_runtime
227    ```
228
2293. 用cmake编译北向demo。
230
231    - TensorFlow Lite头文件和依赖库配置。
232
233      将[TensorFlow Lite头文件](https://github.com/tensorflow/tensorflow/tree/master/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/```下。
234
235    - 交叉编译工具配置。
236
237      在社区的[每日构建](http://ci.openharmony.cn/dailys/dailybuilds)下载对应系统版本的ohos-sdk压缩包,从压缩包中提取对应平台的Native开发套件;指定ohos的cmake, ohos.toolchain.cmake路径,在```foundation/ai/neural_network_runtime/example/cmake_build/build_ohos_tflite.sh```中替换以下两行:
238        ```shell
239        ./tool_chain/native/build-tools/cmake/bin/cmake \
240        -DCMAKE_TOOLCHAIN_FILE=./tool_chain/native/cmake_build/cmake/ohos.toolchain.cmake \
241        ```
242
243    - 修改交叉编译文件。
244
245      进入```foundation/ai/neural_network_runtime/example/cmake_build```,执行以下修改:
246
247        如果需要在arm32架构的CPU上运行:
248
249          # 修改```tflite/CMakeLists.txt```
250            ```text
251            set(CMAKE_CXX_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2 -march=armv7-a")
252            ```
253          # 执行编译命令
254            ```shell
255            bash build_ohos_tflite.sh armeabi-v7a
256            ```
257
258        如果需要在arm64架构的CPU上运行:
259
260          # 修改```tflite/CMakeLists.txt```
261            ```text
262            set(CMAKE_CXX_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2 -march=armv8-a")
263            ```
264
265          # 执行编译命令
266            ```shell
267            bash build_ohos_tflite.sh arm64-v8a
268            ```
269
270    - 创建目录
271
272      在```example/deep_learning_framework/```目录下创建lib和output两个文件夹:
273        ```shell
274        mkdir lib output
275        ```
276
277    - 执行链接命令
278
279      进入```foundation/ai/neural_network_runtime/example/cmake_build```,执行链接命令:
280        ```shell
281        make
282        ```
283
284    - 结果查看
285
286      北向demo成功编译完成后会在```deep_learning_framework/lib```生成libnnrt_delegate.solibnnrt_implementation.so,在```deep_learning_framework/output```下生成label_classify可执行文件,目录结构体如下所示:
287
288        ```text
289        deep_learning_framework
290        ├── lib
291        │   ├── libnnrt_delegate.so                 # 生成的TensorFlow Lite nnrt delegate库
292        │   └── libnnrt_implementation.so           # 生成的nnrt在TensorFlow Lite中接口实现库
293        └── output
294            └── label_classify                      # 生成的可执行文件
295        ```
296
2974. 在开发板上运行北向demo。
298
299    - 推送文件至开发板
300
301      将步骤1生成的libnnrt_implementation.solibnnrt_delegate.so和可执行文件label_classify,libneural_network_runtime.z.sotensorflow-lite.so及其依赖的库、mobilenetv2.tflite模型、标签labels.txt、测试图片grace_hopper.bmp推送到开发板上:
302        ```shell
303        # 假设上述待推送文件均放在push_files/文件夹下
304        hdc_std file send push_files/ /data/demo/
305        ```
306
307    - 执行demo
308
309      进入开发板,执行demo前需要添加环境变量,文件执行权限等:
310        ```shell
311        # 进入开发板
312        hdc_std shell
313
314        # 进入推送文件目录,并增加可执行文件权限
315        cd /data/demo
316        chmod +x ./label_classify
317
318        # 添加环境变量
319        export LD_LIBRARY_PATH=/data/demo:$LD_LIBRARY_PATH
320
321        # 执行demo,-m tflite模型, -i 测试图片, -l 数据标签, -a 1表示使用nnrt, 0表示不使用nnrt推理,-z 1 表示打印输出张量大小的结果
322        ./label_classify -m mobilenetv2.tflite -i grace_hopper.bmp -l labels.txt -a 1 -z 1
323        ```
324
325    - 结果查看
326
327      demo成功执行后,可以看到以下运行结果:
328        ```text
329        INFO: invoked, average time: 194.972 ms
330        INFO: 0.536433: 653 653:military uniform
331        INFO: 0.102077: 835 835:suit, suit of clothes
332        INFO: 0.0398081: 466 466:bulletproof vest
333        INFO: 0.0251576: 907 907:Windsor tie
334        INFO: 0.0150422: 440 440:bearskin, busby, shako
335        ```
336
337## 开发实例
338完整demo可以参考社区实现[Demo实例](https://gitee.com/openharmony-sig/neural_network_runtime/tree/master/example/deep_learning_framework)339