B站視頻地址:
php
https://www.bilibili.com/video/av8284296html
https://www.bilibili.com/video/av16339227python
在這個codelab中,您將學習如何建立和訓練識別手寫數字的神經網絡。一路上,隨着你加強神經網絡的準確率達到99%,你還將學習到專業人員用來訓練模型的高效工具。git
該codelab使用MNIST數據集,收集了60,000個標記的數字。你將學會用不到100行Python / TensorFlow代碼來解決深度學習問題。github
你會學到什麼算法
你須要什麼api
安裝說明在實驗室的下一步中給出。安全
在您的計算機上安裝必要的軟件: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。
咱們將首先觀察正在接受訓練的神經網絡。代碼將在下一節中進行說明,所以您先不須要看。
咱們的用神經網絡訓練手寫數字,並對它們進行分類,即將手寫數字識別爲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
什麼是「 權重 」和「 誤差 」?如何計算「 交叉熵 」?訓練算法究竟如何工做?那麼來看下一節內容吧。
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或更多維度的張量。
如今咱們的神經網絡產生了輸入圖像的預測,咱們須要測量它們的好壞,即網絡告訴咱們與咱們所知道的真相之間的距離。請記住,咱們爲此數據集中的全部圖像的數字都有正確數字的標籤。
任何距離都會有效,普通的歐幾里得距離很好,可是對於分類問題,一個距離,稱爲「交叉熵(cross-entropy)」更有效率。
「 一熱(One-hot) 」編碼意味着您使用10個值的矢量表明標籤「6」,所有爲零,但第6個值爲1.這是由於格式很是相似於咱們的神經網絡輸出預測,也做爲10個值的向量。
「訓練」神經網絡實際上意味着使用訓練圖像和標籤來調整權重和誤差,以便最小化交叉熵損失函數。下面是它的工做原理。
交叉熵是訓練圖像的權重,誤差,像素及其已知標籤的函數。
若是咱們相對於全部權重和全部誤差計算交叉熵的偏導數,咱們得到了對於給定圖像,權重和誤差的標籤和現值計算的「梯度(gradient)」。記住,咱們有7850個權重和誤差,因此計算梯度聽起來好像有不少工做。幸運的是,TensorFlow將爲咱們作好準備。
梯度的數學屬性是它指向「上」。因爲咱們想要走交叉熵低的地方,因此咱們走向相反的方向。咱們將權重和誤差更新一小部分梯度,並使用下一批訓練圖像再次執行相同的操做。但願這讓咱們到達交叉熵最小的坑底。
在該圖中,交叉熵表示爲2個權重的函數。實際上還有更多的。梯度降低算法遵循最快速降低到局部最小值的路徑。訓練圖像也會在每次迭代中更改,以便咱們收斂到適用於全部圖像的局部最小值。
「 學習率」:您沒法在每次迭代時以漸變的整個長度更新您的權重和誤差。這就比如是一個穿着靴子的人,想去一個山谷的底部。他會從山谷的一邊跳到另外一邊。要進入底部,他須要執行較小的步驟,即僅使用漸變的一小部分,一般在1/1000。咱們將這個分數稱爲「學習率」。
總而言之,訓練循環以下所示:
訓練數據和標籤 => 求損失函數=> 求梯度 (偏導數) => 最快降低 => 更新權重和誤差 => 重複下一個小批量的圖像數據和標籤
爲何要使用100個圖像和標籤,用這種「 小批量 」形式進行?
您只需一個示例圖像便可計算您的漸變,並當即更新權重和誤差(在文獻中稱爲「隨機梯度降低」)。這樣作100個例子給出了更好地表示不一樣示例圖像所施加的約束的漸變,所以可能更快地收斂到解決方案。小批量的尺寸是可調參數。還有另外一個更技術的緣由:使用大批量也意味着使用更大的矩陣,這些一般更容易在GPU上進行優化。
常常問的問題
已經寫了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],表明:
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繪圖庫)爲這個實驗室創建的實時可視化。
爲了提升識別精度,咱們將爲神經網絡添加更多層數。第二層中的神經元,而不是計算像素的加權和,將計算來自上一層的神經元輸出的加權和。這裏是一個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%以上的精度。
隨着層次的增長,神經網絡趨向於收斂更多困難。但咱們今天知道如何使他們的工做。以下圖,若是您看到這樣的精度曲線,本節將對您有所幫助:
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時,能夠回到該步驟。
你如今準備好深刻
使用兩個,三個或四個中間層,若是將迭代推送到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。
您將注意到,測試和訓練數據的交叉熵曲線在數千次迭代後開始斷開鏈接。學習算法僅用於訓練數據,並相應地優化訓練交叉熵。它歷來沒有看到測試數據,因此絕不奇怪,一段時間後,它的工做再也不對測試交叉熵產生影響,測試交叉熵中止降低,有時甚至反彈。
這不會當即影響您的模型的真實識別能力,但它將阻止您運行許多迭代,而且一般是訓練再也不具備積極做用的跡象。這個斷開鏈接一般被標記爲「過分擬合」,當您看到它時,您能夠嘗試應用稱爲「丟失信息」的正則化技術。
在丟失數據期間,在每次訓練迭代中,您從網絡中丟棄隨機神經元。您選擇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),並訓練大量的數據,你可能仍然被困在一個性能水平,彷佛沒有什麼能夠提升。這意味着您的神經網絡目前的形狀不能從您的數據中提取更多的信息,就像咱們在這裏同樣。
記住咱們如何使用手寫圖像,將全部像素平坦化爲單個向量?那是一個很糟糕的主意 手寫數字由形狀組成,當咱們平鋪像素時,咱們捨棄了形狀信息。然而,有一種類型的神經網絡能夠利用形狀信息:卷積網絡。讓咱們試試看吧。
在卷積網絡的層中,一個「神經元」僅在圖像的小區域上進行剛好在其上方的像素的加權和。而後,經過添加偏置並經過其激活功能饋送結果來正常地起做用。最大的區別是每一個神經元都會重複使用相同的權重,而在以前看到的徹底鏈接的網絡中,每一個神經元都有本身的權重集。
在上面的動畫中,您能夠看到,經過在兩個方向(卷積)上滑過圖像的權重塊,您能夠得到與圖像中的像素同樣多的輸出值(儘管邊緣須要一些填充)。
要使用4x4的補丁大小和彩色圖像做爲輸入生成一個輸出值平面,如動畫中那樣,咱們須要4x4x3 = 48的權重。這還不夠 爲了增長更多的自由度,咱們用不一樣的權重重複相同的事情。
經過向張量添加維度,能夠將兩個(或多個)權重組重寫爲一個,這給出了卷積層的權重張量的通用形狀。因爲輸入和輸出通道的數量是參數,咱們能夠開始堆疊和連接卷積層。
最後一個問題仍然存在。咱們仍然須要將信息調低。在最後一層,咱們仍然只須要10個神經元來代替咱們的10個數字。傳統上,這是經過「最大池」層完成的。即便今天有更簡單的方法,「最大池(max-pooling)」有助於直觀地瞭解卷積網絡的運行狀況:若是您假設在訓練過程當中,咱們的小塊權重會演變成過濾器,能夠識別基本形狀(水平和垂直線,曲線,……)而後一種將有用信息向下傳遞的方式是經過層數保持最大強度識別形狀的輸出。實際上,在最大池層中,神經元輸出以2x2爲一組進行處理,只保留最多一個。
有一種更簡單的方法:若是您以2像素而不是1像素的速度滑過圖像,則還會得到較少的輸出值。這種方法已被證實是一樣有效的,而今天的卷積網絡僅使用卷積層。
讓咱們創建一個手寫數字識別的卷積網絡。咱們將在頂部使用三個卷積層,咱們的傳統softmax讀出層在底部,並鏈接到一個徹底鏈接的層:
請注意,第二和第三卷積層有兩個步長,這說明爲何它們將輸出值從28x28下降到14x14,而後是7x7。完成這些層的大小,使得神經元的數量在每一層大體降低2倍:28x28x4≈3000→14x14x8≈1500→7x7x12≈500→200.跳轉到下一節執行。
要將咱們的代碼切換到卷積模型,咱們須要爲卷積層定義適當的權重張量,而後將卷積圖層添加到模型中。
咱們已經看到卷積層須要如下形狀的權重張量。這是初始化的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%的屏障。看看測試交叉熵曲線。你是否是能立刻想到解決方案呢?
調整神經網絡的一個很好的方法是實現一個有點太限制的網絡,而後給它一個更多的自由度,並添加丟失信息(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%
您已經創建了您的第一個神經網絡,並一直訓練到99%的準確性。沿途學到的技術並不特定於MNIST數據集,實際上它們在使用神經網絡時被普遍使用。做爲一個分手的禮物,這裏是實驗室的「懸崖筆記」卡,卡通版本。你能夠用它回憶起你學到的東西:
下一步
在徹底鏈接和卷積網絡以後,您應該看看循環神經網絡。
在本教程中,您已經學習瞭如何在矩陣級構建Tensorflow模型。Tensorflow具備更高級的API,也稱爲tf.layers.
要在分佈式基礎架構上在雲中運行培訓或推理,咱們提供Cloud ML服務。
最後,咱們喜歡反饋。請告訴咱們,若是您在本實驗室看到某些東西,或者您認爲應該改進的話。咱們經過GitHub問題處理反饋[ 反饋連接 ]。
The author: Martin Görner
Twitter: @martin_gorner
Google +: plus.google.com/+MartinGorner
轉:https://blog.csdn.net/xummgg/article/details/69214366