咱們都據說過深度學習,可是有多少人知道深度信念網絡是什麼?讓咱們從本章開始回答這個問題。深度信念網絡是一種很是先進的機器學習形式,其意義正在迅速演變。做爲一名機器學習開發人員,對這個概念有必定的瞭解是很重要的,這樣當您遇到它或它遇到您時就會很熟悉它!算法
在機器學習中,深度信念網絡在技術上是一個深度神經網絡。咱們應該指出,深度的含義,當涉及到深度學習或深度信念時,意味着網絡是由多層(隱藏的單位)組成的。在深度信念網絡中,這些鏈接在一層內的每一個神經元之間,而不是在不一樣的層之間。一個深度信念網絡能夠被訓練成無監督學習,以機率重建網絡的輸入。這些層就像「特徵檢測器」同樣,能夠識別或分類圖像、字母等等。c#
在本章,咱們將包括:數組
受限玻爾茲曼機網絡
在c#中建立和培訓一個深度信念網絡框架
構建深度信念網絡的一種流行方法是將其做爲受限玻爾茲曼機(RBMs)的分層集合。這些RMBs的功能是做爲自動編碼器,每一個隱藏層做爲下一個可見層。深度信念網絡將爲訓練前階段提供多層RBMs,而後爲微調階段提供一個前饋網絡。訓練的第一步將是從可見單元中學習一層特性。下一步是從之前訓練過的特性中獲取激活,並使它們成爲新的可見單元。而後咱們重複這個過程,這樣咱們就能夠在第二個隱藏層中學習更多的特性。而後對全部隱藏層繼續執行該過程。機器學習
咱們應該在這裏提供兩條信息。函數
首先,咱們應該稍微解釋一下什麼是自動編碼器。自動編碼器是特徵學習的核心。它們編碼輸入(輸入一般是具備重要特徵的壓縮向量)和經過無監督重構的數據學習。工具
其次,咱們應該注意到,將RBMs堆積在一個深度信念網絡中只是解決這個問題的一種方法。將限制線性單元(ReLUs)疊加起來,再加上刪除和訓練,而後再加上反向傳播,這又一次成爲了最早進的技術。我再說一遍,由於30年前,監督方法是可行的。與其讓算法查看全部數據並肯定感興趣的特徵,有時候咱們人類實際上能夠更好地找到咱們想要的特徵。性能
我認爲深度信念網絡的兩個最重要的特性以下:學習
l 有一個有效的,自上而下的逐層學習過程,生成權重。它決定了一個層中的變量如何依賴於它上面層中的變量。
l 當學習完成後,每一層變量的值均可以很容易地經過一個自底向上的單次遍歷推斷出來,該遍歷從底層的一個觀察到的數據向量開始,並使用生成權值逆向重構數據。
說到這裏,咱們如今來談談RBMs以及通常的玻爾茲曼機。
玻爾茲曼機是一個遞歸神經網絡,具備二進制單元和單元之間的無向邊。無向是指邊(或連接)是雙向的,它們沒有指向任何特定的方向。
下面是一個帶有無向邊的無向圖:
玻爾茲曼機是最先可以學習內部表徵的神經網絡之一,只要有足夠的時間,它們就能解決難題。可是,它們不擅長伸縮,這就引出了咱們的下一個主題,RBMs。
引入RBMs是爲了解決玻爾茲曼機器沒法伸縮的問題。它們有隱藏層,每一個隱藏單元之間的鏈接受到限制,但不在這些單元以外,這有助於提升學習效率。更正式地說,咱們必須深刻研究一些圖論來正確地解釋這一點。
RBMs的神經元必須造成二分圖,這是圖論的一種更高級的形式;來自這兩組單元(可見層和隱藏層)中的每一組的一對節點之間可能具備對稱鏈接。任何組中的節點之間都不能有鏈接。二分圖,有時稱爲生物圖,是將一組圖頂點分解爲兩個不相交的集合,使同一集合內沒有兩個頂點相鄰。
這裏有一個很好的例子,它將有助於可視化這個主題。
注意同一組內沒有鏈接(左邊是紅色的,右邊是黑色的),但兩組之間有鏈接:
更正式地說,RBM是所謂的對稱二分圖。這是由於全部可見節點的輸入都傳遞給全部隱藏節點。咱們說對稱是由於每一個可見節點都與一個隱藏節點相關。
假設咱們的RBM顯示了貓和狗的圖像,咱們有兩個輸出節點,每一個動物一個。在咱們向前經過學習,咱們的RBM問本身:「對於我看到的像素,我應該向貓或狗發送更強的權重信號嗎?」在它想知道「做爲一隻狗,我應該看到哪一個像素分佈?」個人朋友們,這就是今天關於聯合機率的課: 在給定A和給定X的狀況下X的同時機率。在咱們的例子中,這個聯合機率表示爲兩層之間的權重,是RBMs的一個重要方面。
咱們如今來談談重構,這是rbms的一個重要部分。在咱們所討論的示例中,咱們正在學習一組圖像出現哪些像素組(即打開)。當一個隱藏層節點被一個重要的權重激活時(不管決定打開它的權重是什麼),它表示正在發生的事情的共同發生,在咱們的例子中,是狗仍是貓。若是這是一隻貓,尖耳朵、圓臉、小眼睛可能就是咱們要找的。大耳朵+長尾巴+大鼻子可能會讓你的形象變成一隻狗。這些激活表示RBM「認爲」原始數據的樣子。實際上,咱們正在重建原始數據。
咱們還應該迅速指出,RBM有兩個偏見,而不是一個。這是很是重要的,由於這是區別於其餘自動編碼算法。隱藏的誤差幫助咱們的RBM在向前傳遞時產生咱們須要的激活,而可見的層誤差幫助咱們在向後傳遞時學習正確的重構。隱藏的誤差很重要,由於它的主要工做是確保不管咱們的數據有多麼稀疏,一些節點都會被觸發。稍後您將看到這將如何影響一個深層信仰網絡的夢想。
一旦咱們的RBM瞭解了輸入數據的結構,它與在第一個隱藏層中進行的激活相關,數據就會傳遞到下一個隱藏層。第一個隱藏層而後成爲新的可見層。咱們在隱藏層中建立的激活如今成爲咱們的輸入。它們將乘以新隱藏層中的權重,以產生另外一組激活。
這個過程在咱們的網絡的全部隱藏層中繼續進行。隱藏層變成可見層,咱們有另外一個隱藏層,咱們將使用它的權重,而後重複。每一個新的隱藏層都會產生調整後的權重,直到咱們可以識別來自前一層的輸入爲止。
爲了更詳細地說明,這在技術上被稱爲無監督的、貪婪的、分層的培訓。改進每一層的權值不須要輸入,這意味着不涉及任何類型的外部影響。這進一步意味着咱們應該可以使用咱們的算法來訓練之前沒有見過的無監督數據。
正如咱們一直強調的,咱們擁有的數據越多,咱們的結果就越好!隨着每一層圖像的質量愈來愈好,也愈來愈準確,咱們就能夠更好地經過每個隱藏層來提升咱們的學習能力,而權重的做用就是引導咱們在學習的過程當中進行正確的圖像分類。
可是在討論重構時,咱們應該指出,每次重構工做中的一個數字(權重)都是非零的,這代表咱們的RBM從數據中學到了一些東西。在某種意義上,您能夠像處理百分比指標同樣處理返回的數字。數字越大,算法對它所看到的東西就越有信心。記住,咱們有咱們要返回的主數據集,咱們有一個參考數據集用於咱們的重建工做。當咱們的RBM遍歷每一個圖像時,它還不知道它在處理什麼圖像;這就是它想要肯定的。
讓咱們花一點時間來澄清一些事情。當咱們說咱們使用貪婪算法時,咱們真正的意思是咱們的RBM將採用最短路徑來得到最佳結果。咱們將從所看到的圖像中隨機抽取像素,並測試哪些像素引導咱們找到正確答案。
RBM將根據主數據集(測試集)測試每一個假設,這是咱們的正確最終目標。請記住,每一個圖像只是咱們試圖分類的一組像素。這些像素包含了數據的特徵和特徵。例如,一個像素能夠有不一樣的亮度,其中深色像素可能表示邊框,淺色像素可能表示數字,等等。
但若是事情不像咱們想的那樣,會發生什麼呢?若是咱們在給定步驟中學到的東西不正確會發生什麼?若是出現這種狀況,就意味着咱們的算法猜錯了。咱們要作的就是回去再試一次。這並不像看上去那麼糟糕,也不像看上去那麼耗時。
固然,錯誤的假設會帶來時間上的代價,但最終的目標是咱們必須提升學習效率,並在每一個階段減小錯誤。每個錯誤的加權鏈接都會受到懲罰,就像咱們在強化學習中所作的那樣。這些鏈接會減小重量,再也不那麼強。但願下一個遍歷能夠在減小偏差的同時提升精度,而且權重越大,影響就越大。
假設咱們對數字圖像進行分類,也就是數字。有些圖像會有曲線,好比二、三、六、八、9等等。其餘數字,如一、4和7,則不會。這樣的知識是很是重要的,由於咱們的RBM,會用它來不斷提升本身的學習,減小錯誤。若是咱們認爲咱們處理的是數字2,那麼這個路徑的權值就會比其餘路徑的權值更重。這是一個極端的過分簡化,但但願它足以幫助你理解咱們將要開始的內容。
當咱們把全部這些放在一塊兒,咱們如今有了一個深層信仰網絡的理論框架。雖然咱們比其餘章節更深刻地研究了理論,可是正如您所看到的咱們的示例程序所工做的那樣,它將開始變得有意義。您將更好地準備在應用程序中使用它,瞭解幕後發生的事情。
爲了展現深度信念網絡和RBMs,咱們將使用Mattia Fagerlund編寫的出色的開源軟件SharpRBM。這個軟件對開源社區作出了難以想象的貢獻,我絕不懷疑您將花費數小時甚至數天的時間來使用它。這個軟件附帶了一些使人難以置信的演示。在本章中,咱們將使用字母分類演示。
下面的截圖是咱們的深度信念測試應用程序。有沒有想過電腦睡覺時會夢到什麼?
程序的左上角是咱們指定要訓練的圖層的區域。咱們有三個隱藏層,它們都須要在測試以前進行適當的訓練。咱們能夠一次訓練一層,從第一層開始。訓練得越多,你的系統就會越好:
訓練選項以後的下一部分是咱們的進展。當咱們在訓練時,全部相關的信息,如生成,重構偏差,檢測器偏差,學習率,都顯示在這裏:
下一個是咱們的特性檢測器的繪圖,若是選中Draw複選框,它將在整個訓練過程當中更新本身:
當開始訓練一個層時,咱們將注意到重構和特徵檢測器基本上是空的。他們會隨着咱們的訓練不斷完善本身。記住,咱們正在重建咱們已經知道是真實的東西!隨着訓練的繼續,重構的數字變得愈來愈清晰,咱們的特徵檢測器也愈來愈清晰:
下面是訓練期間應用程序的快照。如圖所示,這是在第31代,重建的數字是很是明確的。
它們仍然不完整或不正確,但能夠看到咱們取得了多大的進步:
電腦作夢時會夢到什麼?對咱們來講,直覺是一個特徵,它容許咱們看到計算機在重構階段在想什麼。當程序試圖重建咱們的數字時,特徵檢測器自己將在整個過程當中以各類形式出現。咱們在dream window中顯示的就是這些形式(紅色圓圈表示):
咱們花了不少時間查看應用程序的屏幕截圖。我想是時候看看代碼了。讓咱們先看看如何建立DeepBeliefNetwork對象自己:
DeepBeliefNetwork = new DeepBeliefNetwork(28 * 29, 500, 500, 1000);
一旦建立了這個,咱們須要建立咱們的網絡訓練器,咱們根據咱們正在訓練的層的權重來作這件事:
DeepBeliefNetworkTrainer trainer = new DeepBeliefNetworkTrainer(DeepBeliefNetwork, DeepBeliefNetwork?.LayerWeights?[layerId], inputs);
這兩個對象都在咱們的主TrainNetwork循環中使用,這是應用程序中大部分活動發生的部分。這個循環將繼續,直到被告知中止。
private void TrainNetwork(DeepBeliefNetworkTrainer trainer) { try { Stopping = false; ClearBoxes(); _unsavedChanges = true; int generation = 0; SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED);
while (Stopping == false) { Stopwatch stopwatch = Stopwatch.StartNew(); TrainingError error = trainer?.Train(); label1.Text = string.Format( "Gen {0} ({4:0.00} s): ReconstructionError= {1:0.00}, DetectorError={2:0.00}, LearningRate={3:0.0000}", generation, error.ReconstructionError, error.FeatureDetectorError, trainer.TrainingWeights.AdjustedLearningRate, stopwatch.ElapsedMilliseconds / 1000.0); Application.DoEvents(); ShowReconstructed(trainer); ShowFeatureDetectors(trainer); Application.DoEvents(); if (Stopping) { break; }
generation++; }
DocumentDeepBeliefNetwork(); }
finally { SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS); } }
在前面的代碼中,咱們突出顯示了trainer.Train()方法,它是一個基於數組的學習算法,以下所示:
public TrainingError Train() { TrainingError trainingError = null; if (_weights != null) { ClearDetectorErrors(_weights.LowerLayerSize, _weights.UpperLayerSize); float reconstructionError = 0; ParallelFor(MultiThreaded, 0, _testCount, testCase => { float errorPart = TrainOnSingleCase(_rawTestCases,_weights?.Weights, _detectorError,testCase,
_weights.LowerLayerSize,_weights.UpperLayerSize, _testCount); lock (_locks?[testCase % _weights.LowerLayerSize]) { reconstructionError += errorPart; } }
); float epsilon = _weights.GetAdjustedAndScaledTrainingRate(_testCount); UpdateWeights(_weights.Weights,_weights.LowerLayerSize, _weights.UpperLayerSize,_detectorError, epsilon); trainingError = new TrainingError(_detectorError.Sum(val =>Math.Abs(val)), reconstructionError); _weights?.RegisterLastTrainingError(trainingError); return trainingError; }
return trainingError; }
此代碼使用並行處理(突出顯示的部分)並行地訓練單個案例。這個函數負責處理輸入層和隱藏層的更改,正如咱們在本章開頭所討論的。它使用TrainOnSingleCase函數,以下圖所示:
private float TrainOnSingleCase(float[] rawTestCases, float[] weights, float[] detectorErrors, int testCase, int lowerCount, int upperCount, int testCaseCount) { float[] model = new float[upperCount]; float[] reconstructed = new float[lowerCount]; float[] reconstructedModel = new float[upperCount]; int rawTestCaseOffset = testCase * lowerCount; ActivateLowerToUpperBinary(rawTestCases, lowerCount, rawTestCaseOffset, model, upperCount, weights); // Model ActivateUpperToLower(reconstructed, lowerCount, model,upperCount, weights); // Reconstruction ActivateLowerToUpper(reconstructed, lowerCount, 0, reconstructedModel, upperCount, weights); // Reconstruction model return AccumulateErrors(rawTestCases, lowerCount,rawTestCaseOffset, model, upperCount, reconstructed, reconstructedModel, detectorErrors); // Accumulate detector errors }
最後,咱們在處理過程當中積累錯誤,這就是咱們的模型應該相信的和它實際作的之間的區別。
顯然,錯誤率越低越好,對於咱們的圖像重建最準確。AccumulateErrors函數以下所示:
private float AccumulateErrors(float[] rawTestCases, int lowerCount, int rawTestCaseOffset, float[] model, int upperCount, float[] reconstructed, float[] reconstructedModel, float[] detectorErrors) { float reconstructedError = 0; float[] errorRow = new float[upperCount]; for (int lower = 0; lower < lowerCount; lower++) { int errorOffset = upperCount * lower; for (int upper = 0; upper < upperCount; upper++) { errorRow[upper] = rawTestCases[rawTestCaseOffset + lower] * model[upper] + // 模型應該相信什麼 -reconstructed[lower] * reconstructedModel[upper]; // 模型真正相信什麼 } lock (_locks[lower]) { for (int upper = 0; upper < upperCount; upper++) { detectorErrors[errorOffset + upper] -= errorRow[upper]; } } reconstructedError += Math.Abs(rawTestCases[rawTestCaseOffset + lower] - reconstructed[lower]); } return reconstructedError; }
在本章中,咱們學習了RBMs、一些圖論,以及如何在c#中建立和訓練一個深刻的信念網絡。我建議你對代碼進行試驗,將網絡層訓練到不一樣的閾值,並觀察計算機在重構時是如何作夢的。記住,你訓練得越多越好,因此花時間在每一層上,以確保它有足夠的數據來進行準確的重建工做。
警告:若是啓用特性檢測器和重構輸入的繪圖功能,性能將會極速降低。
若是你正在嘗試訓練你的圖層,你可能但願先在沒有可視化的狀況下訓練它們,以減小所需的時間。相信我,若是你把每個關卡都訓練成高迭代,那麼可視化會讓你感受像一個永恆的過程!在你前進的過程當中,隨時保存你的網絡。
在下一章中,咱們將學習微基準測試,並使用有史以來最強大的開源微基準測試工具包之一!