前言
上月導師在組會上交我們用tensorflow寫深度學習和卷積神經網絡,並把其PPT的參考學習資料給了我們, 這是codelabs上的教程:《TensorFlow and deep learning,without a PhD》
當然登入需要翻牆,我也順帶鞏固下,做個翻譯,不好之處請包含指正。
當然需要安裝python,教程推薦使用python3。如果是Mac,可以參考博主的另外兩片博文,Mac下升級python2.7到python3.6,
Mac安裝tensorflow1.0
好多專業詞太難譯了,查了下,大家有些都是不譯的。
比如:dropout,learning rate decay,pkeep什麼的。。。。
dropout這個詞應該翻譯成什麼?
1. 概述
在這個codelab中,您將學習如何創建和訓練識別手寫數字的神經網絡。一路上,隨着你增強神經網絡的準確率達到99%,你還將學習到專業人員用來訓練模型的高效工具。
該codelab使用MNIST數據集,收集了60,000個標記的數字。你將學會用不到100行Python / TensorFlow代碼來解決深度學習問題。
你會學到什麼
- 什麼是神經網絡和如何訓練它
- 如何使用TensorFlow構建基本的1層神經網絡
- 如何添加更多的神經網絡層數
- 訓練技巧和竅門:過度擬合(overfitting),丟失信息(dropout),學習速率衰退(learning rate decay)…
- 如何排查深層神經網絡的故障
- 如何構建卷積神經網絡(convolutional networks)
你需要什麼
- Python 2或3(推薦Python 3)
- TensorFlow
- Matplotlib(Python可視化庫)
安裝說明在實驗室的下一步中給出。
2. 準備:安裝TensorFlow,獲取示例代碼
在您的計算機上安裝必要的軟件:Python,TensorFlow和Matplotlib。這裏給出了完整的安裝說明:INSTALL.txt
從GitHub的信息庫,克隆源代碼(也可以直接登入這個網址,直接下載)
git clone https://github.com/martin-gorner/tensorflow-mnist-tutorial
下載的文件夾中含多個文件。首先是讓mnist_1.0_softmax.py運行起來。其他很多文件是用於加載數據和可視化結果的解決方案或支持代碼。
當您啓動初始python腳本時,您應該看到一個實時可視化的培訓過程:
python3 mnist_1.0_softmax.py
如果python3 mnist_1.0_softmax.py不起作用,用python命令:
python mnist_1.0_softmax.py
疑難解答:如果無法使實時可視化運行,或者您只希望僅使用文本輸出,則可以通過註釋掉一行並取消註釋另一行來取消激活可視化。請參閱下載文件的底部的說明。
爲TensorFlow構建的可視化工具是TensorBoard。其功能比我們本次教程中所需要的更多。它可以在遠程服務器上跟蹤您的分佈式TensorFlow作業。對於我們在這個實驗中我們只需要matplotlib的結果,能看到訓練過程的實時動畫,就當是個附帶的獎勵吧。但是,如果您需要使用TensorFlow進行跟蹤工作,請確保查看TensorBoard。
3. 理論:訓練神經網絡
我們將首先觀察正在接受訓練的神經網絡。代碼將在下一節中進行說明,因此您先不需要看。
我們的用神經網絡訓練手寫數字,並對它們進行分類,即將手寫數字識別爲0,1,2等等,最多爲9。它的模型基於內部變量(“權重”(weights)和“偏差”(biases),這兩個詞稍後解釋),只有將這些變量訓練成正確值,分類工作才能正確進行,訓練方式稍後也會詳細解釋。現在您需要知道的是,訓練循環如下所示:
訓練數據 => 更新權重和偏差 => 更好的識別 (循環這三步)
讓我們逐個瀏覽可視化的六個面板,看看訓練神經網絡需要什麼。
在這裏,您可以看到100個訓練數字被送入訓練循環,注意是一次100個數字,這圖顯示的是這100個手寫數據被訓練的結果。在目前的訓練狀態下,神經網絡已經能識別(包括白色背景和部分數字),當然也有些是識別錯誤的(圖中紅色背景的是計算機識別錯誤的手寫數字,左側小打印的數字是該書寫字的正確標籤,右側小打印的數字是計算機標識別的錯誤標籤)。
該數據集中有50,000個訓練數字。我們在每次迭代中將其中每100個進行訓練,因此係統將在500次迭代後看到所有數字被訓練了一次。我們稱之爲“紀元(epoch)”。
爲了測試訓練好後模型的識別質量,我們必須使用系統在訓練期間沒有用到過的手寫數字。否則,模型可能會識別所有已訓練的數字,但仍然不能識別我剛纔新寫的數字“8”。MNIST數據集中包含10,000個測試手寫數字。在這圖裏,您可以看到大約1000個數字,其中所有被識別錯誤的,都放在頂部(紅色背景上)。圖左側的比例可以大致表示分類器的準確性。
爲了開展訓練,我們將定義一個損失函數,即代表系統識別數字的程度值,並嘗試將其最小化。損失函數的選擇(這裏是“交叉熵(cross-entropy)”)將在後面解釋。您在這裏看到的是,隨着訓練的進展,訓練和測試數據的損失都會下降:這是好的。這意味着神經網絡正在學習。X軸表示通過學習循環的迭代次數。
準確性只是正確識別的數字的百分比。這是在訓練和測試集上計算的。如果訓練順利,你會看到它上升。
最後兩個圖代表了內部變量採用的所有值的範圍,即隨着訓練的進行,權重和偏差。在這裏,您可以看到,偏差最初從0開始,最終獲得的值大致均勻分佈在-1.5和1.5之間。如果系統不能很好地收斂,這些圖可能很有用。如果你看到權重和偏差擴展到100或1000,訓練可能就有問題了。
圖中的方格代表是百分位數。有7個頻帶,所以每個頻帶是100/7 =所有值的14%。
Keyboard shortcuts for the visualisation GUI:
1 ……… display 1st graph only
2 ……… display 2nd graph only
3 ……… display 3rd graph only
4 ……… display 4th graph only
5 ……… display 5th graph only
6 ……… display 6th graph only
7 ……… display graphs 1 and 2
8 ……… display graphs 4 and 5
9 ……… display graphs 3 and 6
ESC or 0 .. back to displaying all graphs
SPACE ….. pause/resume
O ……… box zoom mode (then use mouse)
H ……… reset all zooms
Ctrl-S …. save current image
什麼是“ 權重 ”和“ 偏差 ”?如何計算“ 交叉熵 ”?訓練算法究竟如何工作?那麼來看下一節內容吧。
4. 理論:1層神經網絡
MNIST數據集中的手寫數字是28x28像素的灰度圖像。對於它們進行分類的最簡單方法是使用28x28 = 784像素作爲第1層神經網絡的輸入。
神經網絡中的每個“神經元”都會對其所有輸入進行加權和,增加一個稱爲“偏差”的常量,然後通過一些非線性激活函數來提取結果。
在這裏,我們設計了一個具有10個神經元的1層神經網絡,作爲輸出層,因爲我們想將數字分爲10個類(0到9),每個神經元都能分類處一個類。
對於一個分類問題,一個很好的激活函數是softmax。通過取每個元素的指數,然後歸一化向量(使用任何範數,例如向量的普通歐幾里德長度)來對向量應用softmax。
爲什麼“softmax”稱爲softmax?指數是急劇增長的函數。它將增加向量元素之間的差異。它也快速產生大的值。然後,當您規範化向量時,支配規範的最大元素將被歸一化爲接近1的值,而所有其他元素將最終除以一個較大的值,並歸一化爲接近零的值。清楚地顯示出哪個是最大的元素,即“最大值”,但保留其價值的原始相對順序,因此是“soft”。
我們現在將使用矩陣乘法將這個單層神經元的處理過程,用一個簡單的公式表示。讓我們直接用100張手寫圖片作爲輸入(如圖中黑灰方塊圖所示,每行表示一張圖片的784個像素值),產生100個預測(10個向量)作爲輸出。
使用加權矩陣W中的第一列加權,我們計算第一張圖像的所有像素的加權和。這個和值對應於第一個神經元。使用第二列權重,我們對第二個神經元做同樣的事情,直到第10個神經元。然後,我們可以重複對剩餘99張圖像的操作。如果我們稱X爲包含我們100個圖像的矩陣,則在100個圖像上計算的我們10個神經元的所有加權和僅僅是XW(矩陣乘法)。
每個神經元現在必須加上它的偏差(一個常數)。由於我們有10個神經元,我們有10個偏置常數。我們將這個10個值的向量稱爲b。必須將其添加到先前計算的矩陣的每一行。使用一些名爲“廣播(broadcasting)”的方法,我們用簡單的加號寫下來。
“ 廣播(broadcasting) ”是Python和numpy的標準技巧,它是科學計算庫裏的內容。它擴展了正常操作對具有不兼容尺寸的矩陣的作用範圍。“廣播添加”是指“如果要相加兩個矩陣,但是由於其尺寸不兼容,請嘗試根據需要複製小尺寸以使其能相加。”
我們最後應用softmax激活函數,得到描述1層神經網絡的公式,應用於100幅圖像:
順便說一下,什麼是“ 張量(tensor) ”?
“張量(tensor)”就像一個矩陣,但是具有任意數量的維度。一維張量是向量。二維張量是矩陣。然後,您可以有3,4,5或更多維度的張量。
5. 理論:梯度下降
現在我們的神經網絡產生了輸入圖像的預測,我們需要測量它們的好壞,即網絡告訴我們與我們所知道的真相之間的距離。請記住,我們爲此數據集中的所有圖像的數字都有正確數字的標籤。
任何距離都會有效,普通的歐幾里得距離很好,但是對於分類問題,一個距離,稱爲“交叉熵(cross-entropy)”更有效率。
“ 一熱(One-hot) ”編碼意味着您使用10個值的矢量代表標籤“6”,全部爲零,但第6個值爲1.這是因爲格式非常類似於我們的神經網絡輸出預測,也作爲10個值的向量。
“訓練”神經網絡實際上意味着使用訓練圖像和標籤來調整權重和偏差,以便最小化交叉熵損失函數。下面是它的工作原理。
交叉熵是訓練圖像的權重,偏差,像素及其已知標籤的函數。
如果我們相對於所有權重和所有偏差計算交叉熵的偏導數,我們獲得了對於給定圖像,權重和偏差的標籤和現值計算的“梯度(gradient)”。記住,我們有7850個權重和偏差,所以計算梯度聽起來好像有很多工作。幸運的是,TensorFlow將爲我們做好準備。
梯度的數學屬性是它指向“上”。由於我們想要走交叉熵低的地方,所以我們走向相反的方向。我們將權重和偏差更新一小部分梯度,並使用下一批訓練圖像再次執行相同的操作。希望這讓我們到達交叉熵最小的坑底。
在該圖中,交叉熵表示爲2個權重的函數。實際上還有更多的。梯度下降算法遵循最快速下降到局部最小值的路徑。訓練圖像也會在每次迭代中更改,以便我們收斂到適用於所有圖像的局部最小值。
“ 學習率”:您無法在每次迭代時以漸變的整個長度更新您的權重和偏差。這就好比是一個穿着靴子的人,想去一個山谷的底部。他會從山谷的一邊跳到另一邊。要進入底部,他需要執行較小的步驟,即僅使用漸變的一小部分,通常在1/1000。我們將這個分數稱爲“學習率”。
總而言之,訓練循環如下所示:
訓練數據和標籤 => 求損失函數=> 求梯度 (偏導數) => 最快下降 => 更新權重和偏差 => 重複下一個小批量的圖像數據和標籤
爲什麼要使用100個圖像和標籤,用這種“ 小批量 ”形式進行?
您只需一個示例圖像即可計算您的漸變,並立即更新權重和偏差(在文獻中稱爲“隨機梯度下降”)。這樣做100個例子給出了更好地表示不同示例圖像所施加的約束的漸變,因此可能更快地收斂到解決方案。小批量的尺寸是可調參數。還有另一個更技術的原因:使用大批量也意味着使用更大的矩陣,這些通常更容易在GPU上進行優化。
經常問的問題
6. 實驗室:讓我們跳入代碼
已經寫了1層神經網絡的代碼。請打開mnist_1.0_softmax.py文件並按照說明進行操作。
您在本節中的任務是瞭解此起始代碼,以便以後可以改進。
您應該看到文件中的說明和代碼之間只有微小的區別。它們對應於用於可視化的功能,並在註釋中做了說明。你可以忽略它們。
import tensorflow as tf
X = tf.placeholder(tf.float32, [None, 28, 28, 1])
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
init = tf.initialize_all_variables()
首先我們定義TensorFlow變量和佔位符。變量是您希望訓練算法爲您確定的所有參數。在我們的情況下,我們的權重和偏見。
佔位符是在訓練期間填充實際數據的參數,通常是訓練圖像。保持訓練圖像的張量的形狀是[None,28,28,1],代表:
- 28,28,1:我們的圖像是每像素28x28像素x 1值(灰度)。彩色圖像的最後一個數字將爲3,這裏並不需要。
- None:此維度將是迷你批次中的圖像數量。這將在訓練時知道。
mnist_1.0_softmax.py
# model
Y = tf.nn.softmax(tf.matmul(tf.reshape(X, [-1, 784]), W) + b)
# placeholder for correct labels
Y_ = tf.placeholder(tf.float32, [None, 10])
# loss function
cross_entropy = -tf.reduce_sum(Y_ * tf.log(Y))
# % of correct answers found in batch
is_correct = tf.equal(tf.argmax(Y,1), tf.argmax(Y_,1))
accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))
第一行是我們的1層神經網絡的模型。公式是我們在以前的理論部分中建立的公式。該tf.reshape命令將我們的28x28圖像轉換爲784像素的單個向量。重塑命令中的“-1”表示“計算機,計算出來,只有一種可能性”。實際上,這將是一個小批量的圖像數量。
然後,我們需要一個附加的佔位符,用於提供與培訓圖像一起的標籤數據。
現在,我們有模型預測和正確的標籤,所以我們可以計算交叉熵。tf.reduce_sum總和一個向量的所有元素。
最後兩行計算正確識別的數字的百分比。留給讀者使用TensorFlow API參考書,以供讀者理解。你也可以跳過它們。
mnist_1.0_softmax.py
optimizer = tf.train.GradientDescentOptimizer(0.003)
train_step = optimizer.minimize(cross_entropy)
這裏將是TensorFlow展示它能力的時候了。您選擇一個優化器(有很多可用),並要求它最小化交叉熵損失。在此步驟中,TensorFlow計算相對於所有權重和所有偏差(梯度)的損失函數的偏導數。這是一個正式的推導,而不是一個數字化的,太費時間了。
然後使用梯度來更新權重和偏差。0.003是學習率。
最後,現在是運行訓練循環的時候了。到目前爲止,所有的TensorFlow指令都已經在內存中準備了一個計算圖,但還沒有計算出來。
TensorFlow的“延遲執行”模型:TensorFlow是爲分佈式計算構建的。在開始實際將計算任務發送到各種計算機之前,必須知道要計算的內容,即執行圖。這就是爲什麼它有一個延遲執行模型,您首先使用TensorFlow函數在內存中創建計算圖,然後開始Session執行並使用實際的計算Session.run。在這一點上,計算圖不能再改變了。
由於該模式,TensorFlow可以接管大量的分佈式計算流。例如,如果您指示在計算機1上運行一部分計算,並在計算機2上運行另一部分,則可以自動進行必要的數據傳輸。
計算需要將實際數據輸入到您在TensorFlow代碼中定義的佔位符。這是以Python字典的形式提供的,其中的鍵值是佔位符的名稱。
mnist_1.0_softmax.py
sess = tf.Session()
sess.run(init)
for i in range(1000):
# load batch of images and correct answers
batch_X, batch_Y = mnist.train.next_batch(100)
train_data={X: batch_X, Y_: batch_Y}
# train
sess.run(train_step, feed_dict=train_data)
在train_step當我們問到TensorFlow出最小交叉熵是這裏執行獲得。那就是計算梯度並更新權重和偏差的步驟。
最後,我們還需要計算一些可以顯示的值,以便我們可以跟蹤我們模型的性能。
在訓練循環中使用該代碼訓練數據計算精度和交叉熵(例如每10次迭代):
# success ?
a,c = sess.run([accuracy, cross_entropy], feed_dict=train_data)
通過提供測試訓練數據,可以在測試數據上計算相同的數值(例如,每100次重複一次,有10,000個測試數字,因此需要一些CPU時間):
# success on test data ?
test_data={X: mnist.test.images, Y_: mnist.test.labels}
a,c = sess.run([accuracy, cross_entropy], feed=test_data)
TensorFlow和NumPy的是朋友:準備計算圖時,你只有操縱TensorFlow張量和如命令tf.matmul,tf.reshape等等。
然而,一旦執行Session.run命令,它返回的值就是Numpy張量,即Numpy numpy.ndarray可以使用的對象以及基於它的所有科學comptation庫。這就是使用matplotlib(這是基於Numpy的標準Python繪圖庫)爲這個實驗室建立的實時可視化。
7. 實驗室:添加圖層
爲了提高識別精度,我們將爲神經網絡添加更多層數。第二層中的神經元,而不是計算像素的加權和,將計算來自上一層的神經元輸出的加權和。這裏是一個5層完全連接的神經網絡:
我們保持softmax作爲最後一層的激活功能,因爲這是最適合分類的。在中間層上,我們將使用最經典的激活函數:sigmoid:
您在本節中的任務是將一個或兩個中間層添加到您的模型中以提高其性能。
解決方案可以在文件中找到mnist_2.0_five_layers_sigmoid.py。使用它,如果你不能寫出來,被卡住了!
要添加圖層,您需要一個額外的權重矩陣和中間層的附加偏置向量:
W1 = tf.Variable(tf.truncated_normal([28*28, 200] ,stddev=0.1))
B1 = tf.Variable(tf.zeros([200]))
W2 = tf.Variable(tf.truncated_normal([200, 10], stddev=0.1))
B2 = tf.Variable(tf.zeros([10]))
權重矩陣的形狀是[N,M],其中N是層的輸入數量和M的輸出。在上面的代碼中,我們在中間層中使用了200個神經元,在最後一層使用了10個神經元。
提示:當你深入時,重要的是用隨機值初始化權重。如果沒有,優化器可能會停留在初始位置。tf.truncated_normal是一個TensorFlow函數,它產生遵循-2* stddev和+ 2 * stddev之間的正態(高斯)分佈的隨機值。
現在將1層模型更改爲2層模型:
XX = tf.reshape(X, [-1, 28*28])
Y1 = tf.nn.sigmoid(tf.matmul(XX, W1) + B1)
Y = tf.nn.softmax(tf.matmul(Y1, W2) + B2)
您現在應該可以使用2箇中間層(例如200和100個神經元)將精度推送到97%以上的精度。
8. 實驗室:深度網絡的特別照顧
隨着層次的增加,神經網絡趨向於收斂更多困難。但我們今天知道如何使他們的工作。如下圖,如果您看到這樣的精度曲線,本節將對您有所幫助:
Relu激活功能
在深層網絡中,S形激活函數(sigmoid函數)實際上是相當有問題的。它壓縮0和1之間的所有值,當您反覆進行時,神經元輸出及其漸變可以完全消失。改進的方法,可以使用如下所示的RELU函數(整流線性單元):
更新1/4:現在用RELU替換所有的S型,並且在加入圖層時,您將獲得更快的初始收斂,避免出現問題。只需在你的代碼中簡單更換tf.nn.sigmoid用tf.nn.relu。
一個更好的優化器
在這樣的非常高的維度空間中,我們有10K的權重和偏差 - “鞍點”是頻繁的。這些是不是局部最小值的點,但梯度仍然爲零,梯度下降優化器仍然停留在那裏。TensorFlow擁有一系列可用的優化器,其中包括一些可以使用一定慣量的優化器,並可以安全避開鞍點。
更新2/4:替換tf.train.GradientDescentOptimiser爲tf.train.AdamOptimizer現在。
隨機初始化
精確度仍然在0.1?你用隨機值初始化了你的權重嗎?對於偏差,當使用RELU時,最佳做法是將其初始化爲小的正值,以使神經元最初在RELU的非零範圍內運行。
W = tf.Variable(tf.truncated_normal([K, L] ,stddev=0.1))
B = tf.Variable(tf.ones([L])/10)
更新3/4:現在檢查所有的權重和偏差是否適當初始化。如上圖所示的0.1將作爲初始偏差。
NaN ???
如果您看到準確度曲線崩潰,並且控制檯輸出NaN作爲交叉熵,請不要驚慌,您正在嘗試計算一個不是數(NaN)的值(0)。請記住,交叉熵涉及在softmax層的輸出上計算的日誌。由於softmax本質上是一個指數,從不爲零,所以我們應該很好,但使用32位精度浮點運算,exp(-100)已經是一個真正的零。說白了就是,小數點後0太多,超出計算機精度,計算機將其判斷爲0,並作了分母,然後就出現這種現象。
幸運的是,TensorFlow具有一個方便的功能,可以在數字穩定的方式下實現單步驟中的softmax和交叉熵。要使用它,您需要在應用softmax之前,將最後一層的原始加權和加上偏差取對數(logits)。
如果您的模型的最後一行是:
Y = tf.nn.softmax(tf.matmul(Y4, W5) + B5)
您需要更換它:
Ylogits = tf.matmul(Y4, W5) + B5
Y = tf.nn.softmax(Ylogits)
現在,您可以以安全的方式計算交叉熵:
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(Ylogits, Y_)
還添加這條線,使測試和訓練交叉熵達到相同的顯示尺度:
cross_entropy = tf.reduce_mean(cross_entropy)*100
更新4/4:請添加tf.nn.softmax_cross_entropy_with_logits到您的代碼。您也可以跳過此步驟,當您在輸出中實際看到NaN時,可以回到該步驟。
你現在準備好深入
9. 實驗:學習率衰減
使用兩個,三個或四個中間層,如果將迭代推送到5000或更高,您現在可以獲得接近98%的準確性。但是你會看到結果不是很一致。
這些曲線真的很嘈雜,看看測試的準確性:它全部上下跳躍。這意味着即使學習率爲0.003,我們也走得太快了。但是,我們不能將學習率除以十,否則訓練將永遠存在。良好的解決方案是開始快速,並將學習速率以指數方式衰減爲0.0001。
這一點變化的影響是壯觀的。您可以看到大部分噪音已經消失,測試精度現在高達98%以上
還要看訓練精度曲線。現在已經達到了幾個紀元的100%(1個紀元= 500次迭代=訓練了所有的訓練圖像一次)。第一次,我們能夠學習完美地識別訓練圖像。
請添加學習率衰減到你的代碼。爲了在每次迭代時將不同的學習率傳遞給AdamOptimizer,您將需要定義一個新的佔位符,並在每次迭代時向它提供一個新的值feed_dict。
以下是指數衰減的公式: lr = lrmin+(lrmax-lrmin)*exp(-i/2000)
解決方案可以在文件中找到mnist_2.1_five_layers_relu_lrdecay.py。
10. 實驗室:丟失信息,過度配合
您將注意到,測試和訓練數據的交叉熵曲線在數千次迭代後開始斷開連接。學習算法僅用於訓練數據,並相應地優化訓練交叉熵。它從來沒有看到測試數據,所以毫不奇怪,一段時間後,它的工作不再對測試交叉熵產生影響,測試交叉熵停止下降,有時甚至反彈。
這不會立即影響您的模型的真實識別能力,但它將阻止您運行許多迭代,並且通常是訓練不再具有積極作用的跡象。這個斷開連接通常被標記爲“過度擬合”,當您看到它時,您可以嘗試應用稱爲“丟失信息”的正則化技術。
在丟失數據期間,在每次訓練迭代中,您從網絡中丟棄隨機神經元。您選擇pkeep保留神經元的概率,通常在50%至75%之間,然後在訓練循環的每次迭代中,隨機移除所有權重和偏差的神經元。不同的神經元將在每次迭代中被丟棄(並且您還需要按比例提升剩餘神經元的輸出,以確保下一層的激活不會移動)。當您測試網絡的性能時,您將所有神經元都放回(pkeep=1)。
TensorFlow提供了一個用於神經元層輸出的壓差函數。它隨機排除一些輸出,並將其餘的輸出提高1 / pkeep。以下是您如何在兩層網絡中使用它:
# feed in 1 when testing, 0.75 when training
pkeep = tf.placeholder(tf.float32)
Y1 = tf.nn.relu(tf.matmul(X, W1) + B1)
Y1d = tf.nn.dropout(Y1, pkeep)
Y = tf.nn.softmax(tf.matmul(Y1d, W2) + B2)
您可以在網絡中的每個中間層之後添加丟失數據(dropout)。這是實驗室的可選步驟。
解決方案可以在文件中找到mnist_2.2_five_layers_relu_lrdecay_dropout.py。
您應該看到,測試損失在很大程度上被控制,噪音重新出現,但在這種情況下,至少測試精度保持不變,這是有點令人失望。這裏出現“過度配合”的另一個原因。
在我們繼續之前,總結一下我們迄今爲止所嘗試的所有工具:
無論我們做什麼,我們似乎無法以顯著的方式打破98%的障礙,我們的損失曲線仍然表現出“過擬合”的問題。什麼是真正的“過擬合”?當一個神經網絡學習“不好”時,過擬合就會發生,這種方式對於訓練樣例起作用,但對於現實世界的數據卻不太好。有正規化技術,如丟失數據(dropout),可以強制它以更好的方式學習,但過擬合也有更深的根源。
當神經網絡對於手頭的問題具有太多的自由度時,會發生基本的過擬合。想象一下,我們有這麼多神經元,網絡可以存儲我們所有的訓練圖像,然後通過模式匹配識別它們。它將完全失真在真實世界的數據。一個神經網絡必須有一定的約束。
如果你有很少的培訓數據,即使一個小的網絡也可以完成學習。一般來說,你總是需要大量的數據來訓練神經網絡。
最後,如果你做的一切都很好,嘗試不同大小的網絡,以確保其自由度受到限制,應用丟失數據(dropout),並訓練大量的數據,你可能仍然被困在一個性能水平,似乎沒有什麼可以提高。這意味着您的神經網絡目前的形狀不能從您的數據中提取更多的信息,就像我們在這裏一樣。
記住我們如何使用手寫圖像,將所有像素平坦化爲單個向量?那是一個很糟糕的主意 手寫數字由形狀組成,當我們平鋪像素時,我們捨棄了形狀信息。然而,有一種類型的神經網絡可以利用形狀信息:卷積網絡。讓我們試試看吧。
11. 理論:卷積網絡
在卷積網絡的層中,一個“神經元”僅在圖像的小區域上進行恰好在其上方的像素的加權和。然後,通過添加偏置並通過其激活功能饋送結果來正常地起作用。最大的區別是每個神經元都會重複使用相同的權重,而在之前看到的完全連接的網絡中,每個神經元都有自己的權重集。
在上面的動畫中,您可以看到,通過在兩個方向(卷積)上滑過圖像的權重塊,您可以獲得與圖像中的像素一樣多的輸出值(儘管邊緣需要一些填充)。
要使用4x4的補丁大小和彩色圖像作爲輸入生成一個輸出值平面,如動畫中那樣,我們需要4x4x3 = 48的權重。這還不夠 爲了增加更多的自由度,我們用不同的權重重複相同的事情。
通過向張量添加維度,可以將兩個(或多個)權重組重寫爲一個,這給出了卷積層的權重張量的通用形狀。由於輸入和輸出通道的數量是參數,我們可以開始堆疊和鏈接卷積層。
最後一個問題仍然存在。我們仍然需要將信息調低。在最後一層,我們仍然只需要10個神經元來代替我們的10個數字。傳統上,這是通過“最大池”層完成的。即使今天有更簡單的方法,“最大池(max-pooling)”有助於直觀地瞭解卷積網絡的運行情況:如果您假設在訓練過程中,我們的小塊權重會演變成過濾器,可以識別基本形狀(水平和垂直線,曲線,……)然後一種將有用信息向下傳遞的方式是通過層數保持最大強度識別形狀的輸出。實際上,在最大池層中,神經元輸出以2x2爲一組進行處理,只保留最多一個。
有一種更簡單的方法:如果您以2像素而不是1像素的速度滑過圖像,則還會獲得較少的輸出值。這種方法已被證明是同樣有效的,而今天的卷積網絡僅使用卷積層。
讓我們建立一個手寫數字識別的卷積網絡。我們將在頂部使用三個卷積層,我們的傳統softmax讀出層在底部,並連接到一個完全連接的層:
請注意,第二和第三卷積層有兩個步長,這說明爲什麼它們將輸出值從28x28降低到14x14,然後是7x7。完成這些層的大小,使得神經元的數量在每一層大致下降2倍:28x28x4≈3000→14x14x8≈1500→7x7x12≈500→200.跳轉到下一節執行。
12. 實驗室:卷積網絡
要將我們的代碼切換到卷積模型,我們需要爲卷積層定義適當的權重張量,然後將卷積圖層添加到模型中。
我們已經看到卷積層需要以下形狀的權重張量。這是初始化的TensorFlow語法:
W = tf.Variable(tf.truncated_normal([4, 4, 3, 2], stddev=0.1))
B = tf.Variable(tf.ones([2])/10) # 2 is the number of output channels
可以tf.nn.conv2d使用使用提供的權重在兩個方向上執行輸入圖像的掃描的功能在TensorFlow中實現卷積層。這只是神經元的加權和部分。您仍然需要添加偏差並通過激活功能提供結果。
stride = 1 # output is still 28x28
Ycnv = tf.nn.conv2d(X, W, strides=[1, stride, stride, 1], padding='SAME')
Y = tf.nn.relu(Ycnv + B)
不要太多地關注跨步的複雜語法。查看文檔的完整詳細信息。在這裏工作的填充策略是從圖像的兩邊複製像素。所有數字都在統一的背景上,所以這只是擴展了背景,不應該添加任何不需要的形狀。
輪到你玩了,修改你的模型,把它變成一個卷積模型。您可以使用上圖中的值來對其進行調整。你可以保持你的學習速度衰減,但是現在請刪除丟失信息(dropout)。
解決方案可以在文件中找到mnist_3.0_convolutional.py。
您的模型應該能夠輕鬆地打破98%的屏障。看看測試交叉熵曲線。你是不是能馬上想到解決方案呢?
13. 實驗室:99%的挑戰
調整神經網絡的一個很好的方法是實現一個有點太限制的網絡,然後給它一個更多的自由度,並添加丟失信息(dropout),以確保它不是過擬合。這樣最終可以爲您的問題提供一個相當理想的神經網絡。
這裏例如,我們在第一個卷積層中只使用了4個像素。如果您接受這些權重補丁在訓練過程中演變成形狀識別器,您可以直觀地看到這可能不足以解決我們的問題。手寫數字是超過4個像素形狀的模式。
所以讓我們稍微增加像素大小,將卷積層中的補丁數量從4,8,12提高到6,12,24,然後在完全連接的層上添加dropout。爲什麼不在卷積層上?他們的神經元重複使用相同的權重,所以通過在一次訓練迭代紀元,凍結一些權重有效地起作用的dropout將不起作用。
去吧,打破99%的限制。增加像素大小和通道數,如上圖所示,並在卷積層上添加dropout。
解決方案可以在文件中找到mnist_3.1_convolutional_bigger_dropout.py
上圖所示的模型僅識別錯了10,000個測試數字中的72個。在MNIST網站上可以找到的世界紀錄約爲99.7%。我們距離我們的模型建立了100行Python / TensorFlow距離世界紀錄就差0.4個百分點。
要完成,這是對我們更大的卷積網絡的差異。給神經網絡增加自由度,將最終準確度從98.9%提高到99.1%。增加dropout不僅馴服了測試損失,而且使我們能夠安全地航行99%以上,甚至達到99.3%
14. 恭喜!
您已經建立了您的第一個神經網絡,並一直訓練到99%的準確性。沿途學到的技術並不特定於MNIST數據集,實際上它們在使用神經網絡時被廣泛使用。作爲一個分手的禮物,這裏是實驗室的“懸崖筆記”卡,卡通版本。你可以用它回憶起你學到的東西:
下一步
在完全連接和卷積網絡之後,您應該看看循環神經網絡。
在本教程中,您已經學習瞭如何在矩陣級構建Tensorflow模型。Tensorflow具有更高級的API,也稱爲tf.learn。
要在分佈式基礎架構上在雲中運行培訓或推理,我們提供Cloud ML服務。
最後,我們喜歡反饋。請告訴我們,如果您在本實驗室看到某些東西,或者您認爲應該改進的話。我們通過GitHub問題處理反饋[ 反饋鏈接 ]。
The author: Martin Görner
Twitter: @martin_gorner
Google +: plus.google.com/+MartinGorner
www.tensorflow.org