使用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.rhkb.cn/news/332340.html

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

相关文章

AGI系列(1):掌握AI大模型提示词优化术,提问准确率飙升秘籍

当我们向AI大模型提问时&#xff0c;通常人们的做法是有什么问题&#xff0c;就直接去问&#xff0c;得到大模型的回复结果&#xff0c;时好时坏&#xff0c;完全没有可控性。 那么有没有一种方式或是一套方法&#xff0c;可以让我们向大模型提问时&#xff0c;得到的结果更准确…

深入理解 Mysql 分层架构:从存储引擎到查询优化器的内部机制解析

一、基础架构 1.连接器 1.会先连接到这个数据库上&#xff0c;这时候接待你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接 2.用户密码连接成功之后&#xff0c;会从权限表中拿出你的权限&#xff0c;后续操作权限都依赖于此时拿出的权限,这就意味着当链…

web如何做接口层面自动化测试?

接口层面约等于集成化测试&#xff0c;且需要启动web容器 一般web项目的&#xff0c;代码都是按照分层开发的&#xff0c;业务主要是集中在service和dao层&#xff0c;而我们如果仅仅是利用之前的单元测试,然后把依赖的代码直接mock掉&#xff0c;仅仅测试controller这一块是没…

java “错误:编码GBK 的不可映射字符”

环境&#xff1a;JDK-17 本机编码&#xff1a;utf-8 代码编码&#xff1a;GBK 错误&#xff1a;java “错误&#xff1a;编码GBK 的不可映射字符” 解决1&#xff1a;记事本打开java源文件&#xff0c;另存为选择ANSI编码 解决2&#xff1a;复制代码再将编码格式改为utf-8,…

网络原理-以太网协议和DNS协议

一、以太网协议 以太网协议会涉及到数据链路层和物理层。 如图&#xff1a; 这里面的目的地址和源地址指的并不是IP地址,而是MAC地址(物理地址)。长度为6个字节。即最多能表示2^48 个地址,也是非常大的,足够给全球每个设备都分配一个地址,因此在网卡出厂的时候都会带有一个唯…

电子技术学习路线

在小破站上看到大佬李皆宁的技术路线分析&#xff0c;再结合自己这几年的工作。发现的确是这样&#xff0c;跟着大佬的技术路线去学习是会轻松很多&#xff0c;现在想想&#xff0c;这路线其实跟大学四年的学习顺序是很像的。 本期记录学习路线&#xff0c;方便日后查看。 传统…

5月23日学习记录

[CSAWQual 2019]Unagi 涉及&#xff1a;xxe漏洞&#xff0c;外来编码xml绕过 打开环境&#xff0c;发现存在文件上传 简单上传一个php 毫无疑问上传失败&#xff0c;说是存在waf&#xff0c;绕过waf才能上传&#xff0c;点击here看看 xml编码&#xff0c;可能存在xxe漏洞&…

Point-Nerf 理论笔记和理解

文章目录 什么是point nerf 和Nerf 有什么区别Point Nerf 核心结构有哪些&#xff1f;什么是point-based radiance field? 点云位置以及置信度是怎么来Point pruning 和 Point Growing 什么是point nerf 和Nerf 有什么区别 基本的nerf 是通过过拟合MLP来完成任意视角场景的重…

c#核心学习1

一、面向对象的概念 1.面向过程编程 2.面向对象编程 3.为什么要学习面向对象编程 提高代码复用率、提高开发效率、提高程序可拓展性、清晰的逻辑关系 4.如何学习 二、面向对象--封装 1&#xff09;类和对象 1.什么是类 2.类申明在哪里 类一般声明在namespace语句块中 3.…

摄像头应用测试

作者简介&#xff1a; 一个平凡而乐于分享的小比特&#xff0c;中南民族大学通信工程专业研究生在读&#xff0c;研究方向无线联邦学习 擅长领域&#xff1a;驱动开发&#xff0c;嵌入式软件开发&#xff0c;BSP开发 作者主页&#xff1a;一个平凡而乐于分享的小比特的个人主页…

【操作系统】发展与分类(手工操作、批处理、分时操作、实时操作)

2.操作系统发展与分类 思维导图 手工操作阶段&#xff08;此阶段无操作系统&#xff09; 需要人工干预 缺点&#xff1a; 1.用户独占全机&#xff0c;资源利用率低&#xff1b; 2.CPU等待手工操作&#xff0c;CPU利用不充分。 批处理阶段&#xff08;操作系统开始出现&#x…

【JavaScript】初识 Promise

出现原由 先看一个例子&#xff1a; 模拟发送表白信息&#xff0c;如果一个失败&#xff0c;那么再给其他人发送&#xff0c;这时就相当于在失败回调函数中套了一层回调&#xff1b;如果后续还有多个表白对象&#xff0c;那么将一层一层地嵌套下去&#xff0c;也就是回调地狱…

Generative Action Description Prompts for Skeleton-based Action Recognition

标题&#xff1a;基于骨架的动作识别的生成动作描述提示 源文链接&#xff1a;https://openaccess.thecvf.com/content/ICCV2023/papers/Xiang_Generative_Action_Description_Prompts_for_Skeleton-based_Action_Recognition_ICCV_2023_paper.pdfhttps://openaccess.thecvf.c…

正运动控制器:视觉纠偏和找孔

一、用户主界面CCD参数设置 通过主界面CCD参数设置&#xff0c;学习如何操作计算相机中心与电批中心的偏移量&#xff0c;以及相机标定的功能。 1、相机中心与电批中心的偏移量计算 1.1、在用户主界面点击CCD参数按钮&#xff0c;进入CCD设置界面。 主界面 CCD参数设置界面 1…

显存碎片化与CUDA OOM解决

目录 一.显存碎片化与CUDA OOM解决 1.查看显卡内存容量 2.显存碎片化 &#xff08;1&#xff09;如何理解显存中“已分配”和“未分配”的内存块? &#xff08;2&#xff09;碎片化形成的原因&#xff1f; &#xff08;3&#xff09;如何减轻显存碎片化&#xff1f; 3.实…

空间注意力机制

第一步是沿着通道维度进行最大池化和平均池化&#xff0c;比如下面3*3的特征图&#xff0c;有3个通道。 第二步新特征图进行拼接并经过卷积调整通道数 第三步经过Sigmoid函数后乘到输入上 代码&#xff1a; class SpatialAttention(layers.Layer):def __init__(self):super(S…

Hibernate

主流ORM框架Object Relation Mapping对象关系映射&#xff0c;将面向对象映射成面向关系。 如何使用 1、导入相关依赖 2、创建Hibernate配置文件 3、创建实体类 4、创建实体类-关系映射文件 5、调用Hibernate API完成操作 具体操作 1、创建 Maven工程&#xff0c;在pom.xml中导…

Milvus Cloud 非结构化数据平台

从技术面来看,向量数据库底座自然而然向外延伸的产品包含: 1)向量提取,从非结构化数据中提取向量,这是向量数据库上游的工作,十分重要; 2)模型选择,选择正确的模型,能够更精准、更高质量地提取向量; 3)映射管理,即管理数据的本体和数据的语义层之间的映射,在…

【Linux杂货铺】进程通信

目录 &#x1f308; 前言&#x1f308; &#x1f4c1; 通信概念 &#x1f4c1; 通信发展阶段 &#x1f4c1; 通信方式 &#x1f4c1; 管道&#xff08;匿名管道&#xff09; &#x1f4c2; 接口 ​编辑&#x1f4c2; 使用fork来共享通道 &#x1f4c2; 管道读写规则 &…

01.并发编程简介

1 什么是并发编程 所谓并发编程是指在一台处理器上“同时”处理多个任务。并发是在同一实体上的多个事件。多个事件在同一时间间隔发生。 2 为什么我们要学习并发编程&#xff1f; 最直白的原因就是因为面试需要&#xff0c;大厂的 Java 岗的并发编程能力属于标配。 而在非大厂…