1. 程式人生 > >【神經網路和深度學習】筆記

【神經網路和深度學習】筆記

文章導讀:

1.交叉熵損失函式

  1.1 交叉熵損失函式介紹

  1.2 在MNIST數字分類上使用交叉熵損失函式

  1.3 交叉熵的意義以及來歷

  1.4 Softmax

2. 過擬合和正則化

  2.1 過擬合

  2.2 正則化

  2.3 為什麼正則化可以減輕過擬合問題

  2.4 正則化的其它方法

3. 引數初始化

4. 其它方法

  4.1 隨機梯度下降演算法的改進

  4.2 其它神經元模型

我們前面已經學習了反向傳播演算法,它是我們學習神經網路的基礎。這一章將會介紹一系列的方法技巧來改善反向傳播演算法的效果,進而改善學習到的神經網路模型。

這些技巧包括:1. 新的損失函式:交叉熵損失函式;2. 四種“正則化”方法(L1,L2,dropout,人造訓練資料),這些是為了我們的模型有更好的擴充套件性,在新的資料集上能有更好的表現,而不至於過擬合;3. 一種更好的初始化權重的方法;4. 一系列幫助選擇超參的啟發式方法。隨後會在之前程式碼的基礎上實現這些優化來改善我們手寫數字識別的準確度。

當然這些也只是優化神經網路的冰山一角,還有很多其他的方法。不過掌握了這些基本的方法,可以加深我們對於問題的理解,對於新的方法技巧也可以很快的上手。

一. 交叉熵損失函式

 失敗是成功之母,我們學習的過程中免不了要犯錯,知錯能改,善莫大焉。神經網路的學習也是如此,如果錯誤都沒有定義好的話,模型的學習過程當然也會很緩慢。

理想情況下,我們希望我們的學習演算法越快收斂到最佳狀態越好,那麼實際情況呢?我們先來看一個例子:

這是一個最簡單的模型,只有一個神經元,一個輸入。我們希望它實現一個簡單的功能:當輸入為1的時候,輸出為0。 這個功能很容易實現,我們手動設定引數都很容易就能滿足。不過為了說明情況,我們還是使用梯度下降學習演算法來學習這些引數。

我們設定初始引數為:$w = 0.6, b = 0.9$。可以計算得出此時對於輸入1的情況輸出為$\frac{1}{1+e^{-(0.6*1+0.9)}} \approx 0.82 $。這個結果距離我們理想的輸出0還是差的很遠。我們設定一個合適的學習率$\eta = 0.15$,選擇二次損失函式,學習的過程如下:

可以看到,學習的過程很快,在前期的epoch中,損失函式迅速下降。最後得到0.09的損失,雖然不是0,但是已經很低了。假設現在我們選另外一組初始引數,此時$w = 2, b = 2$, 此時初始輸出為$\frac{1}{1+e^{-(2*1+2)}}=0.98$,距離0更遠了。此時學習過程如下:

可以看到,學習的開始過程非常慢,前150個epochs,引數基本就沒有變化,最後結束時得到的輸出為0.2跟0還差的比較遠,可見這次訓練得到的模型並不理想。

這個現象拿來跟人的行為比較的話就顯得很奇怪。我們人類在學習的時候,一般錯的越離譜的地方改正起來當然就越快,可是這個神經網路卻不是這樣,初始時距離正確值更遠的時候學習的卻更慢。這當然不是我們希望的,那我們能找到一種好的方法來避免這種效率低下的學習情況嗎?

為了理解這中情況發生的原因,考慮到神經元學習時改變率是跟損失函式的偏導數$\frac{\partial C}{\partial w}$和$\frac{\partial C}{\partial b}$有關的,學習慢就說明這些值很小。為了理解為什麼小,我們先看一下之前定義的二次損失函式:

$$C = \frac{(y-a)^2}{2} \quad (54)$$

這裡$a=\sigma (z)$是輸入為1時神經網路的輸出,然後$y=0$是理想的輸出值,求其偏導數得到:

$$\frac{\partial C}{\partial w} = (a - y)\sigma '(z)x = a\sigma '(z) \quad (55)$$

$$\frac{\partial C}{\partial b} = (a-y)\sigma '(z) = a\sigma '(z) \quad (56)$$

為了理解這些式子的行為,先看一下$\sigma '(z)$的函式影象:

我們發現在輸出接近1的時候,函式非常平緩,也就是說此時$\sigma '(z)$很小,這也會導致$\frac{\partial C}{\partial w}$和$\frac{\partial C}{\partial b}$很小,從而導致學習速度緩慢。後面會看到,對於大多數神經網路來說,學習速率慢都主要是由於這個原因,而不僅僅是針對這個單個神經元的情況。

1.1 交叉熵損失函式介紹

在介紹交叉熵函式之前先看一個稍微複雜點的例子:

由之前的一個輸入改為了多個輸入,其中$z = \sum_jw_jx_j+b$。交叉熵損失函式定義為:

$$C = -\frac{1}{n}\sum_x [yln(a) + (1-y) ln (1-a)] \quad (57)$$

初看這個式子可能會覺得很奇怪,為什麼可以把它當成是一個損失函式呢?

這主要是由於它的兩個性質,首先,它是非負的,即$C>0$,這個從公式(57)的形式可以驗證。其次,當神經元輸出接近我們理想輸出的時候,交叉熵將會接近0(當然這需要假設理想輸出為0或1,對於一般的分類問題是滿足這個條件的)。

然後來看為什麼交叉熵損失就可以避免訓練速度下降,求偏導:

$$\frac{\partial C}{\partial w_j}=-\frac{1}{n}\sum_x (\frac{y}{\sigma (z)} - \frac{(1-y)}{1-\sigma (z)}) \frac{\partial \sigma}{\partial w_j} \quad (58)$$

$$\frac{\partial C}{\partial w_j}=-\frac{1}{n}\sum_x (\frac{y}{\sigma (z)} - \frac{(1-y)}{1-\sigma (z)}) \sigma '(z) x_j \quad (59)$$

$$\frac{\partial C}{\partial w_j} = \frac{1}{n}\sum_x \frac{\sigma '(z)x_j}{\sigma (z)(1-\sigma (z))}(\sigma (z) - y) \quad (60)$$

由於$\sigma (z) = \frac{1}{1+e^{-z}}$,容易驗證$\sigma '(z) = \frac{\sigma (z)}{1-\sigma (z)}$。代入上式得到:

$$\frac{\partial C}{\partial w_j} = \frac{1}{n}\sum_x x_j (\sigma (z) - y) \quad (61)$$

這個式子表明,這個權重的梯度跟$\sigma (z) -y$有關,也就是說,誤差越大,學習的就越快,和我們希望的情況一樣。它避免了二次損失函式中由$\sigma '(z)$導致的學習速率下降的問題。

同樣的方法,對bias有:

$$\frac{\partial C}{\partial b} = \frac{1}{n}\sum_x (\sigma (z) -y) \quad (62)$$

練習:

問題

證明$\sigma '(z) = \sigma (z) (1 - \sigma (z))$

答案:

$\sigma '(z) = -\frac{-e^{-z}}{(1+e^{-z})^2} = \frac{e^{-z}}{1+e^{-z}} \frac{1}{1+e^{-z}} = \sigma (z) (1-\sigma (z))$

回到一開始一個神經元的那個例子,這次使用交叉熵損失函式。初始引數一樣為$w=0.6, b=0.9$,學習過程結果為:

和之前二次損失函式一樣,學習過程很迅速。再來看之前二次損失函數出問題的初始引數$w=2,b=2$,使用交叉熵損失函式:

這次初始的學習過程並沒有因為輸出偏離理想值過多而導致學習緩慢,可以看到損失函式的值在開始階段很陡峭,也就是說學習速率很快。

多層多個輸出神經元的情況,我們也可以寫出起交叉熵損失函式:

$$C=-\frac{1}{n} \sum_x \sum_j [y_j ln(a^L_j) + (1 - y_j) ln (1 - a^L_j)] \quad (65)$$

這裡和之前的公式(57)沒什麼區別,除了之前是一個輸出,這裡是多個輸出,將每個神經元的輸出求一個交叉熵然後再相加。

對於交叉熵的計算,一般情況下,假設有兩個概率分佈$p_j$和$q_j$,有$\sum_j p_j ln (q_j)$,將上面一個神經元的輸出a和1-a看成兩個概率分佈就和這個對應起來了。

但是,當輸出層是多個神經元的時候,向量$a^L$通常就不構成一個概率分佈了,那麼$\sum_j p_j ln (q_j)$就沒什麼意義了。但是我們可以將公式(63)看作是對單個輸出神經元求交叉熵再求和的過程,因為每個神經元輸出都可以被看成是一個在{0, 1}上的概率分佈,概率為a和1-a。

那麼什麼時候該使用交叉熵損失呢?事實上,在啟用函式為sigmoid函式時,交叉熵損失基本上總是更好的選擇。考慮到我們需要隨機初始化引數,這就可能會導致輸出嚴重偏離理想輸出的情況(例如本該輸出0,卻輸出了1)。如果使用二次損失,就會導致學習過程很慢。

練習:

問題一:

有的時候大家大家可能會出現把$-[yln(a) + (1-y) ln(1-a)]$中的y和a記反的情況,記成了$-[aln(y) + (1-a)ln(1-y)]$,當$y=0$或$y=1$時,第二個式子會出現什麼問題,第一個式子會出現這樣的問題嗎,為什麼?

答案:

$y=0$時,$aln(y)$只在$a=0$時為0,其餘為$\infty$,而$(1-a)ln(1-y)=0$;$y=1$的情況類似。

對於第一個式子則沒有這樣的問題,$y=0$時,有$-ln(1-a)$,$y=1$時,有$-ln(a)$。

問題二:

在一開始的單神經元討論中,我們知道當對所有輸入優$\sigma (z) \approx y$時,交叉熵最小,這個討論當時是建立在y為0或1的情況,也就是在分類的情況下討論的。證明對於其他問題(例如迴歸問題),當y是[0,1]之間的數的時候,仍然有$\sigma (z) =y$對於所有輸入成立將最小化交叉熵損失,即此時交叉熵為$$C = -\frac{1}{n}\sum_x [y ln(y) + (1-y) ln(1-y)] \quad (64)$$

答案:

因為每個輸出樣本是獨立的,這其實就是求$C(a) = yln(a) + (1-y) ln(1-a)$最大值時的a。

求導$\frac{\partial C}{\partial a} = \frac{y}{a} + \frac{y-1}{1-a} = \frac{y-a}{a(1-a)}$,因為$a(1-a)>0$,所以當$a<y$時,$\frac{\partial C}{\partial a}>0$, 當$a>y$時,$\frac{\partial C}{\partial a} <0$,函式先增後減,在$y=a$時取最大值,即$a = \sigma (z) \approx y$的時候,交叉熵損失最小。

拓展:

 多層網路多神經元輸出的情況:

對於二次損失函式,此時有:

$$\frac{\partial C}{\partial w^L_{jk}} = \frac{1}{n}\sum_x a^{L-1}_k (a^L_j - y_j) \sigma '(z^L_j) \quad (65)$$

同樣的,$\sigma '(z^L_j)$會導致學習速率下降當該神經元處於錯誤的saturated狀態時。對於交叉熵的情況,有:

$$\delta ^L = a^L -y \quad (66)$$

代入上式有:

$$\frac{\partial C}{\partial w^L_{jk}} = \frac{1}{n}\sum_x a^{L-1}{k}(a^L_j - y_j) \quad (67)$$

$\sigma '(z^L_j)$就被去掉了,說明交叉熵損失同樣可以解決這種情況下學習速率下降的問題。

線性啟用函式的情況:

此時,對於二次損失函式有:

$$\delta ^L = a^L - y \quad (68)$$

由於沒有了sigmoid啟用函式有$a=z$,於是得到:

$$\frac{\partial C}{\partial w^L_{jk}} = \frac{1}{n}\sum_x a^{L-1}_k (a^L_j - y_j) \quad (69)$$

$$\frac{\partial C}{\partial b^L_j} = \frac{1}{n}\sum_x (a^L_j - y_j) \quad$$

可以看出,此時,二次損失函式並不會導致學習速率下降,是合適的損失函式。

1.2 在MNIST數字分類上使用交叉熵損失函式

類似於第一章的神經網路結構,30個神經元的隱藏層,不過這裡使用交叉熵損失函式。

 1 import mnist_loader
 2 
 3 training_data, validation_data, test_data = \
 4     mnist_loader.load_data_wrapper()
 5 import network2
 6 
 7 net = network2.Network([784, 30, 10], cost=network2.CrossEntropyCost)
 8 net.large_weight_initializer()
 9 net.SGD(training_data, 30, 10, 0.5, evaluation_data=test_data,
10     monitor_evaluation_accuracy=True)

注意到,現在我們移除了模型初始化時的引數初始化過程,而改為了使用不同的初始化函式來手動初始化這些引數。不過一開始我們還是使用之前一樣的初始化方法,只是改為了在net.large_weight_initializer()中進行。這次得到的結果是95.49%和之前使用二次損失的情況下的95.42%幾乎一致。

當我們使用100個隱藏層時,得到的結果為96.82%,比二次損失時的96.59提高了不少。考慮到錯誤率從3.41下降到3.18,接近14個百分點,已經稱得上不錯的改進了。

雖然交叉熵損失給我們帶來了或多或少的改進,但是這並不足以證明它就是一個更好的選擇。這是因為這裡並沒有對超參進行最優的選擇。在進行超參的優化選擇後,這個改善就會更加明顯,不過就目前來說,這足以使我們相信交叉熵損失是一種更好的選擇。

就本章乃至本書的後續內容來說,我們經常會對演算法進行一些優化,然後會獲得一些改善的結果。但是要使這些改善非常明顯通常需要大量的超參優化,為了避免這些工作,我們僅僅對這些優化淺嘗輒止,基本上能改善效果就行,就說明我們的優化起到了作用,而不糾結於是否獲得了最優的結果。

到目前為止,我們已經花了很大的篇幅來介紹交叉熵損失函式。可能我們會有疑問,為什麼要花這麼大力氣去使用它,最後在MNIST上也就得到了一個馬馬虎虎的結果?隨後我們還會看到其他技術,尤其是正則化將會帶來更大的改善。那為什麼要用交叉熵損失呢?一方面是因為它是一個廣泛使用的損失函式,值得去學習理解。更主要的原因是神經元的saturation是神經網路中一個很重要的問題,而交叉熵是一個很好的理解這個問題進而去處理這個問題的開端。

1.3 交叉熵的意義以及來歷

目前為止,關於交叉熵的討論還是僅僅停留在公式的推導和實現上。但是它到底有什麼意義呢,它是怎麼被創造出來的呢?

我們先看後一個問題,一開始是出於什麼樣的動機才想出了交叉熵這個概念。假設我們已經知道了演算法學習過程變慢是由於$\sigma '(z)$導致的,那麼我們就在想能不能找到一個合適的損失函式使得$\sigma '(z)$消失。使得:

$$\frac{\partial C}{\partial w_j} = x_j (a-y) \quad (71)$$

$$\frac{\partial C}{\partial b} = (a-y) \quad (72)$$

為了找到這樣的損失函式,注意到:

$$\frac{\partial C}{\partial b} = \frac{\partial C}{\partial a}\frac{\partial a}{\partial z}\frac{\partial z}{\partial b} = \frac{\partial C}{\partial a}\sigma '(z) \quad (73)$$

代入$\sigma '(z) = \sigma(z)(1-\sigma(z)) = a(1-a)$得到:

$$\frac{\partial C}{\partial b} = \frac{\partial C}{\partial a}a(1-a) \quad (74)$$

跟(72)式對比得到:

$$\frac{\partial C}{\partial a} = \frac{a-y}{a(1-a)} \quad (75)$$

通過積分可以得到:

$$C = -[yln(a) + (1-y)ln(1-a)] + constant \quad (76)$$

對於多個樣本的情況也是一樣的:

$$C = -\frac{1}{n}\sum_x [yln(a) + (1-y)ln(1-a)] + constant \quad (77)$$

通過這樣的方式就找到了交叉熵損失函式。

但是它的意義是什麼呢?我們怎麼去理解它呢?詳細解釋需要更多的篇幅,這裡簡單說一下,它來自資訊理論領域,更確切的說它描述的是狀態的隨機性。例如,假設我們神經元的輸出a表示的是輸出為1的概率,1-a為輸出為0的概率,那麼交叉熵測量的就是輸出是0還是1的隨機性。如果我們對於結果很確定,比如說概率為1或者0,那麼這種隨機性就很低,交叉熵就小,反之則大。

拓展

之前討論了在使用二次損失函式的時候,當神經元處於saturated狀態的時候,學習速率很慢。但是注意到影響學習速率的還有另外一個因素:

$$\frac{\partial C}{\partial w_j} = \frac{1}{n}\sum_x x_j (\sigma (z) - y)$$

其中的$x_j$同樣會影響到這個導數值,當其接近0的時候,$w_j$的學習速率會變得很慢。解釋為什麼沒辦法通過選擇合適的損失函式來刪除掉$x_j$的影響。

如果還記得這個式子的由來的話就很容易知道,$x_j$這項是由於$\frac{\partial z}{\partial w_j}$引入的,無論怎麼選擇損失函式都沒辦法將其去除。

1.4 Softmax

這一章我們主要將會使用交叉熵損失函式來解決訓練過程緩慢的問題,但是這裡還是要簡要介紹一下另外一種基於softmax層神經元的方法。

softmax的想法是定義一種新的神經網路輸出層,在該輸出層上,$z^L_j = \sum_k w^L_{jk}a^{L-1}_k + b^L_j$,隨後並不使用sigmoid函式計算輸出,而是對每個$z^L_j$使用softmax函式:

$$a^L_j = \frac{e^{z^L_j}}{\sum_k e^{z^L_k}} \quad (78)$$

很容易驗證$$\sum_j a^L_j = \frac{\sum_j e^{z^L_j}}{\sum_k e^{z^L_k}} = 1 \quad (79)$$

所以當其中一個輸出增加的時候,其他的輸出會減小。而且由於指數函式的原因,這些輸出值都是正數,它們的和為1,所以可以將softmax層的輸出看作是一個概率分佈。

softmax輸出層被當作概率分佈是很有意義的,因為它可以將輸出$a^L_j$看成是輸出為j的概率。例如,對於MNIST分類問題,$a^L_j$可以認為是神經網路估計這個數字是j的概率。而如果使用sigmoid函式,則沒有這樣的性質。輸出值也沒有這樣直觀的解釋。

練習:

問題:

舉出一個例子,證明在使用sigmoid輸出層的時候,$a^L_j$的和不為1

答案:

如果用sigmoid函式,基本跟1就沒有聯絡吧。隨便舉個例子,三個輸出層,分別為$z^L_1 = z^L_2 = z^L_3 = 1$,然後$a^L_1 = a^L_2 = a^L_3 = \frac{1}{1+e^{-1}} \approx 0.73$,它們的和當然不為1。

指數使得softmax輸出為正數,它們之間的和為1,所以我們還可以將softmax看作是一種對$z^L_j$進行放縮的一種方法,使得它們之間構成一個概率分佈。

練習:

問題一:

softmax函式的單調性:證明$\frac{\partial a^L_j}{\partial z^L_k}在$j=k$時為正,在$j \neq k$時為負,於是有增大$z^L_j$將會增大對應神經元j的輸出,會減小其他神經元的輸出。

答案:

由定義有$a^L_j = \frac{e^{z^L_j}}{\sum_k e^{z^L_k}}$, $\frac{\partial a^L_j}{\partial z^L_j} = \frac{e^{z^L_j}(\sum_k e^{z^L_k}) -e^{z^L_j}e^{z^L_j}}{(\sum_k e^{z^L_k})^2}=\frac{e^{z^L_j}\sum_{k\neq j} e^{z^L_k}}{(\sum_k e^{z^L_k})^2}>0$

對於任意$k \neq j$的情況,$\frac{\partial a^L_j}{\partial z^L_k} = -\frac{e^{z^L_j + z^L_k}}{(\sum_k e^{z^L_k})^2} < 0$ 

問題二:

sigmoid輸出層的一個優點是$a^L_j$只和它的加權輸入有關,因為$a^L_j = \sigma (z^L_j)$ , 解釋為什麼softmax輸出層就沒有這樣的性質,它的任意輸出$a^L_j$都和所有神經元的加權輸入有關

答案:

這個從softmax的定義式就能看出來,分母中受到其它$z^L_k$的影響,而且上面的導數值也表明$\frac{\partial a^L_j}{\partial z^L_k}$對於其他$k \neq j$的情形,導數值並不等於0,表明它會受到這些$z^L_k$的影響。

拓展:

設想我們有一個有softmax輸出層的神經網路,啟用量$a^L_j$已知,證明對應的加權輸入的形式為:$z^L_j = ln(a^L_j) + C$,其中$C$為與$j$無關的常數。

由定義可知$a^L_j = \frac{e^{z^L_j}}{\sum_k e^{z^L_k}}$,$e^{z^L_j} = a^L_j \sum_k e^{z^L_k}$,$z^L_j = ln(a^L_j \sum_k e^{z^L_k}) = ln(a^L_j) + C$,其中$C = ln(\sum_k e^{z^L_k})$為與$j$無關的常數。

在瞭解了softmax的這些概念後,我們來看一下它是怎麼處理學習變慢的問題的。先定義對數似然損失函式。$x$為一個訓練樣本輸入,$y$是理想輸出,那麼對數似然損失為:

$$C = -ln(a^L_y) \quad (80)$$

例如,在訓練MNIST圖片的時候,當輸入為7的圖片時,這個損失為$-ln(a^L_7)$。為了理解這個定義,考慮如果我們的神經網路準確度很高,有足夠的信心判斷它時7,則$a^L_7$將會接近1,對數損失將會很小,否則$a^L_7$接近0時,損失就會很大。所以對數似然符合我們對於損失函式的要求。

對該損失函式求偏導數得:

$$\frac{\partial C}{\partial b^L_j} = a^L_j - y_j \quad (81)$$

$$\frac{\partial C}{\partial w^L_{jk}} = a^{L-1}_k (a^L_j -y_j) \quad (82)$$

可以看出這個函式和交叉熵損失函式時計算的偏導數是一樣的。事實上,softmax輸出層結合對數似然損失的情況和sigmoid輸出層結合交叉熵損失的情況非常相似。

既然這兩種情況很相似,那麼該如何做出選擇呢?事實上,對於大多數情況這兩種方案都是可行的。在這章的剩餘部分,我們會使用sigmoid+交叉熵損失的組合,在第六章再使用softmax+log似然損失。轉變的原因只是我們希望迎合一些學術論文中的神經網路的例子。大多數情況下,softmax+log似然損失更適合於講輸出解釋為概率的情況,尤其適合於類似MNIST的分類問題。

拓展:

拓展一:

證明方程(81)和(82)

 $\frac{\partial C}{\partial w^L_{jk}} = \frac{\partial C}{\partial a^L_j}\frac{\partial a^L_j}{\partial z^L_j}\frac{\partial z^L_j}{\partial w^L_{jk}}$

其中$\frac{\partial C}{\partial a^L_j} = -\frac{1}{a^L_j} = -\frac{\sum_k e^{z^L_k}}{e^{z^L_j}}$

由之前計算可得$\frac{\partial a^L_j}{\partial z^L_j} = \frac{e^{z^L_j}\sum_{k\neq j} e^{z^L_k}}{(\sum_k e^{z^L_k})^2}$

$\frac{\partial z^L_j}{\partial w^L_{jk}} = \frac{\partial (w^L_{jk}a^{L-1}_k + b^L_j)}{\partial w^L_{jk}} = a^{L-1}_k$

三式相乘得到$\frac{\partial C}{\partial w^L_{jk}} = -a^{L-1}_k\frac{\sum_{k\neq j} e^{z^L_k}}{\sum_k e^{z^L_k}} = a^{L-1}_k (a^L_j - y_j)$,這裡的$y_j$需要說明一下,$y$是對應j的輸出向量,也就是在分量j上為1,其餘均為0

同理$\frac{\partial C}{\partial b^L_j} = a^L_j -y_j$因為$\frac{\partial z^L_j}{\partial b^L_j} = 1$

拓展二:softmax加對數似然損失情況下的反向傳播演算法

證明$$\delta ^L_j = \frac{\partial C}{\partial z^L_j} = a^L_j - y_j \quad (84)$$

這個其實在拓展一的過程中已經證明了,就是前面兩個偏導數的積。

二. 過擬合和正則化

2.1 過擬合

毋庸置疑,一個引數非常多的模型具有很強的擬合數據的能力,但是僅僅能夠擬合已知的測試資料並不能證明它是一個好模型,它很有可能在面對新的資料的時候就無能無力了,這樣的模型就不是一個可信的模型,這就是我們經常會遇到的過擬合問題。

回到我們隱藏層為30個神經元的那個模型,有將近24000個引數,100個神經元的時候,變成了80000個引數,這麼多引數的情況,我們的模型是否可信呢?

我們先測試一下,我們使用30個神經元的情形訓練模型,但是這次不用50000個訓練圖片,而是隻用1000張測試圖片。訓練結果如下:

結果看上去不錯,損失函式平滑的下降。再來看一下在測試集上的表現:

僅僅在82%左右,雖然我們的損失函式顯示是在一路下降,但是準確率在達到一定水平之後就沒有繼續提高,只是在這個這個值上下波動。所以我們可以認為在epoch280之後,我們的模型在測試資料上就不在具有足夠的泛化能力了,也就是說此時的神經網路出現了過擬合的問題。

這裡我們可能會有疑問,為什麼用訓練資料的損失函式變化去對比測試資料的準確度?讓我們來看一下測試資料的損失函式:

可以看到在epoch15左右的時候,損失就已經開始上升了,而此時訓練資料的損失還在下降,這也是模型已經過擬合的另一個體現。於是就有了一個問題,將epoch15還是epoch280作為過擬合的開始呢?從實際出發,我們一般更關心模型的準確率,而不是損失函式的變化,所以epoch280通常是更好的選擇。

從訓練資料的準確度變化也可以看出過擬合的另一個體現:

準確率幾乎達到了100%,然而我們在測試資料上試驗卻只達到了82.27%的準確率。此時我們的模型只是在學習訓練資料的特點,而不是在學會去識別數字。就類似於僅僅是在記憶訓練集,而沒有學會真正的識別數字的方法,導致在測試集上表現差。

由於神經網路中通常都有大量的權重和偏差,使得過擬合在神經網路中是一個經常發生的問題,於是就有了各種各樣的檢測和消除過擬合的方法。

判斷是否存在過擬合最簡單明瞭的方法就是上面的方法,對比訓練資料和測試資料的準確率。看到測試資料上準確率不再提升時,我們就可以停止訓練了。當然,這並不是過擬合真正的表現,有可能出現訓練準確率和測試準確率都不提升的情況。

在實際中,我們增加一個驗證集來替代測試集去檢測過擬合的問題。

import mnist_loader 
training_data, validation_data, test_data = \
    mnist_loader.load_data_wrapper()

回到MNIST上,這次我們就使用50000的訓練,10000的驗證,10000的測試。那為什麼不能直接使用測試集去避免過擬合呢?簡單的說,可以將驗證集的使用是幫助我們去進行超參選擇的過程,如果使用測試集可能會使得當前的超參是對於測試集最好的,而不是真實的最佳,同樣會導致過擬合問題。

當然,實際上,當我們看到模型在test資料上表現不好的時候,肯定會重新去訓練資料,選擇超參,這樣做下去是不是又會導致過擬合呢?這的確是個難題,不過本文並不討論這些內容。在本文的內容中,並不需要去特意考慮這個問題,將資料分成training,validation和test就可以了。

之前過擬合的時候我們使用了僅僅1000張訓練資料,這次我們使用50000張訓練資料看一下:

可以看出,訓練資料和測試資料的表現很接近。訓練資料上最好的結果97.86%對應了在測試上的95.33%,這期間相差了2.53%對比之前17.73%的差距,說明此時過擬合已經被減少了非常多了。

通常來說,這種增大訓練資料的方法是應對過擬合最好的方法,只要有足夠的資料,過擬合就不容易發生。不幸的是,訓練資料的獲取是要付出代價的,有的時候並不是那麼容易就能獲得,而且資料過多的話,演算法的執行效率有的時候也會成為瓶頸。 

2.2 正則化

 拋開增加資料量這種方法,是否還存在其他從技術角度解決過擬合的方法呢?一種方法是降低模型的複雜度,也就是減小神經網路的模型規模。然而,複雜的神經網路具有解決更復雜問題的能力,所以我們通常並不想使用這種方法。

所幸我們還有一種被稱為正則化的技術。接下來先講一種使用最廣發的正則化技術:L2正則化,通過在損失函式後加上一項模型引數的平方項得到,例如對於交叉熵損失函式:

$$C = -\frac{1}{n}\sum_x \sum_j [y_j ln(a^L_j) + (1-y_j)ln(1-a^L_j)] + \frac{\lambda}{2n}\sum_w w^2 \quad (85)$$

第一項就是之前的交叉熵損失,$\lambda>0$是正則化係數。

以同樣的方式可以定義平方損失函式的L2正則化形式:

$$C = \frac{1}{2n}\sum_x ||y-a^L||^2 + \frac{\lambda}{2n} \sum_w w^2 \quad (86)$$

更通用的形式為:

$$C = C_0 + \frac{\lambda}{2n}\sum_w w^2 \quad (87)$$

$C_0$為其他任意損失函式。

正則化的影響在於引入了模型引數大小的影響,更偏向於選擇引數小的模型。正則化還可以看作是一種在最小化損失和最小化引數之間的折中,$\lambda$越大,更偏向於使引數小,反之,更偏向於選擇損失小。

先來看一個正則化解決過擬合的例子。由(87)式求導得:

$$\frac{\partial C}{\partial w} = \frac{\partial C_0}{\partial w} + \frac{\lambda}{n}w \quad (88)$$

$$\frac{\partial C}{\partial b} = \frac{\partial C_0}{\partial b} \quad (89)$$

其中關於$C_0$的求導部分可以根據之前的反向傳播演算法求,於是可以得到引數的更新公式:

$$b \rightarrow b - \eta \frac{\partial C_0}{\partial b} \quad (90)$$

$$w \rightarrow w - \eta \frac{\partial C_0}{\partial w} - \frac{\eta \lambda}{n}w \quad (91)$$

$$w \rightarrow (1 - \frac{\eta \lambda}{n})w - \eta \frac{\partial C_0}{\partial w} \quad (92)$$

由於$1-\frac{\eta \lambda}{n}$的存在,權重每次更新前都會被縮小。

對於隨機梯度的情況,有:

$$w \rightarrow (1-\frac{\eta \lambda}{n})w - \frac{\eta}{m} \sum_x \frac{\partial C_x}{\partial w} \quad (93)$$

對於bias則沒有變化:

$$b \rightarrow - \frac{\eta}{m}\sum_x \frac{\partial C_x}{\partial b} \quad (94)$$

使用正則化的程式碼執行($\eta = 0.5, \lambda = 0.1$),在訓練資料上,損失函式下降如下:

 但是這一次在測試資料上,準確度卻是一直上升的:

這就說明在正則化的幫助下,過擬合被解決了。而且新的準確率87.1%也高於之前沒有正則項時的82.27%。

那麼如果我們使用50000張圖片訓練呢,之前在50000張圖片的情況下,並沒有遇到很嚴重的過擬合問題,那麼正則項能否進一步改善模型呢?注意到由於$n=1000$這次變成了$n=50000$,所以$1-\frac{\eta \lambda}{n}$也發生了變化,再使用$\lambda = 0.1$將導致權重變小的很少,正則化影響很少,於是將其改為$\lambda = 5.0$

可以看出正則項發生了作用。首先,測試資料上的準確率由95.49%提高到了96.49%。其次可以看到訓練資料的準確率曲線和測試資料的準確率曲線之間的間隔變小了,兩者更加接近了,說明過擬合的情況被減輕了。

這些都表明正則化可以減輕過擬合的影響進而提高分類準確率,除此之外,正則化還有一個好處。從訓練過程中發現,在未使用過擬合之前,由於權重初始化的隨機性,導致每次執行的結果之間差異很大,經常會出現損失函式掉入區域性最小值的情況。但是在加入了正則項之後,不同的執行之間更容易出現相同的結果。

為什麼會這樣呢?直覺上來看,當沒有正則項的時候,weights向量會增加,導致演算法更難正確的在引數空間上找到正確的下降方向。

2.3 為什麼正則化可以減輕過擬合問題

現在有如下圖中的資料來構建一個簡答的模型:

 暫且使用多項式模型來擬合這些資料。圖中有10個點,很顯然使用一個9階的多項式可以完美擬合這些點:

當然我們也可以使用一條直線,也就是1階多項式來擬合它們:

哪一個模型更好呢?哪個模型在面對新資料的時候會有更好的泛化能力呢?

在不知道其他資訊的情況下,我們並不好直接作出結論。先考慮兩種可能:1. 9階多項是真實的模型,可以完美擬合新的資料 2. 1階多項式是真實模型,但是它在預測的時候會出現一些誤差。

在圖中給定的資料上看,兩者之間並沒有太大的差異。但是設想有一個非常大的$x$,由於$x^9$等高階的影響,兩個模型預測的$y$就有非常大的區別了。一種觀點是選擇更簡單的模型,簡單的模型能解釋的情況更不容易是由於巧合出現的。這個9階的模型更可能只是完美的擬合了既有資料,對於未知資料缺乏預測能力。選擇更簡單的模型也被稱為“Occam的剃刀”原則。

回到神經網路上來,對於weights小的的模型,輸入的改變對於輸出的改變影響很小,模型就不容易學習到資料上呈現出來的細枝末節的噪聲規律。

這裡接下來作者將了很多關於過擬合的啟發性的思考就不講了,太長了。。。

作為這一小節的總結,回到一開始的一個問題:為什麼在L2的正則項中不考慮bias?經驗上看,加不加bias並不會太大的影響結果,所以並沒有特別的準則說要不要加bias。但是注意到,相對於大的weights,一個大的bias並不會使得一個神經元對輸入變得更加敏感。我們也不用擔心大的bias會讓模型過多的學習資料中的噪聲規律。而且大的bias會使得模型更加靈活,例如大的bias會讓神經元更容易被saturated。所以我們通常並不對bias做正則化處理。

2.4 正則化的其它方法

除了L2正則化以外,還有很多正則化的手段。這裡稍微講一下常見的幾種方法:L1正則化,dropout,人造資料。

2.4.1 L1正則化:

損失函式為:

$$C = C_0 + \frac{\lambda}{n}\sum_w |w| \quad (95)$$

求導後:

$$\frac{\partial C}{\partial w} = \frac{\partial C_0}{\partial w} + \frac{\lambda}{n}sgn(w) \quad (96)$$

其中$sgn(w)$函式當,$w$為正時為1,$w$為負時為-1。於是有:

$$w \rightarrow w' = w - \frac{\eta \lambda}{n} sgn(w) -\eta \frac{\partial C_0}{\partial w} \quad (97)$$

之前L2的情形:

$$w \rightarrow w' = w(1 - \frac{\eta \lambda}{n}) - \eta \frac{\partial C_0}{\partial w} \quad (98)$$

從兩種式子可以看出,在更新$w$之前,weights都先被縮小了。不過兩者的方式不同,L1是按一個常數$\frac{\eta \lambda}{n}$進行縮小的(這個縮小應該理解為絕對值的縮小),L2則是按照一定的比例$1-\frac{\eta \lambda}{n}$在原有基礎上縮小。所以說,對於一個$|w|$很大的權重,L1正則化對其的減小程度要遠小於L2正則化對其的減小程度。反之,對於一個$|w|$小的權重,L1的作用更加明顯於L2。這也將導致,L1正則化更傾向於保留神經網路中更加重要的少量連結,而將其它的權重減小到0。

 上面其實還有一個細節就是當$w=0$的時候,$sgn(w)$導數是不存在的這個時候怎麼辦。這個時候只要當成沒有正則化項就好了,這也很好理解,L1本來就是將權重減小到0,既然它已經是0了,當然不能再減小了。

2.4.2 Dropout

Dropout是一種完全不同於L1,L2正則化的方法,它並不依賴於修改損失函式,而是直接去修改神經網路的結構。

假設我們現在在訓練這樣一個神經網路:

設想有一個訓練輸入$x$和對應的輸出$y$。正常情況下,我們一次前向傳播遍歷所有神經元,然後通過後向傳播計算梯度。Dropout的過程則不同。開始的時候,隨機刪除掉隱藏層的一半神經元:

同樣的方法,在修改過後的神經網路上進行前向和後向傳播。在完成一次mini-batch後,更新weights和biases。然後復原神經元,再隨機刪掉隱藏層的神經元,重複這樣的過程。

這樣,我們就能學習到一系列的weights和biases。但是這是在只有一半隱藏層神經元的情況下得到的,所以當執行完整神經網路的時候,隱藏神經元都會起作用,為了補償這種差異,對隱藏層的引數取一半值。

當我們dropout不同的神經元的時候,就相當於訓練了多個不同的神經網路。所以dropout的過程就有點像對大量神經網路的影響取平均的過程。不同的神經網路會以不同的方式產生過擬合,dropout就會減少這種過擬合。

2.4.3 人造資料

從之前的訓練結果可以看到,當只使用1000張圖片進行訓練的時候,準確率下降到85%左右。這很正常,因為資料不夠,沒辦法完全體現手寫數字的多樣性。現在試著慢慢增大資料量,觀看其準確率:

可以看出訓練資料越多,準確率越好。

獲取更多的訓練資料是個好方法,不過實際情況下這並不總是可行的。另外一種替代方案是根據現有資料去人為製作一些訓練資料。例如對於MNIST中的一副圖片數字5:

對其旋轉15度得到:

 

可以看到這仍然是一個合理的樣本5,通過這樣的方式就達到了增大樣本資料集的目的。

這個想法是很有效而且在手寫數字以外的問題上也被廣泛使用。變化資料的原則是變換的操作要符合真實世界的情況。例如對於語音識別,人類可以在有背景噪聲等情況下識別出正確的資訊,我們於是可以對語音資料加上背景噪聲作為對資料的擴充套件。

練習:

上面提到對MNIST訓練資料擴充套件的時候使用了微小的旋轉操作,如果操作幅度很大的話會發生什麼呢?

我的感覺是如果幅度非常大的話相當於引入了一些完全不是該數字的樣本,相當於引入了錯誤的標註樣本,會降低學習效果。

除了神經網路,增大資料集會對其他的機器學習演算法造成什麼影響呢?看一下SVM下的結果:

 可以看到,雖然在小資料集上svm的效果差很多,但是隨著資料量的增加,它的結果開始逼近神經網路的結果。而且svm在50000張圖片的效果是好與神經網路在5000張的效果的。所以說,增大資料集有的時候可以彌補不同機器學習演算法之間的差距。

還可以發現,演算法的好壞很多時候是跟資料集有關的,只有結合特定的資料集才可以得出演算法A要好與演算法B的結論。

以上我們討論了過擬合和正則化,當然這並沒有結束,以後還會經常接觸到它們。要知道,過擬合在神經網路中是經常會遇到的問題,特別是隨著現在神經網路結構的複雜性增加,使用有效的正則化方法就顯得尤為重要了。

三. 引數初始化

前面在做引數初始化的時候使用的是$G(0,1)$的標準正態分佈,雖然這是可行的,但是我們還是想看看有沒有更好的方法,沒準還能夠加速演算法的學習過程。

很容易發現標準正態分佈初始化並不是一個很好的選擇。設想對於這樣一個神經網路,有1000個輸入神經元,考慮第一層隱藏層的第一個神經元,對於它與輸入層連結的引數適用標準正態分佈進行初始化:

為了簡化問題,假設這1000個輸入神經元中一半為1,一半為0。考慮隱藏層神經元的加權輸入$z=\sum_j w_j x_j + b$,這就導致$z$相當於是501個正態分佈隨機變數的和,然後就可以得到$z$服從均值為0,標準差為$\sqrt{501}\approx 22.4$。這就說明$z$是一個非常“寬”的正態分佈:

於是就會出現$|z|$非常大的情況,導致$z>>1$或者$z<<-1$。這就導致$\sigma (z)$非常接近0或者1。也就是說這個隱藏層的神經元被saturated了。於是改變這些權重對它的啟用只會造成很小的影響,進而對後續神經網路也只能造成很小的影響,對於最終的損失函式也將只造成很小的影響。於是在梯度下降演算法中,這些權重的更新就非常緩慢。之前介紹的交叉熵損失函式只適用於解決輸出層神經元saturated的情況,對於隱藏層神經元saturated就無能無力了。

一種更好的方式是對於一個有$n_{in}$個輸入權重的時候,對這些權重的初始化按照均值為0,標準差為$\frac{1}{\sqrt{n_{in}}}$的正態分佈初始化,對於bias的話則還是按標準正態分佈初始化(理由稍後再說)。這樣就得到的$z=\sum_j w_j x_j + b$就是一個均值為0,更窄的的正態分佈。設想,對於之前500個輸入為0,500個輸入為1的例子,這樣的$z$的標準差為$\sqrt{3/2} = 1.22$。這個正態分佈更窄,更不容易出現saturated狀態。

 練習:

驗證上面提到的情況下,$z=\sum_j w_j x_j +b$的標準差為$\sqrt{3/2}$

答案

根據概率學的知識知道:相互獨立的隨機變數的和的方差等於它們各自方差的和。由於其中$x$有500個分量為0,最終只有501個隨機變數相加,得到$500*\frac{1}{1000} + 1 = \frac{3}{2}$。

之前提到bias的初始化並沒有變化,因為事實上,它對神經元是否容易saturated並沒有什麼影響。接下來比較兩種初始化下,演算法的結果:

雖然最後兩者都獲得了不錯的結果,但是新的初始化方式無疑使演算法收斂的更快。之後在第四章的一些例子還將表明,在某些情況下,這種初始化不但能加快演算法收斂,還能提高模型的準確度。

拓展:

L2正則化與上述weights初始化之間的聯絡

L2正則化有的時候會起到和上述weights的初始方法一樣的作用。設想我們使用老的使用標準正態分佈的初始化方法:對於以下情況:

(1)$\lambda$是一個不大小的數,導致第一次epoch後,weights受到$1-\frac{\eta \lambda}{m}$的影響已經衰減到很小了

(2)$\lambda$符合$\eta \lambda << n$,每次epoch後,weights按因子$e^{-\frac{\eta \lambda}{m}}$衰減

(3)$\lambda$是一個不太大的數,weights逐漸衰減到$\frac{1}{\sqrt{n}}$左右,其中$n$是神經網路中所有權重的數量。

討論它們之間的聯絡

參考這位大神的這篇部落格:http://charleshm.github.io/2016/03/Regularized-Regression/

四. 其它方法

上面已經講了很多最經典的方法,下面介紹一些其它方法,它們大多是對上述基本方法的改進。

4.1 隨機梯度下降演算法的改進

基於Hessian矩陣

對於關於變數$w=w_1, w_2, ...$,損失函式$C$的泰勒展開為:

$$C(w+\Delta w) = C(w) + \sum_j \frac{\partial C}{\partial w_j}\Delta w_j + \frac{1}{2}\sum_{jk}\Delta w_j \frac{\partial^2 C}{\partial w_j \partial w_k}\Delta w_k + ... \quad (103)$$

可以寫作為:

$$C(w+\Delta w) = C(w) + \bigtriangledown C\cdot \Delta w + \frac{1}{2}\Delta w^TH\Delta w+ ..., \quad (104)$$

其中$\bigtriangledown C$是之前已經定義的梯度向量,$H$為Hessian矩陣,其中第$j$行第$k$列的元素為$\frac{\partial^2 C}{\partial w_j \partial w_k}$。假設不考慮高階項對$C$進行近似處理:

$$C(w+\Delta w) \approx C(w) + \bigtriangledown C \cdot \Delta w + \frac{1}{2}\Delta w^T H \Delta w \quad (105)$$

通過微分可以得到上面右式在

$$\Delta w = -H^{-1}\bigtriangledown C \quad (106)$$時達到最小值。

於是我們可以將$w$移動到$w+\Delta w = w - H^{-1}\bigtriangledown C$使得損失函式減小。於是得到如下演算法:

  • 選擇初始點$w$
  • 更新$w$:$w' = w - H^{-1}\bigtriangledown C$,其中$H$和$\bigtriangledown C$為在$w$下求得
  • 更新$w'$:$w'' = w' - H^{'-1}\bigtriangledown 'C$,其中$H'$和$\bigtriangledown 'C$為在$w'$下求得
  • ...

實際情況下,(105)式只是一個近似,最好選擇小一點的步伐更新$w$,通常乘上一個學習率因子$\Delta w = -\eta H^{-1}\bigtriangledown C$。

Hessian方法通常比標準的梯度下降演算法收斂更快,但是實際中並不好實現。因為Hessian矩陣的存在,對其求逆矩陣是一個非常耗時的操作。實際中通常使用其它基於Hessian矩陣的演算法,而不是直接對Hessian矩陣求逆矩陣。

基於慣性的梯度下降演算法

Hessian矩陣優化的優點是它不僅可以處理梯度的資訊,更能夠獲取梯度變化的資訊。基於慣性的方法也是基於類似的考量,但是避免了大量的矩陣運算。

慣性是物理上的名詞,在這裡引入肯定就需要對原有演算法做一些修改。首先是引入速度的概念,梯度影響的是加速度,可以改變速度,但是並不能直接改變位置,速度才對位置起直接改變的作用。其次是引入摩擦力的概念,它會逐漸減小速度。

對於每個變數$w_j$,引入對應的速度$v = v_1, v_2, ...$,於是得到,代替原來的引數更新公式$w\rightarrow w' = w - \eta \bigtriangledown C$:

$$v \rightarrow v' = \mu v - \eta \bigtriangledown C \quad (107)$$

$$w \rightarrow w' = w + v' \quad (108)$$

其中$\mu$是影響著系統摩擦力的超參。為了理解這兩個方程,不妨令$\mu = 1$,即沒有摩擦力的情況。$\bigtriangledown C$為影響速度的因素,然後速度影響位置$w$的變化率。速度的產生依靠梯度項的不斷疊加導致,這就意味著,如果幾次學習過程中梯度都是一樣的方向,就可以獲得一定規模的速度。

例如當我們沿著斜坡下降的時候由於速度在增大,相對於標準的梯度下降就會更快的到達谷底。但是就會出現跑過頭的情況。這也就是引入$\mu$的原因。當$\mu = 1$時,沒有摩擦,當$\mu = 0$時,摩擦非常大,速度無法維持,(107)式,(108)式又變成了沒有慣性的情況。通常情況下$\mu$是在(0,1)之間的數。

練習:

問題一:

如果$\mu>1$會出現什麼情況

答案:

大於1說明,速度不但會隨著梯度項進行積累,每次學習之間還會按$\mu$倍增大,導致在梯度很小的時候,速度還是很大,無法保障最後時刻順利收斂。

問題二:

如果$\mu<0$會出現什麼情況

答案:

每次速度方向,可能在前進的時候會來回震盪

4.2 其它神經元模型

前面的所有討論一般都是給予sigmoid神經元的。實際中很多情況下,其它神經元有的時候會有比sigmoid神經元更好的表現。

tanh函式

將sigmoid函式替換為tanh函式:

$$tanh(w\cdot x+b) \quad (109)$$

其中tanh函式形式為:

$$tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} \quad (110)$$

容易得到:

$$\sigma (z) = \frac{1 + tanh(z/2)}{2} \quad (111)$$

這樣就可以將tanh看成是sigmoid函式的一個伸縮變換後的版本。得到其影象為:

 一個不同就是tanh的值域為-1到1,而不再是0到1。

練習:

問題:
證明式(111)

答案:

$1+tanh(z/2) = 1+ \frac{e^{z/2} - e^{-z/2}}{e^{z/2} + e^{-z/2}} = \frac{2e^{z/2}}{e^{z/2} + e^{-z/2}} = \frac{2}{1+e^{-z}}$

RELU

rectified linear unit函式:

$$max(0 , w\cdot x + b) \quad (112)$$

可以看到relu和sigmoid或者tanh之間還是有很大的差距的。那麼什麼時候該選擇它呢?目前並沒有非常好的理論來幫助我們進行選擇。不過由前面知道,由於$\sigma '$項的原因,會使神經元處於saturated狀態,tanh也會面臨一樣的問題,不過對於relu來說,增大其輸入並不會導致神經元的saturation。另一方面,當其輸入為負時,該神經元的學習就會完全終止。

本文只是一個筆記性質的總結,其實還有很多作者啟發性的思考實在太長了,就沒有寫,想了解的推薦大家看原文。