手把手跟我入門機器學習(1)——手寫體識別模型


手把手跟我入門機器學習(1)——手寫體識別模型

2021-01-11 電子發燒友網

前言

這篇文檔非常長所以我們會分成5篇來連載,因爲機器學習並不是純軟體開發,簡單地調用庫函數 API,需要有一定的理論支撐,如果完全不介紹理論部分,可能就不知道爲什麼模型要這樣設計,模型出了問題應該怎樣改善。不過文檔如果寫太長大家可能很難有耐心看完,特別是理論部分會有很多公式,但是機器學習確實又對 理論基礎編程能力 都有一些要求,相信堅持看下去還是會有很多收穫的,我也儘可能把理論和應用都介紹清楚。

本項目爲開源項目,關注以上公衆號同步獲取連載教程

前兩篇的連載會以機器學習理論爲主,之後的文檔就基本是純實際應用了,不會有太多理論內容了:[ Darknet 訓練目標檢測模型 ],[ RT-Thread 連接 ROS 小車控制 ]。

這篇文章假定大家都已經會用 RT-Thread 的 env 工具下載軟體包,並且生成項目上傳固件到 stm32 上,因爲這幾天的兩篇連載文章重點在於加載 onnx 通用機器學習模型,關於 RT-Thread 的教程大家可以在官網文檔中心:https://www.rt-thread.org/document/site/上找一找。

首先,簡單介紹一下上面提到的各個話題的範圍 (Domain),人工智慧 (Artifitial Intelligence) 是最大的話題,如果用一張圖來說明的話:

然後機器學習 (Machine Learning) 就是這篇文檔的主題了,但是 機器學習 依舊是一個非常大的話題:

監督學習 (Supervised Learning): 這應當是應用最多的領域了,例如人臉識別,我提前先給你大量的圖片,然後告訴你當中哪些包含了人臉,哪些不包含,你從我給的照片中總結出人臉的特徵,這就是訓練過程。最後我再提供一些從來沒有見過的圖片,如果算法訓練得好的話,就能很好的區分一張圖片中是否包含人臉。所以監督學習最大的特點就是有訓練集,告訴模型什麼是對的,什麼是錯的。

非監督學習 (Unsupervised Learning): 例如網上購物的推薦系統,模型會對我的瀏覽記錄進行分類,然後自動向我推薦相關的商品。非監督學習最大的特點就是沒有一個標準答案,比如水杯既可以分類爲日用品,也可以分類爲禮品,都沒有問題。

強化學習 (Reinforcement Learnong): 強化學習應當是機器學習當中最吸引人的一個部分了,例如 Gym 上就有很多訓練電腦自己玩遊戲最後拿高分的例子。強化學習主要就是通過試錯 (Action),找到能讓自己收益最大的方法,這也是爲什麼很多都例子都是電腦玩遊戲。

所以文檔後面介紹的都是關於 監督學習,因爲手寫體識別需要有一些訓練集告訴我這些圖像實際上應該是什麼數字,不過監督學習的方法也有很多,主要有分類和回歸兩大類:

分類 (Classification): 例如手寫體識別,這類問題的特點在於最後的結果是離散的,最後分類的數字只能是 0, 1, 2, 3 而不會是 1.414, 1.732 這樣的小數。

回歸 (Regression): 例如經典的房價預測,這類問題得到的結果是連續的,例如房價是會連續變化的,有無限多種可能,不像手寫體識別那樣只有 0-9 這 10 種類別。

這樣看來,接下來介紹的手寫體識別是一個 分類問題。但是做分類算法也非常多,這篇文章要介紹的是應用非常多也相對成熟的 神經網絡 (Neural Network)。

人工神經網絡 (Artifitial Neural Network):這是個比較通用的方法,可以應用在各個領域做數據擬合,但是像圖像和語音也有各自更適合的算法。

卷積神經網絡 (Convolutional Neural Network):主要應用在圖像領域,後面也會詳細介紹。

循環神經網絡 (Recurrent Neural Network):比較適用於像聲音這樣的序列輸入,因此在語言識別領域應用比較多。

最後總結一下,這篇文檔介紹的是人工智慧下面發展比較快的機器學習分支,然後解決的是機器學習監督學習下面的分類問題,用的是神經網絡里的卷積神經網絡 (CNN) 方法。

1 神經網絡相關理論

這一部分主要介紹神經網絡的整個運行流程,怎麼準備訓練集,什麼是訓練,爲什麼要訓練,怎麼進行訓練,以及訓練之後得到了什麼。1.1 線性回歸 (Linear Regression)1.1.1 回歸模型要做機器學習訓練預測,我們首先得知道自己訓練的模型是什麼樣的,還是以最經典的線性回歸模型爲例,後面的人工神經網絡 (ANN) 其實可以看做多個線性回歸組合。那麼什麼是線性回歸模型呢?比如下面圖上這些散點,希望能找到一條直線進行擬合,線性回歸擬合的模型就是:

這樣如果以後有一個點 x = 3,不在圖上這些點覆蓋的區域,我們也可以通過訓練好的線性回歸模型預測出對應的 y。不過上面的公式通常使用另外一種表示方法,最終的預測值也就是 y 通常用 hθ (hypothesis) 表示,而它的下標 θ 代表不同訓練參數也就是 k, b。這樣模型就成了:

所以 θ0 對應著 b,θ1 對應著k。但是這樣表示模型還不夠通用,比如 x 可能不是一個一維向量,例如經典的房價預測,我們要知道房價,可能需要房子大小,房間數等很多因素,因此把上面的用更通用的方法表示:這就是線性回歸的模型了,只要會向量乘法,上面的公式計算起來還是挺輕鬆的。順便一提,θ 需要一個轉置 θT,是因爲我們通常都習慣使用列向量。上面這個公式和 y=kx+b 其實是一樣的,只是換了一種表示方法而已,不過這種表示方法就更加通用,而且也更加簡潔優美了:

爲了讓上面的模型能夠很好的擬合這些散點,我們的目標就是改變模型參數 θ0 和 θ1,也就是這條直線的斜率和截距,讓它能很好的反應散點的趨勢,下面的動畫就很直觀的反應了訓練過程。

可以看到,一開始是一條幾乎水平的直線,但是慢慢地它的斜率和截距就移動到一個比較好的位置,那麼問題來了,我們要怎麼評價這條直線當前的位置滿不滿足我們的需求呢?一個很直接的想法就是求出所有散點實際值 y 和我們模型的測試值 hθ 相差的絕對值,這個評價指標我們就稱爲損失函數 J(θ) (cost function):

函數右邊之所以除以了2是爲了求倒數的時候更加方便,因爲如果右邊的公式求導,上面的平方就會得到一個2,剛好和分母里的2抵消了。這樣我們就有了評價指標了,損失函數計算出來的值越小越好,這樣就知道當前的模型是不是能很好地滿足需求,下一步就是告訴模型該如何往更好的方向優化了,這就是訓練 (Training) 過程。1.1.3 模型訓練爲了讓模型的參數 θ 能夠往更好的方向運動,也就是很自然的想法就是向下坡的方向走,比如上面的損失函數其實是個雙曲線,我們只要沿著下坡的方向走總能走到函數的最低點:

那麼什麼是”下坡”的方向呢?其實就是導數的方向,從上面的動畫也可以看出來,黑點一直是沿著切線方向逐漸走到最低點的,如果我們對損失函數求導,也就是對 J(θ) 求導:我們現在知道 θ 應該往哪個方向走了,那每一次應該走多遠呢?就像上面的動畫那樣,黑點就算知道了運動方向,每一次運動多少也是需要確定的。這個每次運動的多少稱之爲學習速率 α (learning rate),這樣我們就知道參數每次應該向哪個方向運動多少了:

這種訓練方法就是很有名的 梯度下降法(Gradient Descent),當然現在也有很多改進的訓練方法例如 Adam,其實原理都差不多,這裡就不做過多的介紹了。

1.1.4 總結

機器學習的流程總結出來就是,我們先要設計一個模型,然後定義一個評價指標稱之爲損失函數,這樣我們就知道怎麼去判斷模型的好壞,接下來就是用一種訓練方法,讓模型參數能朝著能讓損失函數減少的方向運動,當損失函數幾乎不再減少的時候,我們就可以認爲訓練結束了。最終訓練得到的就是模型的參數,使用訓練好的模型我們就可以對其他的數據進行預測了。順便一提,上面的線性回歸其實是有標準理論解的,也就是說不需要通過訓練過程,一步得到最優權值,我們稱之爲 Normal Equation:

那麼,明明有一步到位的理論解,我們爲什麼還需要一步一步的訓練呢?因爲上面的公式里有矩陣的逆運算,當矩陣規模比較小時,對矩陣求逆運算量並不大,但是一旦矩陣的規模提升上去,用現有的計算能力求逆是幾乎不可能了,所以這個時候就需要用梯度下降這樣的訓練方法一步一步的逼近最優解。1.2 非線性回歸 (Logistic Regression)

我們回到手寫體識別的例子,上面介紹的線性回歸最後得到的是一個連續的數值,但是手寫體識別最後的目標是得到一個離散的數值,也就是 0-9,那麼這要怎麼做到呢?

這個就是上一部分的模型,其實很簡單,只需要在最後的結果再加一個 sigmoid 函數,把最終得到的結果限制在 0-1 就可以了。

就像上面圖中的公式那樣,sigmoid 函數就是:

如果把它應用到線性回歸的模型,我們就得到了一個非線性回歸模型,也就是 Logistic Regression:

這樣就可以確保我們最後得到的結果肯定是在 0-1 之間了,然後我們可以定義如果最後的結果大於 0.5 就是 1,小於 0.5 就是 0,這樣一個連續的輸出就被離散了。1.3 人工神經網絡 (ANN)現在我們介紹了連續的線性回歸模型 Linear Regression,和離散的非線性回歸模型 Logistic Regression,模型都非常簡單,寫在紙上也就不過幾厘米的長度。那麼這麼簡單的模型到底是怎麼組合成非常好用的神經網絡的呢?其實上面的模型可以看做是只有一層的神經網絡,我們輸入 x 經過一次計算就得到輸出 hθ 了:

如果我們不那麼快得到計算結果,而是在中間再插入一層呢?就得到了有一層隱藏層的神經網絡了。

上面這張圖裡,我們用 a 代表 激活函數 (activation function) 的輸出,激活函數也就是上一部分提到的 sigmoid 函數,爲了將輸出限制在 0-1,如果不這麼做,很有可能經過幾層神經網絡的計算,輸出值就爆炸到一個很大很大的數了。當然除了 sigmoid 函數外,激活函數還有很多,例如下一部分在卷積神經網絡里非常常用的 Relu。另外,我們用帶括號的數字代表神經網絡的層數。例如 a(1) 代表第一層神經網絡輸出。當然,第一層就是輸入層,並不需要經過任何計算,所以可以看到圖上的 a(1)=x,第一層的激活函數輸出直接就是我們的輸入 x。但是,θ(1) 不是代表第一層的參數,而是第一層與第二層之間的參數,畢竟參數存在於兩層網絡之間的計算過程。● 輸入層:a(1)=x
● 隱藏層:a(2)=g(θ(1)a(1))
● 輸出層:h(θ)=g(θ(2)a(2))如果我們設置最後的輸出層節點是 10 個,那就剛好可以用來表示 0-9 這 10 個數字了。如果我們再多增加幾個隱藏層,是不是看起來就有點像是互相連接的神經元了?

如果我們再深入一點 Go Deeper (論文裡作者提到,他做深度學習的靈感其實源自於盜夢空間)

圖片來源網絡表情包

這樣我們就得到一個深度神經網絡了:

如果你想知道,具體應當選多少層隱藏層,每個隱藏層應該選幾個節點,這就跟你從哪裡來,要到哪裡去一樣,是神經網絡的終極問題了 😛最後,神經網絡的訓練方法是用的 反向傳播 (Back Propagation),如果感興趣可以在這裡找到更加詳細的介紹。1.4 卷積神經網絡 (CNN)終於到了後面會用到的卷積神經網絡了,從前面的介紹可以看到,其實神經網絡的模型非常簡單,用到的數學知識也不多,只需要知道矩陣乘法,函數求導就可以了,而深度神經網絡只不過是反覆地進行矩陣乘法和激活函數的運算:

這樣重複相同的運算顯得有些單調了,下面要介紹的卷積神經網絡就引入了更多更有意思的操作,主要有:

接下來就對這些算子逐一介紹。

1.4.1轉換2D首先圖像領域的神經網絡最大的特點就是引入了卷積操作,雖然這個名字看起來有點神祕,其實卷積運算非常簡單。

這裡說明一下爲什麼要引入卷積運算,儘管前面的矩陣乘法其實已經可以解決很多問題了,但是一旦到了圖像領域,對一個 1920*1080 的圖像做乘法,就是一個 [1, 2,073,600] 的矩陣了,這個運算量已經不算小了,而用卷積操作,計算量就會大大縮減;另一方面,如果把一個二維的圖像壓縮成一個一維的向量,其實就丟失了像素點在上下左右方向相互關聯的信息,例如一個像素點和周圍的顏色通常比較相近,這些信息很多時候是很重要的圖像信息。

介紹完了卷積操作的優勢,那麼到底什麼是卷積運算呢?其實卷積就是簡單的加減乘除,我們需要一幅圖像,然後是一個卷積核 (Kernel):

上面這張圖像經過一個 3×3 的卷積核操作,就很好地把圖像的邊緣提取出來了,下面這個動畫就很清晰地介紹了矩陣運算:

上面動畫用到的卷積核是一個 3×3 的矩陣:

可以看到卷積操作實際上就是把卷積核在圖像上按照行列掃描一遍,把對應位置的數字相乘,然後求和,例如上面的左上角的卷積結果 4 是這麼計算得到的 (這裡用 ∗ 代表卷積):

當然上面的計算過程用等號連接是不嚴謹的,不過可以方便地說明卷積的計算過程。可以看到,卷積的計算量相比全連接的神經網絡是非常小的,而且保留了圖像在二維空間的關聯性,所以在圖像領域應用地非常多。卷積操作非常好用,但是卷積後圖像大小變小了,例如上面的 5×5 矩陣經過一個 3×3 的卷積核運算最後得到的是一個 3×3 的矩陣,所以有的時候爲了保持圖像大小不變,會在圖像周圍一圈用 0 填充,這個操作稱之爲 padding。但是 padding 也沒有辦法完全保證圖像大小不變,因爲上面動畫的卷積核每次都只向一個方向運動一格,如果每次運動 2 格,那麼 5×5 的圖像經過 3×3 的卷積就成了 2×2 的矩陣了,卷積核每次移動的步數我們稱之爲 stride。下面是一幅圖像經過卷積運算後得到的圖像大小計算公式:

比如上面圖像寬度 W = 5,卷積核大小 F = 3,沒有使用 padding 所以 P = 0,每次移動步數 S = 1:

這裡說明一下,上面的計算都是針對一個卷積核而言的,實際上一層卷積層可能有多個卷積核,而且實際上很多 CNN 模型也是卷積核隨著層數往後,越來越多的。

1.4.2最大池化上面提到卷積可以通過 padding 保持圖像大小不變,但是很多時候我們希望能隨著模型的推進,逐漸減小圖像大小,因爲最後的輸出例如手寫體識別,實際上只有 0-9 這 10 個數字,但是圖像的輸入卻是 1920×1080,所以 maxpooling 就是爲了減少圖像尺寸的。

比如左邊 4×4 的輸入,經過 2×2 的 maxpooling,其實就是把左上角 2×2 的方塊取最大值:

所以這樣一個 4×4 的矩陣經過 2×2 的 maxpooling 一下尺寸就縮小了一半,這也就是 maxpooling 的目的了。1.4.3露露之前介紹 sigmoid 函數的時候,提到過它是激活函數的一種,而 Relu 就是另一種在圖像領域更爲常用的激活函數, Relu 相比 sigmoid 就非常簡單了:

其實就是當數字小於0的時候取0,大於0的時候保持不變。1.4.4輟學到這裡一共介紹了3個算子,conv2d, maxpooling,relu,每一個的運算都非常簡單,但是 Dropout 甚至更簡單,連計算都沒有,於是在這個部分一個公式都沒有。

之前沒有提到模型過擬合的問題,因爲神經網絡模型在訓練過程中,很有可能出現模型對自己提供的訓練集擬合非常好,但是一旦碰到沒有見過的數據,就完全預測不出正確的結果了,這種時候就是出現了過擬合。

那麼,怎麼解決過擬合問題呢?Dropout 就是一種非常簡單粗暴的方法,從已經訓練好的參數當中,隨機挑一些出來丟棄掉重置爲 0,這也是爲什麼它的名字叫 Dropout,就是隨機丟掉一些參數。這是個簡單到不可思議的方法,但是卻意外地好用,例如僅僅是在 maxpooling 後隨機丟棄掉 60% 訓練好的參數,就可以很好地解決過擬合問題。1.4.5展平

Flatten 就是像字面意思那樣,把一個2維的矩陣壓平,比如這樣一個矩陣:

1.4.6密集Dense 其實前面已經介紹過了,就是矩陣的乘法,然後加法:

1.4.7 Softmax這一個就是最後一個算子了,比如我們要做手寫體識別,那麼最後的輸出就會是 0-9,這將是一個 1×10 的矩陣,例如下面的預測結果 (實際上是一行,爲了方便顯示寫成兩行了):

上面的 1×10 的矩陣可以看到第 7 個數 0.753 遠遠大於其他幾個數 (下標我們從 0 開始),所以我們可以知道當前預測結果是 7。 所以 softmax 會作爲模型的輸出層輸出 10 個數字,每個數字分別代表圖片是 0-9 的概率,我們取最大的一個概率就是預測結果了。另一方面,上面 10 個數相加剛好是 1,所以其實每個數就代表一個概率,模型認爲這個數是1個概率是 0.000498,是 2 的概率是 0.000027,以此類推,這麼直觀方便的結果就是用 softmax 計算得到的。

比如有兩個數 [1, 2] 經過 softmax 運算:

最後得到的兩個數字就是 [0.269, 0.731]。

到這裡第一部分卷積神經網絡相關的算子就終於介紹完了,第二部分部分就會介紹實際如何用 Keras (Tensorflow) 機器學框架訓練一個手寫體識別模型,最後第三部分就是介紹如何利用把生成的模型導入到 stm32 上面運行。

1.5 參考文獻

斯坦福經典機器學習入門視頻

連結:https://www.coursera.org/learn/machine-learning

線性回歸

連結:

https://towardsdatascience.com/introduction-to-linear-regression-and-polynomial-regression-f8adc96f31cb

https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/https://ujjwalkarn.me/2016/08/11/intuitive-explanation-convnets/

RT-Thread線上活動

1,【RT-Thread能力認證考試12月——RCEA】經過第一次考試的驗證,RT-Thread能力認證得到了更多社區開發者和產業界的大力支持!(點此查看)如果您有晉升、求職、尋找更好機會的需要,有深入學習和掌握RT-Thread的需求,歡迎垂詢/報考!

能力認證官網連結:https://www.rt-thread.org/page/rac.html(在外部瀏覽器打開)

立即報名

#題外話# 喜歡RT-Thread不要忘了在GitHub上留下你的STAR哦,你的star對我們來說非常重要!連結地址:https://github.com/RT-Thread/rt-thread

你可以添加微信18917005679爲好友,註明:公司+姓名,拉進 RT-Thread 官方微信交流羣

RT線程

讓物聯網終端的開發變得簡單、快速,晶片的價值得到最大化發揮。Apache2.0協議,可免費在商業產品中使用,不需要公布源碼,無潛在商業風險。

點擊閱讀原文進入RT-Thread官網