04_利用手寫數字問題引入深度神經網路
一、引言
為什麼要新增這一篇文章呢?上一篇文章我們已經馬馬虎虎的利用了深度學習的神經網路架構,但只是一層的,大家一定很好去理解他。但是如果我們直接使用torch實現一個多層網路架構的模型,那麼對於很多同學來說,一定是迷迷糊糊,心裡面草泥馬飛過的。
為此,我們通過手寫數字這個問題來引入深度神經網路模型的架構,並且在未來利用torch實現手寫數字問題的時候穿插對torch基本語法的介紹,讓大家能夠在應用實踐中學習torch的一些枯燥乏味的基本語法。
二、手寫數字問題介紹
2.1 MNIST資料集介紹
在MNIST資料集中總共有7000張圖片,10個類別,訓練集和測試集的比例分別為6000和1000。
由於圖片是灰度圖片,每張圖片的矩陣表示是[28, 28, 1]
,但在接下來的操作中會對每張圖片進行扁平化處理,即每張圖片的表示將為[784]
2.2 輸入
對於輸入,由於對圖片做了扁平化處理,輸入的x將為一個[b,784]
的矩陣,其中b表示圖片的張數。
2.3 輸出
由於此次任務是分類任務,假設我們使用離散值去表示結果,比如1-2為“飛機”,2-3為“狗”,3-4為“貓”,這樣看起來貌似沒有問題,但是1-2,2-3,3-4之間是有一個順序的,這樣會讓我們構建的模型認為我們的結果是有先後順序的,因此我們不能和線性迴歸一樣使用一個離散值去表示結果。
在這裡,推薦使用一個機器學習中常用的獨熱編碼(one-hot)作為分類的結果。具體是什麼意思呢?假設我們的資料總共有三個分類——狗、貓、虎,那麼狗可以表示為[1,0,0]
;貓表示為[0,1,0]
;虎表示為[0,0,1]
,這樣表示的話,我們就很好的解釋了離散值帶來的順序問題。
獨熱編碼雖然很好,但是我們可以看到獨熱編碼中非0即1,這樣有時候不利於後期我們進行其他的處理,因此我們還可以對結果進行歸一化處理,即對於上述這個例子,我們對結果歸一化,也就是模型預測的結果可能是[0.1,0.2,0.7]
,從上面可以發現,歸一化就是把分類結果變成和為1,且每個值在0-1之間。針對[0.1,0.2,0.7]
,我們可以理所應當的認為預測結果是“虎”,同時我們可以得出預測結果是貓和狗的概率,通過這種概率我們可以進行一定的誤差計算。
三、迴歸和分類模型的區別
在上一篇文章中,我們介紹了迴歸模型無非就是找到一個函式\(f=wx+b\),通過梯度下降演算法得到位置變數w和b的值,進而通過輸入一個x,得到一個預測值\(\hat{y}\)。
但是分類模型和迴歸模型區別還是有的,因為通過迴歸模型,我們可以看到迴歸模型能夠很好的預測離散值的結果,針對結果為非離散值的分類問題,就有點捉襟見肘了。
為此,我們在迴歸模型的基礎上做一點小小的改動,由於我們上面說到手寫數字識別的資料是一個矩陣,由此我們可以讓迴歸模型的函式變成如下函式\(f=relu(X@W+B)\quad\text{其中@表示矩陣乘法}\)。很多同學可能對relu有很大的好奇,這是什麼呢?relu其實是一個啟用函式,類似於sgn函式、sigimoid函式,他們的作用就是針對\(X@W+B\)的結果,做一個啟用處理,無論這個結果值多大或者多小,都將壓縮在0-1之間,這樣我們就可以實現上一節輸出內容講到的那種處理。
如果分類模型得到一個矩陣,我們只要對它做一個argmax處理,即求出矩陣中最大元素的索引既可以得出這個模型對輸入的預測結果。
四、深度模型隱藏層的來源
4.1 啟用函式深度講解
在此,我再強調一遍,pytorch教程的分享,預設你擁有了我前幾章講過的許多基礎,如果你有哪些內容聽不明白,請去補基礎。
上面說到了分類模型無非就是在迴歸模型上使用了一個啟用函式而已,但是為什麼要加這個啟用函式呢?啟用函式有什麼作用呢?現在我給大家稍微解惑。
在這裡我們繼續回到迴歸模型的構造,我們可以發現迴歸模型的構造是通過一個線性函式構造出來的,如果你有一個較好的數學底子,可以發現無論多少個線性函式無論怎麼疊加,疊加的結果始終都是另外一個線性函式。
對於圖片分類這個問題,如果單純的使用線性函式\(f=X@W+B\)去解決,那是是非常困難,甚至是很難做到的。為此科學家們想到了使用非線性函式去解決這個問題,那麼問題來了,該使用哪個非線性函式呢?為此,有人發明了一個辦法,對,就是啟用函式,線上性函式上面套一個啟用函式(也可以稱作非線性因子),因此這個線性函式因為這個啟用函式的作用,變成了一個非線性的函式\(f=relu(X@W+B)\)。
那麼通過這個非線性函式就可以去解決分類問題了嗎?可以想象,可能還是不夠,因為只是新增一個線性因子的非線性函式還是太簡單。
4.2 隱藏層
上面說到新增一個啟用函式的非線性函式\(f=relu(X@W+B)\)還是太簡單,為此科學家們又一次禿了頭,想到了一個更好的辦法,那就是一個非線性因子不夠,那麼我在多套一個非線性因子,在我的多次套娃之下,一定會有一個較好的結果。也因此,就出現了隱藏層。
現在我們假設深度模型沒有隱藏層,那麼模型也就是\(out=relu(X@W+B)\),輸入一個x,直接給出一個結果,但是我們說到,一個啟用函式可能不夠,那麼我們再新增一個啟用函式。
那麼模型將會變成\(out = relu(relu(X@W+B))\),這個模型我們可以看做兩部分,第一部分是\(relu(X@W+B)\),第二部分是\(relu(第一部分)\),第一部分就是隱藏層\(h_1\),第二部分就是隱藏層\(h_2\)。也就是說第一部分的輸出結果只是第二部分的輸入,而第二部分的輸出結果才是真正的結果。
4.3 隱藏層帶來的問題
對於一個深度模型,隱藏層可能有數十層、上百層,甚至上千層,隱藏層的層數也正是深度學習中深度的來源。那麼問題自然而然也就來了,上一個隱藏層是下一個隱藏層的輸入,那麼這個輸入已經被啟用函式啟用過,也就是上一個隱藏層的輸出是一個值,那麼這個值到底代表什麼意思呢?
目前,深度模型還無法給這個值表示什麼意義做出合理的解釋,但是我們可以把這個值抽象成一個特徵,只不過這個特徵是計算機模型能夠認知的,可惜我們無法和模型進行交流。也因此,在一些需要對特徵描述較為詳細的專案中,這個模型可能並不能很好的實現特徵描述的功能。
簡單點說,隱藏層就是模型抽象出了原有輸入的一個個人類看不懂的特徵。就好比我們總是會把很帥的男人看作是有德,而你們總看不到有德的帥。
五、深度模型的流程
5.1 多個隱藏層的分類模型舉例
在這裡,我們舉出一個例項,讓大家很好的去了解如何通過隱藏層去實現類別分類。
- 輸入\(X_{[1,784]}\),
- 假設第一層隱藏層的引數為\({W_1}_{[784, 512]}, {B_1}_{[1,512]}\);假設第二層隱藏層的引數為\({W_2}_{[521,256]}, {B_2}_{[1,256]}\);假設第三層隱藏層的引數為\({W_3}_{[256,10]}, {B_3}_{[1,10]}\)
- \(X\)通過第一層隱藏層\(h_1 = relue(X@W_1+B_1)\),那麼\(h_1\)的輸出將是\({h_1}_{[1,512]}\)
- 第一層隱藏層的輸出通過第二層隱藏層\(h_2=relu(h_1@W_2+B_2)\),那麼\(h_2\)的輸出將是\({h_2}_{[1,256]}\)
- 第二層隱藏層的輸出通過第三層隱藏層\(h_3 = relu(h_2@W_3+B_3)\),那麼\(h_3\)的輸出將是\({h_3}_{[1,10]}\)
相信看到第5步時你已經明白了為什麼步驟2要設定如此的隱藏層引數,對的,目的就是為了讓最後一層隱藏層的結果為\([1,10]\),因為我們是10分類,那麼對於其中的512和256這兩個值,我們能不能變化,其實是沒啥問題的,但是為了更好的利用gpu的效能,推薦一般設定成2的冪方
。
5.2 損失計算
通過上述流程,我們現在已經可以通過輸入得到一個預測結果,並且結果也正好是一個類似於獨熱編碼的矩陣,那麼我們該如何計算預測結果和真實結果的損失呢?
方法很簡單,使用我們中學就學過的一個知識點——歐式距離,假設如果結果是二分類,預測結果為[0.2,0.8]
,真實結果為[0,1]
,其中兩者的歐式距離便是損失,學過線性代數的你,相信清楚,十分類亦是如此。
5.3 優化模型引數—反向傳播演算法
損失得到了,那麼優化模型引數就很簡單了,反向傳播即可。在這裡,都力求簡單化,我給出一個反向傳播的小例項,不嚴謹但容易理解。
假設我們有如下一個模型結構:
其中mul為乘法,add為加法,w和x是第一層隱藏層引數,y和b是第二程隱藏層引數,z是結果。從上圖可以看出該模型的數學表示為:
如果z已知,我們可以:
- 求出\(\frac{\partial{z}}{\partial{y}}=1, \frac{\partial{z}}{\partial{b}}=1\)更新y和b的值
- 再通過求出\(\frac{\partial{z}}{\partial{w}}=\frac{\partial{z}}{\partial{y}}\frac{\partial{y}}{\partial{w}}=1*x,\frac{\partial{z}}{\partial{x}}=\frac{\partial{z}}{\partial{y}}\frac{\partial{z}}{\partial{x}}=1*w\)更新w和x的值。
這就是所謂的反向傳播,只不過實際比這更復雜。
六、總結
這篇文章通過手寫數字問題的引入,算是對深度網路模型做了一個簡單的介紹。
接下來也將通過torch解決手寫數字問題,兩者映照,將會讓你更加了解torch實現一個深度模型解決實際問題的流程。