基于Stm32F746g_disg平台下移植zephry使用TinyML预测模型
Posted 17岁boy想当攻城狮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Stm32F746g_disg平台下移植zephry使用TinyML预测模型相关的知识,希望对你有一定的参考价值。
前言
本文没有使用文件系统,以最小RTOS为例来调用Tensor Flow TFLite模型
Tensor FLow TFLite的工作流程就是先训练好模型,然后转换为TFLite模型文件,最后在通过Tensor Flow Tflite加载调用
相关知识
什么是Tensor Flow和lite以及数据流图_17岁boy的博客-CSDN博客
Tensor Flow V2:基于Tensor Flow Keras的摄氏度到华氏度温度转换的训练模型_17岁boy的博客-CSDN博客
Tensor Flow V2:将Tensor Flow H5模型文件转换为tflite_17岁boy的博客-CSDN博客
Tensor Flow Lite C++ API 介绍_17岁boy的博客-CSDN博客
TinyML与Tensor Flow Lite的关系_17岁boy的博客-CSDN博客
TFLite模型文件转C语言文件_17岁boy的博客-CSDN博客
Zephry对Tensor Flow Lite的支持
Zephry2.6已经内置了Tensor Flow Lite与TinyML源代码,我们不需要去进行移植,并且提供了两个基于示例代码:hello_world以及magic_wand
示例路径存在于:samples/modules/tensorflow
开始实战
开启Tensor Flow Lite/TinyML支持、以及使用C++编译器,Tensor Flow Lite提供的是C++接口必须使用C++编译器
CONFIG_TENSORFLOW_LITE_MICRO=y //TinyML
CONFIG_NEWLIB_LIBC=y //cLib
CONFIG_CPLUSPLUS=y //c++
CONFIG_LIB_CPLUSPLUS=y //c++lib
包含基本头文件
需要注意你的源文件必须是.cc结尾,如果是.c结尾的Zephry构建工具会调用GCC编译那么不会包含c++的库文件会导致缺失头文件,其次记得在你的model文件中加上#pragma pack(1),让内存以1字节对齐,防止出现偏移对齐的问题导致数值不对
//一个允许解释器加载模型使用的操作的类
#include <tensorflow/lite/micro/all_ops_resolver.h>
//可以记录错误并输出以帮助调试的类
#include <tensorflow/lite/micro/micro_error_reporter.h>
//TensorFlow Lite for Microcontrollers 解释器,模型将在解释器中运行
#include <tensorflow/lite/micro/micro_interpreter.h>
//定义 TensorFlow Lite FlatBuffer 数据结构的模型,用于理解 sine_model_data.h 中的模型数据
#include <tensorflow/lite/schema/schema_generated.h>
//用于获取当前tensor flow版本与模型版本
#include <tensorflow/lite/version.h>
//我们生成的model文件
#include "model.h"
//zephyr 核心文件
#include <zephyr.h>
#include <sys/printk.h>
创建log工具
//set up logging
tflite::MicroErrorReporter micro_error_reporter;
tflite::ErrorReporter* error_reporter = µ_error_reporter;
这一段代码详细讲解一下,可以跳过,这一条关于C++中多态的运用
大家看到首先定义了MicroErrorReporter类,然后又定义了ErrorReporter指针指向MicroErrorReporter类型的变量,这是为何?
ErrorReporter是Tensor Flow Lite下的Log调试模块,而MicroErrorReporter是基于ErrorReporter的派生类,它内部重载实现了父类里的调试输出日志的虚函数,使用的代码比ErrorReporter更低功耗,这里用父类指针指向子类的用意在于限制逻辑业务以及增加灵活性,因为派生类里可能有一些与业务不相关的代码与成员变量,当子类派生父类时每次构造都有一个父类的副本,如果这个父类有虚函数的情况下还会有虚函数表指针指向当前全局唯一需虚函数表,当父类指针调用方法时会如果不是虚函数则调用父类副本里的代码,如果是虚函数则从虚函数指针找到属于自己的虚表,然后在虚函数表中找到对应的函数地址然后去调用
这样做还有一个灵活性的好处,就是一些旧代码里类型可能是ErrorReporter,如果你传递MicroErrorReporter进去会报类型不一致的错误,如果进行类型转换还会出现内存丢失可能出现段错误的问题,如果使用这个方法的话即不影响类型又能调用派生类的方法,还能兼容旧函数
获取模型并检查模型版本是否被当前Tensor支持
//getModel
const tflite::Model* model = tflite::GetModel(model_tflite);
//check version
if(model->version() != TFLITE_SCHEMA_VERSION){
TF_LITE_REPORT_ERROR(error_reporter,"Version Mismatch %d != ",model->version(),TFLITE_SCHEMA_VERSION);
return -1;
}
创建对模型操作的接口类
//create AllopsResolver
tflite::AllOpsResolver resolver;
为模型创建张量空间,主要用于存放在模型运行期间产生的输入输出以及中间变量
//create tensor space
const int tensor_arena_size = 2* 1024;
uint8_t tensor_arena[tensor_arena_size];
创建空间绑定类
//create build
tflite::MicroInterpreter interpreter(model,resolver,tensor_arena,tensor_arena_size,error_reporter);
为模型分配内存
//allocate memory for model
TfLiteStatus allocate_status = interpreter.AllocateTensors();
if(allocate_status != kTfLiteOk){
TF_LITE_REPORT_ERROR(error_reporter,"allocate momory error");
return -2;
}
获取输入输出源,这里的源索引跟你的模型有关,之前一篇文章训练keras模型时调用了:tf.keras.layers.Dense(units
=
1
,input_shape
=
[
1
])这个API,其中units就是输出维度,意味着有多少个神经网络,我们给的是1所以只有一个神经网络,需要注意输入与输出要选择对应的下标,因为是C++语言所以下标从0开始计算。
//get input and output index 0
TfLiteTensor* input = interpreter.input(0);
TfLiteTensor* output = interpreter.output(0);
输入值
//input
input->data.f[0] = static_cast < float >(50);
运行解释器
//run
TfLiteStatus invoke_status = interpreter.Invoke();
if(invoke_status != kTfLiteOk){
TF_LITE_REPORT_ERROR(error_reporter,"run error");
return -3;
}
获取输出并打印
//output
float f_output = output->data.f[0];
printk("output:%d\\n",static_cast < int >(f_output));
需要注意我这里使用了static_cast做转换,因为我的处理器没有浮点数运算的FPU,Zephyr没有支持,所以用C++的类型转换可以对浮点数与整数进行转换,C++自己实现了针对浮点数操作的API函数
输出结果:
output:121
输出的是华氏度,摄氏度是50,华氏度是122,相差不大,说明我们训练的还可以,这个模型只训练了1000次
完整代码
//一个允许解释器加载模型使用的操作的类
#include <tensorflow/lite/micro/all_ops_resolver.h>
//可以记录错误并输出以帮助调试的类
#include <tensorflow/lite/micro/micro_error_reporter.h>
//TensorFlow Lite for Microcontrollers 解释器,模型将在解释器中运行
#include <tensorflow/lite/micro/micro_interpreter.h>
//定义 TensorFlow Lite FlatBuffer 数据结构的模型,用于理解 sine_model_data.h 中的模型数据
#include <tensorflow/lite/schema/schema_generated.h>
//用于获取当前tensor flow版本与模型版本
#include <tensorflow/lite/version.h>
//我们生成的model文件
#include "model.h"
//zephyr 核心文件
#include <zephyr.h>
#include <sys/printk.h>
int main(){
printk("********** Tensor Flow Lite Test **********\\n");
//set up logging
tflite::MicroErrorReporter micro_error_reporter;
tflite::ErrorReporter* error_reporter = µ_error_reporter;
//getModel
const tflite::Model* model = tflite::GetModel(model_tflite);
//check version
if(model->version() != TFLITE_SCHEMA_VERSION){
TF_LITE_REPORT_ERROR(error_reporter,"Version Mismatch %d != ",model->version(),TFLITE_SCHEMA_VERSION);
return -1;
}
//create AllopsResolver
tflite::AllOpsResolver resolver;
//create tensor space
const int tensor_arena_size = 2* 1024;
uint8_t tensor_arena[tensor_arena_size];
//create build
tflite::MicroInterpreter interpreter(model,resolver,tensor_arena,tensor_arena_size,error_reporter);
//allocate memory for model
TfLiteStatus allocate_status = interpreter.AllocateTensors();
if(allocate_status != kTfLiteOk){
TF_LITE_REPORT_ERROR(error_reporter,"allocate momory error");
return -2;
}
//get input and output index 0
TfLiteTensor* input = interpreter.input(0);
TfLiteTensor* output = interpreter.output(0);
//input
input->data.f[0] = static_cast < float >(50);
//run
TfLiteStatus invoke_status = interpreter.Invoke();
if(invoke_status != kTfLiteOk){
TF_LITE_REPORT_ERROR(error_reporter,"run error");
return -3;
}
//output
float f_output = output->data.f[0];
printk("output:%d\\n",static_cast < int >(f_output));
return 0;
}
以上是关于基于Stm32F746g_disg平台下移植zephry使用TinyML预测模型的主要内容,如果未能解决你的问题,请参考以下文章
基于Zephyr在微型MCU上使用Tensor Flow Lite Micro做图像分类