使用 ESP-DL 深度學(xué)習(xí)庫基于 ESP32-S3 實現(xiàn)手勢識別
人工智能改變了計算機與現(xiàn)實世界交互的方式。過去,人們通過微小的低功率設(shè)備和傳感器獲取數(shù)據(jù),并傳輸至云端進(jìn)行決策。這樣的方式在設(shè)備連接性、成本和數(shù)據(jù)隱私方面帶來了一定挑戰(zhàn)。相對地,邊緣人工智能是在物理設(shè)備上另一種處理數(shù)據(jù)的方式,它無需在設(shè)備和云之間來回發(fā)送數(shù)據(jù),改善了信息延遲、提高了安全性、減少了對帶寬的要求和功耗水平。
邊緣人工智能:https://www.youtube.com/watch?v=DAPgDuw1uZM

樂鑫推出了深度學(xué)習(xí)開發(fā)庫 ESP-DL,您可以使用它在樂鑫的 AI SoC ESP32-S3 上部署您自己的高性能深度學(xué)習(xí)模型。在這篇文章中,我們將為您介紹如何使用 ESP-DL 在 ESP32-S3 上部署深度學(xué)習(xí)模型。本文僅用于開發(fā)者學(xué)習(xí)使用,其中模型并非商業(yè)化項目。
ESP-DL:?https://github.com/espressif/esp-dl
ESP32-S3:?https://www.espressif.com/zh-hans/products/socs/esp32-s3
部署:https://github.com/espressif/esp-dl/tree/master/tutorial/quantization_tool_example
目錄
本文的主要內(nèi)容為:
深度學(xué)習(xí)模型開發(fā)
ESP-DL 格式
深度學(xué)習(xí)模型部署
未來的計劃

使用 ESP-DL 前的準(zhǔn)備工作
在深入了解 ESP-DL 之前,我們假設(shè)讀者已經(jīng)具備了以下知識儲備:
?構(gòu)建和訓(xùn)練神經(jīng)網(wǎng)絡(luò)的知識(點此了解深度學(xué)習(xí)的相關(guān)基礎(chǔ)知識)
?能夠配置樂鑫物聯(lián)網(wǎng)開發(fā)框架 ESP-IDF release 4.4 版本的開發(fā)環(huán)境(更多細(xì)節(jié)請閱讀如何搭建 ESP-IDF 開發(fā)環(huán)境或 ESP-IDF 工具鏈說明)
?具備基本的 C 和 C++ 編程語言知識
具備模型訓(xùn)練后量化的知識
深度學(xué)習(xí)的相關(guān)基礎(chǔ)知識:https://www.youtube.com/watch?v=WvoLTXIjBYU
ESP-IDF release 4.4 版本:https://github.com/espressif/esp-idf/tree/release/v4.4
ESP-IDF 工具鏈說明:https://blog.espressif.com/esp-idf-development-tools-guide-part-i-89af441585b
基本的 C 語言知識:https://www.youtube.com/watch?v=KJgsSFOSQv0&t=12665s

1.?模型開發(fā)
為了簡單起見,我們以分類問題為例進(jìn)行闡述。我們開發(fā)了一個簡單的深度學(xué)習(xí)模型,并對 6 種不同的手勢進(jìn)行了分類。盡管有許多可以直接使用的開源預(yù)訓(xùn)練模型,但對于這個演示,我們從零開始建立模型,以更好地講解模型中的每一層。
注:我們使用了 Google Co-lab 進(jìn)行模型開發(fā)。
預(yù)訓(xùn)練模型:https://blog.espressif.com/hand-gesture-recognition-on-esp32-s3-with-esp-deep-learning-176d7e13fd37#2db9
Co-lab:?https://colab.research.google.com
1.1 數(shù)據(jù)集
針對該分類問題,我們使用了 Kaggle 手勢識別數(shù)據(jù)集中的一個開源數(shù)據(jù)集。原始數(shù)據(jù)集包括 10 個類別,我們只使用了其中 6 個。這些類別更容易識別,且日常生活中更有用,如下表所示。我們的數(shù)據(jù)集較原數(shù)據(jù)集還有一處關(guān)于圖像大小的區(qū)別,在原始數(shù)據(jù)集中,圖像的大小為 (240, 640),但為了方便起見,我們將數(shù)據(jù)集的大小調(diào)整為 (96, 96)。本文中使用的數(shù)據(jù)集可以在這里 (https://github.com/alibukharai/Blogs/tree/main/ESP-DL)?找到。
Kaggle 手勢識別數(shù)據(jù):https://www.kaggle.com/datasets/gti-upm/leapgestrecog

1.2 測試/訓(xùn)練分離
我們需要將數(shù)據(jù)集分為測試和訓(xùn)練數(shù)據(jù)集。這些數(shù)據(jù)集是我們原始數(shù)據(jù)集的子集,訓(xùn)練數(shù)據(jù)集用于訓(xùn)練模型、測試數(shù)據(jù)集用于測試模型的性能。校準(zhǔn)數(shù)據(jù)集在模型量化階段用于校準(zhǔn),您可以從訓(xùn)練集和測試集中抽選一部分作為校準(zhǔn)數(shù)據(jù)集。生成以上數(shù)據(jù)集的過程是相同的,我們使用了 train_test_split 以實現(xiàn)此目標(biāo)。
原始數(shù)據(jù)集:https://github.com/alibukharai/Blogs/blob/main/ESP-DL/building_with_espdl.md#11-dataset
模型量化:https://github.com/alibukharai/Blogs/blob/main/ESP-DL/building_with_espdl.md#22-optimization-and-quantization
注:點擊這里 (https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html),了解關(guān)于 train_test_split 的更多細(xì)節(jié)
如果您需要轉(zhuǎn)載本教程,您可以在此 GitHub 頁面?(https://github.com/alibukharai/Blogs/tree/main/ESP-DL)?獲得數(shù)據(jù),并在您的工作環(huán)境中開放數(shù)據(jù)。
1.3 建立模型
我們?yōu)榇朔诸悊栴}創(chuàng)建了一個基本的卷積神經(jīng)網(wǎng)絡(luò) (Convolution Neural Network, CNN)。它由 3 個卷積層組成,然后是最大池和全連接層,輸出層有 6 個神經(jīng)元。您可以點擊這里 (https://github.com/filipefborba/HandRecognition/blob/master/project3/project3.ipynb),了解創(chuàng)建 CNN 的更多內(nèi)容。以下是用于建立 CNN 的代碼。

1.4 訓(xùn)練模型
該模型運行了 5 個 epochs,最終準(zhǔn)確率為 99% 左右。

1.5 保存模型
將訓(xùn)練好的模型保存為分層數(shù)據(jù)格式 (.h5)。您可以點擊這里 (https://www.tensorflow.org/guide/keras/save_and_serialize#how_to_save_and_load_a_model),了解關(guān)于如何保存 Keras 模型的更多內(nèi)容。
1.6 模型轉(zhuǎn)化
ESP-DL 使用開放式神經(jīng)網(wǎng)絡(luò)交換 (ONXX) 格式的模型。您可以點擊這里 (https://onnx.ai),了解 ONXX 是如何工作的。為了與 ESP-DL 兼容,請使用下方代碼將訓(xùn)練的 .h5 格式的模型轉(zhuǎn)換為 ONXX 格式。
最后,下載 H5 格式的模型、ONNX 格式的模型和模型檢查點,以供將來使用。

2. ESP-DL 格式
當(dāng)模型的 ONNX 格式準(zhǔn)備就緒,您可以按照以下步驟將您的模型轉(zhuǎn)換為 ESP-DL 格式。
注:我們使用 Pychram IDE 進(jìn)行 ESP-DL 格式轉(zhuǎn)換。
Pychram:?https://www.jetbrains.com/pycharm/
2.1 格式轉(zhuǎn)換要求
首先,您需要成功搭建環(huán)境,并安裝正確的模塊版本,否則將出現(xiàn)錯誤。您可以點擊這里 (https://github.com/espressif/esp-dl/tree/master/tutorial/quantization_tool_example#step-121-set-up-the-environment),閱讀關(guān)于 ESP-DL 格式轉(zhuǎn)換要求的更多信息。

接下來,您需要下載 ESP-DL,并從 GitHub 倉庫克隆 ESP-DL。
ESP-DL:?https://github.com/espressif/esp-dl
2.2 優(yōu)化與量化
為了運行 ESP-DL 提供的優(yōu)化器,以 Window 系統(tǒng)為例,我們需要找到并將以下文件放入 pychram - IDE 的工作目錄中。
calibrator.pyd
calibrator_acc.pyd
evaluator.pyd
optimizer.py
接下來,您需要將在 1.2 節(jié)中生成的校準(zhǔn)數(shù)據(jù)集和在 1.5 節(jié)中保存的 ONNX 格式模型放在一起。您的工作目錄應(yīng)該是這樣的。
1.2 節(jié):https://github.com/alibukharai/Blogs/blob/main/ESP-DL/building_with_espdl.md#12-testtrain-split
1.5 節(jié):https://github.com/alibukharai/Blogs/blob/main/ESP-DL/building_with_espdl.md#15-saving-model

您可以按照下面的步驟生成優(yōu)化后的模型和量化參數(shù)。
2.2.1 導(dǎo)入庫
2.2.2 加載 ONNX 模型
2.2.3 優(yōu)化 ONNX 模型
2.2.4 加載校準(zhǔn)數(shù)據(jù)集
2.2.5 校準(zhǔn)
如果一切正常,這時您可以在路徑中生成兩個擴展名為 .cpp 和 .hpp 的文件,如下圖。
注:稍后您還將用到這個輸出結(jié)果,建議先截圖保存。

2.3 評估
這一步并不是模型格式轉(zhuǎn)化的必要步驟,如您希望評估優(yōu)化后模型的性能,您可以使用以下代碼。
注:請點擊這里 (https://github.com/espressif/esp-dl/blob/master/tools/quantization_tool/quantization_tool_api.md),了解更多關(guān)于 ESP-DL API 的信息。

3. 模型部署
模型部署是最后的關(guān)鍵步驟。在這里,我們將在 ESP32-S3 微控制器上運行模型并得到結(jié)果。
注:我們使用 Visual Studio Code 在 ESP32-S3 上部署模型。
ESP32-S3:?https://www.espressif.com/en/products/socs/esp32-s3
Visual Studio Code:?https://code.visualstudio.com
3.1 ESP-IDF 項目的層次結(jié)構(gòu)
首先,您需要在 VSCode 中創(chuàng)建一個基于 ESP-IDF 標(biāo)準(zhǔn)的新項目。您可以觀看此視頻 (https://www.youtube.com/watch?v=Lc6ausiKvQM),或閱讀本文檔 (https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/linux-macos-setup.html),了解如何基于 ESP32 系列芯片創(chuàng)建 VSCode 項目。
其次,您需要將前面 2.2 節(jié)?(https://github.com/alibukharai/Blogs/blob/main/ESP-DL/building_with_espdl.md#22-optimization-and-quantization)中生成的 .cpp 和 .hpp 文件復(fù)制到當(dāng)前的工作目錄中。
再次,請您將所有依賴的組件添加到工作目錄下的組件文件夾中。
最后,sdkconfig 文件是 ESP-WHO 示例 (https://github.com/espressif/esp-who/tree/master/examples/human_face_recognition/terminal)中的默認(rèn)文件,您可以在 GitHub 倉庫 (https://github.com/alibukharai/Blogs/tree/main/ESP-DL)中找到它。
項目目錄如下圖所示:
注:ESP-WHO 不是本教程必須的項目。
3.2 模型定義
我們將在 “model_define.hpp” 文件中定義模型,您可以依照下面的步驟進(jìn)行操作。
3.2.1 導(dǎo)入庫
首先導(dǎo)入所有相關(guān)的庫。接下來您需要知道模型的具體結(jié)構(gòu),您可以使用開源工具 Netron 查看前面 2.2 節(jié)結(jié)束時優(yōu)化生成的 ONNX 模型。您可以在這里 (https://github.com/espressif/esp-dl/tree/master/include/layer)?查看 ESP-DL 目前支持的所有庫。
Netron:?https://netron.app
ESP-DL:?https://github.com/espressif/esp-dl
3.2.2 定義層
接下來是定義每個層。
我們一般不認(rèn)為輸入是一個獨立的層,在這里我們沒有對其作定義。
除了輸出層,所有層都定義為私有層。
在建立模型時,請按照前面 1.3 節(jié) (https://github.com/alibukharai/Blogs/blob/main/ESP-DL/building_with_espdl.md#13-building-a-model)?中定義的順序放置各層。
3.2.3 初始化層
在定義了各層之后,我們需要初始化每個層的權(quán)重、偏置激活函數(shù)和形狀。我們可以逐層進(jìn)行檢查。
在詳述細(xì)節(jié)前,讓我們先在 Netron 里打開模型,導(dǎo)入該模型的目的是獲得一些初始化的參數(shù)。

圖中的第一層是 reshape 層(輸入沒有被作為單獨的層)。這一層的輸入是 (96, 96, 1),順序是 (H, W, C),輸出的形狀是 (1, 96, 96),順序變?yōu)榱?(C, H, W)。在之前 1.3 節(jié)的訓(xùn)練代碼中,我們沒有加入 reshape 層,但優(yōu)化后的 ONNX 模型出現(xiàn)了 reshape 層,這是因為 ONNX 要求的運算(例如卷積層)張量順序為 (C, H, W),在 1.6 節(jié)模型轉(zhuǎn)化所述的過程中會添加 reshape 層來滿足要求。我們的訓(xùn)練平臺與 ESP-DL 要求的順序是一致的,為 (H, W, C),與輸入的順序一致,因此不需要在部署的代碼里添加 reshape 層,您可以點擊此 GitHub 鏈接 (https://github.com/espressif/esp-dl/blob/master/docs/zh_CN/about_type_define.md)?了解更多。
對于二維卷積層,我們可以從前面 2.2 節(jié)末尾生成的 .hpp 文件中獲得該層的過濾器、偏置項和激活函數(shù)的名稱。但是對于指數(shù),我們需要檢查 2.2.5 節(jié)中生成的輸出。
對于最大池化層 (max-pooling layer),我們可以使用與建立模型時相同的參數(shù),請參見本文的 1.3 節(jié)。另一種了解參數(shù)和層的方法是使用開源工具 Netron 打開您在 2.2 節(jié)結(jié)束時優(yōu)化生成的 ONNX 模型。
對于密集層或全連接層,我們使用了二維卷積模塊。我們可以從前面 2.2 節(jié)末尾生成的 .hpp 文件中獲得該層的過濾器、偏置項和激活函數(shù)的名稱。但是對于指數(shù),我們需要檢查 2.2.5 節(jié)中生成的輸出。
輸出層是一個 SoftMax 層權(quán)重,其名稱可以從 2.2.5 節(jié)的輸出中獲取。
3.2.4 構(gòu)建層
下一步是建立每個層。請點擊這里 (https://github.com/espressif/esp-dl/tree/master/include/layer),了解構(gòu)建每層的構(gòu)建函數(shù)。
3.2.5 調(diào)用層
最后,我們需要將這些層連接起來,并通過調(diào)用函數(shù)逐一調(diào)用它們。請點擊這里 (https://github.com/espressif/esp-dl/tree/master/include/layer),了解每層的調(diào)用函數(shù)。
3.3 模型運行
在模型建立后,需要給定輸入并運行模型來進(jìn)行推理。您可以將生成的輸入內(nèi)容放在 “app_main.cpp”文件里,然后在 ESP32-S3 上運行模型。
3.3.1 導(dǎo)入庫
3.3.2 輸入聲明
我們訓(xùn)練好的模型的輸入大小為 (96, 96, 1),詳情請見 1.3 節(jié)。input_exponent 可以從 2.2.5 節(jié)生成的輸出中獲得其指數(shù)值。您可以把輸入/測試圖片的像素寫在這里。
3.3.3 設(shè)置輸入?yún)?shù)
每個輸入的像素將根據(jù)上面聲明的 input_exponent 進(jìn)行調(diào)整。
3.3.4. 調(diào)用模型
通過調(diào)用 forward 函數(shù)并傳入輸入來調(diào)用模型。延遲時間 (Latency) 用于計算 ESP32-S3 運行神經(jīng)網(wǎng)絡(luò)的時間。
3.3.5. 監(jiān)測輸出
輸出來自公共層,例如?l10,您可以在終端打印結(jié)果。
插圖 6 顯示了輸出結(jié)果,在 ESP32-S3 上,模型的延遲時間約為 0.7 秒,每個神經(jīng)元的輸出和最后的預(yù)測結(jié)果都能夠顯示出來。

4. 未來的計劃
接下來,我們將為 ESP32-S3-EYE 開發(fā)板設(shè)計一個模型,它可以實時捕捉圖像并進(jìn)行手勢識別。未來您可以在此 GitHub 頁面查看相關(guān)代碼。
ESP32-S3-EYE:?https://www.espressif.com/en/products/devkits/esp-eye/resourceswww.espressif.com2
此 GitHub 頁面:https://github.com/alibukharai/Blogs/tree/main/ESP-DL