目錄node
這個教程源於谷歌開發者博客的 Codelabs 項目python
爲但願學習機器學習的軟件開發人員提供六集的速成課程,包括示例,理論概念,工程技巧,技巧和最佳實踐,以構建和培訓解決您問題的神經網絡。git
爲軟件工程師構建神經網絡的基礎知識。 神經權重和誤差,激活函數,監督學習和梯度降低。
有效培訓的技巧和最佳實踐:學習率衰減,Dropout、正規化和過分擬合的複雜性。 密集和卷積神經網絡。
此會話以低級Tensorflow開始,還包含使用圖層和數據集的高級Tensorflow代碼示例。 代碼示例:MNIST手寫數字識別,準確率達99%。 持續時間:55分鐘github
在 codelab 項目中,你將學習如何構建並訓練出可以識別手寫數字(recognises handwritten digits)的神經網絡。在這過程當中,當這個神經網絡的準確度提高至 99%時,你還將學習到專業人員用來訓練模型的高效工具。算法
這個 codelab 項目使用的是 MNIST 數據集,這個包含 60,000 個有標記數字的集合是幾屆博士努力近二十年的成果。你將會用不到 100 行的 Python/TensorFlow 代碼來解決上述問題。express
你將學到:安全
對此,你將須要:網絡
git clone https://github.com/GoogleCloudPlatform/tensorflow-without-a-phd.git cd tensorflow-without-a-phd/tensorflow-mnist-tutorial python3 mnist_1.0_softmax.py
而後能夠看到可視化的訓練結果
dom
MNIST數據集中的手寫數字是28x28像素灰度圖像。 對它們進行分類的最簡單方法是使用28x28 = 784像素做爲單層神經網絡的輸入。
機器學習
神經網絡中的每一個「神經元」對其全部輸入進行加權求和,添加一個稱爲「偏置」的常數,而後經過一些非線性激活函數提供結果。
在這裏,咱們設計了一個具備10個輸出神經元的單層神經網絡,由於咱們想要將數字分類爲10個類(0到9)。
對於分類問題,運行良好的激活函數是 softmax
。 在矢量上應用 softmax
是經過取每一個元素的指數而後歸一化矢量(使用任何範數,例如矢量的普通歐氏長度)來完成的。
如今,咱們將使用矩陣乘法將這一單層神經元的行爲歸納爲一個簡單的公式。 讓咱們直接進行100個圖像的「小批量(mini-batch)」做爲輸入,產生100個預測(10個元素向量)做爲輸出。
咱們最終應用softmax激活函數並得到描述單層神經網絡的公式,應用於100個圖像:
如今須要計算預測結果與實際結果之間的誤差,普通的歐幾里德距離很好
可是對於分類問題,「交叉熵(cross-entropy)」更有效
"One-hot" 編碼表示一個向量只有一個1,其他都爲0
「訓練」神經網絡實際上意味着使用訓練圖像和標籤來調整權重和誤差,以便最小化交叉熵損失函數。
交叉熵是權重,誤差,訓練圖像的像素及其已知標籤的函數。
若是咱們相對於全部權重和全部誤差計算交叉熵的偏導數,咱們得到「梯度」,針對給定圖像,標籤和權重和誤差的現值計算。 請記住,咱們有7850個權重和誤差,因此計算梯度聽起來要作不少工做。 幸運的是,TensorFlow將爲咱們作到這一點。
梯度的數學特性是它指向「向上」。 因爲咱們想要去交叉熵低的地方,咱們走向相反的方向。 咱們經過梯度的一小部分更新權重和誤差,並使用下一批訓練圖像再次執行相同的操做。 但願這能讓咱們到達交叉熵最小的坑的底部。
在該圖中,交叉熵表示爲2個權重的函數。 實際上,還有更多。 梯度降低算法遵循最速降低到局部最小值的路徑。 訓練圖像也在每次迭代時改變,以便咱們收斂到適用於全部圖像的局部最小值。
「學習率」:您沒法在每次迭代時按梯度的整個長度更新權重和誤差。 要到達底部,您須要執行較小的步驟,即僅使用漸變的一小部分,一般在1/1000區域中。 咱們稱這個分數爲「學習率」。
訓練數字和標籤=>損失函數=>漸變(偏導數)=>最速降低=>更新權重和誤差=>重複下一批訓練圖像和標籤
爲何要使用100個圖像和標籤的「迷你批次」?
您只能在一個示例圖像上計算梯度並當即更新權重和誤差(在科學文獻中稱爲「隨機梯度降低」)。 在100個示例中這樣作會給出一個梯度,該梯度更好地表示不一樣示例圖像所施加的約束,所以可能更快地收斂到解決方案。 儘管如此,小批量的大小是可調整的參數。 還有一個更技術性的緣由:使用批處理也意味着使用更大的矩陣,這些一般更容易在GPU上進行優化。
最終能夠實現的準確率爲 92%
爲了提升識別精度,咱們將爲神經網絡添加更多層。這是一個5層徹底鏈接的神經網絡:
咱們將 softmax
保持爲最後一層的激活函數,由於這最適合分類。 然而,在中間層咱們將使用最經典的激活函數: sigmoid
最終能夠實現的準確率爲 97%
Sigmoid激活函數在深度網絡中其實是很成問題的。 它會壓縮0到1之間的全部值,當你反覆這樣作時,神經元輸出和它們的梯度能夠徹底消失。
RELU以下所示:
採用RuLU以後能夠解決 「梯度爆炸」 問題,並且能夠加快訓練速度
在像這裏這樣的很是高維度的空間中 - 咱們有大約10K的重量和誤差 - 「馬鞍點」是常見的。 這些點不是局部最小值,可是梯度爲零而且梯度降低優化器仍然停留在那裏。 TensorFlow擁有一整套可用的優化器,包括一些可使用慣性而且能夠安全駛過鞍點的優化器。
準確度仍然保持在0.1? 你是否用隨機值初始化了權重? 對於誤差,當使用RELU時,最佳作法是將它們初始化爲小的正值,以便神經元最初在RELU的非零範圍內運行。
若是將迭代次數推至5000或更高,則使用兩個,三個或四個中間層,如今能夠得到接近98%的準確度。 可是你會發現結果不是很一致。
這些曲線很是嘈雜,看看測試的準確性:它的上下跳動百分之一。 這意味着即便學習率爲0.003,咱們也會走得太快。 但咱們不能只將學習率除以10,不然培訓須要永遠。 好的解決方案是快速啓動並將學習速率指數衰減到0.0001。
這一小變化的影響是巨大的。 您能夠看到大部分噪音消失,測試精度如今持續超過98%。
最終能夠實現的準確率爲 98%
您將注意到,測試和訓練數據的交叉熵曲線在幾千次迭代後開始斷開鏈接。 學習算法僅用於訓練數據,並相應地優化訓練交叉熵。 它永遠不會看到測試數據,因此一段時間後它的工做再也不對測試交叉熵產生影響就不足爲奇了,它會中止降低,有時甚至會反彈。
這不會當即影響模型的實際識別功能,但它會阻止您運行屢次迭代,而且一般代表訓練再也不具備正面效果。 這種斷開一般標記爲「overfitting(過分擬合)」,當您看到它時,您能夠嘗試應用稱爲「dropout(丟失)」的 正則化技術
。
在每次訓練迭代中,您從網絡中丟棄隨機神經元。 您選擇保留神經元的機率保持,一般在50%和75%之間,而後在訓練循環的每次迭代中,您隨機移除具備全部重量和誤差的神經元。 每次迭代都會丟棄不一樣的神經元(你還須要按比例增長剩餘神經元的輸出,以確保下一層的激活不會發生變化)。 當你測試網絡性能時,你會把全部神經元都放回去(pkeep = 1)。
你應該看到測試損失在很大程度上獲得了控制,噪聲再次出現,但至少在這種狀況下,測試精度保持不變,這有點使人失望。
不管咱們作什麼,咱們彷佛都沒法以顯着的方式突破98%的障礙,咱們的損失曲線仍然表現出「過分擬合」的脫節。 什麼是「過分擬合」? 當神經網絡以一種適用於訓練示例但在實際數據上不太好的方式學習「嚴重」時,會發生過分擬合。 有一些正則化技術,如 Dropout,能夠迫使它以更好的方式學習,但過分擬合也有更深層次的根源。
當神經網絡對於手頭的問題具備太多的自由度時,會發生基本過分擬合。想象一下,咱們有這麼多神經元,網絡能夠將全部訓練圖像存儲在其中,而後經過模式匹配識別它們。神經網絡必須受到必定限制,以便強制推廣它在訓練期間學到的東西。
若是您的訓練數據不多,即便是小型網絡也能夠用心去學習。通常來講,您老是須要大量數據來訓練神經網絡。
最後,若是你已經作好了一切,嘗試了不一樣大小的網絡,以確保其自由度受到限制,應用Dropout,並對大量數據進行訓練,你可能仍然會陷入性能平臺,彷佛沒有任何東西可以提升。這意味着您的神經網絡目前的形狀沒法從您的數據中提取更多信息,如咱們的案例所示。
還記得咱們如何使用咱們的圖像,將全部像素平鋪成單個矢量?這是一個很是糟糕的主意。手寫數字由形狀組成,當咱們平整像素時咱們丟棄了形狀信息。然而,有一種神經網絡能夠利用形狀信息:卷積網絡。讓咱們試試吧。
通過批量標準化後最終能夠實現的準確率爲 98.2%
在卷積網絡的層中,一個「神經元」僅在圖像的小區域上對其正上方的像素進行加權和。 而後經過添加誤差並經過其激活功能饋送結果來正常動做。 最大的區別在於每一個神經元重複使用相同的權重,而在以前看到的徹底鏈接的網絡中,每一個神經元都有本身的一組權重。
在上面的動畫中,您能夠看到經過在兩個方向(卷積)上滑動圖像的權重塊,您能夠得到與圖像中的像素同樣多的輸出值(儘管邊緣處須要一些填充)。
要使用4x4的色塊大小和彩色圖像做爲輸入生成一個輸出值平面,就像在動畫中同樣,咱們須要4x4x3 = 48個權重。 這還不夠。 爲了增長更多的自由度,咱們用不一樣的權重集重複相同的事情。
經過向張量添加維度,能夠將兩組(或更多組)權重重寫爲一組,這給出了卷積層的權重張量的通用形狀。 因爲輸入和輸出通道的數量是參數,咱們能夠開始堆疊和連接卷積層。
在最後一層,咱們仍然只須要10個神經元來處理10類數字。 傳統上,這是經過「最大池」層完成的。 即便今天有更簡單的方法,「max-pooling」也有助於直觀地理解卷積網絡的運做方式:若是你認爲在訓練過程當中,咱們的小塊權重會演變爲識別基本形狀的濾波器(水平和垂直線條,曲線,...... 。)而後向下挖出有用信息的一種方法是經過層保持最大強度識別形狀的輸出。 在實踐中,在max-pool層中,神經元輸出以2x2的組進行處理,而且僅保留一個最大的輸出。
但有一種更簡單的方法:若是您以2像素而不是1的步幅在圖像上滑動補丁,則也會得到更少的輸出值。 事實證實這種方法一樣有效,而當今的卷積網絡只使用卷積層。
讓咱們創建一個手寫數字識別的卷積網絡。 咱們將在頂部使用三個卷積層,在底部使用傳統的softmax讀出層,並將它們與一個徹底鏈接的層鏈接:
請注意,第二個和第三個卷積層的步長爲2,這就解釋了爲何它們將輸出值的數量從28x28下降到14x14而後下降到7x7。 完成各層的大小調整,使每層神經元的數量大體減小兩倍:28x28x4≈3000→14x14x8≈1500→7x7x12≈500→200。
最終能夠實現的準確率爲 98.9%
調整神經網絡大小的一個好方法是實現一個有點過於受限的網絡,而後給它更多的自由度並添加Droupout 以確保它不會過分擬合。 這最終會提供一個至關優化的網絡。
所以,讓咱們稍微提升patches大小,將卷積層中的patches數量從4,8,12增長到6,12,24,而後在徹底鏈接的層上添加 Droupout。 爲何不在卷積層? 他們的神經元重複使用相同的權重,所以在一次訓練迭代期間經過凍結一些權重而有效地工做的丟失對它們不起做用。
最終能夠實現的準確率爲 99.3%
上圖所示的模型在10,000個測試數字中僅丟失了72個。 您能夠在MNIST網站上找到的世界紀錄大約爲99.7%。 咱們的模型使用100行Python / TensorFlow構建,距離它只有0.4個百分點。
總而言之,這是咱們更大的卷積網絡的差別。 爲神經網絡提供額外的自由度,使最終的準確率從98.9%提升到99.1%。 增長 Dropout 不只下降了測試損失,並且讓咱們的準確率超過99%,甚至達到99.3%
輸入數據的均值和方差與中心零點誤差較大,須要對數據作批量標準化
隨着網絡的訓練,網絡會學到最合適的 α 和 β ,最終中間層的輸出將服從均值爲 β ,標準差爲 α 的正態分佈。
ϵ 的解釋:爲了不方差爲 0 而加入的微小正數(eg:0.001)。
如今幾乎全部的卷積神經網絡都會使用批量標準化操做,它能夠爲咱們的網絡訓練帶來一系列的好處。具體以下:
通過批量標準化後最終能夠實現的準確率爲 99.5%
改進方法 | 精度 | 過擬合程度 | 改進緣由 |
---|---|---|---|
單全鏈接+Softmax | 92% | 弱 | |
5層全鏈接 | 97% | 強 | 提高精度 |
ReLU激活函數、隨機初始化、下降學習率 | 98% | 強 | 加快收斂速度且提高 |
加入Drouptout層 | 98.2% | 弱 | 下降過擬合 |
三層卷積層[5,4,4](4,8,12) | 98.9% | 強 | 新結構 |
三層卷積層[6,5,4](6,12,24)+ Drouptout層 | 99.3% | 弱 | 提高精度 |
批量標準化 | 99.5% | 弱 | 下降過擬合 |