UFLDL 是史丹佛大學 CS294A課程 的教學 wiki。課程設定很科學,循序漸進,每次課程都是在上次的基礎上增添一些東西,還有作業可以讓你直觀地感受所學內容。雖然沒有涉及 RNN (迴圈神經網路),但作為 CNN(卷積神經網路)的基礎課程還是很不錯的。 如果你對 監督學習、邏輯迴歸、梯度下降 等基礎概念並不熟悉,可以先學習 之前的課程。 關於課程作業的 Python 程式碼已經放到了 Github 上,點選 課程程式碼 就能去 Github 檢視,程式碼中的錯誤和改進歡迎大家指出。
稀疏自編碼器
大家知道現在深度學習在計算機視覺領域全面開花結果,得到了許多之前無法想象的好結果。而就在這之前大家還要花費很大的精力來人工設計特徵。這部分學習的 稀疏自編碼器 正是向自動學習特徵邁出的第一步,也是這門課程的基礎。( 下圖為作業中的神經網路,左圖為輸入影像,右圖為訓練後的輸出影像 )
稀疏自編碼器 的基本模型是一個三層的神經網路,在學習時讓網路輸出的目標值接近於輸入的影像本身,從而學習影像中的特徵。直接學習一個恆等函式的話沒有什麼意義,所以我們要對隱含層做出一些限制,比如減小神經元的個數,網路就會被迫壓縮資料並嘗試重建輸入影像( 類似 PCA 所做的降維 )。當我們加入懲罰讓神經元在大部分情況下都不啟用的時候,網路能夠學習到十分有趣的邊緣特徵。隱含層的神經元在觀察到輸入影像中某個特定角度的邊緣特徵時才會被啟用( 下圖為作業的訓練結果,這和神經科學發現的人類視覺皮層V1中的神經元的啟用方式相似 )。 對於 稀疏自編碼器 詳細的學習方法,可以參考 上一篇文章。向量化
向量化 是在程式碼中竟可能地使用向量運算來代替原有 For 迴圈。由於能夠使用向量運算的庫通常對向量運算進行過優化,所以可以大大加速程式碼的執行速度。
舉一段 反向傳播 演算法中的例子,它的作用是對於每一組資料執行反向傳播並累計誤差。
for i in range(m):
d3 = (a3[i] - data[i]) * a3[i] * (1.0 - a3[i])
d2 = (np.dot(d3,Theta2)[1:] + beta * sparsity_delta) * a2[i][1:] * (1.0 - a2[i][1:])
delta1 = delta1 + np.dot(d2.reshape(-1,1),a1[[i]])
delta2 = delta2 + np.dot(d3.reshape(-1,1),a2[[i]])
複製程式碼
wiki 中有建議將這段程式碼向量化,不過沒有給出具體的虛擬碼。筆者原以為最後只剩這部分沒改的話對效率也不會有十分大的影響,但事實是向量化之後,學習速度得到了明顯的提升 Orz。( 這個章節對於後面的課程可以說是必須的,因為最痛苦的事莫過於學習速度很慢,你的程式碼又有問題需要重新學習 )
d3 = (a3 - data) * a3 * (1.0 - a3)
d2 = (np.dot(d3, Theta2)[:,1:] + beta * sparsity_delta) * a2[:,1:] * (1.0 - a2[:,1:])
delta1 = np.dot(d2.T, a1)
delta2 = np.dot(d3.T, a2)
複製程式碼
作業將 稀疏自編碼器 使用在 MNIST 資料集上,學習到了很有意思的邊緣特徵。
預處理
課程中使用的預處理為 ZCA Whitening,它的想法是去除輸入特徵的相關性。首先將特徵變換到無相關性的座標下,通過相關性係數拉伸,再變換到原先的座標系,使得特徵在原先座標系下不再相關。( 對於二維特徵,整個過程如下圖 )
事實證明這也是一種視網膜處理影像的粗糙模型。具體而言,當你的眼睛感知影像時,由於一幅影像中相鄰的部分在亮度上十分相關,大多數臨近的“畫素”在眼中被感知為相近的值。因此,如果人眼需要分別傳輸每個畫素值(通過視覺神經)到大腦中,會非常不划算。取而代之的是,視網膜進行一個與 ZCA 中相似的去相關操作 ( 這是由視網膜上的 ON-型 和 OFF-型 光感受器細胞將光訊號轉變為神經訊號完成的 )。由此得到對輸入影像的更低冗餘的表示,並將它傳輸到大腦。Softmax
在 Softmax 這部分,我們使用它來為 稀疏自編碼器 自學習到的特徵來分類,它的特點是輸出向量是歸一化的( 和為一 )。不過在之前 MNIST分類問題 中,我們對每個輸出層的神經元使用了邏輯迴歸( 每個輸出都是二元分類 )。這就產生了一個問題,什麼時候該使用 Softmax,什麼時候使用邏輯迴歸呢?
Ng 告訴我們問題的關鍵在於類別之間是否互斥。比如我們的手寫字元識別問題,每個數字類別是互斥的,你寫的數字不可能既是 1 又是 2。如果你的資料可能既屬於分類 A 又 屬於分類 B,這時候你就需要使用邏輯迴歸了。
增加深度
這時課程開始漸入正題,Ng 告訴我們 稀疏自編碼器 能夠自己學習到區域性影像的邊緣特徵,如果使用中間層的輸出作為特徵再訓練一個稀疏自編碼器,這時的隱含層會學習如何去組合邊,從而構成輪廓、角等更高階的特徵。再加上稀疏自編碼器是無監督學習,所以我們可以反覆使用這種方式來構建更深的網路,並在最後對整個網路進行 微調 來高效地完成深層網路的訓練。
上圖是作業中訓練的網路,它的整個訓練步驟如下:
- 使用輸入影像訓練稀疏自編碼器,獲得隱含層的權值
- 將隱含層的輸出作為特徵訓練第二層稀疏自編碼器
- 將第二層稀疏自編碼器隱含層的輸出作為特徵,原資料的結果目標值,訓練 Softmax 分類器
- 微調 整個網路的權值,完成網路的訓練
大影像處理
在先前的網路中,隱含層的每個神經元連線了所有輸入層的神經元。當訓練圖片增大時,所要學習的權值也不斷增多,以至於訓練速度慢到不可接受。受 視覺皮層的神經元只感受區域性接受資訊 的啟發( 同時也是自然影像有其固有特性,也就是說,影像的一部分的統計特性與其他部分是一樣的 ),我們可以讓隱含層只連線一部分的輸入單元來提取特徵。
比如輸入資料是 96x96 的圖片,我們先隨機取這些圖片中 8x8 的區域性影像,用 稀疏自編碼器 學習 100 個特徵( 隱含層單元及輸入的權值,作業中學習到的特徵如下圖 )。
然後使用每個隱含層神經元,對原圖的每個部分做特徵提取( 這個過程相當於對原影像做 卷積 的操作,如下圖 )。對於每張 96x96 的影像,我們將得到 100 個 89x89 的卷積特徵影像,用於後續處理。不難發現,當我在原圖上通過卷積提取特徵的時候,只有某些位置的啟用是最強烈的,所以我們使用 池化 來減小冗餘。池化 就是在卷積輸出的每個區域,使用均值或者最大值來代替原來的輸出,它帶來的一個好處是提取的特徵對影像的平移不敏感。
經過上述 卷積 與 池化 的處理,特徵數量已經大幅減少。最後我們使用這些特徵來訓練 Softmax 分類器。後續
通過 Coursera 課程,我們學習了一些基本的 機器學習 演算法,對整個領域有了一個大體的認識。UFLDL 課程在它的基礎上更近一步,介紹了深度學習中 CNN 的網路架構和學習方法。有了這些基礎就不難理解由 CNN 架構演化而來的各種網路( CNN 架構的演進 )。
對於之後要學習的深度學習知識點,一方面考慮學習一下 RNN( 迴圈神經網路 ),以及基於這個架構演化的用來處理序列的網路;另一方面考慮學習不同的學習方法,比如 強化學習。