Maix Bit(K210)保姆級(jí)入門上手教程系列
Maix Bit(K210)保姆級(jí)入門上手教程—環(huán)境搭建
Maix Bit(K210)保姆級(jí)入門上手教程—外設(shè)基本使用
這是K210快速上手系列文章,主要內(nèi)容是,介紹MaixHub這個(gè)線上訓(xùn)練模型的使用,以及如何部署到K210中。
閱讀本文的前提:讀者對(duì)基本的監(jiān)督式學(xué)習(xí)有一定的了解,之道學(xué)習(xí)率、迭代次數(shù)、網(wǎng)絡(luò)模型等有一定的概念。沒有的話,自行補(bǔ)充啦或者點(diǎn)這里,閱讀官方文檔學(xué)習(xí)相關(guān)基礎(chǔ)~
本文內(nèi)容多來自官方文檔,目的是為了幫助讀者快速上手訓(xùn)練自己的模型,侵權(quán)刪!
一、K210硬件介紹
一般來說運(yùn)行神經(jīng)網(wǎng)絡(luò)模型有兩種方式。一種是直接通過CPU進(jìn)行運(yùn)行,一種是通過KPU或者GPU加速運(yùn)行。K210中使用KPU的方式加速運(yùn)行網(wǎng)絡(luò)模型,使得其運(yùn)行速度得到加快。
1、內(nèi)存介紹
MaixPy 中的存儲(chǔ)介質(zhì)主要由 Flash,SD 卡組成,分為三塊區(qū)域,分別是 MaixPy.bin 固件區(qū),xxx.kmodel 模型區(qū),文件系統(tǒng)區(qū):Flash 上為 spiffs(SPI Flash File System),SD 卡為 Fatfs(FAT file system)。
通常起始于 0x300000,模型文件之所以一般不燒錄在 Flash 的文件系統(tǒng),原因有下:
Flash 中文件系統(tǒng)擁有的內(nèi)存并不夠大,不足以放入大模型,更大的模型可以放入 SD 卡中。直接讀取模型文件比經(jīng)過文件系統(tǒng)讀取速率更快。
但是也會(huì)有以下原因會(huì)將模型燒錄在flash中,就是模型本身比較大,就需要燒錄到flash中,而不是SD卡中。因?yàn)槟P痛?,SD卡加載模型可能會(huì)出現(xiàn)內(nèi)存不足的情況。
內(nèi)存管理:
在 MaixPy 中, 目前使用了兩種內(nèi)存管理, 一種是 GC(垃圾回收), 另一種是系統(tǒng)堆內(nèi)存, 兩者同時(shí)存在。
比如:芯片有 6MiB 內(nèi)存,加入固件使用了前面的 2MiB, 還剩 4MiB, 默認(rèn) GC使用 512KiB, 剩下的給系統(tǒng)堆內(nèi)存管理。
注意:
GC 內(nèi)存的總大小是可以設(shè)置的, 所以,根據(jù)具體的使用情況可以適當(dāng)修改GC內(nèi)存大小, 比如:
為了加載更大的模型,可以把 GC內(nèi)存設(shè)置小一點(diǎn)
如果分配新的變量提示內(nèi)存不足, 可以適當(dāng)將GC內(nèi)存設(shè)置大一點(diǎn)即可。如果都不夠了, 就要考慮縮減固件大小,或者優(yōu)化代碼了。
2、KPU介紹
KPU是通用的神經(jīng)網(wǎng)絡(luò)處理器,它可以在低功耗的情況下實(shí)現(xiàn)卷積神經(jīng)網(wǎng)絡(luò)計(jì)算,時(shí)時(shí)獲取被檢測(cè)目標(biāo)的大小、坐標(biāo)和種類,對(duì)人臉或者物體進(jìn)行檢測(cè)和分類。KPU 實(shí)現(xiàn)了 卷積、批歸一化、激活、池化 這 4 種基礎(chǔ)操作的硬件加速, 但是它們不能分開單獨(dú)使用,是一體的加速模塊。KPU使用也有一些限制。
內(nèi)存限制
- K210 有 6MB 通用 RAM 和 2MB KPU 專用 RAM。模型的輸入和輸出特征圖存儲(chǔ)在 2MB KPU RAM 中。權(quán)重和其他參數(shù)存儲(chǔ)在 6MB 通用 RAM 中。
加速限制
加速限制比較多,有一些算子是能完全加速,有一些不能,大概知道這個(gè)就行了。具體哪些可以,哪些不可以自己上官方查啦~
二、MaixHub介紹
MaixHub網(wǎng)站提供模型訓(xùn)練功能和模型分享功能, 只需要準(zhǔn)備好需要訓(xùn)練的數(shù)據(jù)集, 不需要搭建訓(xùn)練環(huán)境和代碼, 上傳訓(xùn)練數(shù)據(jù)即可快速訓(xùn)練出模型,方便快速制作你的 AI 應(yīng)用,或者入門學(xué)習(xí) AI 訓(xùn)練的流程和原理。
三、獲取圖片數(shù)據(jù)
1、網(wǎng)上獲取
獲取圖片數(shù)據(jù),可以自己爬蟲獲取,也可以從實(shí)際運(yùn)行的開發(fā)環(huán)境中獲取。如果是實(shí)際運(yùn)行的環(huán)境獲取的話(建議采用這個(gè)方式,用自己獲取到的圖片訓(xùn)練,精度會(huì)更高),可以參考下面的代碼。如果是爬蟲獲取,可以參考
百度爬取圖片
2、線下獲取
我這里寫了一個(gè)簡(jiǎn)單從攝像頭獲取數(shù)據(jù)的代碼,可以參考一下。目前支持兩種獲取圖片的方式,定時(shí)獲取或者按鍵獲取。
但是由于個(gè)人水平實(shí)在有限,不能解決python中使用uos.mkdri()創(chuàng)建的目錄不能正常使用python進(jìn)行刪除的問題(建議通過相關(guān)接口修改字典的內(nèi)容,因?yàn)閯?chuàng)建的目錄不能正常刪除,除非在PC上操作)。
如果出現(xiàn)目錄或者文件異常
,可能是沒有正常關(guān)閉文件導(dǎo)致的??梢栽赑C上面把創(chuàng)建的東西刪除就可以。
注意,需要SD
卡,文件都是在SD卡中操作的。
基本流程的話,首先調(diào)用init()創(chuàng)建索引文件。之后會(huì)根據(jù)字典的內(nèi)容創(chuàng)建相應(yīng)的目錄存放文件。例如:init()創(chuàng)建的字典文格式如下。
會(huì)自動(dòng)創(chuàng)建目錄/pic/sit/right和/pic/sit/error,之后會(huì)自動(dòng)讀取以及保存right或者error的值作為圖片名詞。
dictx={'sit': {'right': 0, 'error': 0}, 'object': {'portable_battery': 0, 'Instant_noodles': 0,'mouse': 0}}
如果是使用Maix Bit板子,下面代碼能直接運(yùn)行:按鍵就是開發(fā)板上的按鍵
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)
# 函數(shù)整個(gè)字典讀取,文件默認(rèn)名字是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轉(zhuǎn)換為字典
file.close()
return index
# 添加一個(gè)字典,第一次創(chuàng)建,寫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)) #把字典轉(zhuǎn)化為str
file.close()
else:# 第一次創(chuàng)建
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)) #把字典轉(zhuǎn)化為str
file.close()
# 刪除一個(gè)字典
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)) #把字典轉(zhuǎn)化為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];
# 設(shè)置某一個(gè)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()創(chuàng)建一個(gè)索引文件,保存標(biāo)簽圖片
# 比如,/sd/pic/index.txt中的數(shù)據(jù)就是dictx的數(shù)據(jù),使用前需要先調(diào)用init()
# 存放圖片也跟這個(gè)有關(guān)系,存放圖片的路徑會(huì)是,/sd/pic/sit/right或者
# /sd/pic/sit/error
def init():
# init()執(zhí)行一次就行了,之后屏蔽掉就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
# 獲取獲取圖片計(jì)數(shù)和圖片設(shè)置路徑
pic_num=1
save_path=''
# 保存圖片設(shè)置
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
#開發(fā)板上RST的按鍵IO
KEY=16
# 保存圖片,保存的數(shù)量,1張圖片拍攝的時(shí)間間隔(ms),這里使用計(jì)時(shí)器的方式
# 參數(shù):ikey是字典類標(biāo)簽,iikey是某一個(gè)標(biāo)簽
# 參數(shù):num是保存圖片數(shù)量,file_index_path就是標(biāo)簽保存途徑
# 參數(shù):interval是’period‘獲取一張圖片間隔,推薦300~500ms
# 參數(shù):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
# 配置定時(shí)器
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)
# 根據(jù)索引的進(jìn)行處理
for pkey in pic_index:
if iikey==pkey:
save_path='/sd/pic/'+ikey+'/'+pkey # 保存文件目錄
create_dir('/sd/pic/'+ikey) # 創(chuàng)建相應(yīng)路徑的文件夾
create_dir(save_path) # 創(chuàng)建相應(yīng)路徑的文件夾
last_num=pic_index[pkey] #獲取上次已經(jīng)保存到的數(shù)量
pic_num=last_num #設(shè)置當(dāng)前數(shù)量
while(pic_num<num+last_num): # 等待獲取圖片信息完成
#print(save_path+' now has get pic_num %d'%(pic_num))
if mode=='period':
tim.start() #啟動(dòng)
# 更新索引
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')
運(yùn)行結(jié)果:
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
四、訓(xùn)練模型
訓(xùn)練模型的數(shù)據(jù),簡(jiǎn)單做個(gè)分類算法的訓(xùn)練,我做了兩個(gè)簡(jiǎn)單的訓(xùn)練,都是從Maxi bit 獲取數(shù)據(jù),訓(xùn)練識(shí)別充電寶和書本的模型。數(shù)據(jù)集做的比較隨便,作為演示足夠了。
本來打算用gitee的,然后會(huì)有圖片和外鏈的限制,還是github吧。如果不能訪問github,點(diǎn)擊下面鏈接,里面有加速器。
Maix Bit(K210)保姆級(jí)入門上手教程—環(huán)境搭建
數(shù)據(jù)和相關(guān)代碼點(diǎn)這里下載:
github:自訓(xùn)練模型演示代碼
添加數(shù)據(jù)集
不用添加驗(yàn)證集,全部都添加訓(xùn)練集就可以,驗(yàn)證集可以留空,訓(xùn)練時(shí)會(huì)自動(dòng)從訓(xùn)練集中分割 10% 作為驗(yàn)證集。
查看上傳的訓(xùn)練數(shù)據(jù)
上傳完畢可以在這里查看上傳的數(shù)據(jù):
開始排隊(duì)訓(xùn)練
選擇模型選擇nncase
,其他的默認(rèn)就可以了,點(diǎn)擊創(chuàng)建訓(xùn)練任務(wù)即可開始排隊(duì)訓(xùn)練。
等待訓(xùn)練完成即可
訓(xùn)練結(jié)果就是如下:因?yàn)閿?shù)據(jù)集簡(jiǎn)單,場(chǎng)景單一,精度確實(shí)可以,接近1。
五、部署模型
如果不能訪問github,點(diǎn)擊下面鏈接,里面有加速器。
Maix Bit(K210)保姆級(jí)入門上手教程—環(huán)境搭建
數(shù)據(jù)和相關(guān)代碼點(diǎn)這里下載:
github:自訓(xùn)練模型演示代碼
1、普通模型部署
點(diǎn)擊部署模型:
選擇手動(dòng)部署,下載模型,得到壓縮包,里面有模型文件和參考演示文件已經(jīng),訓(xùn)練結(jié)果report文件。
我們選擇將模型放到SD卡,從SD卡加載模型,將模型導(dǎo)入SD卡中,記得修改參考代碼中的路徑
。如果出現(xiàn)什么奇奇怪怪的錯(cuò)誤,建議重新燒錄固件,固件選擇mini版本的,固件已經(jīng)在代碼中了。
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中和固件加載的模式一樣,這里就不進(jìn)行演示了。選擇 # main(labels=labels, model_addr=0x300000)運(yùn)行這一條代碼就行,選擇模型部署到0x300000就OK。
運(yùn)行模型出現(xiàn),模型不兼容的情況,注意看看自己選擇訓(xùn)練的模型是否與平臺(tái)匹配。
2、大模型部署
如果模型本身比較大,可能會(huì)出現(xiàn)內(nèi)存不足的提示,遇到這種情況一般有以下三種解決方案。
- 修改gc內(nèi)存,將其縮小
- 選擇更小的固件,推薦mini版本
- kpu.load_flash加載模型
a、修改gc內(nèi)存
點(diǎn)擊查看官方文檔
b、加載mini的固件
這個(gè)很簡(jiǎn)單,就不進(jìn)行演示了。固件在我分享的代碼當(dāng)中了,當(dāng)然是maix bit版本的。
c、kpu.load_flash加載模型
選擇kpu.load_flash
的方式加載大模型,理論上可以加載無限大的模型,只要模型每層<4M,但實(shí)際上K210的flash有限,模型也只能有限制。kpu.load_flash
僅支持從內(nèi)部 flash 加載,不支持從文件系統(tǒng)加載
加載前我們需要對(duì)在 PC 上對(duì) kmodel 模型文件進(jìn)行預(yù)處理(對(duì)模型字節(jié)序進(jìn)行翻轉(zhuǎn)),運(yùn)行maix bit官方的模型轉(zhuǎn)換代碼。代碼在我分享的gitee中了。
模型轉(zhuǎn)換是在PC上面進(jìn)行的,記得查看自己的python版本是不是3。
具體可以看分享的官方的說明,說明在分享的文件中了。
使用powershell打開,cd切換model所在目錄。
執(zhí)行命令:
- cd 切換目錄
cd C:\Users\13029\Desktop\Used\Online-training\code\load_big_model
python model_le2be.py demo_load_big_model.py
燒錄模型,選擇燒錄轉(zhuǎn)換之后的_be.kmodel
具體代碼修改僅僅是將加載模型的方式修改了,其他代碼和上面從SD卡加載模型的方式一樣。文章來源:http://www.zghlxwxcb.cn/news/detail-604718.html
#task = kpu.load(model_addr)
task = kpu.load_flash (model_addr,1, 0xC000, 60000000)
五、參考資料
KPU硬件基礎(chǔ)
深度學(xué)習(xí)基礎(chǔ)
Mirco py 文檔
Sipeed官方文檔文章來源地址http://www.zghlxwxcb.cn/news/detail-604718.html
到了這里,關(guān)于Maix Bit(K210)保姆級(jí)入門上手教程---自訓(xùn)練模型之云端訓(xùn)練的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!