深度學習與計算機視覺系列(7)_神經網絡數據預處理,正則化與損失函數

作者:寒小陽 && 龍心塵
時間:2016年1月。
出處:
http://blog.csdn.net/han_xiaoyang/article/details/50451460
http://blog.csdn.net/longxinchen_ml/article/details/50451493
聲明:版權所有,轉載請聯繫作者並註明出處

1. 引言

上一節我們講完了各種激勵函數的優缺點和選擇,以及網絡的大小以及正則化對神經網絡的影響。這一節我們講一講輸入數據預處理、正則化以及損失函數設定的一些事情。

2. 數據與網絡的設定

前一節提到前向計算涉及到的組件(主要是神經元)設定。神經網絡結構和參數設定完畢之後,我們就得到得分函數/score function(忘記的同學們可以翻看一下之前的博文),總體說來,一個完整的神經網絡就是在不斷地進行線性映射(權重和input的內積)和非線性映射(部分激勵函數作用)的過程。這一節我們會展開來講講數據預處理權重初始化損失函數的事情。

2.1 數據預處理

在卷積神經網處理圖像問題的時候,圖像數據有3種常見的預處理可能會用到,如下。我們假定數據表示成矩陣爲X,其中我們假定X是[N*D]維矩陣(N是樣本數據量,D爲單張圖片的數據向量長度)。

  • 去均值,這是最常見的圖片數據預處理,簡單說來,它做的事情就是,對待訓練的每一張圖片的特徵,都減去全部訓練集圖片的特徵均值,這麼做的直觀意義就是,我們把輸入數據各個維度的數據都中心化到0了。使用python的numpy工具包,這一步可以用X -= np.mean(X, axis = 0)輕鬆實現。當然,其實這裏也有不同的做法:簡單一點,我們可以直接求出所有像素的均值,然後每個像素點都減掉這個相同的值;稍微優化一下,我們在RGB三個顏色通道分別做這件事。
  • 歸一化,歸一化的直觀理解含義是,我們做一些工作去保證所有的維度上數據都在一個變化幅度上。通常我們有兩種方法來實現歸一化。一個是在數據都去均值之後,每個維度上的數據都除以這個維度上數據的標準差(X /= np.std(X, axis = 0))。另外一種方式是我們除以數據絕對值最大值,以保證所有的數據歸一化後都在-1到1之間。多說一句,其實在任何你覺得各維度幅度變化非常大的數據集上,你都可以考慮歸一化處理。不過對於圖像而言,其實這一步反倒可做可不做,因爲大家都知道,像素的值變化區間都在[0,255]之間,所以其實圖像輸入數據天生幅度就是一致的。

上述兩個操作對於數據的作用,畫成示意圖,如下:
數據的去均值與歸一化

  • PCA和白化/whitening,這是另外一種形式的數據預處理。在經過去均值操作之後,我們可以計算數據的協方差矩陣,從而可以知道數據各個維度之間的相關性,簡單示例代碼如下:
# 假定輸入數據矩陣X是[N*D]維的
X -= np.mean(X, axis = 0) # 去均值
cov = np.dot(X.T, X) / X.shape[0] # 計算協方差

得到的結果矩陣中元素(i,j)表示原始數據中,第i維和第j維直接愛你的相關性。有意思的是,其實協方差矩陣的對角線包含了每個維度的變化幅度。另外,我們都知道協方差矩陣是對稱的,我們可以在其上做矩陣奇異值分解(SVD factorization):

U,S,V = np.linalg.svd(cov)

其中U爲特徵向量,我們如果相對原始數據(去均值之後)做去相關操作,只需要進行如下運算:

Xrot = np.dot(X, U)

這麼理解一下可能更好,U是一組正交基向量。所以我們可以看做把原始數據X投射到這組維度保持不變的正交基底上,從而也就完成了對原始數據的去相關。如果去相關之後你再求一下Xrot的協方差矩陣,你會發現這時候的協方差矩陣是一個對角矩陣了。而numpy中的np.linalg.svd更好的一個特性是,它返回的U是對特徵值排序過的,這也就意味着,我們可以用它進行降維操作。我們可以只取top的一些特徵向量,然後做和原始數據做矩陣乘法,這個時候既降維減少了計算量,同時又保存下了絕大多數的原始數據信息,這就是所謂的主成分分析/PCA

Xrot_reduced = np.dot(X, U[:,:100])

這個操作之後,我們把原始數據集矩陣從[N*D]降維到[N*100],保存了前100個能包含絕大多數數據信息的維度。實際應用中,你在PCA降維之後的數據集上,做各種機器學習的訓練,在節省空間和時間的前提下,依舊能有很好的訓練準確度。

最後我們再提一下whitening操作。所謂whitening,就是把各個特徵軸上的數據除以特徵向量,從而達到在每個特徵軸上都歸一化幅度的結果。whitening變換的幾何意義和理解是,如果輸入的數據是多變量高斯,那whitening之後的 數據是一個均值爲0而不同方差的高斯矩陣。這一步簡單代碼實現如下:

#白化數據
Xwhite = Xrot / np.sqrt(S + 1e-5)

提個醒:whitening操作會有嚴重化噪聲的可能。注意到我們在上述代碼中,分母的部分加入了一個很小的數1e-5,以防止出現除以0的情況。但是數據中的噪聲部分可能會因whitening操作而變大,因爲這個操作的本質是把輸入的每個維度都拉到差不多的幅度,那麼本不相關的有微弱幅度變化的噪聲維度,也被拉到了和其他維度同樣的幅度。當然,我們適當提高墳墓中的安全因子(1e-5)可以在一定程度上緩解這個問題。

下圖爲原始數據到去相關白化之後的數據分佈示意圖:
去相關與白化

我們來看看真實數據集上的操作與得到的結果,也許能對這些過程有更清晰一些的認識。大家都還記得CIFAR-10圖像數據集吧。訓練集大小爲50000*3072,也就是說,每張圖片都被展成一個3072維度的列向量了。然後我們對原始50000*3072數據矩陣做SVD分解,進行上述一些操作,再可視化一下,得到的結果示意圖如下:

CIFAR-10降維與可視化

我們稍加解釋一下,最左邊是49張原始圖片;左起第2幅圖是最3072個特徵向量中最top的144個,這144個特徵向量包含了絕大多數數據變量信息,而其實它們代表的是圖片中低頻的信息;左起第3幅圖表示PCA降維操作之後的49張圖片,使用上面求得的144個特徵向量。我們可以觀察到圖片好像被蒙上了一層東西一樣,模糊化了,這也就表明了我們的top144個特徵向量捕捉到的都是圖像的低頻信息,不過我們發現圖像的絕大多數信息確實被保留下來了;最右圖是whitening的144個數通過乘以U.transpose()[:144,:]還原回圖片的樣子,有趣的是,我們發現,現在低頻信息基本都被濾掉了,剩下一些高頻信息被放大呈現。

實際工程中,因爲這個部分講到數據預處理,我們就把基本的幾種數據預處理都講了一遍,但實際卷積神經網中,我們並沒有用到去相關和whitening操作。當然,去均值是非常非常重要的,而每個像素維度的歸一化也是常用的操作。

特別說明,需要特別說明的一點是,上述的預處理操作,一定都是在訓練集上先預算的,然後應用在交叉驗證/測試集上的。舉個例子,有些同學會先把所有的圖片放一起,求均值,然後減掉均值,再把這份數據分作訓練集和測試集,這是不對的親!!!

2.2 權重初始化

我們之前已經看過一個完整的神經網絡,是怎麼樣通過神經元和連接搭建起來的,以及如何對數據做預處理。在訓練神經網絡之前,我們還有一個任務要做,那就是初始化參數。

錯誤的想法:全部初始化爲0,有些同學說,那既然要訓練和收斂嘛,初始值就隨便設定,簡單一點就全設爲0好了。親,這樣是絕對不行的!!!爲啥呢?我們在神經網絡訓練完成之前,是不可能預知神經網絡最後的權重具體結果的,但是根據我們歸一化後的數據,我們可以假定,大概有半數左右的權重是正數,而另外的半數是負數。但設定全部初始權重都爲0的結果是,網絡中每個神經元都計算出一樣的結果,然後在反向傳播中有一樣的梯度結果,因此迭代之後的變化情況也都一樣,這意味着這個神經網絡的權重沒有辦法差異化,也就沒有辦法學習到東西。

很小的隨機數,其實我們依舊希望初始的權重是較小的數,趨於0,但是就像我們剛剛討論過的一樣,不要真的是0。綜合上述想法,在實際場景中,我們通常會把初始權重設定爲非常小的數字,然後正負儘量一半一半。這樣,初始的時候權重都是不一樣的很小隨機數,然後迭代過程中不會再出現迭代一致的情況。舉個例子,我們可能可以這樣初始化一個權重矩陣W=0.0001*np.random.randn(D,H)。這個初始化的過程,使得每個神經元的權重向量初始化爲多維高斯中的隨機採樣向量,所以神經元的初始權重值指向空間中的隨機方向。

特別說明:其實不一定更小的初始值會比大值有更好的效果。我們這麼想,一個有着非常小的權重的神經網絡在後向傳播過程中,回傳的梯度也是非常小的。這樣回傳的”信號”流會相對也較弱,對於層數非常多的深度神經網絡,這也是一個問題,回傳到最前的迭代梯度已經很小了。

方差歸一化,上面提到的建議有一個小問題,對於隨機初始化的神經元參數下的輸出,其分佈的方差隨着輸入的數量,會增長。我們實際上可以通過除以總輸入數目的平方根,歸一化每個神經元的輸出方差到1。也就是說,我們傾向於初始化神經元的權重向量爲w = np.random.randn(n) / sqrt(n),其中n爲輸入數。

我們從數學的角度,簡單解釋一下,爲什麼上述操作可以歸一化方差。考慮在激勵函數之前的權重w與輸入x的內積s=niwixi 部分,我們計算一下s 的方差:

Var(s)=Var(inwixi)=inVar(wixi)=in[E(wi)]2Var(xi)+E[(xi)]2Var(wi)+Var(xi)Var(wi)=inVar(xi)Var(wi)=(nVar(w))Var(x)

注意,這個推導的前2步用到了方差的性質。第3步我們假定輸入均值爲0,因此E[xi]=E[wi]=0 。不過這是我們的一個假設,實際情況下並不一定是這樣的,比如ReLU單元的均值就是正的。最後一步我們假定wi,xi 是獨立分佈。我們想讓s的方差和輸入x的方差一致,因此我們想讓w的方差取值爲1/n,又因爲我們有公式Var(aX)=a2Var(X) ,所以a應該取值爲a=1/n ,numpy裏的實現爲w = np.random.randn(n) / sqrt(n)

對於初始化權重還有一些類似的研究和建議,比如說Glorot在論文Understanding the difficulty of training deep feedforward neural networks就推薦使用能滿足Var(w)=2/(nin+nout) 的權重初始化。其中nin,nout 是前一層和後一層的神經元個數。而另外一篇比較新的論文Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification,則指出尤其對於ReLU神經元,我們初始化方差應該爲2.0/n,也就是w = np.random.randn(n) * sqrt(2.0/n)目前的神經網絡中使用了很多ReLU單元,因此這個設定其實在實際應用中使用最多。

偏移量/bias初始化:相對而言,bias項初始化就簡單一些。我們很多時候簡單起見,直接就把它們都設爲0.在ReLU單元中,有些同學會使用很小的數字(比如0.01)來代替0作爲所有bias項的初始值,他們解釋說這樣也能保證ReLU單元一開始就是被激活的,因此反向傳播過程中不會終止掉回傳的梯度。不過似乎實際的實驗過程中,這個優化並不是每次都能起到作用的,因此很多時候我們還是直接把bias項都初始化爲0。

2.3 正則化

在前一節裏我們說了我們要通過正則化來控制神經網絡,使得它不那麼容易過擬合。有幾種正則化的類型供選擇:

  • L2正則化,這個我們之前就提到過,非常常見。實現起來也很簡單,我們在損失函數裏,加入對每個參數的懲罰度。也就是說,對於每個權重w ,我們在損失函數里加入一項12λw2 ,其中λ 是我們可調整的正則化強度。順便說一句,這裏在前面加上1/2的原因是,求導/梯度的時候,剛好變成λw 而不是2λw 。L2正則化理解起來也很簡單,它對於特別大的權重有很高的懲罰度,以求讓權重的分配均勻一些,而不是集中在某一小部分的維度上。我們再想想,加入L2正則化項,其實意味着,在梯度下降參數更新的時候,每個權重以W += -lambda*W的程度被拉向0。

  • L1正則化,這也是一種很常見的正則化形式。在L1正則化中,我們對於每個權重w 的懲罰項爲λ|w| 。有時候,你甚至可以看到大神們混着L1和L2正則化用,也就是說加入懲罰項λ1w+λ2w2 ,L1正則化有其獨特的特性,它會讓模型訓練過程中,權重特徵向量逐漸地稀疏化,這意味着到最後,我們只留下了對結果影響最大的一部分權重,而其他不相關的輸入(例如『噪聲』)因爲得不到權重被抑制。所以通常L2正則化後的特徵向量是一組很分散的小值,而L1正則化只留下影響較大的權重。在實際應用中,如果你不是特別要求只保留部分特徵,那麼L2正則化通常能得到比L1正則化更好的效果

  • 最大範數約束,另外一種正則化叫做最大範數約束,它直接限制了一個上行的權重邊界,然後約束每個神經元上的權重都要滿足這個約束。實際應用中是這樣實現的,我們不添加任何的懲罰項,就按照正常的損失函數計算,只不過在得到每個神經元的權重向量w⃗  之後約束它滿足w⃗ 2<c 。有些人提到這種正則化方式幫助他們提高最後的模型效果。另外,這種正則化方式倒是有一點很吸引人:在神經網絡訓練學習率設定很高的時候,它也能很好地約束住權重更新變化,不至於直接掛掉。

  • Dropout,親,這個是我們實際神經網絡訓練中,用的非常多的一種正則化手段,同時也相當有效。Srivastava等人的論文Dropout: A Simple Way to Prevent Neural Networks from Overfitting最早提到用dropout這種方式作爲正則化手段。一句話概括它,就是:在訓練過程中,我們對每個神經元,都以概率p保持它是激活狀態,1-p的概率直接關閉它。

下圖是一個3層的神經網絡的dropout示意圖:


Dropout示意圖

可以這麼理解,在訓練過程中呢,我們對全體神經元,以概率p做了一個採樣,只有選出的神經元要進行參數更新。所以最後就從左圖的全連接到右圖的Dropout過後神經元連接圖了。需要多說一句的是,在測試階段,我們不用dropout,而是直接從概率的角度,對權重配以一個概率值。

簡單的Dropout代碼如下(這是簡易實現版本,但是不建議使用,我們會分析爲啥,並在之後給出優化版):


p = 0.5 # 設定dropout的概率,也就是保持一個神經元激活狀態的概率

def train_step(X):
  """ X contains the data """

  # 3層神經網絡前向計算
  H1 = np.maximum(0, np.dot(W1, X) + b1)
  U1 = np.random.rand(*H1.shape) < p # 第一次Dropout
  H1 *= U1 # drop!
  H2 = np.maximum(0, np.dot(W2, H1) + b2)
  U2 = np.random.rand(*H2.shape) < p # 第二次Dropout
  H2 *= U2 # drop!
  out = np.dot(W3, H2) + b3

  # 反向傳播: 計算梯度... (這裏省略)
  # 參數更新... (這裏省略)

def predict(X):
  # 加上Dropout之後的前向計算
  H1 = np.maximum(0, np.dot(W1, X) + b1) * p 
  H2 = np.maximum(0, np.dot(W2, H1) + b2) * p 
  out = np.dot(W3, H2) + b3

上述代碼中,在train_step函數中,我們做了2次Dropout。我們甚至可以在輸入層做一次dropout。反向傳播過程保持不變,除了我們要考慮一下U1,U2

很重要的一點是,大家仔細看predict函數部分,我們不再dropout了,而是對於每個隱層的輸出,都用概率p做了一個幅度變換。可以從數學期望的角度去理解這個做法,我們考慮一個神經元的輸出爲x(沒有dropout的情況下),它的輸出的數學期望爲px+(1p)0 ,那我們在測試階段,如果直接把每個輸出x都做變換xpx ,其實是可以保持一樣的數學期望的。

上述代碼的寫法有一些缺陷,我們必須在測試階段對每個神經的輸出都以p的概率輸出。考慮到實際應用中,測試階段對於時間的要求非常高,我們可以考慮反着來,代碼實現的時候用inverted dropout,即在訓練階段就做相反的幅度變換/scaling(除以p),這樣在測試階段,我們可以直接把權重拿來使用,而不用附加很多步用p做scaling的過程。inverted dropout的示例代碼如下:

""" 
Inverted Dropout的版本,把本該花在測試階段的時間,轉移到訓練階段,從而提高testing部分的速度
"""

p = 0.5 # dropout的概率,也就是保持一個神經元激活狀態的概率

def train_step(X):
  # f3層神經網絡前向計算
  H1 = np.maximum(0, np.dot(W1, X) + b1)
  U1 = (np.random.rand(*H1.shape) < p) / p # 注意到這個dropout中我們除以p,做了一個inverted dropout
  H1 *= U1 # drop!
  H2 = np.maximum(0, np.dot(W2, H1) + b2)
  U2 = (np.random.rand(*H2.shape) < p) / p # 這個dropout中我們除以p,做了一個inverted dropout
  H2 *= U2 # drop!
  out = np.dot(W3, H2) + b3

  # 反向傳播: 計算梯度... (這裏省略)
  # 參數更新... (這裏省略)

def predict(X):
  # 直接前向計算,無需再乘以p
  H1 = np.maximum(0, np.dot(W1, X) + b1) 
  H2 = np.maximum(0, np.dot(W2, H1) + b2)
  out = np.dot(W3, H2) + b3

對於dropout這個部分如果你有更深的興趣,歡迎閱讀以下文獻:
1) 2014 Srivastava 的論文Dropout paper
2) Dropout Training as Adaptive Regularization

  • bias項的正則化,其實我們在之前的博客中提到過,我們大部分時候並不對偏移量項做正則化,因爲它們也沒有和數據直接有乘法等交互,也就自然不會影響到最後結果中某個數據維度的作用。不過如果你願意對它做正則化,倒也不會影響最後結果,畢竟總共有那麼多權重項,才那麼些bias項,所以一般也不會影響結果。

實際應用中:我們最常見到的是,在全部的交叉驗證集上使用L2正則化,同時我們在每一層之後用dropout,很常見的dropout概率爲p=0.5,你也可以通過交叉驗證去調整這個值。

2.4 損失函數

剛纔討論了數據預處理、權重初始化與正則化相關的問題。現在我們回到訓練需要的關鍵之一:損失函數。對於這麼複雜的神經網絡,我們也得有一個評估準則去評估預測值和真實結果之間的吻合度,也就是損失函數。神經網絡裏的損失函數,實際上是計算出了每個樣本上的loss,再求平均之後的一個形式,即L=1NiLi ,其中N是訓練樣本數。

2.4.1 分類問題

  • 分類問題是到目前爲止我們一直在討論的。我們假定一個數據集中每個樣本都有唯一一個正確的標籤/類別。我們之前提到過有兩種損失函數可以使用,其一是SVM的hinge loss:

Li=jyimax(0,fjfyi+1)

另外一個是Softmax分類器中用到的互熵損失:

Li=logefyijefj
  • 問題:特別多的類別數。當類別標籤特別特別多的時候(比如ImageNet包含22000個類別),層次化的Softmax,它將類別標籤建成了一棵樹,這樣任何一個類別,其實就對應tree的一條路徑,然後我們在每個樹的結點上都訓練一個Softmax以區分是左分支還是右分支。

  • 屬性分類,上述的兩種損失函數都假定,對於每個樣本,我們只有一個正確的答案yi 。但是在有些場景下,yi 是一個二值的向量,每個元素都代表有沒有某個屬性,這時候我們怎麼辦呢?舉個例子說,Instagram上的圖片可以看作一大堆hashtag裏的一個tag子集,所有一張圖片可以有多個tag。對於這種情況,大家可能會想到一個最簡單的處理方法,就是對每個屬性值都建一個二分類的分類器。比如,對應某個類別的二分類器可能有如下形式的損失函數:

Li=jmax(0,1yijfj)

其中的求和是針對有所的類別j,而yij 是1或者-1(取決於第i個樣本是否有第j個屬性的標籤),打分向量fj 在類別/標籤被預測到的情況下爲正,其他情況爲負。注意到如果正樣本有比+1小的得分,或者負樣本有比-1大的得分,那麼損失/loss就一直在累積。

另外一個也許有效的解決辦法是,我們可以對每個屬性,都單獨訓練一個邏輯迴歸分類器,一個二分類的邏輯迴歸分類器只有0,1兩個類別,屬於1的概率爲:

P(y=1x;w,b)=11+e(wTx+b)=σ(wTx+b)

又因爲0,1兩類的概率和爲1,所以歸屬於類別0的概率爲P(y=0x;w,b)=1P(y=1x;w,b) 。一個樣本在σ(wTx+b)>0.5 的情況下被判定爲1,對應sigmoid函數化簡一下,對應的是得分wTx+b>0 。這時候的損失函數可以定義爲最大化似然概率的形式,也就是:

Li=jyijlog(σ(fj))+(1yij)log(1σ(fj))

其中標籤yij 爲1(正樣本)或者0(負樣本),而δ 是sigmoid函數。

2.4.2 迴歸問題

迴歸是另外一類機器學習問題,主要用於預測連續值屬性,比如房子的價格或者圖像中某些東西的長度等。對於迴歸問題,我們一般計算預測值和實際值之間的差值,然後再求L2範數或者L1範數用於衡量。其中對一個樣本(一張圖片)計算的L2範數損失爲:

Li=fyi22

而L1範數損失函數是如下的形式:

Li=fyi1=jfj(yi)j

注意

  • 迴歸問題中用到的L2範數損失,比分類問題中的Softmax分類器用到的損失函數,更難優化。直觀想一想這個問題,一個神經網絡最後輸出離散的判定類別,比訓練它去輸出一個個和樣本結果對應的連續值,要簡單多了。
  • 我們前面的博文中提到過,其實Softmax這種分類器,對於輸出的打分結果具體值是不怎麼在乎的,它只在乎各個類別之間的打分幅度有沒有差很多(比如二分類兩個類別的得分是1和9,與0.1和0.9)。
  • 再一個,L2範數損失健壯性更差一些,異常點和噪聲都可能改變損失函數的幅度,而帶來大的梯度偏差。
  • 一般情況下,對於迴歸問題,我們都會首先考慮,這個問題能否轉化成對應的分類問題,比如說我們把輸出值劃分成不同的區域(切成一些桶)。舉個例子,如果我們要預測一部電影的豆瓣打分,我們可以考慮把得分結果分成1-5顆星,而轉化成一個分類問題。
  • 如果你覺得問題確實沒辦法轉化成分類問題,那要小心使用L2範數損失:舉個例子,在神經網絡中,在L2損失函數之前使用dropout是不合適的。

如果我們遇到迴歸問題,首先要想想,是否完全沒有可能把結果離散化之後,把這個問題轉化成一個分類問題。

3. 總結

總結一下:

  • 在很多神經網絡的問題中,我們都建議對數據特徵做預處理,去均值,然後歸一化到[-1,1]之間。
  • 從一個標準差爲2/n 的高斯分佈中初始化權重,其中n爲輸入的個數。
  • 使用L2正則化(或者最大範數約束)和dropout來減少神經網絡的過擬合。
  • 對於分類問題,我們最常見的損失函數依舊是SVM hinge loss和Softmax互熵損失。

參考資料與原文

cs231n 神經網絡數據預處理正則化與損失函數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章