文章導讀:
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。另一方面,當其輸入為負時,該神經元的學習就會完全終止。
本文隻是一個筆記性質的總結,其實還有很多作者啟發性的思考實在太長了,就沒有寫,想了解的推薦大家看原文。