【嵌入式AI部署神经网络】STM32CubeIDE上部署神经网络之指纹识别(Pytorch)——篇一|环境搭建与模型初步部署篇

前言:本篇主要讲解搭建所需环境,以及基于pytorch框架在stm32cubeide上部署神经网络,部署神经网络到STM32单片机,本篇实现初步部署模型,没有加入训练集与验证集,将在第二篇加入。篇二详细讲解STM32CubeIDE上部署神经网络之指纹识别(Pytorch)的数据准备和模型训练过程等,进行实战,第二篇在本专栏查阅。

目录

1. 环境安装和配置

2. AI神经网络模型搭建

2.1 数据集介绍

2.2 网络模型

2.3 训练

3. STM32CubeIDE上进行模型转换与模型部署到单片机

4. STM32 CubeUDE上进行模型验证

5. 结果统计与分析


1. 环境安装和配置

本文介绍在STM32cubeIDE上部署AI模型,开发板型号STM32F429IGT6。

与AI加速器不同,ST支持神经网络计算是因为之前的芯片已经内置了DSP处理器,可以执行高精度浮点运算,正好可以拿来做神经网络计算。如何判断自己准备购买的板子适不适合做AI计算,最好也按以下步骤在CUBE-AI上模拟部署一遍,若模拟成功,所选开发板就是可以的。

STM32cubeIDE可直接在ST官网下载,下载链接

https://www.st.com/zh/development-tools/stm32cubeide.html

默认安装即可,不懂可自行上网查教程。

2. AI神经网络模型搭建

2.1 数据集介绍

针对tinyML开发了自己的指纹识别数据集,数据集和完整代码见文末下载链接。指纹识别数据集包含100个类别,大小为260*260,训练集30张,测试集5张。在测试时使用128*128与64*64的分辨率。

数据集在如下文件夹中

生成测试集的方法:

import os
import numpy as np
from PIL import Image
import torchvision.transforms as transformsnormalize = transforms.Normalize(mean=[0.5],std=[0.5])
test_transforms = transforms.Compose([# transforms.RandomResizedCrop(224),transforms.Resize(128),transforms.ToTensor(),normalize])def prepare_eval_data(data_file, transform=None):datas = os.listdir(data_file)imgs=[]labels=[]for  img_path in datas:data = Image.open(data_file + '/' + img_path)  # 260*260*1label, _ = img_path.split('_')label = int(label) - 1label_ohot=np.zeros(100)label_ohot[label]=1# print(data.shape, label)data1 = transform(data)data2 = transform(data)data3 = transform(data)data = np.concatenate([data1, data2, data3], 0)labels.append(label_ohot)imgs.append(data)imgs = np.array(imgs)labels = np.array(labels)print(imgs.shape,labels.shape)return imgs,labelsif __name__ == '__main__':data_path = '/disks/disk2/dataset/fingerprint/'testx,testy=prepare_eval_data(data_path+'test',test_transforms )print(testx.shape,testy.shape)print(testy[0])np.save('fpr100*5_testx_128.npy', testx)np.save('fpr100*5_testy_128.npy', testy)

2.2 网络模型

  测试了多个轻量化神经网络模型,如压缩后的MobileNet v2:

class MobileNetV2Slim(nn.Module):def __init__(self, num_classes=1000):super(MobileNetV2Slim,self).__init__()self.first_conv = Conv3x3BNReLU(3,4,2,groups=1)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)# self.layer1 = self.make_layer(in_channels=8, out_channels=4, stride=1, block_num=1)self.layer2 = self.make_layer(in_channels=4, out_channels=6, stride=1, block_num=1)self.layer3 = self.make_layer(in_channels=6, out_channels=8, stride=2, block_num=2)self.layer4 = self.make_layer(in_channels=8, out_channels=16, stride=2, block_num=3)self.layer5 = self.make_layer(in_channels=16, out_channels=24, stride=1, block_num=3)self.layer6 = self.make_layer(in_channels=24, out_channels=32, stride=2, block_num=3)self.layer7 = self.make_layer(in_channels=32, out_channels=64, stride=1, block_num=1)self.last_conv = Conv1x1BNReLU(64,128)self.avgpool = nn.AvgPool2d(kernel_size=2,stride=1)self.dropout = nn.Dropout(p=0.2)self.linear = nn.Linear(in_features=128,out_features=num_classes)def make_layer(self, in_channels, out_channels, stride, block_num):layers = []layers.append(InvertedResidual(in_channels, out_channels, stride))for i in range(1, block_num):layers.append(InvertedResidual(out_channels,out_channels,1))return nn.Sequential(*layers)def init_params(self):for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight)nn.init.constant_(m.bias, 0)elif isinstance(m, nn.Linear) or isinstance(m, nn.BatchNorm2d):nn.init.constant_(m.weight, 1)nn.init.constant_(m.bias, 0)def forward(self, x):x = self.first_conv(x)x=self.maxpool(x)# x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = self.layer5(x)x = self.layer6(x)x = self.layer7(x)x = self.last_conv(x)# print(x.size())x = self.avgpool(x)# x = x.view(x.size(0),-1)x=x.reshape(int(x.size(0)), -1)x = self.dropout(x)out = self.linear(x)return out

2.3 训练

基于pytorch搭建模型进行训练,核心代码如下:


'''***********- trainer -*************'''
class trainer:def __init__(self, loss_f,loss_dv,loss_fn, model, optimizer, scheduler, config):self.loss_f = loss_fself.loss_dv = loss_dvself.loss_fn = loss_fnself.model = modelself.optimizer = optimizerself.scheduler = schedulerself.config = configdef batch_train(self, batch_imgs, batch_labels, epoch):predicted = self.model(batch_imgs)loss = self.myloss(predicted, batch_labels)predicted = softmax(predicted, dim=-1)del batch_imgs, batch_labelsreturn loss, predicteddef train_epoch(self, loader,epoch):self.model.train()tqdm_loader = tqdm(loader)# acc = Accuracy_score()losses = AverageMeter()top1 = AverageMeter()top5 = AverageMeter()print("\n************Training*************")for batch_idx, (imgs, labels) in enumerate(tqdm_loader):#print("data",imgs.size(), labels.size())#[128, 3, 32, 32]) torch.Size([128]imgs, labels=imgs.cuda(), labels.cuda()#permute(0,3,1,2).# print(self.optimizer.param_groups[0]['lr'])loss, predicted = self.batch_train(imgs, labels, epoch)losses.update(loss.item(), imgs.size(0))# print(predicted.size(),labels.size())self.optimizer.zero_grad()loss.backward()self.optimizer.step()# self.scheduler.step()err1, err5 = accuracy(predicted.data, labels, topk=(1, 5))top1.update(err1.item(), imgs.size(0))top5.update(err5.item(), imgs.size(0))tqdm_loader.set_description('Training: loss:{:.4}/{:.4} lr:{:.4} err1:{:.4} err5:{:.4}'.format(loss, losses.avg, self.optimizer.param_groups[0]['lr'],top1.avg, top5.avg))# if batch_idx%1==0:#     breakreturn 100-top1.avg, losses.avgdef valid_epoch(self, loader, epoch):self.model.eval()# acc = Accuracy_score()# tqdm_loader = tqdm(loader)losses = AverageMeter()top1 = AverageMeter()print("\n************Evaluation*************")for batch_idx, (imgs, labels) in enumerate(loader):with torch.no_grad():batch_imgs = imgs.cuda()#permute(0,3,1,2).batch_labels = labels.cuda()predicted= self.model(batch_imgs)loss = self.myloss(predicted, batch_labels).detach().cpu().numpy()loss = loss.mean()predicted = softmax(predicted, dim=-1)losses.update(loss.item(), imgs.size(0))err1, err5 = accuracy(predicted.data, batch_labels, topk=(1, 5))top1.update(err1.item(), imgs.size(0))return 100-top1.avg, losses.avgdef myloss(self,predicted,labels):#print(predicted.size(),labels.size())#[128, 10]) torch.Size([128])loss = self.loss_f(predicted,labels,)# loss = loss1+loss2return lossdef run(self, train_loder, val_loder,model_path):best_acc = 0start_epoch=0# model, optimizer, start_epoch=load_checkpoint(self.model,self.optimizer,model_path)for e in range(self.config.epochs):e=e+start_epoch+1print("------model:{}----Epoch: {}--------".format(self.config.model_name,e))self.scheduler.step(e)# torch.cuda.empty_cache()train_acc, train_loss = self.train_epoch(train_loder,e)val_acc, val_loss=self.valid_epoch(val_loder,e)#print("\nval_loss:{:.4f} | val_acc:{:.4f} | train_acc:{:.4f}".format(val_loss, val_acc,train_acc))if val_acc > best_acc:best_acc = val_accprint('Current Best (top-1 acc):',val_acc)#new_model = quant_dorefa.prepare(self.model, inplace=False, a_bits=8, w_bits=8)x = torch.rand(1, 3,data_config.input_size, data_config.input_size).float().cuda()save_path=data_config.MODEL_PATH+data_config.model_name+'_epoch{}_params.onnx'.format(e)torch.onnx.export(self.model, x, save_path, export_params=True, verbose=False,opset_version=10)# 支持Opset 7, 8, 9 and 10 of ONNX 1.6 is supported.print("saving model sucessful !",save_path)print('\nbest score:{}'.format(data_config.model_name))print("best accuracy:{:.4f}  ".format(best_acc))

模型训练后得到***.onnx模型、训练和测试数据,这是我们后续步骤要用到的。

3. STM32CubeIDE上进行模型转换与模型部署到单片机

    步骤2得到了torch神经网络框架训练好的“***.onnx”模型文件,下一步需要把该AI模型转换成C程序代码,并嵌入到整个项目工程中。整个项目工程包括硬件代码可以直接通过 STM32CubeIDE工具生成。

具体步骤如下:

打开安装好的STM32CubeIDE软件。

新建,stm32 project。

搜索,选择目标开发板型号STM32F429IGT6,点击next。

填写项目名,点击finish。

点击Yes 。

点击I hava read ....   ;然后点击Finish。

等待初始化完成,左边是IDE编译器中是自动生成的底板代码,右边是CUBEMX软件界面,可对开发板硬件资源进行配置。

 下载X-CUBE-AI,需在software packs点manage software packs找到X-CUBE-AI下载。

 

选取ai开发需要支持的软件包cue-ai,在software packs点select components。


 

在X-CUBE-AI行下core勾选,application选validation,双核开发板建议选M7系列,点OK完成

回到引脚配置页面,最下方就是上面导入的CUBE-AI工具包,点击进入配置,选中上方两个√号,然后在configration下方platform setting 选择并配置串口,用来进行数据交互的

回到cube-ai,选择添加网络

根据训练产生的文件进行配置:选择模型文件、压缩比、加载验证数据

点击分析:工具会根据模型与模型参数推断出模型占用(ROM、RAM),进而判断神经网络能否部署到开发板。

若出现以下报错 

错误信息建议通过将注册表中的LongPathsEnabled键设置为1来启用长路径。

以下是具体操作步骤:

  1. 通过在Windows搜索栏中键入“regedit”并按Enter来打开注册表编辑器。
  2. 导航至以下键:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem
  3. 在右侧窗格上右键单击,然后选择“新建” -> “DWORD (32位) 值”。
  4. 将新值命名为LongPathsEnabled
  5. 双击LongPathsEnabled并将其值设置为1
  6. 单击“确定”以保存更改。
  7. 关闭注册表编辑器,然后尝试重新运行命令。

网络分析运行成功

根据ROM、RAM判断出模型可部署 

 点击,validation on desktop 在pc上进行模型验证。(也可以实现原模型与转换后模型的对比。)

在前面步骤基础上,点时钟配置栏,系统会自动进行时钟配置

点项目管理栏,进行配置

在IDE编译器中,选择project,选择生成代码

生成代码后,右键工程,选择build project

修改代码,等待编译完成后,若没有错误则下一步。

重新编译后,右下侧可以看到占用的flash与RAM,表明模型是否成功部署,

右键工程名运行。

配置,默认即可

烧录完成

至此,模型部署完成,下面开始进行板上模型验证。

4. STM32 CubeUDE上进行模型验证

基于上述步骤,下面进行模型验证,验证AI模型在目标板上的运行情况。验证原理如下:

现在tinyML解决方案厂商都在做AutoML,就是为方便初学者不需要掌握专门的硬件知识就能够自动部署到MCU开发板。因此开发者只需要按照指定的格式准备好数据,validate on target 就是将准备好的数据通过串口传输到RAM内存中,一般是神经网络指定的输入缓冲块,经神经网络后再将结果通过串口传回前端程序显示。

运行前,不要忘记连接开发板,以及检测端口号

双击.ioc文件,会自动返回cubeMX页面,与validate on desktop相同的配置进行validate on target

自动即可

 validation on target结果如下

重新 validation on desktop和validation on target,结果是一样的,

参数分析

运行时间

5. 结果统计与分析

结果分析如下:

当前暂时没有加入测试与验证数据集,将在第二篇加入。

更多详细信息需参考txt报告:

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

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

相关文章

NestJS必备:TypeORM对DB的操作

文章概叙 本文大概1300字,讲的是一些关于TypeORM的基础知识以及在NestJS中使用TypeORM操作DB的例子。 关于TypeORM TypeORM 是一个ORM框架,它可以运行在 NodeJS、Browser、Cordova、PhoneGap、Ionic、React Native、Expo 和 Electron 平台上&#xff0…

Kubectl常见排查pod问题命令

一.查看命名空间pod及其日志 #查看命名空间pod kubectl get pods -n <命名空间名称> #该命令不加-n命名空间名称&#xff0c;默认是查看default命名空间的pod#查看对应pod的日志kubectl logs -f <pod-name> -n <namespace>#同样的如果查看的是default命名空…

jasypt组件死锁bug案例分享

事故描述 1、上午9.55发布了一个Apollo动态配置参数&#xff1b; 2、片刻后&#xff0c;服务器接口开始出现大量的超时告警&#xff0c;似乎是某资源被耗尽不足分配&#xff1b; 3、正值业务请求高峰的上午十点&#xff08;平台上午10点会有一些活动会拉一波用户流量&#x…

使用eNSP进行路由策略与引入实验

一、实验拓扑 二、实验要求 1、按照图示配置 IP 地址&#xff0c;R1&#xff0c;R3&#xff0c;R4 上使用 oopback 口模拟业务网段&#xff0c; 2、R2&#xff0c;R3 和 R4 运行 OSPF&#xff0c;各自协议内部互通2R1和R2运丁RIPv2 3、在 RIP 和 OSPF 间配黑双向路由引入&#…

按照以下步骤使用Transformer模型

“Transformer”是一种深度学习模型架构&#xff0c;用于处理序列数据&#xff0c;特别是在自然语言处理&#xff08;NLP&#xff09;领域中表现出色。它由Google Brain团队于2017年提出&#xff0c;并在机器翻译任务中取得了突破性的成果。Transformer的核心思想是完全基于自注…

捕捉信号的处理

文章目录 信号捕捉 信号捕捉 信号捕捉是进程从内核态返回用户态时会对信号进行检测处理。 如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处…

退役军人档案管理系统|DW-S403是一套成熟系统

退役军人档案管理系统是一种专门用于管理退役军人档案的信息系统&#xff0c;旨在提高退役军人档案的管理效率和利用价值。该系统采用先进的信息技术手段&#xff0c;对退役军人的档案进行全面、精准、高效的管理&#xff0c;为退役军人的就业、社保、优抚安置等提供有力支持。…

机器学习(XgBoost)预测顶和底

之前的文章中&#xff0c;我们对中证1000指数进行了顶和底的标注。这一篇我们将利用这份标注数据&#xff0c;实现机器学习预测顶和底&#xff0c;并探讨一些机器学习的原理。 我们选取的特征非常简单–上影线和WR&#xff08;William’s R&#xff09;的一个变种。选取这两个…

【python】python新闻文本数据统计和聚类 (源码+文本)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

Appium一本通

Appium介绍 概念&#xff1a;Appium是一个移动App(手机应用)自动化工具。 用途&#xff1a;重复性任务、爬虫、自动化测试。 特点&#xff1a;开源免费、多平台支持(ios\android)、多类型支持(native\webview)、类selenium支持多语言(java\python\js\ruby) Appium原理 三个主…

如何使用trackerjacker映射和跟踪分析WiFi网络设备

关于trackerjacker trackerjacker是一款针对WiFi无线网络的强大安全工具&#xff0c;该工具功能类似于Nmap&#xff0c;可以帮助广大研究人员映射未连接的WiFi网络&#xff0c;并进行设备跟踪。 该工具基于IEEE 802.11实现其功能&#xff0c;支持通过原始802.11实现网络映射和…

Prometheus blackbox_exporter 黑盒监控

概述 在监控体系里面&#xff0c;通常我们认为监控分为&#xff1a;白盒监控、黑盒监控。 黑盒监控&#xff1a;主要关注的现象&#xff0c;一般都是正在发生的东西&#xff0c;例如出现一个告警&#xff0c;业务接口不正常&#xff0c;那么这种监控就是站在用户的角度能看到的…

[vite] ts写配置根目录别名

参考:配置 Vite | Vite 写对象的形式吧 import { defineConfig } from vite import vue from vitejs/plugin-vue import path from path// https://vitejs.dev/config/ export default defineConfig({plugins: [vue()],resolve: {alias: {"": path.resolve(__dirname…

Datawhale ChatGPT基础科普

根据课程GitHub - datawhalechina/hugging-llm: HuggingLLM, Hugging Future. 摘写自己不懂得一些地方&#xff0c;具体可以再到以上项目地址 LM&#xff1a;这是ChatGPT的基石的基石。 Transformer&#xff1a;这是ChatGPT的基石&#xff0c;准确来说它的一部分是基石。 G…

2024新算法角蜥优化算法(HLOA)和经典灰狼优化器(GWO)进行无人机三维路径规划设计实验

简介&#xff1a; 2024新算法角蜥优化算法&#xff08;HLOA&#xff09;和经典灰狼优化器&#xff08;GWO&#xff09;进行无人机三维路径规划设计实验。 无人机三维路径规划的重要意义在于确保飞行安全、优化飞行路线以节省时间和能源消耗&#xff0c;并使无人机能够适应复杂…

数据持久化第四课-EF的基本使用

数据持久化第四课-EF的基本使用 一.预习笔记 1.数据实体模型概述 ORM全称是“对象-关系映射”&#xff08;Object-Relation Mapping&#xff09; ORM是将关系数据库中的数据用对象的形式表现出来&#xff0c;并通过面向对象的方式将这些对象组织起来&#xff0c;实现系统业务…

理发师问题的业务建模方案

背景 题目&#xff1a; 假设有一个理发店只有一个理发师&#xff0c;一张理发时坐的椅子&#xff0c;若干张普通椅子顾客供等候时坐。没有顾客时&#xff0c;理发师睡觉。顾客一到&#xff0c;叫醒理发师 。如果理发师没有睡觉&#xff0c;而在为别人理发&#xff0c;他就会坐…

Chrome 网络调试程序 谷歌网络调试 network

目录 1.网络面板总览2.概况了解3.Waterfall接口排队等待时间4.关注请求接口的Size,可能是占据内存溢出的接口5.过滤器一栏 fetch/xhr 什么意思6. Stalled 什么意思7.Queueing 什么意思8.Queueing和Stalled之间什么关系9.为什么会有阻塞状态10.Time列是pending 什么意思 1.网络面…

实现Spring底层机制(二)

文章目录 阶段2—封装bean定义信息到Map1.代码框架图2.代码实现1.文件目录2.新增注解Scope存储单例或多例信息Scope.java3.修改MonsterService.java指定多例注解4.新增bean定义对象存储bean定义信息BeanDefinition.java5.修改pom.xml增加依赖6.修改容器实现bean定义信息扫描Sun…

基于Vue+ElementPlus自定义带历史记录的搜索框组件

前言 基于Vue2.5ElementPlus实现的一个自定义带历史记录的搜索框组件 效果如图&#xff1a; 基本样式&#xff1a; 获取焦点后&#xff1a; 这里的历史记录默认最大存储10条&#xff0c;同时右侧的清空按钮可以清空所有历史记录。 同时搜索记录也支持点击搜索&#xff0c;按…