編者按:在日常工作、學(xué)習(xí)中,數(shù)據(jù)科學(xué)家最常遇到的問(wèn)題之一就是過(guò)擬合。你是否曾有過(guò)這樣一個(gè)模型,它在訓(xùn)練集上表現(xiàn)優(yōu)秀,在測(cè)試集上卻一塌糊涂。你是否也曾有過(guò)這樣的經(jīng)歷,當(dāng)你參加建模競(jìng)賽時(shí),從跑分上看你的模型明明應(yīng)該高居榜首,但在賽方公布的成績(jī)榜上,它卻名落孫山,遠(yuǎn)在幾百名之后。如果你有過(guò)類似經(jīng)歷,那本文就是專為寫的——它會(huì)告訴你如何避免過(guò)擬合來(lái)提高模型性能。
在這篇文章中,我們將詳細(xì)講述過(guò)擬合的概念和用幾種用于解決過(guò)擬合問(wèn)題的正則化方法,并輔以Python案例講解,以進(jìn)一步鞏固這些知識(shí)。注意,本文假設(shè)讀者具備一定的神經(jīng)網(wǎng)絡(luò)、Keras實(shí)現(xiàn)的經(jīng)驗(yàn)。
目錄
1. 什么是正則化
2. 正則化和過(guò)擬合
3. 深度學(xué)習(xí)中的正則化
-
L2和L1正則化
-
Dropout
-
數(shù)據(jù)增強(qiáng)
-
早停法
什么是正則化
在深入探討這個(gè)話題之前,請(qǐng)看一下這張圖片:
每次談及過(guò)擬合,這張圖片就會(huì)時(shí)不時(shí)地被拉出來(lái)“鞭尸”。如上圖所示,剛開(kāi)始的時(shí)候,模型還不能很好地?cái)M合所有數(shù)據(jù)點(diǎn),即無(wú)法反映數(shù)據(jù)分布,這時(shí)它是欠擬合的。而隨著訓(xùn)練次數(shù)增多,它慢慢找出了數(shù)據(jù)的模式,能在盡可能多地?cái)M合數(shù)據(jù)點(diǎn)的同時(shí)反映數(shù)據(jù)趨勢(shì),這時(shí)它是一個(gè)性能較好的模型。在這基礎(chǔ)上,如果我們繼續(xù)訓(xùn)練,那模型就會(huì)進(jìn)一步挖掘訓(xùn)練數(shù)據(jù)中的細(xì)節(jié)和噪聲,為了擬合所有數(shù)據(jù)點(diǎn)“不擇手段”,這時(shí)它就過(guò)擬合了。
換句話說(shuō),從左往右看,模型的復(fù)雜度逐漸提高,在訓(xùn)練集上的預(yù)測(cè)錯(cuò)誤逐漸減少,但它在測(cè)試集上的錯(cuò)誤率卻呈現(xiàn)一條下凸曲線。
來(lái)源:Slideplayer
如果你之前構(gòu)建過(guò)神經(jīng)網(wǎng)絡(luò),想必你已經(jīng)得到了這個(gè)教訓(xùn):網(wǎng)絡(luò)有多復(fù)雜,過(guò)擬合就有多容易。為了使模型在擬合數(shù)據(jù)的同時(shí)更具推廣性,我們可以用正則化對(duì)學(xué)習(xí)算法做一些細(xì)微修改,從而提高模型的整體性能。
正則化和過(guò)擬合
過(guò)擬合和神經(jīng)網(wǎng)絡(luò)的設(shè)計(jì)密切相關(guān),因此我們先來(lái)看一個(gè)過(guò)擬合的神經(jīng)網(wǎng)絡(luò):
如果你之前閱讀過(guò)我們的從零學(xué)習(xí):從Python和R理解和編碼神經(jīng)網(wǎng)絡(luò)(完整版),或?qū)ι窠?jīng)網(wǎng)絡(luò)正則化概念有初步了解,你應(yīng)該知道上圖中帶箭頭的線實(shí)際上都帶有權(quán)重,而神經(jīng)元是儲(chǔ)存輸入輸出的地方。為了公平起見(jiàn),也就是為了防止網(wǎng)絡(luò)在優(yōu)化方向上過(guò)于放飛自我,這里我們還需要加入一個(gè)先驗(yàn)——正則化懲罰項(xiàng),用來(lái)懲罰神經(jīng)元的加權(quán)矩陣。
如果我們?cè)O(shè)的正則化系數(shù)很大,導(dǎo)致一些加權(quán)矩陣的值幾乎為零——那***我們得到的是一個(gè)更簡(jiǎn)單的線性網(wǎng)絡(luò),它很可能是欠擬合的。
因此這個(gè)系數(shù)并不是越大越好。我們需要優(yōu)化這個(gè)正則化系數(shù)的值,以便獲得一個(gè)良好擬合的模型,如下圖所示。
深度學(xué)習(xí)中的正則化
L2和L1正則化
L1和L2是最常見(jiàn)的正則化方法,它們的做法是在代價(jià)函數(shù)后面再加上一個(gè)正則化項(xiàng)。
代價(jià)函數(shù) = 損失(如二元交叉熵) + 正則化項(xiàng)
由于添加了這個(gè)正則化項(xiàng),各權(quán)值被減小了,換句話說(shuō),就是神經(jīng)網(wǎng)絡(luò)的復(fù)雜度降低了,結(jié)合“網(wǎng)絡(luò)有多復(fù)雜,過(guò)擬合就有多容易”的思想,從理論上來(lái)說(shuō),這樣做等于直接防止過(guò)擬合(奧卡姆剃刀法則)。
當(dāng)然,這個(gè)正則化項(xiàng)在L1和L2里是不一樣的。
對(duì)于L2,它的代價(jià)函數(shù)可表示為:
這里λ就是正則化系數(shù),它是一個(gè)超參數(shù),可以被優(yōu)化以獲得更好的結(jié)果。對(duì)上式求導(dǎo)后,權(quán)重w前的系數(shù)為1−ηλ/m,因?yàn)?eta;、λ、m都是正數(shù),1−ηλ/m小于1,w的趨勢(shì)是減小,所以L2正則化也被稱為權(quán)重衰減。
而對(duì)于L1,它的代價(jià)函數(shù)可表示為:
和L2不同,這里我們懲罰的是權(quán)重w的絕對(duì)值。對(duì)上式求導(dǎo)后,我們得到的等式里包含一項(xiàng)-sgn(w),這意味著當(dāng)w是正數(shù)時(shí),w減小趨向于0;當(dāng)w是負(fù)數(shù)時(shí),w增大趨向于0。所以L1的思路就是把權(quán)重往0靠,從而降低網(wǎng)絡(luò)復(fù)雜度。
因此當(dāng)我們想要壓縮模型時(shí),L1的效果會(huì)很好,但如果只是簡(jiǎn)單防止過(guò)擬合,一般情況下還是會(huì)用L2。在Keras中,我們可以直接調(diào)用regularizers在任意層做正則化。
例:在全連接層使用L2正則化的代碼:
- from keras import regularizersmodel.add(Dense(64, input_dim=64, kernel_regularizer=regularizers.l2(0.01)
注:這里的0.01是正則化系數(shù)λ的值,我們可以通過(guò)網(wǎng)格搜索對(duì)它做進(jìn)一步優(yōu)化。
Dropout
Dropout稱得上是正則化方法中最有趣的一種,它的效果也很好,所以是深度學(xué)習(xí)領(lǐng)域常用的方法之一。為了更好地解釋它,我們先假設(shè)我們的神經(jīng)網(wǎng)絡(luò)長(zhǎng)這樣:
那么Dropout到底drop了什么?我們來(lái)看下面這幅圖:在每次迭代中,它會(huì)隨機(jī)選擇一些神經(jīng)元,并把它們“滿門抄斬”——把神經(jīng)元連同相應(yīng)的輸入輸出一并“刪除”。
比起L1和L2對(duì)代價(jià)函數(shù)的修改,Dropout更像是訓(xùn)練網(wǎng)絡(luò)的一種技巧。隨著訓(xùn)練進(jìn)行,神經(jīng)網(wǎng)絡(luò)在每一次迭代中都會(huì)忽視一些(超參數(shù),常規(guī)是一半)隱藏層/輸入層的神經(jīng)元,這就導(dǎo)致不同的輸出,其中有的是正確的,有的是錯(cuò)誤的。
這個(gè)做法有點(diǎn)類似集成學(xué)習(xí),它能更多地捕獲更多的隨機(jī)性。集成學(xué)習(xí)分類器通常比單一分類器效果更好,同樣的,因?yàn)榫W(wǎng)絡(luò)要擬合數(shù)據(jù)分布,所以Dropout后模型大部分的輸出肯定是正確的,而噪聲數(shù)據(jù)影響只占一小部分,不會(huì)對(duì)最終結(jié)果造成太大影響。
由于這些因素,當(dāng)我們的神經(jīng)網(wǎng)絡(luò)較大且隨機(jī)性更多時(shí),我們一般用Dropout。
在Keras中,我們可以使用keras core layer實(shí)現(xiàn)dropout。下面是它的Python代碼:
- from keras.layers.core import Dropoutmodel = Sequential([Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu'),Dropout(0.25),Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),])
注:這里我們把0.25設(shè)為Dropout的超參數(shù)(每次“刪”1/4),我們可以通過(guò)網(wǎng)格搜索對(duì)它做進(jìn)一步優(yōu)化。
數(shù)據(jù)增強(qiáng)
既然過(guò)擬合是模型對(duì)數(shù)據(jù)集中噪聲和細(xì)節(jié)的過(guò)度捕捉,那么防止過(guò)擬合最簡(jiǎn)單的方法就是增加訓(xùn)練數(shù)據(jù)量。但是在機(jī)器學(xué)習(xí)任務(wù)中,增加數(shù)據(jù)量并不是那么容易實(shí)現(xiàn)的,因?yàn)樗鸭?、?biāo)記數(shù)據(jù)的成本太高了。
假設(shè)我們正在處理的一些手寫數(shù)字圖像,為了擴(kuò)大訓(xùn)練集,我們能采取的方法有——旋轉(zhuǎn)、翻轉(zhuǎn)、縮小/放大、位移、截取、添加隨機(jī)噪聲、添加畸變等。下面是一些處理過(guò)的圖:
這些方式就是數(shù)據(jù)增強(qiáng)。從某種意義上來(lái)說(shuō),機(jī)器學(xué)習(xí)模型的性能是靠數(shù)據(jù)量堆出來(lái)的,因此數(shù)據(jù)增強(qiáng)可以為模型預(yù)測(cè)的準(zhǔn)確率提供巨大提升。有時(shí)為了改進(jìn)模型,這也是一種必用的技巧。
在Keras中,我們可以使用ImageDataGenerator執(zhí)行所有這些轉(zhuǎn)換,它提供了一大堆可以用來(lái)預(yù)處理訓(xùn)練數(shù)據(jù)的參數(shù)列表。以下是實(shí)現(xiàn)它的示例代碼:
- from keras.preprocessing.image import ImageDataGeneratordatagen = ImageDataGenerator(horizontal flip=True)datagen.fit(train)
早停法
這是一種交叉驗(yàn)證策略。訓(xùn)練前,我們從訓(xùn)練集中抽出一部分作為驗(yàn)證集,隨著訓(xùn)練的進(jìn)行,當(dāng)模型在驗(yàn)證集上的性能越來(lái)越差時(shí),我們立即手動(dòng)停止訓(xùn)練,這種提前停止的方法就是早停法。
在上圖中,我們應(yīng)該在虛線位置就停止訓(xùn)練,因?yàn)樵谀侵螅P途烷_(kāi)始過(guò)擬合了。
在Keras中,我們可以調(diào)用callbacks函數(shù)提前停止訓(xùn)練,以下是它的示例代碼:
- from keras.callbacks import EarlyStoppingEarlyStopping(monitor='val_err', patience=5)
在這里,monitor指的是需要監(jiān)控的epoch數(shù)量;val_err表示驗(yàn)證錯(cuò)誤(validation error)。
patience表示經(jīng)過(guò)5個(gè)連續(xù)epoch后模型預(yù)測(cè)結(jié)果沒(méi)有進(jìn)一步改善。結(jié)合上圖進(jìn)行理解,就是在虛線后,模型每訓(xùn)練一個(gè)epoch就會(huì)有更高的驗(yàn)證錯(cuò)誤(更低的驗(yàn)證準(zhǔn)確率),因此連續(xù)訓(xùn)練5個(gè)epoch后,它會(huì)提前停止訓(xùn)練。