Maix Bit(K210)保姆级入门上手教程---自训练模型之云端训练

news2024/12/26 12:21:37

Maix Bit(K210)保姆级入门上手教程系列

Maix Bit(K210)保姆级入门上手教程—环境搭建

Maix Bit(K210)保姆级入门上手教程—外设基本使用


这是K210快速上手系列文章,主要内容是,介绍MaixHub这个线上训练模型的使用,以及如何部署到K210中。

阅读本文的前提:读者对基本的监督式学习有一定的了解,之道学习率、迭代次数、网络模型等有一定的概念。没有的话,自行补充啦或者点这里,阅读官方文档学习相关基础~

本文内容多来自官方文档,目的是为了帮助读者快速上手训练自己的模型,侵权删!

文章目录

  • Maix Bit(K210)保姆级入门上手教程系列
  • 一、K210硬件介绍
    • 1、内存介绍
    • 2、KPU介绍
  • 二、MaixHub介绍
  • 三、获取图片数据
    • 1、网上获取
    • 2、线下获取
  • 四、训练模型
  • 五、部署模型
    • 1、普通模型部署
    • 2、大模型部署
      • a、修改gc内存
      • b、加载mini的固件
      • c、kpu.load_flash加载模型
  • 五、参考资料


一、K210硬件介绍

一般来说运行神经网络模型有两种方式。一种是直接通过CPU进行运行,一种是通过KPU或者GPU加速运行。K210中使用KPU的方式加速运行网络模型,使得其运行速度得到加快。

1、内存介绍

在这里插入图片描述
MaixPy 中的存储介质主要由 Flash,SD 卡组成,分为三块区域,分别是 MaixPy.bin 固件区,xxx.kmodel 模型区,文件系统区:Flash 上为 spiffs(SPI Flash File System),SD 卡为 Fatfs(FAT file system)。

通常起始于 0x300000,模型文件之所以一般不烧录在 Flash 的文件系统,原因有下:

Flash 中文件系统拥有的内存并不够大,不足以放入大模型,更大的模型可以放入 SD 卡中。直接读取模型文件比经过文件系统读取速率更快。

但是也会有以下原因会将模型烧录在flash中,就是模型本身比较大,就需要烧录到flash中,而不是SD卡中。因为模型大,SD卡加载模型可能会出现内存不足的情况。

内存管理:
在 MaixPy 中, 目前使用了两种内存管理, 一种是 GC(垃圾回收), 另一种是系统堆内存, 两者同时存在。

比如:芯片有 6MiB 内存,加入固件使用了前面的 2MiB, 还剩 4MiB, 默认 GC使用 512KiB, 剩下的给系统堆内存管理。

注意:
GC 内存的总大小是可以设置的, 所以,根据具体的使用情况可以适当修改GC内存大小, 比如:

为了加载更大的模型,可以把 GC内存设置小一点
如果分配新的变量提示内存不足, 可以适当将GC内存设置大一点即可。如果都不够了, 就要考虑缩减固件大小,或者优化代码了。

2、KPU介绍

KPU是通用的神经网络处理器,它可以在低功耗的情况下实现卷积神经网络计算,时时获取被检测目标的大小、坐标和种类,对人脸或者物体进行检测和分类。KPU 实现了 卷积、批归一化、激活、池化 这 4 种基础操作的硬件加速, 但是它们不能分开单独使用,是一体的加速模块。KPU使用也有一些限制。

内存限制

  • K210 有 6MB 通用 RAM 和 2MB KPU 专用 RAM。模型的输入和输出特征图存储在 2MB KPU RAM 中。权重和其他参数存储在 6MB 通用 RAM 中。

加速限制
加速限制比较多,有一些算子是能完全加速,有一些不能,大概知道这个就行了。具体哪些可以,哪些不可以自己上官方查啦~

二、MaixHub介绍

MaixHub网站提供模型训练功能和模型分享功能, 只需要准备好需要训练的数据集, 不需要搭建训练环境和代码, 上传训练数据即可快速训练出模型,方便快速制作你的 AI 应用,或者入门学习 AI 训练的流程和原理。

三、获取图片数据

1、网上获取

获取图片数据,可以自己爬虫获取,也可以从实际运行的开发环境中获取。如果是实际运行的环境获取的话(建议采用这个方式,用自己获取到的图片训练,精度会更高),可以参考下面的代码。如果是爬虫获取,可以参考
百度爬取图片

2、线下获取

我这里写了一个简单从摄像头获取数据的代码,可以参考一下。目前支持两种获取图片的方式,定时获取或者按键获取。

但是由于个人水平实在有限,不能解决python中使用uos.mkdri()创建的目录不能正常使用python进行删除的问题(建议通过相关接口修改字典的内容,因为创建的目录不能正常删除,除非在PC上操作)。

如果出现目录或者文件异常,可能是没有正常关闭文件导致的。可以在PC上面把创建的东西删除就可以。

注意,需要SD卡,文件都是在SD卡中操作的。

基本流程的话,首先调用init()创建索引文件。之后会根据字典的内容创建相应的目录存放文件。例如:init()创建的字典文格式如下。

会自动创建目录/pic/sit/right和/pic/sit/error,之后会自动读取以及保存right或者error的值作为图片名词。

dictx={'sit': {'right': 0, 'error': 0}, 'object': {'portable_battery': 0, 'Instant_noodles': 0,'mouse': 0}}

如果是使用Maix Bit板子,下面代码能直接运行:按键就是开发板上的按键

from machine import Timer
from Maix import GPIO 
from fpioa_manager import fm

import sensor, lcd
import uos,sys

def create_dir(dir,chdir='/sd'):
    try:
        if(uos.stat(dir)):
            print("PATH existed")
    except:
            uos.mkdir(dir)
# 函数整个字典读取,文件默认名字是index.txt
def read_pic_index(file_path='/sd/pic'):
    uos.chdir('/sd/pic')
    file = open('index.txt','r+',encoding='utf-8')
    index= eval(file.read())   #读取的str转换为字典
    file.close()
    return index
# 添加一个字典,第一次创建,写key=='none'
def add_pic_index(key,dict_value,file_path='/sd/pic'):
    if(key!='none' ):
        index = read_pic_index(file_path)
        file = open('index.txt',"w+",encoding='utf-8')
        index[key]=dict_value
        file.write(str(index))      #把字典转化为str
        file.close()
    else:# 第一次创建
        create_dir('pic',file_path)
        uos.chdir(file_path)
        #print(uos.getcwd())
        file = open('index.txt',"w+",encoding='utf-8')
        file.write(str(dict_value))      #把字典转化为str
        file.close()
        
# 删除一个字典
def del_pic_index(key,file_path='/sd/pic'):
    import sys
    global index   # 声明全局索引
    
    index = read_pic_index('index.txt')
    file = open('index.txt','w+')
    
    try:
        del index[str(key)]
    except KeyError:
        print('key :'+key +' have been del or without this key')   
        #file.close()
    else:
        pass
    file.write(str(index))      #把字典转化为str
    file.close()

# 打印所有的索引
def print_pic_index(file_path='sd/pic'):
    cat_index=read_pic_index('index.txt')
    for key in cat_index:
        print("key:%s value:%s"%(key,str(cat_index[key])))

# 获取key的索引
def get_pic_key_index(key,file_path='sd/pic'):
    cat_index=read_pic_index('index.txt')
    for fkey in cat_index:
        if fkey==key:
            return cat_index[key];
# 设置某一个key的index
def set_pic_key_index(key,dict_value,file_path='sd/pic'):
    add_pic_index(key,dict_value,file_path)
# 清楚所有的index
def clear_pic_index(file_path='sd/pic'):
    cat_index=read_pic_index(file_path)
    for key in cat_index:
        del_pic_index(key,file_path)


# 这里解释以下,init()创建一个索引文件,保存标签图片
# 比如,/sd/pic/index.txt中的数据就是dictx的数据,使用前需要先调用init()
# 存放图片也跟这个有关系,存放图片的路径会是,/sd/pic/sit/right或者
# /sd/pic/sit/error
def init():
    # init()执行一次就行了,之后屏蔽掉就OK
    dictx={'sit': {'right': 0, 'error': 0}, 'object': {'portable_battery': 0, 'Instant_noodles': 0,
                                                        'mouse': 0}}
    try:
        if uos.stat('/sd/pic'):
            print("PATH existed")
    except:
        uos.mkdir('/sd/pic')
        add_pic_index('none',dictx)
        index=read_pic_index()
        print(index)

# 初始化摄像头
def sensor_init():
    sensor.reset()
    sensor.set_pixformat(sensor.RGB565)
    sensor.set_framesize(sensor.QVGA)
    sensor.run(1)
    sensor.skip_frames()

# 获取一帧图片
def get_frame_pic():
    img = sensor.snapshot()
    return img

# 获取获取图片计数和图片设置路径
pic_num=1
save_path=''

# 保存图片设置
def save_pic(timer):
    global pic_num
    global save_path
    img=get_frame_pic()
    lcd.display(img)
    file_name=save_path+'/'+str(pic_num)+'.jpg'
    print(file_name)
    img.save(file_name, quality=95)
    pic_num+=1

#开发板上RST的按键IO
KEY=16 

# 保存图片,保存的数量,1张图片拍摄的时间间隔(ms),这里使用计时器的方式
# 参数:ikey是字典类标签,iikey是某一个标签
# 参数:num是保存图片数量,file_index_path就是标签保存途径
# 参数:interval是’period‘获取一张图片间隔,推荐300~500ms
# 参数:mode是模式,按键’key‘,模式’period‘
# 注意:使用按键的方式,interval将不再起作用
def start_obtain(ikey,iikey,file_index_path='/sd/pic/index.txt',num=100,interval=300,mode='period'):
    global pic_num
    global save_path
    print('sensor_init & lcd')
    pic_index=get_pic_key_index(ikey,file_index_path)
    print(pic_index)
    sensor_init()
    lcd.init()
    if mode=='period':
        if interval < 150:
            interval = 150
        # 配置定时器
        print('init irq')
        tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PERIODIC, period=interval, unit=Timer.UNIT_MS, callback=save_pic, arg=save_pic, start=False, priority=1, div=0)
    elif mode=='key':
        # 配置按键外部中断
        fm.register(KEY, fm.fpioa.GPIOHS0)
        key = GPIO(GPIO.GPIOHS0, GPIO.IN, GPIO.PULL_NONE)
        key.irq(save_pic, GPIO.IRQ_RISING, GPIO.WAKEUP_NOT_SUPPORT,1)    
    else:
        print('input mode error please input mode = period or key')
        return 0
    # 获取索引
    pic_index=get_pic_key_index(ikey,file_index_path)
    print(pic_index)
    # 根据索引的进行处理
    for pkey in pic_index:
        if iikey==pkey:
            save_path='/sd/pic/'+ikey+'/'+pkey # 保存文件目录
            create_dir('/sd/pic/'+ikey) # 创建相应路径的文件夹
            create_dir(save_path) # 创建相应路径的文件夹
            last_num=pic_index[pkey] #获取上次已经保存到的数量
            pic_num=last_num #设置当前数量
            while(pic_num<num+last_num): # 等待获取图片信息完成
                #print(save_path+' now has get pic_num %d'%(pic_num))
                if mode=='period':
                    tim.start() #启动
                # 更新索引
                print("have done = %f"%((pic_num-last_num)/num))
                pic_index[pkey]=pic_num
                set_pic_key_index(ikey,pic_index) # 更新key index
    if mode=='period':    
        tim.stop()
        tim.deinit()
    elif mode=='key':
        key.disirq() # 禁用中断
        fm.unregister(KEY)
    else :
        return 0

    print(pkey +  'have right all')

init()
start_obtain(ikey='sit',iikey='error',file_index_path='/sd/pic/index.txt',num=500,interval=100,mode='period')

运行结果:

PATH existed
sensor_init & lcd
{'error': 101, 'right': 9}
init i2c:2 freq:100000
[MAIXPY]: find ov5642
[MAIXPY]: find ov sensor
init irq
{'error': 101, 'right': 9}
PATH existed
PATH existed
have done = 0.000000
/sd/pic/sit/right/9.jpg
have done = 0.002000
/sd/pic/sit/right/10.jpg
have done = 0.004000
/sd/pic/sit/right/11.jpg
have done = 0.006000
/sd/pic/sit/right/12.jpg

四、训练模型

训练模型的数据,简单做个分类算法的训练,我做了两个简单的训练,都是从Maxi bit 获取数据,训练识别充电宝和书本的模型。数据集做的比较随便,作为演示足够了。

本来打算用gitee的,然后会有图片和外链的限制,还是github吧。如果不能访问github,点击下面链接,里面有加速器。
Maix Bit(K210)保姆级入门上手教程—环境搭建
数据和相关代码点这里下载:

github:自训练模型演示代码
在这里插入图片描述
添加数据集
不用添加验证集,全部都添加训练集就可以,验证集可以留空,训练时会自动从训练集中分割 10% 作为验证集。

在这里插入图片描述
查看上传的训练数据
上传完毕可以在这里查看上传的数据:
在这里插入图片描述
开始排队训练
选择模型选择nncase,其他的默认就可以了,点击创建训练任务即可开始排队训练。
在这里插入图片描述
等待训练完成即可

在这里插入图片描述

训练结果就是如下:因为数据集简单,场景单一,精度确实可以,接近1。
在这里插入图片描述

五、部署模型

如果不能访问github,点击下面链接,里面有加速器。
Maix Bit(K210)保姆级入门上手教程—环境搭建
数据和相关代码点这里下载:

github:自训练模型演示代码

1、普通模型部署

点击部署模型:
在这里插入图片描述

选择手动部署,下载模型,得到压缩包,里面有模型文件和参考演示文件已经,训练结果report文件。

在这里插入图片描述

我们选择将模型放到SD卡,从SD卡加载模型,将模型导入SD卡中,记得修改参考代码中的路径。如果出现什么奇奇怪怪的错误,建议重新烧录固件,固件选择mini版本的,固件已经在代码中了。

if __name__ == "__main__":
    try:
        # main(labels=labels, model_addr=0x300000)
        # 修改模型所在路径
        main(labels=labels, model_addr="/sd/model/model-32464.kmodel")
    except Exception as e:
        sys.print_exception(e)
        lcd_show_except(e)
    finally:
        gc.collect()

还可以部署到flash中,部署到flash中和固件加载的模式一样,这里就不进行演示了。选择 # main(labels=labels, model_addr=0x300000)运行这一条代码就行,选择模型部署到0x300000就OK。

运行模型出现,模型不兼容的情况,注意看看自己选择训练的模型是否与平台匹配。

2、大模型部署

如果模型本身比较大,可能会出现内存不足的提示,遇到这种情况一般有以下三种解决方案。

  • 修改gc内存,将其缩小
  • 选择更小的固件,推荐mini版本
  • kpu.load_flash加载模型

a、修改gc内存

点击查看官方文档

b、加载mini的固件

这个很简单,就不进行演示了。固件在我分享的代码当中了,当然是maix bit版本的。

c、kpu.load_flash加载模型

选择kpu.load_flash 的方式加载大模型,理论上可以加载无限大的模型,只要模型每层<4M,但实际上K210的flash有限,模型也只能有限制。kpu.load_flash 仅支持从内部 flash 加载,不支持从文件系统加载

加载前我们需要对在 PC 上对 kmodel 模型文件进行预处理(对模型字节序进行翻转),运行maix bit官方的模型转换代码。代码在我分享的gitee中了。

模型转换是在PC上面进行的,记得查看自己的python版本是不是3。

具体可以看分享的官方的说明,说明在分享的文件中了。
使用powershell打开,cd切换model所在目录。
执行命令:

  • cd 切换目录
cd C:\Users\13029\Desktop\Used\Online-training\code\load_big_model

python model_le2be.py demo_load_big_model.py

在这里插入图片描述
烧录模型,选择烧录转换之后的_be.kmodel
在这里插入图片描述

具体代码修改仅仅是将加载模型的方式修改了,其他代码和上面从SD卡加载模型的方式一样。

#task = kpu.load(model_addr)
task = kpu.load_flash (model_addr,1, 0xC000, 60000000)

五、参考资料

KPU硬件基础
深度学习基础
Mirco py 文档
Sipeed官方文档

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

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

相关文章

《机器学习》基础概念之【P问题】与【NP问题】

《机器学习》基础概念之【P问题】与【NP问题】 这里写目录标题《机器学习》基础概念之【P问题】与【NP问题】一、多项式&时间复杂度1.1. 多项式1.2.时间复杂度二、P问题 & NP问题2.1. P问题2.2.NP问题2.3.举例理解NP问题-TSP旅行商推销问题三、NP-hard问题&NP-C问题…

PMP项目管理项目成本管理

目录1 项目成本管理概述2 规划成本管理3 估算成本4 制定预算5 控制成本1 项目成本管理概述 项目成本管理包括为使项目在批准的预算内完成而对成本进行规划、估算、预测、融资、筹资、管理和控制的各个过程&#xff0c;从而确保项目在批准的预算内完工。核心概念 项目成本管理旨…

vuex getters的作用和使用(求平均年龄),以及辅助函数mapGetters

getters作用&#xff1a;派生状态数据mapGetters作用&#xff1a;映射getters中的数据使用&#xff1a;方法名自定义&#xff0c;系统自动注入参数&#xff1a;state&#xff0c;每一个方法中必须有return&#xff0c;其return的结果被该方法名所接收。在state中声明数据listst…

PyTorch深度学习实战 | 计算机视觉

深度学习领域技术的飞速发展&#xff0c;给人们的生活带来了很大改变。例如&#xff0c;智能语音助手能够与人类无障碍地沟通&#xff0c;甚至在视频通话时可以提供实时翻译&#xff1b;将手机摄像头聚焦在某个物体上&#xff0c;该物体的相关信息就会被迅速地反馈给使用者&…

【教学典型案例】21.面向对象复用、面向对象实现、立体化权限落地

目录一&#xff1a;背景介绍1、针对于激励配置的功能体现出来的&#xff1a;面向对象的思想就可以实现极大程度的复用性的问题2、脱离学习通设计图&#xff0c;过程化&#xff0c;如何用面向对象的思想来去组织管理流程图3、词云位置记录定制化二&#xff1a;思路&方案1、面…

CSDN每日一练(编程题)

目录1. 2023/1/31-非降序数组(类型&#xff1a;数组&排序 难度&#xff1a;中等)2. 2023/2/1- 走楼梯(类型&#xff1a;递归&循环 难度&#xff1a;中等)3. 2023/2/1-蛇形矩阵(难度&#xff1a;困难)4. 2023/2/2-奇偶排序(类型&#xff1a;数组&奇偶排序 难度&…

环境配置之Keepass

前言很久以前&#xff0c;就有了想要一个自己密码管理器的念头。毕竟&#xff0c;即使浏览器能记住各个网站的账号密码&#xff0c;但是在登录单独客户端的时候&#xff0c;仍然要翻找密码。为了省事&#xff0c;也曾经是一个密码走天下。然后被劫持了QQ给同学发黄色小网站&…

手写Mybatis

Mybatis总体流程 (1)加载配置并初始化触发条件:加载配置文件 配置来源于两个地方&#xff0c;一个是配置文件(主配置文件conf. xml, mapper文件*.xml),一个是java代码中的注解&#xff0c;将主配置文件内容解析封装到Configuration,将sql的配置信息加载成为一个mappedstateme…

ReentrantLock源码分析(一)加锁流程分析

一、ReetrantLock的使用示例 static ReentrantLock lock new ReentrantLock(); public static void main(String[] args) throws InterruptedException { new Thread(ClassLayOutTest::reentrantLockDemo, "threadA").start(); Thread.sleep(1000);…

Netty权威指南总结(二)

三、Netty代码相关&#xff1a;(四) EventLoop与EventLoopGroup&#xff1a;Netty的Nio线程是NioEventLoop。1. Reactor线程模型&#xff1a;Reactor模型的三个角色&#xff1a;Reactor&#xff1a;把IO事件分配给对应的Handler处理&#xff0c;功能像是调度器。Acceptor【饿渴…

【C++】C++核心编程(一)---内存四区

C程序在执行时&#xff0c;将内存大方向划分为4个区域 代码区 存放函数体的二进制代码&#xff0c;由操作系统进行管理全局区 存放全局变量和静态变量以及常量(字符串常量、全局常量)栈区 由编译器自动分配释放&#xff0c;存放函数的参数值、局部变量等堆区 由程序员分配和释…

jenkins问题

目录 python 不是内部或外部命令&#xff0c;也不是可运行的程序 ‘cmd’ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。 git 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。 pywintypes.com_error: (-2147024891, ‘拒绝访问。’, None,…

Qt实用技巧:Qt中浮点数的相等比较方式(包括单精度和双精度)

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/129464152 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…

Spring——AOP切入点表达式和AOP通知类型

切入点:要进行增强的方法 切入点表达式:要进行增强的方法的描述式 第一种方法的本质是基于接口实现的动态代理(jdk) 第二种是基于cglib实现的动态代理 AOP切入点表达式 而需要加载多个切入点时&#xff0c;不可能每个切入点都写一个切入点表达式 例子 下面的代理描述的是匹配…

条件语句(分支语句)——“Python”

各位CSDN的uu们你们好呀&#xff0c;最近总是感觉特别特别忙&#xff0c;但是却又不知道到底干了些什么&#xff0c;好像啥也没有做&#xff0c;还忙得莫名其妙&#xff0c;言归正传&#xff0c;今天&#xff0c;小雅兰的内容还是Python呀&#xff0c;介绍一些顺序结构的知识点…

Hadoop学习:Yarn

1.YARN介绍 一个通用的资源管理系统和调度平台 YARN不分配磁盘&#xff0c;由HDFS分配 相当于一个分布式的操作系统平台&#xff0c;为上层MR等计算程序提供运算所需要的资源&#xff08;内存、CPU等&#xff09; 2.YARN三大组件 不要忘记AppMaster&#xff0c;他是程序内部…

Android 进程间通信机制(二) mmap 原理

一. 前言 Binder中一次拷贝的实现就是利用mmap(memory mapping)内存映射机制,我们来看看它的工作原理. 二. 参考文章 下面这几篇文章建议先好好阅读一下,都是总结的很好的文章, 每个人理解可能不一样 笔者也是看了好多博客文章和B站视频讲解, 然后加上自己的理解后 输出的一…

程序的编译和链接

程序的编译和链接程序的编译和链接程序的两种环境翻译环境详解编译和链接预处理编译汇编链接运行环境程序的编译和链接 程序的两种环境 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令。 …

《数据分析-JiMuReport04》JiMuReport报表设计入门介绍-页面优化

报表设计 2 页面优化 如上图所示的报表&#xff0c;仅仅是展示数据&#xff0c;不过这样看起来似乎太草率了&#xff0c;所以再优化一下吧 保存报表后&#xff0c;在积木报表中就可以看到对应的报表文件 此时我们如果还需要编辑报表&#xff0c;就点击这个报表即可 2.1 居中…

如何设计企业节点的『工业互联网标识解析系统』

一、『星火 链网』体系架构 『星火 链网』以节点形式进行组织互联互通&#xff0c;其中包括三类节点&#xff1a;超级节点、骨干节点、业务节点。 其底层采用“1N”主从链群架构&#xff0c;支持同构和异构区块链接入主链。在全国重点区域部署『星火 链网』超级节点&#…