使用TensorFlow Lite Micro流程记录(带源码)

文章目录

  • 0 关于tflite micro
  • 1 克隆仓库
  • 2 编译静态库
  • 3 模型转换
  • 4 编写工程
  • 5 编写demo
    • 5.1 进行算子注册
  • 5.2 推理过程
  • 6 debug记录
    • 6.1 缺少算子
    • 6.2 注册表太小
    • 6.3 段错误
    • 6.4 进一步减小库体积
  • 7 实际部署

0 关于tflite micro

请添加图片描述

关于tflite micro在这里接不做过多介绍了,可以查看相关论文:TensorFlow Lite Micro: Embedded Machine Learning on TinyML Systems

我非常喜欢tflite micro,占用资源很少,是一个用来学习推理框架的好工程。

1 克隆仓库

在tensorflow的git里面,看到git已经迁移至一个单独的仓库。

As of June 25, 2021, the TFLM codebase has moved to a stand-alone github repository.

于是执行命令:
git clone https://github.com/tensorflow/tflite-micro.git

2 编译静态库

求助了ChatGPT 4.0:

「在这个 https://github.com/tensorflow/tflite-micro , 怎么编译静态库?」

在TensorFlow Lite Micro的GitHub仓库中,你可以使用以下步骤来编译静态库:首先,你需要克隆仓库:
git clone https://github.com/tensorflow/tflite-micro.git进入克隆下来的仓库目录:
cd tflite-micro使用make命令编译静态库:
make -f tensorflow/lite/micro/tools/make/Makefile TARGET=<TARGET> TARGET_ARCH=<ARCH> microlite在上述命令中,<TARGET>应替换为你的目标平台(如linux),<ARCH>应替换为你的目标架构(如x86_64)。这将在tensorflow/lite/micro/tools/make/gen/<TARGET>_<ARCH>/lib/目录下生成一个名为libtensorflow-microlite.a的静态库。请注意,你可能需要根据你的具体需求和环境来修改这些步骤。例如,你可能需要安装额外的依赖库,或者修改makefile来包含你需要的特性。

真是太棒了,于是我执行指令:

make -f tensorflow/lite/micro/tools/make/Makefile TARGET=linux TARGET_ARCH=x86_64 microlite

在 make/download/ 目录下,分别下载了flatbuffers、gemmlowp、kissfft、pigweed、ruy(如果你“网络不佳”,这里可能要花些精力)。

编译完成后,得到了静态库文件——
ar: creating gen/linux_x86_64_default/lib/libtensorflow-microlite.a

3 模型转换

以lenet5模型为例:

  1. 先把tflite模型转换为cpp文件:
    xxd -i lenet5.tflite > lenet5.cpp
  2. 包一下模型接口
    在lenet5.cpp的文件最后加入了这几行代码
    unsigned char * get_model_pointer()
    {return lenet5_tflite;
    }unsigned int get_model_size()
    {return lenet5_tflite_len;
    }
    
  3. 增加函数头文件
    #ifndef __MODEL_INTERFACE_H__
    #define __MODEL_INTERFACE_H__unsigned char * get_model_pointer();
    unsigned int get_model_size();#endif
    
    这样代码相对比较规范一些,当然也可以直接xxd成头文件直接引用。

4 编写工程

整个工程比较简单,为了方便引用头文件,我在tflite-micro下新建了一个demo文件夹:

.
├── demo
│   └── x86
│       ├── libtensorflow-microlite.a
│       ├── Makefile
│       ├── models
│       │   ├── lenet5.cpp
│       │   ├── lenet5.tflite
│       │   └── model_interface.h
│       ├── model_test.cpp
│       └── test

相关工程已经开源至github,欢迎star,欢迎pr~

5 编写demo

5.1 进行算子注册

首先可以看一下模型有哪些算子,以便于确认算子注册类型。(在netron查看tflite模型)
在这里插入图片描述

namespace {using OpResolver = tflite::MicroMutableOpResolver<8>;TfLiteStatus RegisterOps(OpResolver& op_resolver) {TF_LITE_ENSURE_STATUS(op_resolver.AddAdd());TF_LITE_ENSURE_STATUS(op_resolver.AddConv2D());TF_LITE_ENSURE_STATUS(op_resolver.AddFullyConnected());TF_LITE_ENSURE_STATUS(op_resolver.AddMaxPool2D());TF_LITE_ENSURE_STATUS(op_resolver.AddMul());TF_LITE_ENSURE_STATUS(op_resolver.AddReshape());TF_LITE_ENSURE_STATUS(op_resolver.AddSoftmax());TF_LITE_ENSURE_STATUS(op_resolver.AddTanh());return kTfLiteOk;}
}  // namespace

这个过程就是把要用到的算子进行注册。实际上我是缺什么算子加什么就好了。详细过程可以见算子注册debug过程

5.2 推理过程

TfLiteStatus LoadFloatModelAndPerformInference() {// get_model_pointer() 送入的就是lenet5的模型指针了const tflite::Model* model =::tflite::GetModel(get_model_pointer());// 检查模型的版本是否匹配当前的 TFLite 版本。TFLITE_CHECK_EQ(model->version(), TFLITE_SCHEMA_VERSION);// printf("model->version() = %d\n", model->version()); // 好奇的话可以看看版本// 创建一个操作符解析器。OpResolver op_resolver; // 注册模型中使用的操作符。TF_LITE_ENSURE_STATUS(RegisterOps(op_resolver)); // Arena size just a round number. The exact arena usage can be determined// using the RecordingMicroInterpreter.// 定义一个 2MB 的张量内存区域(tensor_arena),用于解释器分配张量。先往大了写,之后再往小了调constexpr int kTensorArenaSize = 1024 * 2000; uint8_t tensor_arena[kTensorArenaSize];// 创建解释器实例。tflite::MicroInterpreter interpreter(model, op_resolver, tensor_arena,kTensorArenaSize);// 调用 AllocateTensors 方法在 tensor_arena 中分配模型所需的张量内存。TF_LITE_ENSURE_STATUS(interpreter.AllocateTensors());float input_data[32*32];float output_data[10];for(int i = 0; i < 32*32; i++) {input_data[i] = 1.f;}// 获取输入和输出张量的指针,并检查它们是否为空。TfLiteTensor* input = interpreter.input(0);TFLITE_CHECK_NE(input, nullptr);TfLiteTensor* output = interpreter.output(0);TFLITE_CHECK_NE(output, nullptr);// 将输入数据复制到输入张量中。float* inTensorData = tflite::GetTensorData<float>(input);memcpy(inTensorData, input_data, input->bytes);// 调用 interpreter.Invoke() 执行推理。TF_LITE_ENSURE_STATUS(interpreter.Invoke());// 将输出张量的数据复制到 output_data 中,并打印第一个输出值。// 当然也可以直接打印 tflite::GetTensorData<float>(output)memcpy(&output_data[0], tflite::GetTensorData<float>(output), output->bytes);printf("output = %f\n", output_data[0]);// 打印使用的内存大小,现在可以根据这个数值去调整 kTensorArenaSize 了。printf("arena_used_bytes = %ld\n", interpreter.arena_used_bytes());return kTfLiteOk;
}

6 debug记录

6.1 缺少算子

make后运行./test, 报错:

Didn't find op for builtin opcode 'TANH'
Failed to get registration from op code TANHSegmentation fault (core dumped)

问题很明确,没有进行tanh的算子注册。
具体怎么写呢?在tflite-micro/tensorflow/lite/micro/micro_mutable_op_resolver.h这里很容易找到。

6.2 注册表太小

正在一个一个加算子的过程中,遇到这么一个问题:

Couldn't register builtin op #22, resolver size 
is too small (5).

这是因为我定义的数量是5个。
using OpResolver = tflite::MicroMutableOpResolver<5>;
把这个增大到算子类型的数量一样就可以了。
这种小细节不注意的话确实容易把人劝退。

6.3 段错误

一旦执行到interpreter.input(0)->data.f[0] = 1.f;就段错误。
解决方式:
在makefile里面的CFLAGS -DTF_LITE_STATIC_MEMORY

6.4 进一步减小库体积

为了压缩体积,BUILD_TYPE使用了release进行编译,这期间会遇到MicroPrintf不支持的问题(release_with_logs是可以的),进行一些注释就可以。

以及进行-Os编译,可以减少很多体积占用。

7 实际部署

x86端调试完毕,接下来可以交叉编译tflite micro的库,然后代码移植到另一个工程就好了。

这个过程需要注意一下头文件不要少了。

这个过程可能会遇到诸多问题,欢迎评论交流。


相关源码已经开源至github,欢迎star,欢迎pr~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/13906.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2831. 找出最长等值子数组 Medium

给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。 如果子数组中所有元素都相等&#xff0c;则认为子数组是一个 等值子数组 。注意&#xff0c;空数组是 等值子数组 。 从 nums 中删除最多 k 个元素后&#xff0c;返回可能的最长等值子数组的长度。 子数组 是数组中一…

javaSwing仓库商品管理系统(文档+视频+源码)

摘要 Java swing实现的一款简单的仓库商品管理系统&#xff0c;数据库采用的是mysql&#xff0c;本系统实现了两个角色层面的功能&#xff0c;管理员可以管理用户、仓库、商品信息等。普通用户登录后可以查看商品、仓库信息及个人信息。 系统实现 登录界面&#xff1a; 我们…

分布式音乐播放器适配了Stage模型

OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;应用开发自API 8及其更早版本一直使用的是FA模型进行开发。FA模型是Feature Ability的缩写&#xff0c;它和PA&#xff08;Particle Ability&#xff09;两种类型是过往长期推广的术语&#xff0c;深入人心…

stm32常用编写C语言基础知识,条件编译,结构体等

位操作 宏定义#define 带参数的宏定义 条件编译 下面是头文件中常见的编译语句&#xff0c;其中_LED_H可以认为是一个编译段的名字。 下面代码表示满足某个条件&#xff0c;进行包含头文件的编译&#xff0c;SYSTEM_SUPPORT_OS可能是条件&#xff0c;当非0时&#xff0c;可以…

js代码控制bootstrap的模态框(modal)

在Bootstrap 5中&#xff0c;模态框&#xff08;Modal&#xff09;是一个自定义的HTML元素&#xff0c;可以用于创建对话框。通过导入Bootstrap的JavaScript模块&#xff0c;可以使用其API来控制模态框的显示和隐藏。以下是一个简单的示例说明如何使用Bootstrap 5中的模态框。 …

.NET8 动态添加定时任务(CRON Expression, Whatever)

需要使用 Quartz .NET <PackageReference Include"Quartz.AspNetCore"/>建立 Global JobKey public static class GlobalJobKey {public static JobKey CustomizedKey > new JobKey("xxx scheduled", "group1"); }建立 Job public clas…

前端发起请求的5种方式

好的&#xff0c;以下是针对上述五种方式的代码实现&#xff0c;每种方式都将使用给定的请求地址、请求方法&#xff08;POST&#xff09;、请求体和请求头来发送请求&#xff1a; XMLHttpRequest (XHR): var xhr new XMLHttpRequest(); var url "https://open.bigmod…

xorm和gorm数据库结构体当返回参数

我们在开发golang项目代码的时候&#xff0c;都会用到数据数据库工具&#xff0c;现在主流的就是xorm和gorm&#xff0c;然后我们一般会创建数据库表的结构体表单&#xff0c;如下所示&#xff1a; type Task struct {ID int64 gorm:"column:id;ty…

数据仓库之ClickHouse

ClickHouse是一个用于联机分析处理&#xff08;OLAP&#xff09;的列式数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;特别适用于在线分析处理&#xff08;OLAP&#xff09;场景中的快速数据查询。以下是关于ClickHouse作为数据仓库的一些主要特点和优势&#xff1a;…

C++设计模式|结构型 适配器模式

1.什么是适配器模式&#xff1f; 可以将⼀个类的接⼝转换成客户希望的另⼀个接⼝&#xff0c;主要⽬的是 充当两个不同接⼝之间的桥梁&#xff0c;使得原本接⼝不兼容的类能够⼀起⼯作。 2. 适配器模式的组成 &#xff08;1&#xff09;接口类&#xff0c;给客户端调用&…

vue的异步操作,钩子函数,和Element组件

使用vue进行异步操作 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </…

【NumPy】关于numpy.zeros()函数,看这一篇文章就够了

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

XV4001KD汽车级应用的数字输出陀螺传感器

XV4001KD是一款专为汽车导航系统和远程信息处理而设计的数字输出陀螺传感器。采用SPI/I2C串行接口&#xff0c;具有高精度的16位的角速率输出和11位的温度输出功能&#xff0c;能够准确地测量车辆的运动状态和环境温度&#xff0c;为导航系统和信息处理提供可靠的数据支持。以及…

《二》MP3在线搜索所歌曲的实现

上一期我们大致实现了布局等操作 那么这一期我们来实现如何去搜索歌曲&#xff1a; 首先呢&#xff0c;我们是设计多媒体&#xff0c;要包含多媒体类头文件&#xff0c;还要能在线搜索&#xff0c;那就要包含网络上的头文件&#xff0c;还要实现打开文件操作&#xff0c;处理…

Pytorch DDP分布式细节分享

自动微分和autograde 自动微分 机器学习/深度学习关键部分之一&#xff1a;反向传播&#xff0c;通过计算微分更新参数值。 自动微分的精髓在于它发现了微分计算的本质&#xff1a;微分计算就是一系列有限的可微算子的组合。 自动微分以链式法则为基础&#xff0c;依据运算逻…

kubeadm部署k8s v1.28

一、主机准备 主机硬件配置说明 作用IP地址操作系统配置k8s-master01192.168.136.55openEuler-22.03-LTS-SP12颗CPU 4G内存 50G硬盘k8s-node01192.168.136.56openEuler-22.03-LTS-SP12颗CPU 4G内存 50G硬盘k8s-node02192.168.136.57openEuler-22.03-LTS-SP12颗CPU 4G内存 50G…

安全生产月答题pk小程序怎么做

在当今信息化时代&#xff0c;小程序已成为人们日常生活和工作中不可或缺的一部分。特别是在安全生产领域&#xff0c;通过小程序进行答题PK活动&#xff0c;不仅可以提高员工的安全意识&#xff0c;还能促进团队间的协作与交流。本文将详细介绍如何制作一款安全生产月答题PK小…

C语言实现贪吃蛇游戏

文章目录 前言一、蛇的结构的创建二、游戏测试的逻辑三、初始化游戏四、游戏运行五、游戏结束总结 前言 C语言实现贪吃蛇游戏 一、蛇的结构的创建 先创建一个蛇身的节点在创建蛇的结构&#xff08;包括&#xff1a;指向蛇头的指针&#xff0c;指向食物的指针&#xff0c;游戏…

初识DataX3.0

目前接到任务&#xff0c;让同步表数据。市面很多同步工具不一一尝试了&#xff0c;信赖阿里&#xff0c;所以调研了一下阿里的dataX,一点点来吧&#xff0c;学习为主 环境准备&#xff1a;linux6.8 python自带的2.7 MySQL 5.7.1 1.先下载&#xff1a; wget http://datax-o…

油猴脚本使用cookie一般是某请求返回的setcookie,一般不是js生成的,直接请求拼接

写完hook脚本 删除页面cooike&#xff0c;打开开发者模式&#xff0c;刷新页面 cookie一般是某请求返回的setcookie,一般不是js生成的&#xff0c;直接请求拼接带cookie请求 看网络里的cookie httponly打钩的是服务器返回的&#xff0c;但不一定对&#xff0c;还是要看保存日…