BA-16 cn231n斯坦佛大學神經網路影象識別課程Refine
零、鳥瞰卷積神經網路
卷積神經網路由多個層次所構成。按照順序有:輸入 - 卷積層 - 激發層 - 縮放層 - 全連線層(放在最後的全連線層被稱作分類器層)。
英文:INPUT-CONV-RELU-POOL-FC
其中,卷積層用於縮小資料規模的同時負責一部分的學習任務,激發層是純粹的激發函式陣列,縮放層純粹用於縮小資料規模,全連線層會承擔一部分學習任務。
只有卷積層和全連線層具有學習能力。
如果去掉卷積層和激發層,縮放層,剩下 {輸入 - 全連線層},那麼就得到了一個純粹線性分類器層,仍然具有一定的學習能力。
如果將[卷積層-激發層]打包,[全連線層-縮放層]打包,然後將其堆疊起來 形成如下結構:
輸入 -[卷積層-激發層] x 10-[全連線層-縮放層] x 3-全連線層分類器
就形成了一個比較完整的卷積神經網路,卷積層輔以激發層可以選擇縮放或者不縮放,多層堆疊。每一層都可以學習。
全連線層輔以縮放層進行縮放,將縮放後且經過資料交給分類器得到最終結果。每一層也都可以學習。
以上是卷積神經網路的結構。
訓練時,每個週期都計算一次懲罰函式,並且利用反向傳播演算法,逐層調整所有層次中卷積層與全連線層的引數。
完成訓練,給進去輸入,就能拿到輸出。準確率一般能達到95%以上。
一、概述
斯坦佛大學的課程包括三個主模組以及一個作業模組。
主模組包括:0號模組:環境搭建,該部分指導學生搭建課程所需要的軟體環境與資料環境以及作業提交環境。包括python的安裝以及相關科學元件numpy等模組。以及用於生成斯坦佛格式的作業檔案的命令列程式。但是在自己的原裝python環境下,事實上只需要額外命令列執行:
pip install numpy
即可進行課程中所有的實驗。原裝python環境除了沒有來自CIFA-10的大資料集以及沒有作業提交系統之外,並沒有什麼區別。
1號模組:神經網路。分為八個小章節。分別介紹了:
影象識別入門:小學級影象分類技術(kNN)
線性分類器Classifier:SVM向量機以及Softmax
優化策略:隨機梯度下降法Stochastic Gradiant Descent
優化的具體方式:反向傳播Backpropogation以及對反向傳播方法的整體把握
建立神經網路的第一步:建立整體結構(啟用函式、神經網路層次架構以及選擇)
建立神經網路第二步:準備好資料與懲罰函式(資料預處理、引數初始化、泛化規則、懲罰函式)
建立神經網路第三步:訓練與監視(梯度檢查、健康評估、訓練過程監管、引數更新、超引數優化、模型評估)
一個迷你的神經網路模型程式碼實現
2號模組:卷積神經網路。分為三個小章節。
卷積神經網路的結構以及卷積層(卷積層次、層次結構)
解釋卷積神經網路與視覺化(相似視覺化疊層、反捲積、資料梯度、欺騙、人工比較)
如何藉助面向CV程式設計技術快速搭建自己的卷積神經網路
二、精煉
1號模組:主要介紹神經網路,神經網路包含引數,具有學習能力,在卷積神經網路中,卷積層和全連線層都用到了神經網路。
前面幾個小單元主要是介紹了為了實現神經網路所需要的技術準備。然後三步走建立完整神經網路。最後一個單元將會完整實現一個神經網路。
1、影象識別入門:小學級影象分類技術(kNN-k nearest neighbors)
最近鄰影象分類技術:衡量個圖片中相同位置畫素之間的距離。將距離求總和獲得的值作為懲罰函式。值越大說明兩張圖片距離越遠越不相似。該技術不需要訓練,只需要將圖片依次和訓練集中每個圖片相互比較,輸出畫素距離最近的那張圖片的標籤即可。正確率根據距離計算公式不同在30%到40%間擺動。
最近鄰距離計算方式:最近鄰的距離有幾種計算方式,包括直接取差值之和、對差值取絕對值(L1 Loss)、對差值取平方(L2 Loss)。本質上沒有什麼不同。
kNN:如果取相似度前k高的圖片,然後將其標籤得投票得結果作為分類結果,就叫做kNN。k Nearest Neighbors。正確率一般在30%左右。
kNN的超引數優化:kNN有一個超引數(需要認為調整的引數)——k,一般需要從零開始迴圈,並不斷輸出每個引數下模型的準確率找到最合適k值。
2、線性分類器:SVM向量機以及Softmax
分類器是一個神經模型中最後一站。但並不一定是整個模型的最後一站分類器和分類器層還是不一樣的。但是不管是哪一層,只要是神經網路,一定就需要有一個分類器。因為分類器的功能不僅僅是分類,還包括懲罰函式的生成。在學習時,分類器層是最先接觸“錯題”的正是分類器使得模型有了輸出結果與學習的能力。
只要是一層神經網路(比如鳥瞰中的卷積層和全連線層),那就一定會配著分類器。可以說,分類器是神經網路層的組成部分。
線性分類器,顧名思義其分類方式都是線性的,即通過一次函式匯出評分。
假設W是引數,x是樣本資料,而b是偏倚引數,那麼一個線性分類器的引數包括W和b。線性地計算評分:
評分所代表的是該樣本是該類的可能性值。如果W是向量,那麼就能同時計算多個類的評分,如果W是矩陣,x是向量,就可以同時計算多維資料的多個類的評分。如果兩個都是矩陣就可以同時給多個多維資料分類了。實際應用中W一般是矩陣,x一般是向量。
而不同的線性分類器之間的區別在於——其生成懲罰函式的方式不同。
SVM的懲罰函式使用公式
Li是第i個訓練樣本的懲罰,sj與syi分別表示第i個訓練樣本對於j標籤的評分(對不正確分類的評分)、對本身標籤的評分,Δ是閾值,可以手動設定。
本質上,就是把犯的錯誤進行量化並求和。
而Softmax的懲罰使用公式
Li是第i個訓練樣本的懲罰,fyi代表對正確標籤的評分,fj代表對錯誤標籤的評分。
本質上,是計算了分類正確率的相反數。
此外,兩種線性分類器的懲罰函式都會需要包含一項額外的懲罰函式:泛化懲罰函式——所有引數的平方和 與一個引數的乘積
該引數是一個超引數,可以人為調整。這一項的作用是去峰值,防止出現單個引數對整個模型影響太大。使所有引數均勻地發揮作用,使得模型能夠比較好的泛化。
3、優化策略:隨機梯度下降法Stochastic Gradiant Descent
完全隨機法很蠢,每個週期都隨機生成所有的引數,企圖隨機生成出一個好的模型。很蠢,被否定。準確率跟盲猜差不多。
區域性隨機法是從一的點開始在引數空間(每個引數作為一個維度形成高維空間)內布朗運動,稍微聰明點,但是還是很蠢,被否定。準確率比盲猜好一點,15%,但還是不靠譜。
梯度下降法:取二維引數空間與懲罰函式組成的三維曲面為例,梯度指向的方向就是平面上升的方向,那麼取反,就是懲罰函式下降的方向。而這個方向正好就可以給這兩個引數以指導。
因此將引數減去這個梯度乘以一個學習速率即可完成一個週期的訓練。
當前所有神經網路的訓練都是使用梯度下降法。但是有個問題,那就是不一定能夠準確的找到最低點。很有可能會找到一個小坑然後就停止了。因此就需要多隨機設定幾個起點。
這就是隨機梯度下降法Stochastic Gradiant Descent
另外梯度下降法一般只適用於懲罰函式為凸函式的情況。反例一般是沒有的。因為任何函式在區域性都可能成為區域性凸函式。
如果資料太多,那麼可以取一小部分,分批次進行訓練,這不耽誤整體效果,這就是分塊梯度下降Mini-batch gradient descent
4、優化的具體方式:反向傳播Backpropogation以及對反向傳播方法的整體把握
由於神經網路具有多層,而能夠直接計算得到梯度的只有最後一層——分類器層。但是前面的神經網路也需要訓練。這就需要進行反向傳播演算法。
根據鏈式法則,函式的導數可以分解成複合函式的偏導的連積。
那麼在計算上一層梯度的時候就可以將這一層的引數直接乘上由結果直接生成的梯度矩陣再與上一層的引數矩陣相乘得到。不會收到最終複雜函式形式的影響。
PS.過去經常使用sigmoid函式作為啟用函式,因為其導數的形式十分簡潔sigmoid(1-sigmoid)。但是後來被拋棄了,因為這個函式有個致命的缺陷。那就是在資料很大時,函式值會逼近1,這時求導數就幾乎沒有意義了,這就是眾所周知的——數值飽和中毒問題。當然,資料太小也會有這樣的問題。所以應該使用ReLU作為啟用函式。x是得分。
呃,好像劇透了。那做個標記,這邊記錄過了。#1
5、建立神經網路的第一步:建立整體結構(啟用函式、神經網路層次架構以及選擇)
神經網路就是模擬神經元的活動,眾多的引數相當軸突axon,引數為正那就有連線,引數為零那就無連線。貼在人家樹突dendrites臉上,資料來了那就是訊號,如果超過了閾值那就激發了啟用函式activation function形成了輸出。
所以仿生唄,那神經網路就需要有這些結構:分類器,啟用函式。
分類器就是使用用前面幾個單元裡面介紹的分類器了。而啟用函式就是使用#1處的啟用函數了ReLU。此外還有另外幾種啟用函式,Tanh正切函式,不過毛病和sigmoid差不多LeackyReLU是二進位制版ReLU,MaxOUT取最大值。
分類器的作用已經做過筆記了。啟用函式的的作用就是根據分類器的輸出決定是否要形成訊號向下一層輸出。
這裡課程單獨介紹了神經網路的命名習慣。一般來說,包括最終的分類器層在內,有幾層神經網路那就算是幾層的網路。
最後課程通過數學證明,說,神經網路是萬能的。
6、建立神經網路第二步:準備好資料與懲罰函式(資料預處理、引數初始化、泛化規則、懲罰函式)
資料預處理:第一種方式:用mean把資料中心挪到原點,然後把資料上下界縮放一下。
第二種方式:主成分分析PCA——principle components analysis——利用奇異值分解獲得原始資料協方差的正定二次型空間向量基矩陣,取前N列與原來的資料矩陣相乘即可得到前N種最重要的主成分。這種方式,可以將資料中的相關變數合併成一個變數並相應加權,並且能夠自動排序獲得主成分序列。由此可以大大地降低資料的維度。
# 假設原始資料 X 規模為 [ N x D ]
X -= np.mean(X, axis = 0) # 以零為中心 #2
cov = np.dot(X.T, X) / X.shape[0] # 獲得協方差矩陣
U,S,V = np.linalg.svd(cov) # U V均可以作為空間向量基矩陣
Xrot_reduced = np.dot(X, U[:,:100])
最後將其進行上下限的整理。規約到同一個數量級。
引數初始化:全零:不行,模型學不動。
小隨機數:行,不一定總是好,因為太小了,學起來很慢。
動態矯正Calibrating the variances with 1/sqrt(n).:特別行,將每個引數都初始化成 1/樣本數
稀疏初始化:區域性進行動態矯正初始化,避免對稱性。
偏倚量(b)初始化:0.01不錯。
批初始化Batch Normalization:新興的初始化方式,很火特別好http://arxiv.org/abs/1502.03167。但是初學沒必要深究。
泛化:實現方式就是前面分類器裡介紹的泛化懲罰函式,除了那裡記錄的L2方式還有L1方式(絕對值)以及拋棄方式(dropout扔掉沒用的引數) 。與L2的泛化方式不同。L1和dropout不是讓引數均勻地起作用而是武斷地扔掉一些引數,這就很有可能會造成最終的引數空間不再包含最優點甚至於偏差很遠。因此實際經常使用L2方式。
懲罰函式:由組成神經網路的分類器中的懲罰函式提供。
7、建立神經網路第三步:訓練與監視(梯度檢查、健康評估、訓練過程監管、引數更新、超引數優化、模型評估)
檢查梯度:初步確定應該使用多大的學習速率等超引數。檢查引數大小,以及各個變數,避免因為過小或者過大造成資料溢位。確定好合適的泛化引數,不要讓引數們被拍成餅。
在檢查過程中確保不要使用dropout與引數增強。只檢查其中幾個維度即可。
健康評估:估算一下初始懲罰函式大小。嘗試執行一個週期,進行比對。
做好警惕不能對任何資料進行度過擬合。
過程監管:通過作圖實時監控準確率與懲罰函式的變化情況。
引數更新:主要是學習速率的確定方法。小的模型可以隨便自己試幾個數。
大的模型就需要嚴格研究了有這些方法步衰step decay,指數衰exponential decay,倒數衰1/t decay,另外還有啥也不管(Vanilla),動量變化量(Momentum update),納斯特羅動量(Nestorov Momentum),Adagrad,RMSprop,Adam。比較了半天,發現還是RMSprop最好。
cache = decay_rate * cache + (1 - decay_rate) * dx**2 x += - learning_rate * dx / (np.sqrt(cache) + eps)
事實上是用了一個動態的梯度平方均值。
超引數優化:一般情況下,多執行緒進行。多個執行緒檢驗不同的超引數,把結果記錄一下,然後由主執行緒找出最好的那套超引數。
超引數優化一般取一部分資料而不是全體進行。並且一般是在一個範圍內進行隨機選擇。範圍從大到小,精度從低到高。
此外新興了貝葉斯超引數優化。Bayesian Hyperparameter Optimizationhttps://github.com/JasperSnoek/spearmint、http://www.cs.ubc.ca/labs/beta/Projects/SMAC/、http://jaberg.github.io/hyperopt/。但是對於複雜卷積網路沒啥區別。不看也罷。
模型評估:得到一堆模型然後挑挑揀揀。
可以通過同模型多種初始化、取準確率排名靠前的模型、同一個模型的不同時間點的快照、平均引數這些方法來得到效果比較好的模型。
8、一個迷你的神經網路模型程式碼實現
總體結構:為{輸入層-隱藏層-輸出層} 共兩層神經網路。其中輸出層的的神經網路的偏倚量不必要進行梯度下降。
以下程式碼是可以跑通的(裝有numpy),不過在jupyter notebook下執行味道最好
# 進行資料的生成 import numpy as np N = 100 # 每個類別的樣本數 D = 2 # 資料的維度,這裡是二維的平面所以資料的維度是2 K = 3 # 種類數 X = np.zeros((N*K,D))# 資料矩陣每一行都是一個單獨的樣本。一共N*K個樣本,每個樣本都有D個維度 y = np.zeros(N*K,dtype='uint8')# 每個樣本的標籤 import matplotlib.pyplot as plt for j in range(K):# 對所有的K種樣本類別 ix = range(N*j,N*(j+1))# ix將會根據樣本種類1,2,3分別選擇序列:1..100,101..200,201..300對這些點進行對應的類別標記。 r = np.linspace(0.0,1,N)# 獲得等差數列,作為向徑r t = np.linspace(j*4,(j+1)*4,N) + np.random.randn(N)*0.2 # 獲得具有最大100x0.2=20度浮動的Θ角度值 X[ix] = np.c_[r*np.sin(t),r*np.cos(t)] # 獲得螺旋散點 y[ix] = j # 將這100個數據都標記成當前的類別 plt.scatter(X[:,0],X[:,1],c=y,s=40,cmap=plt.cm.Spectral)# 做散點圖,引數1,2為x,y軸座標,顏色用y標記 這裡將會展示樣本點即訓練資料的分佈情況 plt.show() # 神經網路 # 初始化 h = 100 # 隱藏層的尺度 W = 0.01 * np.random.randn(D,h) # 隨機生成相對於每個維度總和期望均為1的引數,將第一層網路的引數進行初始化 b = np.zeros((1,h))# 將偏倚引數(常數項)初始化為零 W2 = 0.01 * np.random.randn(h,K) # 隨機生成h*K的引數,將第二層網路進行初始化 b2 = np.zeros((1,K))# 將第二層引數進行初始化 # 超引數(訓練過程中可以手動改變而且可以進行優化的引數) step_size = 1e-0 # 學習速率 reg = 1e-3 # 泛化強度。用於引數的泛化規約。 # 梯度下降迴圈 num_examples = X.shape[0] # 獲得訓練資料的樣本量 for i in range(10000): # 進行10000次訓練
# SoftMax分類器.線性分類功能 # 獲得所有樣本的分類結果, [N x K] hidden_layer = np.maximum(0, np.dot(X, W) + b) # 這裡使用的ReLU啟用函式 正則化線性啟用單元,對線性啟用函式進行了小於0時的歸零修正。 scores = np.dot(hidden_layer, W2) + b2 # 將通過第一層的資料再經過第二層分類器獲得全樣本分類的評分結果 # 到這裡資料就已經完全通過了兩層神經網路,接下來就是懲罰函式和梯度下降了
# SoftMax線性分類器.懲罰函式功能 # 計算類別的概率,[N x K],通過自然指數函式化歸probs的實際意義是將N個樣本對K種類別的分類結果全部轉換為概率的形式。方便後續的損失函式計算 exp_scores = np.exp(scores)# 中間表示式 probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True) # [N x K] # 損失函式,計算總熵以及反泛化懲罰 correct_logprobs = -np.log(probs[range(num_examples),y])# 這裡的probs陣列以批處理的方式形成N * K 個分類結果的概率形式損失。使用了正向的評判 # 標準。事實上就是判斷正確的樣本的總標量(probs中存放的就是分類正確的概率型。) # 陣列中使用雙重批處理將會形成一位結果。N*K。將y中 0,1,2類別的分量依次從Probs中取出。 # 事實上, 就是選擇了樣本源標籤位置處生成的損失值累加。 data_loss = np.sum(correct_logprobs)/num_examples # 獲得預測總損失 # SoftMax線性分類器.反泛化懲罰 reg_loss = 0.5*reg*np.sum(W*W) + 0.5*reg*np.sum(W2*W2)# 獲得總的反泛化損失。目的是消除樣本中的引數峰。 loss = data_loss + reg_loss # 真正的總損失 if i % 1000 == 0: print ("iteration %d: loss %f" % (i, loss)) # 每進行1000次迭代時輸出迭代次數與懲罰
# 反向傳播 # 通過預測的類別概率矩陣計算梯度 dscores = probs dscores[range(num_examples),y] -= 1 dscores /= num_examples # 通過反向傳播計算梯度 # 第一組反向傳播,通過結果層向第二層引數進行傳播 dW2 = np.dot(hidden_layer.T, dscores) db2 = np.sum(dscores, axis=0, keepdims=True) # 第二組反向傳播到隱藏層 dhidden = np.dot(dscores, W2.T) # backprop the ReLU non-linearity # 反向傳播到ReLU的非線性結構 dhidden[hidden_layer <= 0] = 0 # finally into W,b dW = np.dot(X.T, dhidden) db = np.sum(dhidden, axis=0, keepdims=True) # add regularization gradient contribution # 向步進引數中新增上泛化引數 dW2 += reg * W2 dW += reg * W # 進行最後的引數步進 W += -step_size * dW b += -step_size * db W2 += -step_size * dW2 # 評估引數的準確率 hidden_layer = np.maximum(0, np.dot(X, W) + b) scores = np.dot(hidden_layer, W2) + b2 predicted_class = np.argmax(scores, axis=1) print ('training accuracy: %.2f' % (np.mean(predicted_class == y)))
2號模組:介紹了卷積神經網路。以及一些具體的技巧與注意事項。
1、卷積神經網路的結構以及卷積層(卷積層次、層次結構)
鳥瞰已經介紹過了,卷積神經網路組成為:輸入 - 卷積層 - 激發層 - 縮放層 - 全連線層(放在最後的全連線層被稱作分類器層)。英文:INPUT-CONV-RELU-POOL-FC
輸入層包含資料。
卷積層CONV,已經記錄過,與全連線層FC(FC是標準的神經網路)都是一種神經網路。但是事實上,CONV並不是標準的神經網路。
一個神經網路是兩個層之間,所有的引數之間都能兩兩連結,但是卷積層並不是這樣。卷積層在接收上一個層次資料時,每個每個資料點不會同時連線上一層所有的資料,而是僅僅連線一個小區域內的資料Local Connectivity,這個區域叫做接收區receptive field。這樣就更加貼切地模擬了神經元的實際情況(神經元之間不可能總是全連線)。
假設W標誌接收的引數個數,F表示接受區大小,P表示邊界處空輸入個數(填充以0),S表示兩個接受區之間的步長。接收之後生成的引數數量就是(W-F+2P)/S+1。比如輸入7x7接受區3x3步長1,P=0,那麼就會得到同樣3 x 3的輸出。
卷積神經網路能在形成比較複雜的架構的同時擁有該表規模的能力。
縮放層Pooling layer又稱池化層。是一個純粹用於縮放資料規模的層次結構。可以扔http://arxiv.org/abs/1412.6806
泛化層Nomolization Layer有時候會有泛化層,作用和泛化懲罰差不多。
此外有很多業界的卷積模型:LeNet,AlexNet,ZF Net,GoogleNet,VGGNet,ResNet
需要額外注意的是:每個網路層都要保留好其梯度。每個版本的完整神經網路模型都要做好快照(萬一哪天優化著優化著突然學壞了呢( ̄_ ̄|||))。
2、解釋卷積神經網路與視覺化(相似視覺化疊層、反捲積、資料梯度、欺騙、人工比較)
(一定程度上)理解自己的神經網路,可以將卷積層的結果、啟用函式的結果視覺化。
或者用t-SNE方法,將自己模型的想法視覺化為雲——一堆圖,我空間距離近的,模型會說:“俺也一樣”。我感覺空間距離遠的,模型會說:“俺也一樣”。
處理遮掩http://arxiv.org/abs/1311.2901
騙,偷襲,神經網路http://arxiv.org/abs/1412.6572
3、如何藉助面向CV程式設計技術快速搭建自己的卷積神經網路
分類討論:
如果我的資料集比他們小,那麼不管和他們是否相似,那就訓練個分類器層安上就得了。
如果我資料集比他們大而且類似,那就放心繼續用自己的資料訓練。
如果我的資料集大而且非常不一樣,那就重新初始化並訓練整個網路。
神經網路界的github:https://github.com/BVLC/caffe/wiki/Model-Zoo
三、尾聲
神經網路指的是兩個資料矩陣之間的網。不是資料矩陣的某個狀態。也不僅僅是指引數。它是由分類器和激發器組成的,就像一塊完整的神經組織。