社区分享 | 从零开始学习 TinyML(一)
TinyML 簡介
1. 概要
Pete Warden 與?Daniel Situnayake 合著了一本介紹在 Arduino 和超低功耗微控制器上如何運行 ML 的書,?TinyML:Machine Learning with TensorFlow Lite on Arduino and Ultra-Low-Power Microcontrollers,這本書由 O’Reilly 于 2019 年 12 月 13 號出版。
?
我作為國內 IoT 和 ML 兩個方向的 GDE,對 AI 在嵌入式系統和物聯網上的應用也一直在持續關注。得到書本出版的消息之后,就迫不及待的入手了一本英文原板紙質書,看后愛不釋手,同時也想以書本的概念為原型,把如何去搭建 TinyML 完整的工程流程分享給大家。
?
2.?ML 以及 TinyML 簡介
在閱讀本文之前,我先簡單介紹一下 TinyML 。
?
Machine Learning (ML) 這一個學科,在學術界有 40 年左右的歷史,但是前面的 30 多年研究,只是在學術上有一些突破。
?
真正讓 ML 從學界走入產業界的劃時代改革的里程碑,源于 2010 年 ImageNet 挑戰賽 (ILSVRC)。2012 年,Hiton (ML 業界元老級人物) 課題組首次參加 ImageNet 圖像識別比賽,AlexNet 奪得冠軍,并碾壓了第二名 (SVM) 的分類性能。ML 在工業應用的熱情在這一年被徹底點燃。
?
ML 最近幾年已經在工業、消費領域獲得了大量的應用,隨著云資源的不斷完善,研發了更多的激動人心的 AI 模型。云端 AI 的應用,已經獲得長足的進步。
?
在 ML 的工業應用發展的這幾年,物聯網也處于快速處在發展期。從最早的智能家居,到現在遍地的物聯網智能設備。AI 應用逐步從云端走向了設備端,現在設備端的 AI 應用已經占了很大的比例,手機上 AI 的應用已經非常普遍。
?
但是,在物聯網世界里,有數以億計的體積小、功耗低、資源受限的設備支撐著物聯網應用。如何在超低功耗 (mV 功率范圍) 的設備上運行人工智能應用,同時又要滿足設備長時間低功耗的運行 AI 應用的需求,已經形成了一個新的課題。
?
TinyML 指的是在 mW 功率的微處理器上,實現機器學習的方法、工具和技術。它連接了物聯網設備,邊緣計算和機器學習。
?
TinyML 基金會在 2019 年組織了第一屆峰會,這屆峰會的成果如下:
TinyML 的技術硬件已經進入了實用性的階段;
算法,網絡以及低于 100KB 的 ML 模型,已經取得重大突破;
視覺,音頻的低功耗需求快速增長。
?
TinyML 將在以后幾年,隨著智能化的發展,獲得更快的發展。這一領域也有著巨大的機會。
?
3. 書籍作者介紹
Pete Warden 原是 Jetpac 的 CTO 和創始人。于 2014 年正式加入 Google,現在為移動和嵌入式端 TensorFlow 的技術負責人(Technical Leader)。需要解釋一下的是 Jetpac 公司擁有強大的分析社交媒體照片能力,為旅行者提供城市指南服務,這家公司在 2014 年被 Google 收購。
?
Daniel Situnayake 曾是 Google 的TensrorFlow Lite 的技術推廣工程師 (Developer Advocate),同時一直積極參與 Meetup 上 TinyML 社區的工作。他也是美國第一家以工業自動化的方式生產昆蟲蛋白質的公司 Tiny Farm 的聯合創始人。
?
兩位作者中的一位側重于物聯網上的 AI 技術研發,另一位則側重于運用 AI 技術去實現工業化,他們強強聯合出版了這本書,帶我們進一步探索物聯網端 AI 的所有技術環節工業化實現,原汁原味的體現了利用 Google 的技術去促進發展的思考脈絡。
?
4. 開發環境
作為開發環境,我們只需要在電腦上用 USB 接口實現外設接入就行了。當然根據每一個讀者的習慣,可以用自己所熟悉的編譯工具來編譯這個環境,所有的這些代碼都可以在 Windows,Linux 或者 macOS 上運行。當然,已經訓練出來許多模型在 Google Cloud 中可以下載。也可以用 Google Colab 來運行所有的代碼。就不必要去擔心需要擁有獨特的硬件開發環境。
?
推薦使用大概 $15 可以買到的 Spark Fun Edge 開發板。由于在這本書發布的時候。Spark Fun 的第 2 版已經開發出來了,并可支持運行所有的示例項目。讀者對硬件開發板的硬件版本兼容性亦無需太多擔心。
?
當然,也有另外兩款開發板的支持:Arduino Nano 33 BLE 和 STM32F746G 開發板,開發者可以根據自己的需求靈活選用。
?
我們這一系列主要分享如何用 Arduino Nano 33 BLE 開發板運行最簡單的示例代碼。
?
5. 軟件準備
這本書所有的項目是依賴于 TensorFlow Lite 在微控制器上的開發框架,所依賴的硬件環境,只有幾十 kb 左右的存儲空間。
-
項目
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/micro
?
我們也知道,對于開源軟件來說,由于軟件不斷的更新,包括優化,bug 修改以及其他設備的支持,造成代碼一直在不斷的變動。或許書中舉出例子的代碼跟 Github 的代碼不太一致,但是最基本的原則是相通的。
?
在軟件開發中間,我們也可以選擇適合自己的 IDE 開發工具。但是由于我(非原書作者,是本人)已經用于 Linux 很多年,是 Vim 的死忠粉,所以我后面給大家介紹的內容全部是基于 Vim+ 終端的模式進行講解。如果涉及到開發工具相關的問題,歡迎在本文末留言,我們一起討論。關于命令的運行,在 Linux 和 macOS 中,我們很輕易的用終端。在 Windows 系中,可以用命令行工具去解決開發問題。
?
嵌入式開發另外一個問題就是需要和開發板進行通訊。如果用 Spark Fun 開發板,需要用 Python 命令來做項目的編譯,如果用 Arduino 開發板,只需要在 Arduino 的開發環境中,加載開發包就可以了。
?
6. 機器學習工程化流程
整個機器學習工程部署的大體流程如下:
確定目標
收集數據集
設計模型架構
訓練模型:注意一下過擬合和欠擬合所產生的各種問題
轉換模型
運行推斷
評估并排除故障
?
工程流程一般都是基于以上的步驟進行部署的。在下一章中,我們將用學習任何語言的經典入門 “Hello World”,來闡述基于項目的 TinyML 工程開發流程。
?
?
Hello World — 夢開始的地方(上)
Hello World 是每一個程序員進入程序世界,學說的第一句話。它的意義并不在于可以以一種特殊的方式去輸入一串字符,而在于用一句簡單的話,去了解最基本的流程,為自己的新世界打開一扇大門。
?
我們將 “Hello World — 夢開始的地方”分為上、中、下,三個部分來講述如何去構建一個完整的、可以運行的、基于 TensorFlow Micro 的工程系統。其中上篇是手把手教開發者如何去建立并且訓練模型,中篇主要講解如何創建工程應用,下篇主要是講解如何把工程部署到微控制器上。
?
事不宜遲,我們趕緊進入上篇吧。
?
1.?環境準備
項目在三個不同的開發板上都可以正常運行,我們以Arduino? Nano 33 BLE Sense?(https://www.arduino.cc/en/Guide/NANO33BLE)?為硬件,來實現基于 TensorFlow 的 ML 項目。
?
2.?項目流程
實現基于微控制器的 ML 項目開發流程如下所示:
1. 獲得簡單數據集
2. 訓練深度學習模型
3. 評估模型性能
4. 轉換成設備上運行的模型
5. 將代碼轉換成二進制文件
6. 部署二進制文件到微控制器
?
文章中所有的代碼都基于?TensorFlow Micro?上的代碼。當然,代碼中也包括許多注釋,我們會一一的分析代碼中的最關鍵的部分以及為何要這么實現。
-
TensorFlow Micro
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/micro?
?
3.?準備前的工作
我們分享的 Hello World 示例,是用數學中的最基本的sine函數為原型,用 ML 的方式去預測數據。關于 sine 函數,我們在初中學三角函數時就接觸過。這個函數在工業應用中非常廣泛。一般的函數圖形如圖所示:
?
我們的目標是,如果有一個 x 值,我們能夠預測出 x 的 sin 值 y。在真實的環境中,用數學計算的方法可以更快速的得到結果。這個例子是用 ML 的方法去實現預測,從而了解 ML 的整個流程。
?
整從數學角度來看,sine 函數能夠在 -1 到 1 之間,周期性的平滑波動。我們可以用這種平滑的值,來控制 LED 燈光的亮度。
?
利用 Arduino 開發板運行這個項目的效果如圖所示:
?
工具準備:
配置開發環境,所用的編程語言當然是當之無愧的 Python,這是現在使用最廣泛的,運用于科學、數學、以及 AI 領域的編程語言。版本為 3.x Python 可以在命令行下運行,但是還是推薦用 Jupyter Notebook 來開發,它的好處是可以把代碼、文檔、還有圖片放在一起,既能當教程,又能分步運行。
?
如果有條件上 Google Colab,這個編譯環境也是一個很好的選擇。Colab 一直是由 Google 開發并維護的平臺,并且在云平臺上,已經安裝了各種依賴軟件。并且還可以免費去用 Google TPU 的服務。通過 Web 瀏覽器,可以運行自己編寫的任何代碼。甚至可以用 Colab 的配置文件,來選擇加速硬件,從而加速模型訓練。
?
ML 平臺的選擇,毫無疑問,選擇 TensorFlow。因為 TensorFlow 是現在使用最廣泛的AI加速平臺,并且有成熟的 pip 安裝包可以用。當然,在接下來的應用中,我們需要用 TensorFlow Lite,能得模型在嵌入式硬件上運行,我們也會用 TensorFlow 的高階 API,Keras 來完成一些編程工作。
?
4.?創建模型
項目相關代碼:
-
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/micro/examples/hello_world/create_sine_model.ipynb
?
當然,如果在 Google Colab 上運行項目,可以直接運行。
?
我們就在本地以 Jupter notebook 的方式,運行我們的代碼。我是以 git clone 的方式下載了 TensorFlow 的所有源碼,從本地的 Linux 命令行進行操作。由于我運行環境的 Linux 系統,已經安裝了所有的依賴軟件,所以在 Jupter 的環境中,涉及到 Linux 中相關的 pip 安裝和 apt 安裝的代碼都注釋掉了。
?
首先運行 Jupyter:
?
啟動之后彈出 chrome 瀏覽器,內容如圖所示:
?
其中需要注意的是,在右上角的區域,如果提示不受信任,點擊“不受信任”的文字,按提示操作,最終文字變成“信任”。如果說右上角區域 Python 版本為 2,那么需要在 Jupyter 的菜單欄中,Kernel ?Change Kernel 中選擇 Python 3。
?
當然,代碼的第一步是導入 TensorFlow、Numpy、Matplotlib 以及Math 庫。其中 Numpy 用于數據處理。Matplotlib 用于數據的可視化。
?
其實在代碼中,我注釋掉了 #!pip install tensorflow==2.0.0-beta0 這一行,原因是因為我的 PC 機已經安裝完了 TensorFlow 的最新版。
?
5.?產生數據
首先基于 sin 函數產生一系列的標準化數據。
可以看出,運行之后,數據相當規范。作者為了讓大家細致入微的去理解代碼,對這一段代碼做了一些解釋說明。當然,如果是要處理數據的話,Numpy 是一個最優的數學處理函數庫。x_values = np.random.uniform(low=0, high=2*math.pi, size=SAMPLES), 用來產生特定范圍內的一系列隨機數。
?
當原始數據產生之后,我們要接著做的事情,便是數據清洗。我們要確保數據以真正的隨機的方式反饋。很幸運,Numpy 的radom.shuffle()提供了這樣的方法。
?
最后再把數據以二維坐標的方式畫在圖片上。
?
這么漂亮的數據,直接去訓練可好?當然沒有問題,但是,ML 做的是什么樣的事情?從各種噪音中篩選數據,經過訓練,最后預測的準確率越來越高。第一部操作,我們需要加入一些噪音進來。這也是機器學習中常用的方法。當樣本的容易不夠時,我們如何增加樣本的數量,以便訓練時準確率更高。
?
我們就完全創建的所有的數據。
?
接下來便是分成訓練集和測試集:
?
就這一部分的關鍵代碼詳細解釋一下:
TRAIN_SPLIT = int(0.6 * SAMPLES) TEST_SPLIT = int(0.2 * SAMPLES + TRAIN_SPLIT)?
整個數據集包括三個部分,訓練集,測試集,驗證集。示例中用了 60% 的數據當訓練集,20% 的數據當測試集,20% 的數據當驗證集。這個比例不是恒定的,可以按需求調整。
?
x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT]),雖然輸入參數中只包括了訓練的測試的部分,但是由于整個數據集分割了三部分,所以 np.split 的返回結果是三個,代碼我們的三種場景。
?
6.?定義基本模型
?
我們用 Keras 來構建最開始的模型。
?
第一層采用標量輸入,并且基于 “relu” 激活,用了 16 個神經元的密集層 (Dense Layer,也可叫做全連接層)。當我們進行預測時,它是推理過程中的神經元之一。每個神經元將然后被激活到一定程度。每個神經元的激活量是基于在訓練過程中獲得的 weight 和 bias 值來定義激活功能。神經元的激活將作為數字輸出。激活是通過一個簡單的公式來計算的,如 Python 中所示。我們將永遠不需要自己編寫此代碼,因為它由 Keras 和 TensorFlow 處理, 在深入學習時但會有所幫助,計算公式的偽代碼如下所示:
activation = activation_function((input * weight) + bias)?
要計算神經元的激活程度,需要將其輸入乘以權重和偏差被添加到結果中。計算出的值被傳遞到激活函數中。結果就是神經元的激活。激活函數是一種數學函數,用于塑造神經元的輸出。在我們的網絡中,我們使用的是稱為整流線性單元 (Rectified Linear Unit) 的激活函數,或簡稱為 ReLU。這在 Keras 中由參數 activation = relu 指定。ReLU 是一個簡單的函數,如 Python 所示:
def relu(input): return max(0.0, input)?
ReLU 返回較大的值:如果其輸入值為負,ReLU 返回零。如果其輸入值大于零,則輸出保持不變。
?
用 ReLU 做為激活函數意義在哪兒?
?
沒有激活函數,神經元的輸出將始終是線性函數.這意味著網絡只能建模線性關系,其中 x 和 y 之比在整個值范圍內保持不變。但是正弦波又是非線性的,這將阻止網絡對我們的正弦波進行建模。由于 ReLU 是非線性的,因此它允許多層神經元聯合作用并建立模型復雜的非線性關系,每次x增量不會使y值以相同的方式增加。還有其他激活功能,但是ReLU是最常用的功能。作為ML算法,運用最優的激活函數是必要的。
?
我們再對輸入、輸出層做一些解讀:
由于輸出層是單個神經元,它將接收 16 個輸入。由于這是我們的輸出層,因此我們不指定確定激活功能-我們只需要原始結果。由于此神經元有多個輸入,因此每個神經元都有一個對應的權重值。神經元的輸出通過以下公式計算得出,如 Python 中所示:其中 “inputs” 和 “weights” 都是 NumPy 數組,每個數組有 16 個元素
output = sum((inputs * weights)) + bias?
通過將每個輸入與其對應的乘積獲得輸出值 weights,對結果求和,然后加上神經元的 bias。該網絡的 weights 和 bias 是在培訓期間學習的。?
?
接下來,編譯階段的關鍵點,便是優化器的損失函數了。
model_1.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])?
其中優化器,損失函數以及指標,都有許多種選擇,我們不展開詳述。您可以去以下鏈接去做更多的了解。
-
https://keras.io/optimizers/
-
https://keras.io/losses/
?
最后我們再來看看模型概況:
?
其中輸入層有 16 個神經元,共 2 層連接,所以全部的連接數為 16x2=32,每一個神經元都有一個 bias,網絡總共有 17 個 bias。輸入的 16,以及輸出的 1。所以總的參數為 32+17=49。
?
7.?訓練模型
?
利用 keras 的 fit() 方法能夠很好的訓練。下面就一些參數做一下最基本的解釋:
-
X_train, y_train 表示最基本的訓練數據。
-
epochs 訓練的周期,一般來說,周期越長,訓練越精確,但是,一般來說,訓練的時間到一定階段,訓練精度不會有很大差別。在這種清況下,一般要考慮去優化模型了。
-
batch_size 用于往網絡中一次送入多少數據,如果值為 1,我們每一次會更新 weight 和 bias,并且會估計網絡預測的損失,為下一次的運行做更精確的估計。越小的值,會帶來很大的計算量,占用更多的計算資源。如果我們把值定要 600,一次性可以計算出更多的數據,但是會降低模型的精度。所以最好的方式是把值設置為 16 或者是 32。這個值的選擇,實際上精度與時間花費權衡的結果。
?
接下來,我們最關心的,當然是訓練的結果了。
?
該圖顯示了每個時期的損失(或模型的預測與實際數據之間的差異)。有幾種計算損失的方法,我們使用的方法是均方誤差。對于訓練和驗證數據有明顯的損失值。
?
我們可以看到,損失的數量在前 25 個時期迅速減少,然后趨于平穩。這意味著該模型正在改進并產生更準確的預測!
?
我們的目標是在模型不再改善或訓練損失小于驗證損失 (Validation Loss) 時停止訓練,這意味著該模型已經學會了很好地預測訓練數據,也不需要新的數據來提高精度。
?
為了使圖表的平坦部分更具可讀性,我們用代碼 SKIP = 50 跳過前 50 個 epochs,這僅僅是便于我們看圖方便。
?
從圖中可以分析出,大概 epochs 到了 600 左右,訓練開始趨于穩定。意味著我們的 epochs 應該不需要超過 600。
?
但是,我們還可以看到最低的損失值仍在 0.155 左右。這意味著我們網絡的預測平均降低了約 15%。另外,驗證損失值 (Validation loss) 產生的很大的跳躍,并不穩定。我們需要改進方法。這次,我們將繪制平均絕對誤差 (Mean Absolute Error)圖,這是另一種衡量網絡預測與實際數字的距離的方法:
?
?
從平均絕對誤差圖可以看到,訓練數據顯示出的錯誤始終比驗證數據低,這意味著網絡可能存在過擬合 (Overfit),或者過分地學習了訓練數據,從而無法對新數據做出有效的預測。
?
此外,平均絕對誤差值非常高,最多約為 0.305,這意味著該模型的某些預測至少可降低 30%。30% 的誤差意味著我們離精確建模正弦波函數還很遙遠。
?
?
該圖清楚地表明,我們的網絡已經學會了以非常有限的方式近似正弦函數。從 0 <= x <= 1.1 開始,該行最適合,但是對于我們的其他 x 值,充其量只是一個大概的近似值。
?
結果表明,該模型沒有足夠的能力來學習正弦波函數的全部復雜度,因此只能以過于簡單的方式對其進行近似。通過優化模型,我們應該能夠改善其性能。
?
8.?優化模型
優化的關鍵是增加全連接層,這一層包含了 16 個神經元。
?
我們再對模型做評估:
?
?
通過圖片分析,我們的網絡達到了峰值精度的速度要快得多(在 200 個時期內,而不是 600 個時期內)。總損失和 MAE 比我們以前的網絡要好得多。驗證結果比訓練結果更好,這意味著網絡不會過度擬合。驗證指標優于訓練指標的原因是,驗證指標是在每個時期結束時計算的,而訓練指標是在整個時期計算的。
?
這一切都意味著我們的網絡似乎運行良好!為了確認這一點,讓我們對照我們先前放置的測試數據集檢查其預測:
?
最終的測試結果如下所示:
?
我們來看這張圖片,并不能說明這預測值完全跟 sine 的曲線一模一樣。但是現在預測精度不是主要問題了,我們只想通過平滑的曲線來對開關進行控制。模型精度已經足夠了。
?
9.?模型轉換
模型轉換的要點,就是 TensorFlow 到 TensorFlow Lite 的轉換。其中有兩個主要的組成部分:
?
TensorFlow Lite 轉換器 (TensorFlow Lite Converter)
這會將 TensorFlow 模型轉換為一種節省空間的特殊格式,以用于內存受限的設備,并且可以應用進一步減少并優化模型尺寸,使其在小型設備上運行更快。
?
TensorFlow Lite 解釋器 (Tensorflow Lite Interpreter)
這會使用最有效的方式運行經過適當轉換的 TensorFlow Lite 模型到給定設備的有效操作。
?
同樣,我們不需要操作,但是需要了解 convert 的 python API,以及創建 FlatBuffer,當然,還包括量化,即浮點精度的轉換問題。這一類的話題,在我以前關于 TensorFlow Lite 的許多線下分享中都有過詳細的原理上的分析及解釋。
?
整個轉換代碼如下:
?
產生了兩個模型,第一個模型是沒有經過量化的。第二個模型是經過量化的。
?
對 TF Lite 模型進行預測,我們還需要完成如下的工作:
申明解釋器對象實體
為模型分配內存
加載模型
從傳感器中讀取輸出數據
?
由于這一段代碼比較長,我們就看一下最終的分析結果:
?
我們再用代碼來對比一下,量化和非量化模型的大小區別:
?
差距有20個字節。
?
10.?模型轉換成二進制文件
準備模型以用于 TensorFlow Lite for Microcontrol 的最后一步。
?
到目前為止,在本章中,我們一直在使用 TensorFlow Lite 的 Python API。表示我們已經能夠使用 Interpreter 構造函數加載模型磁盤中的文件。但是,大多數微控制器都沒有文件系統,即使有,鑒于我們的限制,從磁盤加載模型所需的額外代碼將很浪費空間。相反,作為一種優雅的解決方案,我們在 C 源文件中提供了該模型,包含在我們的二進制文件中并直接加載到內存中。
?
在文件中,模型定義為字節數組。幸運的是,有一個名為 xxd 的 Unix 工具,它能夠將給定的文件轉換為所需的格式。
?
以下輸出為在我們的量化模型上運行 xxd,將輸出寫入名為sine_model_quantized.cc,并將其打印到屏幕上:
?
最終,生成二進制模型代碼的工作搞定。
?
11.?結論
至此,我們完成了模型的構建。我們已經進行了訓練,評估和配置建立了一個 TensorFlow 深度學習網絡,該網絡可以采用 0 到 2π 之間的數字并輸出正弦的近似值。
?
這是我們使用 Keras 訓練小模型的初次體驗。在未來的項目中,我們將訓練模型仍然很小,但是要復雜得多。
?
在下一篇中,我們將會分析如何去編寫微控制器能夠運行的 ML 應用。
總結
以上是生活随笔為你收集整理的社区分享 | 从零开始学习 TinyML(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 启动FastDFS服务,使用python
- 下一篇: Flask项目--爱家租房项目结构图