目標檢測的任務是找出影像中所有感興趣的目標(物體),確定它們的類別和位置,是計算機視覺領域的核心問題之一,目標檢測已應用到諸多領域,比如如安防、無人銷售、自動駕駛和軍事等,
在許多情況下,運行目標檢測程式的設備并不是常用的電腦,而是僅包含必要外設的嵌入式設備,別看嵌入式設備簡陋,但在上面照樣能夠跑程式,實作我們的想法,設計一個嵌入式AI產品產品,一般會首先考慮成本,在有限的成本內充分利用硬體的性能,因此,不同高低性能的硬體使用場景各不同,
我們講到如何部署一個基礎CNN分類模型來識別數字,本篇更進一步,將帶你訓練一個手勢檢測器,并將其部署到嵌入式設備上,
本文思維導圖如下:

干貨推薦
- 浙大博士導師深度整理:Tensorflow 和 Pytorch 的筆記(包含經典專案實戰)
- 值得收藏,這份機器學習演算法資料著實太香
- 比 PyTorch 的官方檔案還香啊,吃透PyTorch中文版來了
- 趕快收藏,PyTorch 常用代碼段PDF合輯版來了
01 資料集準備
我們使用一個開源資料集平臺:https://gas.graviti.com/ ,網站匯總了AI開發者常見的公開資料集,針對我們特定任務,搜索對應的資料集即可,更方便一點,呼叫其SDK就能直接在線訓練,
a. 打開本文對應資料集鏈接 https://gas.graviti.com/dataset/datawhale/HandPoseforK210
b. 右上角注冊登錄
c. fork資料集


d. 點擊網頁上方Developer Tools,獲取使用SDK所需的AccessKey,獲取到 AccessKey 后,將其存在專案根目錄的gas_key.py里:
KEY = "<Your-Key>"
然后即可以通過AccessKey可以上傳資料、讀取資料、使用資料,靈活對接模型開發和訓練,與資料pipeline快速集成,
e. AccessKey寫入后就可以自由讀取資料了,讀取前可以開啟快取功能,SDK會在讀取data后將其自動快取至指定路徑,此處我們設定快取路徑為data檔案夾,
import numpy as np
from PIL import Image
from tensorbay import GAS
from tensorbay.dataset import Dataset
KEY = '復制你的ACCESSKEY替換'
def read_gas_image(data):
with data.open() as fp:
image = Image.open(fp)
image.load()
return np.array(image)
gas = GAS(KEY)
dataset = Dataset("HandPose", gas)
dataset.enable_cache("data")
segments = dataset.keys()
segment = dataset["train"]
for data in segment:
# 獲取的Numpy格式的圖片資料
image = read_gas_image(data)
# 標簽資料
for label_box2d in data.label.box2d:
xmin = label_box2d.xmin
ymin = label_box2d.ymin
xmax = label_box2d.xmax
ymax = label_box2d.ymax
box2d_category = label_box2d.category
02 目標檢測模型訓練
有兩種方法,第一種是使用本地GPU直接訓練,若沒有GPU課采用方法二,
方法 1 本地GPU進行訓練
第一步: 拉取docker鏡像
# 鏡像名稱
IMAGE="daocloud.io/neucrack/tensorflow-gpu-py3-jupyterlab"
# 拉取深度學習平臺鏡像
docker pull $IMAGE
# 測驗,當日志出現gpu=True表示能成功使用GPU
docker run --gpus all -it --rm $IMAGE python -c "import tensorflow as tf; print('-----version:{}, gpu:{}, 1+2={}'.format(tf.__version__, tf.test.is_gpu_available(), tf.add(1, 2).numpy()) );"
第二步: 拉取本文代碼,配置NNCASE,開啟docker容器
git clone https://github.com/QiangZiBro/pytorch-k210
cd pytorch-k210/handpose_detection/maix_train
# 配置NNCASE
mkdir -p tools/ncc/ncc_v0.2 && cd tools/ncc/ncc_v0.2
wget https://github.com/kendryte/nncase/releases/download/v0.2.0-beta4/ncc_linux_x86_64.tar.xz
tar xvf ncc-linux-x86_64.tar.xz
cd ../../
上面的NNCASE只需要下載、解壓即可,我們接下來使用Docker構建一個Linux虛擬容器環境,如下面命令開啟環境:
# 開啟容器
docker run --gpus all --name jupyterlab-gpu -it -p 8889:8889 -e USER_NAME=$USER -e USER_ID=`id -u $USER` -e GROUP_NAME=`id -gn $USER` -e GROUP_ID=`id -g $USER` -v `pwd`:/tf $IMAGE
開啟容器后,有兩種操作方式供選擇
-
第一種,使用jupyter-notebook運行
-
第二種,直接進入容器內部
docker exec -it jupyterlab-gpu /bin/bash # 在容器內部,建議切換到普通用戶運行 su - yourname cd /tf
這里筆者選用第二種方式,創建容器時,Docker自動在鏡像里創建了一個和本機用戶名相同的用戶,在容器里運行程式時,強烈建議切換成普通用戶運行,否則后期遇到權限問題還需要手動改權限,
第三步: 開啟訓練,使用Yolo V2演算法資料集進行訓練,具體操作方法為:
# 安裝必備的包
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
cd /handpose_detection/maix_train
# 初始化
python3 train.py init
# 開始訓練
python3 train.py -t detector -d datasets/game train
運行上述命令即使用maix_train工具箱開始訓練,這個程式能一鍵完成訓練、引數轉換、量化等功能

使用100個Epoch進行訓練,得到的結果生成在out檔案下,
方法2 使用在線GPU進行訓練
運行時默認呼叫GPU訓練,如果沒有GPU則使用CPU,訓練時間相對CPU較慢,沒有GPU的小伙伴也不用擔心,可以直接將資料集上傳到maixhub進行在線訓練,這里的限制是需要有一塊K210開發板,并且資料集大小在20M以內,下面簡要介紹方法:
第一步, 打開maixhub官網,https://www.maixhub.com/ModelTraining,注冊、登錄,根據官網提示獲得機器碼;
第二步, 上傳資料集準備環節中從格物鈦平臺快取的資料集,開始訓練;
大概二十分鐘后,模型就訓練好了,點擊Download下載訓練的模型,這里的結果和上面的out檔案夾下的壓縮檔案一樣,

03 嵌入式設備部署
3.1 開發環境準備
使用kflash作為燒錄工具,這個工具圖形化和命令列工具都有,選取我們需要的即可,
代碼撰寫方面,使用VSCode編輯Python代碼,模型訓練好的Python代碼還需要微調,這里筆者已經除錯好公布在托管的倉庫里,大家可以直接拷貝到記憶體卡,或者串口連接使用,下面介紹如何將訓練的模型部署到最終的硬體設備中,
3.2 燒錄Micropython韌體
第一步 下載Micropython韌體 到https://dl.sipeed.com/shareURL/MAIX/MaixPy/release/master 下載一個bin檔案,這里筆者使用的是minimum_with_kmodel_v4_support
wget https://dl.sipeed.com/fileList/MAIX/MaixPy/release/master/maixpy_v0.6.2_72_g22a8555b5/maixpy_v0.6.2_72_g22a8555b5_minimum_with_kmodel_v4_support.bin
第二步 查看K210的串口號,以筆者使用的MacOS為例:
ls /dev/cu.usbserial-*
# /dev/cu.usbserial-14330
第三步 燒錄
使用命令列進行燒錄示例
kflash -p /dev/cu.usbserial-14330 -b 115200 -t maixpy_v0.6.2_72_g22a8555b5_minimum_with_kmodel_v4_support.bin
使用kflash圖形界面的燒錄示例

3.3 燒錄目標檢測模型引數
有兩種方式將訓練模型引數部署到嵌入式設備上:
-
使用程式燒錄器直接燒錄到flash中的一片地址,在程式中直接加載這塊地址指向的記憶體
-
將模型引數拷貝在SD卡上,在程式中從SD卡加載
對于第一種方法,我們一般將模型引數燒錄在以0x30000為開始的記憶體上,結尾不需要制定,程式底層會自己判斷結束點,因此,在燒錄時,只需要在kflash GUI的0x00000改為0x30000即可,當然,你也可以使用命令列燒錄,同樣的效果,
對于第二種方法,直接將上面訓練好的所用檔案拷貝到SD卡,再插入K210開發板即可,
3.4 模型推理腳本
Micropython韌體和模型引數燒錄成功后,使用下面腳本便可以加載腳本,進行檢測了,建議先打開串口,將程式復制過去運行,觀察運行結果,除錯到正常運行后再放置到SD卡中
第一步: 復制下面代碼
# object detector boot.py
# generated by maixhub.com
import sensor, image, lcd, time
import KPU as kpu
import gc, sys
def lcd_show_except(e):
import uio
err_str = uio.StringIO()
sys.print_exception(e, err_str)
err_str = err_str.getvalue()
img = image.Image(size=(240,240))
img.draw_string(0, 10, err_str, scale=1, color=(0xff,0x00,0x00))
lcd.display(img)
def main(anchors, labels = None, model_addr="/sd/m.kmodel", sensor_window=(240,240), lcd_rotation=0, sensor_hmirror=False, sensor_vflip=False):
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing(sensor_window)
sensor.set_hmirror(sensor_hmirror)
sensor.set_vflip(sensor_vflip)
sensor.run(1)
lcd.init(type=1, invert=True)
lcd.rotation(lcd_rotation)
lcd.clear(lcd.WHITE)
if not labels:
with open('labels.txt','r') as f:
exec(f.read())
if not labels:
print("no labels.txt")
img = image.Image(size=(320, 240))
img.draw_string(90, 110, "no labels.txt", color=(255, 0, 0), scale=2)
lcd.display(img)
return 1
try:
img = image.Image("startup.jpg")
lcd.display(img)
except Exception:
img = image.Image(size=(320, 240))
img.draw_string(90, 110, "loading model...", color=(255, 255, 255), scale=2)
lcd.display(img)
try:
task = None
task = kpu.load(model_addr)
kpu.init_yolo2(task, 0.5, 0.3, 5, anchors) # threshold:[0,1], nms_value: [0, 1]
while(True):
img = sensor.snapshot()
t = time.ticks_ms()
objects = kpu.run_yolo2(task, img)
t = time.ticks_ms() - t
if objects:
for obj in objects:
pos = obj.rect()
img.draw_rectangle(pos)
img.draw_string(pos[0], pos[1], "%s : %.2f" %(labels[obj.classid()], obj.value()), scale=2, color=(255, 0, 0))
img.draw_string(0, 200, "t:%dms" %(t), scale=2, color=(255, 0, 0))
lcd.display(img)
except Exception as e:
raise e
finally:
if not task is None:
kpu.deinit(task)
if __name__ == "__main__":
try:
labels = ["raised_fist", "victory_hand", "hand_with_fingers_splayed"]
anchors = [3.96875, 8.8125, 3.25, 7.5, 4.4375, 6.4375, 3.3125, 5.15625, 6.125, 8.25]
#main(anchors = anchors, labels=labels, model_addr=0x300000, lcd_rotation=0)
main(anchors = anchors, labels=labels, model_addr="/sd/m.kmodel")
except Exception as e:
sys.print_exception(e)
lcd_show_except(e)
finally:
gc.collect()
上面代碼有幾點注意:
-
使用雅博的K210開發板需要在LCD初始化將invert引數置為True,即
lcd.init(type=1, invert=True) -
main函式有兩種運行方式,分別表示從flash加載模型和從SD卡加載模型
第二步: 打開終端,Mac上可以使用screen和串口進行通信
screen /dev/cu.usbserial-14340 115200
# 串口號不一定一樣
(常用功能退出:先ctrl+a,再按k鍵,按y確認)
如果成功會出現下面結果:

第三步: 拷貝程式通過串口進行互動式運行
首先按Ctrl+E,進入拷貝模式,出現下面結果

復制后Ctrl+D即可開始運行,程式運行無誤后,拷貝全部檔案到SD卡,程式運行成功后,效果如下

總結和思考
本文提供了一個從影像深度學習演算法訓練的開始,最終將其部署在嵌入式設備K210上,嵌入式AI涉及到的知識廣泛,光其中的目標檢測和嵌入式編程都可各寫一本書,如果你是一個初學者,不要被其中深厚的知識嚇到,我們可以“邊打仗邊學習”,在實踐的程序學習,
下面提供幾個小問題,以供課后思考:
-
有哪些可能的方法能夠提升識別的準確率?
-
能否從資料本身出發,提高識別的性能?
-
本文的神經網路引數的引數轉換流程是怎樣的?
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/413646.html
標籤:其他
