<b>本文講的是[譯] Scratch 平台的神經網絡實作(R 語言),</b>
<b></b>
這篇文章是針對那些有統計或者經濟學背景的人們,幫助他們通過 R 語言上的 Scratch 平台更好地學習和了解機器學習知識。
“我們有意識地在設計課程的時候,于反向傳播算法的程式設計作業中包含了對最底層的資料的計算要求。學生們需要在原始的 numpy 庫中使資料在各層中正向、反向傳播。一些學生因而難免在課程的留言闆上抱怨(這些複雜的計算)”
如果架構已經為你完成了反向傳播算法(BP 算法)的計算,你又何苦折磨自己而不去探尋更多有趣的深度學習問題呢?
“人們很容易陷入這樣的誤區中-認為你可以簡單地将任意的神經層組合在一起然後反向傳播算法會‘令它們自己在你的資料上工作起來’。”
是以,我寫這篇文章的目的有兩層:
了解神經網絡背後的抽象洩漏(通過在 Scratch 平台上操作),而這些東西的重要性恰恰是我開始所忽略的。這樣如果我的模型沒有達到預期的學習效果,我可以更好地解決問題,而不是盲目地改變優化方案(甚至更換學習架構)。
一個深度神經網絡(DNN),一旦被拆分成塊,對于 AI 領域之外的人們也再也不是一個黑箱了。相反,對于大多數有基本的統計背景的人來說,是一個個非常熟悉的話題的組合。我相信他們隻需要學習很少的一些(隻是那些如何将這一塊塊知識組合一起)知識就可以在一個全新的領域獲得不錯的洞察力。
從線性回歸開始,借着 R-notebook,通過解決一系列的數學和程式設計問題直至了解深度神經網絡(DNN)。希望能夠借此展示出來,你所需學習的新知識其實隻有很少的一部分。

筆記
<a href="https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Filkarman%2FDemoNeuralNet%2Fblob%2Fmaster%2F01_LinearRegression.ipynb" target="_blank">github.com/ilkarman/De…</a>
<a href="https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Filkarman%2FDemoNeuralNet%2Fblob%2Fmaster%2F02_LogisticRegression.ipynb" target="_blank">github.com/ilkarman/De…</a>
<a href="https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Filkarman%2FDemoNeuralNet%2Fblob%2Fmaster%2F03_NeuralNet.ipynb" target="_blank">github.com/ilkarman/De…</a>
<a href="https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Filkarman%2FDemoNeuralNet%2Fblob%2Fmaster%2F04_Convolutions.ipynb" target="_blank">github.com/ilkarman/De…</a>
在 R 中解決最小二乘法的電腦的閉包解決方案隻需如下幾行:
變量 beta_hat 所形成的向量包含的數值,定義了我們的“機器學習模型”。線性回歸是用來預測一個連續的變量的(例如:這架飛機會延誤多久)。在預測分類的時候(例如:這架飛機會延誤嗎-會/不會),我們希望我們的預測能夠落在0到1之間,這樣我們可以将其轉換為各個種類的事件發生的可能性(根據所給的資料)。
從随機地猜測權重開始
将所猜測的權重值代入損失函數中
将猜測值移向梯度的相反方向移動一小步(即我們所謂的“學習頻率”)
重複上述步驟 N 次
線性回歸中所用的損失函數是均方誤差函數:
要使用 GD 方法我們隻需要找出 beta_hat 的偏導數(即 'delta'/梯度)
在 R 中實作方法如下:
200次的疊代之後我們會得到和閉包方法一樣的梯度與參數。除了這代表着我們的進步意外(我們使用了 GD),這個疊代方法在當閉包方法因矩陣過大,而無法計算矩陣的逆的時候,也非常有用(因為有記憶體的限制)。
邏輯回歸即一種用來解決二項分類的線性回歸方法。它與标準的線性回歸主要的兩種不同在于:
我們使用一種稱為 logistic-sigmoid 的 ‘激活’/連結函數來将輸出壓縮至 0 到 1 的範圍内
不是最小化損失的方差而是最小化伯努利分布的負對數似然
其它的都保持不變。
我們可以像這樣計算我們的激活函數:
我們可以在 R 中這樣建立對數似然函數:
這個損失函數(邏輯損失或對數損失函數)也叫做交叉熵損失。交叉熵損失根本上來講是對“意外”的一種測量,并且會成為所有接下來的模型的基礎,是以值得多花一些時間。
如果我們還像以前一樣建立最小平方損失函數,由于我們目前擁有的是一個非線性激活函數(sigmoid),那麼損失函數将因不再是凸函數而使優化變得困難。
我們可以為兩個分類設立自己的損失函數。當 y=1 時,我們希望我們的損失函數值在預測值接近0的時候變得非常高,在接近1的時候變得非常低。當 y=0 時,我們所期望的與之前恰恰相反。這導緻了我們有了如下的損失函數:
這裡的損失函數中的 delta 與我們之前的線性回歸中的 delta 非常相似。唯一的不同在于我們在這裡将 sigmoid 函數也應用在了預測之中。這意味着邏輯回歸中的梯度下降函數也會看起來很相似:
邏輯回歸的推廣即為多項邏輯回歸(也稱為 ‘softmax 函數’),是對兩項以上的分類進行預測的。我尚未在 R 中建立這個例子,因為下一步的神經網絡中也有一些東西簡化之後與之相似,然而為了完整起見,如果你仍然想要建立它的話,我還是要強調一下這裡主要的不同。
首先,我們不再用 sigmoid 函數來講我們所得的值壓縮在 0 至 1 之間:
我們用 softmax 函數來将 n 個值的和壓縮至 1:
這樣意味着每個類别所得的值,可以根據所給的條件,被轉化為該類的機率。同時也意味着當我們希望提高某一分類的權重來提高它所獲得的機率的時候,其它分類的出現機率會有所下降。也就是說,我們的各個類别是互斥的。
其次,我們使用一個更加通用的交叉熵損失函數:
這與我們剛開始的等式是相同的。然而,我們現在将 j=2 的條件放寬。這裡的交叉熵損失函數可以被看出來有着與二項分類的邏輯輸出的交叉熵有着相同的梯度。
然而,即使梯度有着相同的公式,也會因為激活函數代入了不同的值而不一樣(用了 softmax 而不是邏輯中的 sigmoid)。
在大多數的深度學習架構中,你可以選擇‘二項交叉熵(binary_crossentropy)’或者‘分類交叉熵(categorical_crossentropy)’損失函數。這取決于你的最後一層神經包含的是 sigmoid 還是 softmax 激活函數,相對應着,你可以選擇‘二項交叉熵(binary_crossentropy)’或者‘分類交叉熵(categorical_crossentropy)’。而由于梯度相同,神經網絡的訓練并不會被影響,然而所得到的損失(或評測值)會由于搞混它們而錯誤。
之是以要涉及到 softmax 是因為大多數的神經網絡,會在各個類别互斥的時候,用 softmax 層作為最後一層(讀出層),用多項交叉熵(也叫分類交叉熵)損失函數,而不是用 sigmoid 函數搭配二項交叉熵損失函數。盡管多項 sigmoid 也可以用于多類别分類(并且會被用于下個例子中),但這總體上僅用于多項不互斥的時候。有了 softmax 作為輸出,由于輸出的和被限制為 1,我們可以直接将輸出轉化為機率。
一個神經網絡可以被看作為一系列的邏輯回歸堆疊在一起。這意味着我們可以說,一個邏輯回歸實際上是一個(帶有 sigmoid 激活函數)無隐藏層的神經網絡。
如果将神經網絡看作兩個東西的結合會很有用:1)很多的邏輯回歸堆疊在一起形成‘特征生成器’ 2)一個 softmax 回歸函數構成的單個讀出層。近來深度學習的成功可歸功于‘特征生成器’。例如:在以前的計算機視覺領域,我們需要痛苦地聲明我們需要找到各種長方形,圓形,顔色和結合方式(與經濟學家們如何決定哪些互相作用需要用于線性回歸中相似)。現在,隐藏層是對決定哪個特征(哪個‘互相作用’)需要提取的優化器。很多的深度學習實際上是通過用一個訓練好的模型,去掉讀出層,然後用那些特征作為輸入(或者是促進決策樹(boosted decision-trees))來生成的。
我們現在可以在 Scratch 平台上用 R 通過四個函數建立一個神經網絡了。
我們首先初始化權重:
neuralnetwork <- function(sizes, training_data, epochs, mini_batch_size, lr, C, verbose=FALSE, validation_data=training_data)
由于我們将參數進行了複雜的結合,我們不能簡單地像以前一樣将它們初始化為 1 或 0,神經網絡會是以而在計算過程中卡住。為了防止這種情況,我們采用高斯分布(不過就像那些優化方法一樣,這也有許多其他的方法):
我們使用随機梯度下降(SGD)作為我們的優化方法:
作為 SGD 方法的一部分,我們更新了
我們用來計算 delta 的算法是反向傳播算法。
在這個例子中我們使用交叉熵損失函數,産生了以下的梯度:
同時,為了與我們的邏輯回歸例子保持連續,我們在隐藏層和讀出層上使用 sigmoid 激活函數:
反向傳播函數被定義為:
請在筆記中檢視完整的代碼-然而原則還是一樣的:我們有一個正向傳播,使得我們在網絡中将權重傳導過所有神經層,并産生預測值。然後将預測值代入損失梯度函數中并将所有神經層中的權重更新。
這總結了神經網絡的建成(搭配上你所需要的盡可能多的隐藏層)。将隐藏層的激活函數換為 ReLU
CNN 卷積神經網絡用一種很有趣的方式看待圖像(提取特征)。開始時,他們隻觀察圖像的很小一部分(每次),比如說一個大小為 5*5 像素的框(一個過濾器)。2D 用于圖像的卷積,是将這個框掃遍整個圖像。這個階段會專門用于提取顔色和線段。然而,下一個神經層會轉而關注之前過濾器的結合,因而‘放大來觀察’。在一定數量的層數之後,神經網絡會放的足夠大而能識别出形狀和更大的結構。
這些過濾器最終會成為神經網絡需要去學習、識别的‘特征’。接着,它就可以通過統計各個特征的數量來識别其與圖像标簽(如‘籃球’或‘貓’)的關系。這個方法看起來對圖檔來講很自然-因為它們可以被拆成小塊來描述(它們的顔色,紋理等)。CNN 看起來在圖像分形特征分析方面會蓬勃發展。這也意味着它們不一定适合其他形式的資料,如 excel 工作單中就沒有固有的樣式:我們可以改變任意幾列的順序而資料還是一樣的——不過在圖像中交換像素點的位置就會導緻圖像的改變。
我們在 R 中如此定義 2D 卷積函數:
并用它對一個圖檔應用了一個 3*3 的過濾器:
你可以檢視筆記來看結果,然而這看起來是從圖檔中提取線段。否則,卷積可以‘銳化’一張圖檔,就像一個3*3的過濾器:
很顯然我們可以随機地随機地初始化一些個數的過濾器(如:64個):
我們可以用以下的函數可視化這個 map:
在運作這個函數的時候我們意識到了整個過程是如何地高密度計算(與标準的全連接配接神經層相比)。如果這些 feature-map 不是那些那麼有用的集合(也就是說,很難在此時降低損失)然後反向傳播會意味着我們将會得到不同的權重,與不同的 feature-map 相關聯,對于進行的聚類很有幫助。
<b>原文釋出時間為:2017年7月12日</b>
<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>