ESP-DL部署魔改MobilenetV1—3. 模型部署

在完成模型训练和模型量化后,就可以开始我们的模型部署了。这部分的关键在于Model类中层的初始化以及build和call的实现。

环境依赖

  • esp-idf > 5.0
  • esp-dl

模型定义

在模型定义时,我们需要用到量化时输出的层信息、cat_vs_dog_coefficient.hpp,必要时还可以使用netron查看神经网络的结构。

项目结构如下所示:

├── CMakeLists.txt
├── components
│   └── esp-dl
├── main
│   ├── app_main.cpp
│   ├── input_data.h
│   └── CMakeLists.txt
├── model
│   ├── cat_vs_dog_coefficient.cpp
│   ├── cat_vs_dog_coefficient.hpp
│   └── model_define.hpp
├── partitions.csv
├── sdkconfig
├── sdkconfig.defaults
├── sdkconfig.defaults.esp32
├── sdkconfig.defaults.esp32s2
└── sdkconfig.defaults.esp32s3

量化时输出的层信息如下:

Generating the quantization table:
Converting coefficient to int16 per-tensor quantization for esp32s3
Exporting finish, the output files are: ./cat_vs_dog_coefficient.cpp, ./cat_vs_dog_coefficient.hppQuantized model info:
model input name: input_1, exponent: -15
Transpose layer name: StatefulPartitionedCall/model/conv1/Conv2D__6, output_exponent: -15
Conv layer name: StatefulPartitionedCall/model/conv1/Conv2D, output_exponent: -11
DepthwiseConv layer name: StatefulPartitionedCall/model/conv_dw_1/depthwise, output_exponent: -10
Conv layer name: StatefulPartitionedCall/model/conv_pw_1/Conv2D, output_exponent: -9
DepthwiseConv layer name: StatefulPartitionedCall/model/conv_dw_2/depthwise, output_exponent: -10
Conv layer name: StatefulPartitionedCall/model/conv_pw_2/Conv2D, output_exponent: -10
DepthwiseConv layer name: StatefulPartitionedCall/model/conv_dw_4/depthwise, output_exponent: -10
Conv layer name: StatefulPartitionedCall/model/conv_pw_4/Conv2D, output_exponent: -10
DepthwiseConv layer name: StatefulPartitionedCall/model/conv_dw_5/depthwise, output_exponent: -10
Conv layer name: StatefulPartitionedCall/model/conv_pw_5/Conv2D, output_exponent: -11
DepthwiseConv layer name: StatefulPartitionedCall/model/conv_dw_6/depthwise, output_exponent: -11
Conv layer name: StatefulPartitionedCall/model/conv_pw_6/Conv2D, output_exponent: -12
GlobalAveragePool layer name: StatefulPartitionedCall/model/global_average_pooling2d/Mean, output_exponent: -12
Squeeze layer name: StatefulPartitionedCall/model/global_average_pooling2d/Mean_Squeeze__118, output_exponent: -12
Gemm layer name: fused_gemm_0, output_exponent: -10
Softmax layer name: StatefulPartitionedCall/model/softmax/Softmax, output_exponent: -14

我们以下关于模型的操作均在model_define.hpp文件中完成。

层初始化

导入必要头文件

首先,参考量化时输出的层信息,我们要将所有用到的层的头文件以及一些其它必要的头文件包含进来

#include "cat_vs_dog_coefficient.hpp"
#include "dl_layer_model.hpp"
#include "dl_layer_base.hpp"#include "dl_layer_conv2d.hpp"
#include "dl_layer_depthwise_conv2d.hpp"
#include "dl_layer_global_avg_pool2d.hpp"
#include "dl_layer_softmax.hpp"
#include <stdint.h>

定义层

接下来是定义每个层。

  • 由于onnx中张量的顺序为CHW,而我们训练时使用的是HWC顺序,因此在模型输入端会有一个reshape或transpose,这里这层和输入不需要定义。
  • 最后的全连接层为矩阵乘法,在优化模型时会将 其和其后的add层 转换为Gemm层,如果缺少add层则不会进行转换。Gemm层依然使用conv2d来实现。
  • 在最后的全连接层,Squeeze会将为1的维度删除,但是会导致紧随其后的conv2d报错,因此这里的Squeeze我们也不定义。
  • 除了输出层,所有层都定义为私有变量。
  • 在建立模型时,请按照量化时输出的层信息中的顺序放置各层。

剔除掉不需要构建的层之后,我们的层结构如下:

Conv layer name: StatefulPartitionedCall/model/conv1/Conv2D, output_exponent: -11
DepthwiseConv layer name: StatefulPartitionedCall/model/conv_dw_1/depthwise, output_exponent: -10
Conv layer name: StatefulPartitionedCall/model/conv_pw_1/Conv2D, output_exponent: -9
DepthwiseConv layer name: StatefulPartitionedCall/model/conv_dw_2/depthwise, output_exponent: -10
Conv layer name: StatefulPartitionedCall/model/conv_pw_2/Conv2D, output_exponent: -10
DepthwiseConv layer name: StatefulPartitionedCall/model/conv_dw_4/depthwise, output_exponent: -10
Conv layer name: StatefulPartitionedCall/model/conv_pw_4/Conv2D, output_exponent: -10
DepthwiseConv layer name: StatefulPartitionedCall/model/conv_dw_5/depthwise, output_exponent: -10
Conv layer name: StatefulPartitionedCall/model/conv_pw_5/Conv2D, output_exponent: -11
DepthwiseConv layer name: StatefulPartitionedCall/model/conv_dw_6/depthwise, output_exponent: -11
Conv layer name: StatefulPartitionedCall/model/conv_pw_6/Conv2D, output_exponent: -12
GlobalAveragePool layer name: StatefulPartitionedCall/model/global_average_pooling2d/Mean, output_exponent: -12
Gemm layer name: fused_gemm_0, output_exponent: -10
Softmax layer name: StatefulPartitionedCall/model/softmax/Softmax, output_exponent: -14

因此我们定义的层如下:

class CAT_VS_DOG : public Model<int16_t> // Derive the Model class in "dl_layer_model.hpp"
{
private:// Declare layers as member variablesConv2D<int16_t> l1;DepthwiseConv2D<int16_t> l2;Conv2D<int16_t> l3;DepthwiseConv2D<int16_t> l4;Conv2D<int16_t> l5;DepthwiseConv2D<int16_t> l6;Conv2D<int16_t> l7;DepthwiseConv2D<int16_t> l8;Conv2D<int16_t> l9;DepthwiseConv2D<int16_t> l10;Conv2D<int16_t> l11;GlobalAveragePool2D<int16_t> l12;Conv2D<int16_t> l13;public:Softmax<int16_t> l14; //Output layer
}

构建层

有关如何初始化不同运算层,请查看 esp-dl/include/layer/ 文件夹中相应的 .hpp 文件。

Conv2D<int16_t> l1为例,它的定义可以参考如下

l1(Conv2D<int16_t>(-11, get_statefulpartitionedcall_model_conv1_conv2d_filter(), get_statefulpartitionedcall_model_conv1_conv2d_bias(), get_statefulpartitionedcall_model_conv1_conv2d_activation(), PADDING_SAME_END, {}, 2,2, "l1")),
  • -11:量化时输出的该层指数位
  • get_statefulpartitionedcall_model_conv1_conv2d_filter(), get_statefulpartitionedcall_model_conv1_conv2d_bias(), get_statefulpartitionedcall_model_conv1_conv2d_activation():分别为model_define.hpp中定义的获取该层权重、偏差、激活层参数的函数
  • PADDING_SAME_END:padding的方式,与模型构建时定义有关
  • 2,2:步长,也与模型构建时定义有关

构建完成的结果如下:

CAT_VS_DOG () : l1(Conv2D<int16_t>(-11, get_statefulpartitionedcall_model_conv1_conv2d_filter(), get_statefulpartitionedcall_model_conv1_conv2d_bias(), get_statefulpartitionedcall_model_conv1_conv2d_activation(), PADDING_SAME_END, {}, 2,2, "l1")),l2(DepthwiseConv2D<int16_t>(-10, get_statefulpartitionedcall_model_conv_dw_1_depthwise_filter(), get_statefulpartitionedcall_model_conv_dw_1_depthwise_bias(), get_statefulpartitionedcall_model_conv_dw_1_depthwise_activation(), PADDING_SAME_END, {}, 1,1, "l2")),l3(Conv2D<int16_t>(-9, get_statefulpartitionedcall_model_conv_pw_1_conv2d_filter(), get_statefulpartitionedcall_model_conv_pw_1_conv2d_bias(), get_statefulpartitionedcall_model_conv_pw_1_conv2d_activation(), PADDING_SAME_END, {}, 1,1, "l3")),l4(DepthwiseConv2D<int16_t>(-10, get_statefulpartitionedcall_model_conv_dw_2_depthwise_filter(), get_statefulpartitionedcall_model_conv_dw_2_depthwise_bias(), get_statefulpartitionedcall_model_conv_dw_2_depthwise_activation(), PADDING_VALID, {}, 2,2, "l4")),l5(Conv2D<int16_t>(-10, get_statefulpartitionedcall_model_conv_pw_2_conv2d_filter(), get_statefulpartitionedcall_model_conv_pw_2_conv2d_bias(), get_statefulpartitionedcall_model_conv_pw_2_conv2d_activation(), PADDING_SAME_END, {}, 1,1, "l5")),l6(DepthwiseConv2D<int16_t>(-10, get_statefulpartitionedcall_model_conv_dw_4_depthwise_filter(), get_statefulpartitionedcall_model_conv_dw_4_depthwise_bias(), get_statefulpartitionedcall_model_conv_dw_4_depthwise_activation(), PADDING_VALID, {}, 2,2, "l6")),l7(Conv2D<int16_t>(-10, get_statefulpartitionedcall_model_conv_pw_4_conv2d_filter(), get_statefulpartitionedcall_model_conv_pw_4_conv2d_bias(), get_statefulpartitionedcall_model_conv_pw_4_conv2d_activation(), PADDING_SAME_END, {}, 1,1, "l7")),l8(DepthwiseConv2D<int16_t>(-10, get_statefulpartitionedcall_model_conv_dw_5_depthwise_filter(), get_statefulpartitionedcall_model_conv_dw_5_depthwise_bias(), get_statefulpartitionedcall_model_conv_dw_5_depthwise_activation(), PADDING_SAME_END, {}, 1,1, "l8")),l9(Conv2D<int16_t>(-11, get_statefulpartitionedcall_model_conv_pw_5_conv2d_filter(), get_statefulpartitionedcall_model_conv_pw_5_conv2d_bias(), get_statefulpartitionedcall_model_conv_pw_5_conv2d_activation(), PADDING_SAME_END, {}, 1,1, "l9")),l10(DepthwiseConv2D<int16_t>(-11, get_statefulpartitionedcall_model_conv_dw_6_depthwise_filter(), get_statefulpartitionedcall_model_conv_dw_6_depthwise_bias(), get_statefulpartitionedcall_model_conv_dw_6_depthwise_activation(), PADDING_VALID, {}, 2,2, "l10")),l11(Conv2D<int16_t>(-12, get_statefulpartitionedcall_model_conv_pw_6_conv2d_filter(), get_statefulpartitionedcall_model_conv_pw_6_conv2d_bias(), get_statefulpartitionedcall_model_conv_pw_6_conv2d_activation(), PADDING_SAME_END, {}, 1,1, "l11")),l12(GlobalAveragePool2D<int16_t>(-11, "l12")),l13(Conv2D<int16_t>(-10, get_fused_gemm_0_filter(), get_fused_gemm_0_bias(), NULL, PADDING_VALID, {}, 1,1, "l13")),l14(Softmax<int16_t>(-14,"l14")){}

实现build

只需要将每一层串起来即可,结果如下:

void build(Tensor<int16_t> &input)
{this->l1.build(input);this->l2.build(this->l1.get_output());this->l3.build(this->l2.get_output());this->l4.build(this->l3.get_output());this->l5.build(this->l4.get_output());this->l6.build(this->l5.get_output());this->l7.build(this->l6.get_output());this->l8.build(this->l7.get_output());this->l9.build(this->l8.get_output());this->l10.build(this->l9.get_output());this->l11.build(this->l10.get_output());this->l12.build(this->l11.get_output());this->l13.build(this->l12.get_output());this->l14.build(this->l13.get_output());
}

实现call

同build,只需要将每一层串起来即可:

void call(Tensor<int16_t> &input)
{this->l1.call(input);input.free_element();this->l2.call(this->l1.get_output());this->l1.get_output().free_element();this->l3.call(this->l2.get_output());this->l2.get_output().free_element();this->l4.call(this->l3.get_output());this->l3.get_output().free_element();this->l5.call(this->l4.get_output());this->l4.get_output().free_element();this->l6.call(this->l5.get_output());this->l5.get_output().free_element();this->l7.call(this->l6.get_output());this->l6.get_output().free_element();this->l8.call(this->l7.get_output());this->l7.get_output().free_element();this->l9.call(this->l8.get_output());this->l8.get_output().free_element();this->l10.call(this->l9.get_output());this->l9.get_output().free_element();this->l11.call(this->l10.get_output());this->l10.get_output().free_element();this->l12.call(this->l11.get_output());this->l11.get_output().free_element();this->l13.call(this->l12.get_output());this->l12.get_output().free_element();this->l14.call(this->l13.get_output());this->l13.get_output().free_element();
}

模型运行

设置输入

输入的数据存储在input_data.h文件中,首先对其进行处理

int input_height = 96;
int input_width = 96;
int input_channel = 3;
int input_exponent = -15;
int classes = 2;__attribute__((aligned(16))) int16_t *input_data = (int16_t *)dl::tool::malloc_aligned_prefer(input_height*input_width*input_channel, sizeof(int16_t *));for(int i=0 ;i<input_height*input_width*input_channel; i++){float normalized_input = example_input[i];input_data[i] = (int16_t)DL_CLIP(normalized_input * (1 << -input_exponent), -32768, 32767);
}

之后定义输入张量并设置其尺寸

Tensor<int16_t> input;
input.set_element((int16_t *)input_data).set_exponent(input_exponent).set_shape({input_height, input_width, input_channel}).set_auto_free(false);

调用模型

定义模型

CAT_VS_DOG model;

调用模型

model.forward(input);

获取结果

float *score = model.l14.get_output().get_element_ptr();
float max_score = score[0];
int max_index = 0;
for (size_t i = 0; i < classes; i++)
{printf("%f, ", score[i]*100);if (score[i] > max_score){max_score = score[i];max_index = i;}
}
printf("\n");

运行结果

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

分析源码学习c++(srs中http客户端)

文章目录 背景基础知识c标准库虚函数虚函数使用方法 虚析构函数 HTTP客户端使用方法TCP传输层分析使用方法结构分析连接函数读写函数 协议层分析初始化函数发送请求响应数据解析 背景 通过阅读源码&#xff0c;编写分析笔记来学习C是一种非常有效且深入的方法&#xff0c;能帮助…

论文解读 | ACL2024 Outstanding Paper:因果指导的主动学习方法:助力大语言模型自动识别并去除偏见...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击阅读原文观看作者直播讲解回放&#xff01; 作者简介 孙洲浩&#xff0c;哈尔滨工业大学SCIR实验室博士生 概述 尽管大语言模型&#xff08;LLMs&#xff09;展现出了非常强大的能力&#xff0c;但它们仍然…

数据源10min自动断开连接导致查询抛异常(未获取可用连接)

由于个人能力有限&#xff0c;本文章仅仅代表本人想法&#xff0c;若有不对请即时指出&#xff0c;若有侵权&#xff0c;请联系本人。 1 背景 工作中引入druid来管理数据源连接&#xff0c;由于数据源每隔10分钟强制管理空闲超过10分钟的连接&#xff0c;导致每隔10分钟出现1…

Web攻防之应急响应(二)

目录 前提 &#x1f354;学习Java内存马前置知识 内存马 内存马的介绍 内存马的类型众多 内存马的存在形式 Java web的基础知识&#xff1a; Java内存马的排查思路&#xff1a; &#x1f354;开始查杀之前的需要准备 1.登录主机启动服务器 2.生成jsp马并连接成功 …

【Linux】多线程:线程概念,线程与进程的区别与联系,多线程相较于多进程的优势

目录 一、进程基本属性回顾 二、线程概念 三、操作系统为什么要引入线程—多进程和多线程的区别 为什么多线程比多线程调度效率更快&#xff1f; 四、线程的优点 五、线程的缺点 六、线程异常 一、进程基本属性回顾 在学习线程之前&#xff0c;我们先来回顾一下进程的基…

注册安全分析报告:熊猫频道

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

【Rust】007-包管理与模块管理

【Rust】007-包管理与模块管理 文章目录 【Rust】007-包管理与模块管理一、包管理器&#xff1a;Cargo1、简介Cargo 官方文档仓库 2、项目初始化3、写一个小程序任务目标寻找合适的库添加库到我们的项目中代码实现cargo run运行 二、模块管理1、概述2、文件作为模块第一步&…

前端模拟面试:如何检查JavaScript对象属性是否存在?

你正在参加一场关键的前端开发面试&#xff0c;面试官提出了一个经典的JavaScript问题&#xff1a;“在JavaScript中&#xff0c;如何检查对象是否包含某个属性&#xff1f;请你详细介绍几种不同的方法&#xff0c;并解释它们的区别。” 这个问题不仅考验你对JavaScript的基础掌…

怎样在公司将手机屏幕(远程)投屏到家里的大电视上?

我不住家里&#xff0c;前几次回去都会替老爸老妈清理手机。这两个星期没空回去&#xff0c;老爸吐槽手机用几天就又卡了&#xff0c;其实就是清理一些手机缓存的问题。 我说我远程控制他的手机&#xff0c;给他清理一下。他一听“控制”就不喜欢&#xff0c;说我大了&#xf…

sM4040B科学级显微制冷相机特性

sM4040B科学级显微制冷相机特性 sM4040B搭载了 GSENSE4040BSI 3.2 英寸图像传感器&#xff0c;针对传感器固有的热噪声&#xff0c;专门设计了高效制冷模块&#xff0c;使得相机传感器的工作温度比环境温度低达 35-40 度。针对制冷相机常见的低温结雾现象设计了防结雾机制&…

【Python百日进阶-Web开发-音频】Day707 - 时域处理 librosa.autocorrelate

文章目录 一、时域处理1.1 librosa.autocorrelate1.1.1 语法与参数1.1.2 例子1.1.2.1 计算完全自相关y1.1.2.2 计算长达 4 秒的起始强度自相关 一、时域处理 1.1 librosa.autocorrelate https://librosa.org/doc/latest/generated/librosa.autocorrelate.html 1.1.1 语法与参…

哪款宠物空气净化器能更好的清理浮毛?希喂、352、IAM测评分享

家里这三只可爱的小猫咪&#xff0c;已然成为了我们生活中不可或缺的家庭成员&#xff0c;陪伴我们度过了说长不长说短不短的五年时光。时常庆幸自己当年选择养它们&#xff0c;在我失落的时候总能给我安慰&#xff0c;治愈我多时。 但这个温馨的背后也有一点小烦恼&#xff0…

记一种常用的实时数据同步方案:Canal+Kafka+Flume

记一种常用的实时数据同步方案&#xff1a;CanalKafkaFlume 在当今数据驱动的业务环境中&#xff0c;数据同步是确保系统间数据一致性的关键环节。一种高效、稳定且可扩展的数据同步方案对于支撑企业的数据处理和分析需求至关重要。本文将介绍一种结合了Canal、Kafka和Flume的…

IOS 20 发现界面(UITableView)歌单列表(UICollectionView)实现

发现界面完整效果 本文实现歌单列表效果 文章基于 IOS 19 发现界面&#xff08;UITableView&#xff09;快捷按钮实现 继续实现发现界面歌单列表效果 歌单列表Cell实现 实现流程&#xff1a; 1.创建Cell&#xff0c;及在使用UITableView的Controller控制器上注册Cell&#x…

STM32F103C8----GPIO(跟着江科大学STM32)

一&#xff0c;GPIO简介 GPIO&#xff08;General Purpose Input Output&#xff09;通用输入输出口 可配置为8种输入输出模式 引脚电平&#xff1a;0V~3.3V&#xff08;0V&#xff09;&#xff0c;部分引脚可容忍5V 输出模式下可控制端口输出高低电平&#xff0c;用以驱动…

AI-Talk开发板之LED

一、说明 AI-Talk开发板上有一颗用户LED&#xff0c;连接在CH32 PA2管脚&#xff0c;低电平亮&#xff0c;高电平灭。 相关电路图如下&#xff1a; 需要提前给CH32V003烧录特定的固件才能将CH32作为CSK6011A的exmcu&#xff0c;参考AI-Talk开发板更新CH32固件。​​​​​​​…

如何查看Mac的处理器架构‌‌是ARM还是x86

‌通过命令行查看Mac的处理器架构‌‌ 打开终端&#xff08;Terminal&#xff09;。输入命令 uname -m 并回车。如果输出结果是 arm64&#xff0c;则表示你的Mac使用的是ARM架构&#xff1b;如果输出结果是 x86_64&#xff0c;则表示你的Mac使用的是x86架构。 如图&#xff1…

2024/9/4黑马头条跟学笔记(二)

app端文章列表 学习内容 需求分析 上方分类频道切换 布局&#xff0c;无图&#xff0c;单图&#xff0c;三张图 文章数据库表 导入文章数据库 结构分析 配置-文章 一对一&#xff0c;拆表&#xff0c;冷热数据分离满足范式 表的拆分-垂直分表 优势 查文章信息不会连带查…

Day10_0.1基础学习MATLAB学习小技巧总结(10)——程序流程控制

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 素材来源“数学建模清风” 特此说明&#xff1a;本博客的内容只在于总结在…

页面小组件-搜索栏(一)

样例展示 效果示例-折叠状态 效果示例-展开状态 代码示例 <custom-search-wrapper><!--showFoldBtn 需要展示折叠按钮时传值--><template slotleft><el-form:model"searchFormData"inlinesize"small"><el-form-item><e…