0
本文作者: 陳鳴鳩 | 2017-01-18 09:39 |
雷鋒網(wǎng)按:本文是介紹用TensorFlow構建圖像識別系統(tǒng)的第三部分。 在前兩部分中,我們構建了一個softmax分類器來標記來自CIFAR-10數(shù)據(jù)集的圖像,實現(xiàn)了約25-30%的精度。 因為有10個不同可能性的類別,所以我們預期的隨機標記圖像的精度為10%。25-30%的結果已經(jīng)比隨機標記的結果好多了,但仍有很大的改進空間。在這篇文章中,作者Wolfgang Beyer將介紹如何構建一個執(zhí)行相同任務的神經(jīng)網(wǎng)絡??纯纯梢蕴岣哳A測精度到多少!雷鋒網(wǎng)對全文進行編譯,未經(jīng)許可不得轉載。
關于前兩部分,可以參看《機器學習零基礎?手把手教你用TensorFlow搭建圖像識別系統(tǒng)》(一)和(二)。
神經(jīng)網(wǎng)絡是基于生物大腦的工作原理設計的,由許多人工神經(jīng)元組成,每個神經(jīng)元處理多個輸入信號并返回單個輸出信號,然后輸出信號可以用作其他神經(jīng)元的輸入信號。我們先來看看一個單獨的神經(jīng)元,大概長這樣:
一個人工神經(jīng)元:其輸出是其輸入加權和的ReLU函數(shù)值。
在單個神經(jīng)元中發(fā)生的情況與在softmax分類器中發(fā)生的情況非常相似。一個神經(jīng)元有一個輸入值的向量和一個權重值的向量,權重值是神經(jīng)元的內(nèi)部參數(shù)。輸入向量和權重值向量包含相同數(shù)量的值,因此可以使用它們
WeightedSum=input1×w1+input2×w2+...
到目前為止,我們正在做與softmax分類器完全相同的計算,現(xiàn)在開始,我們要進行一些不同的處理:只要加權和的結果是正值,神經(jīng)元的輸出是這個值;但是如果加權和是負值,就忽
由 f(x) = max(0, x)定義的整流線性單元
使用ReLU的原因是其具備非線性特點,因而現(xiàn)在神經(jīng)元的輸出并不是嚴格的輸入線性組合(也就是加權和)。當我們不再從單個神經(jīng)元而是從整個網(wǎng)絡來看時,會發(fā)現(xiàn)非線性很有用處。
人工神經(jīng)網(wǎng)絡中的神經(jīng)元通常不是彼此隨機連接的,大多數(shù)時候是分層排列的:
人工神經(jīng)網(wǎng)絡具有隱藏層和輸出層2個層。
輸入并不被當作一層,因為它只是將數(shù)據(jù)(不轉換它)饋送到第一個合適的層。
輸入圖像的像素值是第1層網(wǎng)絡中的神經(jīng)元的輸入。第1層中的神經(jīng)元的輸出是第2層網(wǎng)絡的神經(jīng)元的輸入,后面的層之間以此類推。如果沒有每層的ReLU,我們只是得到一個加權和的序列;并且堆積的加權和可以被合并成單個加權和,這樣一來,多個層并沒有比單層網(wǎng)絡有任何改進之處。這就是為什么要具有非線性的重要原因。ReLU非線性解決了上述問題,它使每個附加層的確給網(wǎng)絡添加了一些改進。
我們所關注的是圖像類別的分數(shù),它是網(wǎng)絡的最后一層的輸出。在這個網(wǎng)絡架構中,每個神經(jīng)元連接到前一層的所有神經(jīng)元,因此這種網(wǎng)絡被稱為完全連接的網(wǎng)絡。我們將會在本教程的第3部分中看到一些不同于此的其他情況。
對神經(jīng)網(wǎng)絡理論的簡短介紹到此結束。 讓我們開始建立一個真正的神經(jīng)網(wǎng)絡!
此示例的完整代碼在Github上提供。它需要TensorFlow和CIFAR-10數(shù)據(jù)集(雷鋒網(wǎng)的此前文章有提及)。
如果你已經(jīng)通過我以前的博客文章,你會看到神經(jīng)網(wǎng)絡分類器的代碼非常類似于softmax分類器的代碼。 除了切換出定義模型的代碼部分之外,我還添加了一些小功能使TensorFlow可以做以下一些事情:
正則化:這是一種非常常見的技術,用于防止模型過擬合。它的工作原理是在優(yōu)化過程中施加反作用力,其目的是保持模型簡單
使用TensorBoard可視化模型:TensorBoard包含TensorFlow,允許您根據(jù)模型和模型生成的數(shù)據(jù)生成表格和圖形。這有助于分析您的模型,并且對調(diào)試特別有用。
檢查點:此功能允許您保存模型的當前狀態(tài)以供以后使用。訓練一個模型可能需要相當長的時間,所以它是必要的,當您想再次使用模型時不必從頭開始。
這次代碼被分成兩個文件:定義模型two_layer_fc.py和運行模型run_fc_model.py(提示:'fc'代表完全連接的意思)。
讓我們先看看模型本身,然后進行一些運行和訓練處理。two_layer_fc.py包含以下函數(shù):
inference(),使我們從輸入數(shù)據(jù)到類分數(shù)。
loss(),從類分數(shù)中計算損失值。
training(),執(zhí)行單個訓練步驟。
evaluation(),計算網(wǎng)絡的精度。
inference()描述了通過網(wǎng)絡的正向傳遞。那么,類分數(shù)是如何從輸入圖片開始被計算的呢?
參數(shù)images是包含實際圖像數(shù)據(jù)的TensorFlow占位符。接下來的三個參數(shù)描述網(wǎng)絡的形狀或大小。 image_pixels是每個輸入圖像的像素數(shù),classes是不同輸出標簽的數(shù)量,hidden_units是網(wǎng)絡的第一個層或者隱藏層中的神經(jīng)元數(shù)量。
每個神經(jīng)元從上一層獲取所有值作為輸入,并生成單個輸出值。因此,隱藏層中的每個神經(jīng)元都具有image_pixels輸入,并且該層作為整體生成hidden_units輸出。然后將這些輸入到輸出層的類神經(jīng)元中,生成類輸出值,每個類一個分數(shù)。
reg_constant是正則化常數(shù)。TensorFlow允許我們非常容易地通過自動處理大部分計算來向網(wǎng)絡添加正則化。 當使用到損失函數(shù)時,我會進一步講述細節(jié)。
由于神經(jīng)網(wǎng)絡有2個相似的圖層,因此將為每個層定義一個單獨的范圍。 這允許我們在每個作用域中重復使用變量名。變量biases以我們熟悉的tf.Variable()方式來定義。
此處會更多地涉及到weights變量的定義。tf.get_variable()允許我們添加正則化。weights是以hidden_units(輸入向量大小乘以輸出向量大?。榫S度的image_pixels矩陣。initialier參數(shù)描述了weights變量的初始值。目前為止我們已經(jīng)將weights變量初始化為0,但此處并不會起作用。關于單層中的神經(jīng)元,它們都接收完全相同的輸入值,如果它們都具有相同的內(nèi)部參數(shù),則它們將進行相同的計算并且輸出相同的值。為了避免這種情況,需要隨機化它們的初始權重。我們使用了一個通常可以很好運行的初始化方案,將weights初始化為正態(tài)分布值。丟棄與平均值相差超過2個標準偏差的值,并且將標準偏差設置為輸入像素數(shù)量的平方根的倒數(shù)。幸運的是TensorFlow為我們處理了所有這些細節(jié),我們只需要指定調(diào)用truncated_normal_initializer便可完成上述工作。
weights變量的最終參數(shù)是regularizer?,F(xiàn)在要做的是告訴TensorFlow要為weights變量使用L2-正則化。我將在這里討論正則化。
第一層的輸出等于images矩陣乘以weights矩陣,再加上bisa變量。這與上一篇博文中的softmax分類器完全相同。然后應用tf.nn.relu(),取ReLU函數(shù)的值作為隱藏層的輸出。
第2層與第1層非常相似,其輸入值為hidden_units,輸出值為classes,因此weights矩陣的維度是是[hidden_units,classes]。 由于這是我們網(wǎng)絡的最后一層,所以不再需要ReLU。 通過將輸入(hidden)互乘以weights,再加上bias就可得到類分數(shù)(logits)。
tf.histogram_summary()允許我們記錄logits變量的值,以便以后用TensorBoard進行分析。這一點稍后會介紹。
總而言之,整個inference()函數(shù)接收輸入圖像并返回類分數(shù)。這是一個訓練有素的分類器需要做的,但為了得到一個訓練有素的分類器,首先需要測量這些類分數(shù)表現(xiàn)有多好,這是損失函數(shù)要做的工作。
首先,我們計算logits(模型的輸出)和labels(來自訓練數(shù)據(jù)集的正確標簽)之間的交叉熵,這已經(jīng)是我們對softmax分類器的全部損失函數(shù),但是這次我們想要使用正則化,所以必須給損失添加另一個項。
讓我們先放一邊吧,先看看通過使用正則化能實現(xiàn)什么。
當捕獲數(shù)據(jù)中隨機噪聲的統(tǒng)計模型是被數(shù)據(jù)訓練出來的而不是真實的數(shù)據(jù)基礎關系時,就被稱為過擬合。
紅色和藍色圓圈表示兩個不同的類。綠線代表過擬合模型,而黑線代表具有良好擬合的模型。
在上面的圖像中有兩個不同的類,分別由藍色和紅色圓圈表示。綠線是過度擬合的分類器。它完全遵循訓練數(shù)據(jù),同時也嚴重依賴于訓練數(shù)據(jù),并且可能在處理未知數(shù)據(jù)時比代表正則化模型的黑線表現(xiàn)更差。因此,我們的正則化目標是得到一個簡單的模型,不附帶任何不必要的復雜。我們選擇L2-正則化來實現(xiàn)這一點,L2正則化將網(wǎng)絡中所有權重的平方和加到損失函數(shù)。如果模型使用大權重,則對應重罰分,并且如果模型使用小權重,則小罰分。
這就是為什么我們在定義權重時使用了regularizer參數(shù),并為它分配了一個l2_regularizer。這告訴了TensorFlow要跟蹤l2_regularizer這個變量的L2正則化項(并通過參數(shù)reg_constant對它們進行加權)。所有正則化項被添加到一個損失函數(shù)可以訪問的集合——tf.GraphKeys.REGULARIZATION_LOSSES。將所有正則化損失的總和與先前計算的交叉熵相加,以得到我們的模型的總損失。
global_step是跟蹤執(zhí)行訓練迭代次數(shù)的標量變量。當在我們的訓練循環(huán)中重復運行模型時,我們已經(jīng)知道這個值,它是循環(huán)的迭代變量。直接將這個值添加到TensorFlow圖表的原因是想要能夠拍攝模型的快照,這些快照應包括有關已執(zhí)行了多少訓練步驟的信息。
梯度下降優(yōu)化器的定義很簡單。我們提供學習速率并告訴優(yōu)化器它應該最小化哪個變量。 此外,優(yōu)化程序會在每次迭代時自動遞增global_step參數(shù)。
模型精度的計算與softmax情況相同:將模型的預測與真實標簽進行比較,并計算正確預測的頻率。 我們還對隨著時間的推移精度如何演變感興趣,因此添加了一個跟蹤accuracy的匯總操作。 將在關于TensorBoard的部分中介紹這一點。
總結我們迄今為止做了什么,已經(jīng)定義了使用4個函數(shù)的2層人工神經(jīng)網(wǎng)絡的行為:inference()構成通過網(wǎng)絡的正向傳遞并返回類分數(shù)。loss()比較預測和真實的類分數(shù)并生成損失值。 training()執(zhí)行訓練步驟,并優(yōu)化模型的內(nèi)部參數(shù)。evaluation()測量模型的性能。
現(xiàn)在神經(jīng)網(wǎng)絡已經(jīng)定義完畢,讓我們看看run_fc_model.py是如何運行、訓練和評估模型的。
在強制導入之后,將模型參數(shù)定義為外部標志。 TensorFlow有自己的命令行參數(shù)模塊,這是一個圍繞Python argparse的小封裝包。 在這里使用它是為了方便,但也可以直接使用argparse。
在代碼開頭兩行中定義了命令行參數(shù)。每個標志的參數(shù)是標志的名稱(其默認值和一個簡短的描述)。 使用-h標志執(zhí)行文件將顯示這些描述。第二個代碼塊調(diào)用實際解析命令行參數(shù)的函數(shù),然后將所有參數(shù)的值打印到屏幕上。
用常數(shù)定義每個圖像的像素數(shù)(32 x 32 x 3)和不同圖像類別的數(shù)量。
使用一個時鐘來記錄運行時間。
我們想記錄關于訓練過程的一些信息,并使用TensorBoard顯示該信息。 TensorBoard要求每次運行的日志都位于單獨的目錄中,因此我們將日期和時間信息添加到日志目錄的名稱地址。
load_data()加載CIFAR-10數(shù)據(jù),并返回包含獨立訓練和測試數(shù)據(jù)集的字典。
定義TensorFlow占位符。 當執(zhí)行實際計算時,這些將被填充訓練和測試數(shù)據(jù)。
images_placeholder將每張圖片批處理成一定尺寸乘以像素的大小。 批處理大小設定為“None”允許運行圖片時可隨時設定大?。ㄓ糜谟柧毦W(wǎng)絡的批處理大小可以通過命令行參數(shù)設置,但是對于測試,我們將整個測試集作為一個批處理) 。
labels_placeholder是一個包含每張圖片的正確類標簽的整數(shù)值向量。
這里引用了我們之前在two_layer_fc.py中描述的函數(shù)。
inference()使我們從輸入數(shù)據(jù)到類分數(shù)。
loss()從類分數(shù)中計算損失值。
training()執(zhí)行單個訓練步驟。
evaluation()計算網(wǎng)絡的精度。
為TensorBoard定義一個summary操作函數(shù) (更多介紹可參見前文).
生成一個保存對象以保存模型在檢查點的狀態(tài)(更多介紹可參見前文)。
開始TensorFlow會話并立即初始化所有變量。 然后我們創(chuàng)建一個匯總編輯器,使其定期將日志信息保存到磁盤。
這些行負責生成批輸入數(shù)據(jù)。讓我們假設我們有100個訓練圖像,批次大小為10.在softmax示例中,我們只為每次迭代選擇了10個隨機圖像。這意味著,在10次迭代之后,每個圖像將被平均選取一次。但事實上,一些圖像將被選擇多次,而一些圖像不會被添加到任何一個批次。但只要重復的次數(shù)夠頻發(fā),所有圖片被隨機分到不同批次的情況會有所改善。
這一次我們要改進抽樣過程。要做的是首先對訓練數(shù)據(jù)集的100個圖像隨機混洗。混洗之后的數(shù)據(jù)的前10個圖像作為我們的第一個批次,接下來的10個圖像是我們的第二批,后面的批次以此類推。 10批后,在數(shù)據(jù)集的末尾,再重復混洗過程,和開始步驟一致,依次取10張圖像作為一批次。這保證沒有任何圖像比任何其它圖像被更頻繁地拾取,同時仍然確保圖像被返回的順序是隨機的。
為了實現(xiàn)這一點,data_helpers()中的gen_batch()函數(shù)返回一個Python generator,它在每次評估時返回下一個批次。generator原理的細節(jié)超出了本文的范圍(這里有一個很好的解釋)。使用Python的內(nèi)置zip()函數(shù)來生成一個來自[(image1,label1),(image2,label2),...]的元組列表,然后將其傳遞給生成函數(shù)。
next(batch)返回下一批數(shù)據(jù)。 因為它仍然是[(imageA,labelA),(imageB,labelB),...]的形式,需要先解壓它以從標簽中分離圖像,然后填充feed_dict,字典包含用單批培訓數(shù)據(jù)填充的TensorFlow占位符。
每100次迭代之后模型的當前精度會被評估并打印到屏幕上。此外,正在運行summary操作,其結果被添加到負責將摘要寫入磁盤的summary_writer(看此章節(jié))。
此行運行train_step操作(之前定義為調(diào)用two_layer_fc.training(),它包含用于優(yōu)化變量的實際指令)。
當訓練模型需要較長的時間,有一個簡單的方法來保存你的進度的快照。 這允許您以后回來并恢復模型在完全相同的狀態(tài)。 所有你需要做的是創(chuàng)建一個tf.train.Saver對象(我們之前做的),然后每次你想拍攝快照時調(diào)用它的save()方法?;謴湍P鸵埠芎唵?,只需調(diào)用savever的restore()。 代碼示例請看gitHub存儲庫中的restore_model.py文件。
在訓練完成后,最終模型在測試集上進行評估(記住,測試集包含模型到目前為止還沒有看到的數(shù)據(jù),使我們能夠判斷模型是否能推廣到新的數(shù)據(jù))。
讓我們使用默認參數(shù)通過“python run_fc_model.py”運行模型。 我的輸出如下所示:
可以看到訓練的準確性開始于我們所期望到隨機猜測水平(10級 - > 10%的機會選擇到正確的)。 在第一次約1000次迭代中,精度增加到約50%,并且在接下來的1000次迭代中圍繞該值波動。 46%的測試精度不低于訓練精度。 這表明我們的模型沒有顯著過度擬合。 softmax分級器的性能約為30%,因此46%的改進約為50%。不錯!
TensorBoard允許您從不同方面可視化TensorFlow圖形,并且對于調(diào)試和改進網(wǎng)絡非常有用。 讓我們看看TensorBoard相關的代碼。
在 two_layer_fc.py 我可以看到以下代碼:
這三行中的每一行都創(chuàng)建一個匯總操作。通過定義一個匯總操作告訴TensorFlow收集某些張量(在本例中l(wèi)ogits,loss和accuracy)的摘要信息。匯總操作的其他參數(shù)就只是一些想要添加到總結的標簽。
有不同種類的匯總操作。使用scalar_summary記錄有關標量(非矢量)值以及histogram_summary收集有關的多個值分布信息(有關各種匯總運算更多信息可以在TensorFlow文檔中找到)。
在 run_fc_model.py 是關于TensorBoard 可視化的一些代碼:
TensorFlow中的一個操作本身不運行,您需要直接調(diào)用它或調(diào)用依賴于它的另一個操作。由于我們不想在每次要收集摘要信息時單獨調(diào)用每個摘要操作,因此使用tf.merge_all_summaries創(chuàng)建一個運行所有摘要的單個操作。
在TensorFlow會話的初始化期間,創(chuàng)建一個摘要寫入器,摘要編入器負責將摘要數(shù)據(jù)實際寫入磁盤。在摘要寫入器的構造函數(shù)中,logdir是日志的寫入地址??蛇x的圖形參數(shù)告訴TensorBoard渲染顯示整個TensorFlow圖形。每100次迭代,我們執(zhí)行合并的匯總操作,并將結果饋送到匯總寫入器,將它們寫入磁盤。要查看結果,我們通過“tensorboard --logdir = tf_logs”運行TensorBoard,并在Web瀏覽器中打開localhost:6006。在“事件”標簽中,我們可以看到網(wǎng)絡的損失是如何減少的,以及其精度是如何隨時間增加而增加的。
tensorboard圖顯示模型在訓練中的損失和精度。
“Graphs”選項卡顯示一個已經(jīng)定義的可視化的tensorflow圖,您可以交互式地重新排列直到你滿意。我認為下面的圖片顯示了我們的網(wǎng)絡結構非常好。
Tensorboard1以交互式可視化的方式顯示Tensorboard圖像
有關在“分布”和“直方圖”標簽的信息可以進一步了解tf.histogram_summary操作,這里不做進一步的細節(jié)分析,更多信息可在官方tensorflow文件相關部分。
也許你正在想訓練softmax分類器的計算時間比神經(jīng)網(wǎng)絡少了很多。事實確實如此,但即使把訓練softmax分類器的時間增加到和神經(jīng)網(wǎng)絡來訓練所用的時間一樣長,前者也不會達到和神經(jīng)網(wǎng)絡相同的性能,前者訓練時間再長,額外的收益和一定程度的性能改進幾乎是微乎其微的。我們也已經(jīng)在神經(jīng)網(wǎng)絡中也驗證也這點,額外的訓練時間不會顯著提高準確性,但還有別的事情我們可以做。
已選的默認參數(shù)值表現(xiàn)是相當不錯的,但還有一些改進的余地。通過改變參數(shù),如隱藏層中的神經(jīng)元的數(shù)目或學習率,應該能夠提高模型的準確性,模型的進一步優(yōu)化使測試精度很可能大于50%。如果這個模型可以調(diào)整到65%或更多,我也會相當驚喜。但還有另一種類型的網(wǎng)絡結構能夠比較輕易實現(xiàn)這一點:卷積神經(jīng)網(wǎng)絡,這是一類不完全連通的神經(jīng)網(wǎng)絡,相反,它們嘗試在其輸入中理解局部特征,這對于分析圖像非常有用。它使得在解讀圖像獲取空間信息的時候有非常直觀的意義。在本系列的下一部分中,我們將看到卷積神經(jīng)網(wǎng)絡的工作原理,以及如何構建一個自己的神經(jīng)網(wǎng)絡.。
雷鋒網(wǎng)將關注下一部分關于卷積神經(jīng)網(wǎng)絡的介紹,敬請期待。
via wolfib,雷鋒網(wǎng)編譯
雷峰網(wǎng)版權文章,未經(jīng)授權禁止轉載。詳情見轉載須知。