1. 程式人生 > 其它 >深度學習--解決模型過擬合的問題

深度學習--解決模型過擬合的問題

文章目錄

 

一、過擬合

1.什麼是過擬合

你剛開始訓練出來的模型是不是在留出的驗證資料上的效能總是在幾輪後達到最高點,然後開始下降。
如下圖所示,模型在訓練集上的精度一直不斷上升,但模型在驗證集上的精度在第二輪就達到最高點然後就開始下降,這時模型在訓練資料上開始出現過擬合的現象,模型開始學習僅和訓練資料有關的模式,但這種模式對新資料來說是錯誤的或無關緊要的。

2.模型為什麼會產生過擬合呢?這是因為:

(1)很多資料都有噪聲。模型會盡量去覆蓋噪音點,導致對資料過擬合。
(2)訓練資料不足,導致模型只能過度提取與預測方向無關特徵。
(3)構建的模型太複雜,對訓練資料特徵提取過度,並不適用與測試集。

3.調參訓練模型的目的:

訓練模型是為了得到好的神經網路,好的網路要求能夠對資料有高的預測準確率,這就要求:
1.要調節模型以在訓練資料上得到最佳效能,即優化,這很好實現。
2.但同時,要讓訓練好的模型在前所未見的資料上的效能表現良好,即好的泛化能力,基於訓練資料調節模型就是為了提高模型的泛化能力。
深度學習模型通常都很擅長擬合訓練資料,但真正的挑戰在於泛化,而不是擬合。

4.對欠擬合的說明

對於訓練好的模型,若在訓練集表現差,在測試集表現同樣會很差,這可能是欠擬合導致。欠擬合是指模型擬合程度不高,資料距離擬合曲線較遠,或指模型沒有很好地捕捉到資料特徵,不能夠很好地擬合數據。若存在訓練資料上的損失越小,測試資料上的損失也越小。這時的模型是欠擬合的,一般是因為網路太小,網路還沒有對訓練資料中所有相關模式建模,適當加大網路隱藏單元或者層數可以基本解決。

二、如何降低過擬合

1.獲取更多的訓練資料。

為了防止模型從訓練資料中學到錯誤或無關緊要的模式,最優解決方法是獲取更多的訓練資料。模型的訓練資料越多,泛化能力自然也越好。

如果無法獲取更多資料,次優解決方法是調節模型允許儲存的資訊量,或對模型允許儲存的資訊加以約束。如果一個網路只能記住幾個模式,那麼優化過程會迫使模型集中學習最重要的模式,這樣更可能得到良好的泛化。
這種降低過擬合的方法叫作正則化。

2.減小網路大小

防止過擬合的最簡單的方法就是減小模型大小,即減少模型中可學習引數的個數(這由層數和每層的單元個數決定)。但同時使用的模型應該具有足夠多的引數,以防欠擬合,這是需要你去尋找的一個平衡。
為了尋找這個平衡,我們需要評估一系列不同的網路架構(在驗證集上評估),以便為資料找到最佳的模型大小。要找到合適的模型大小,一般的工作流程是開始時選擇相對較少的層和引數,然後逐漸增加層的大小或增加新層,直到這種增加對驗證損失的影響變得很小。

3.新增權重正則化

簡單模型比複雜模型更不容易過擬合。簡單模型是指引數更少的模型,另一種常見的降低過擬合的方法就是強制讓模型權重只能取較小的值,從而限制模型的複雜度,這使得權重值的分佈更加規則。這種方法叫作權重正則化。
實現方法是向網路損失函式中新增與較大權重值相關的成本(cost)。 這個成本有兩種形式。
L1 正則化(L1 regularization):新增的成本與權重係數的絕對值(權重的 L1 範數)成正比。
L2 正則化(L2 regularization):新增的成本與權重係數的平方(權重的 L2 範數)成正比。 神經網路的 L2 正則化也叫權重衰減。

4.新增 dropout 正則化 (丟棄一些輸出資料)

dropout 是神經網路最有效也最常用的正則化方法之一,對某一層使用 dropout,就是在訓練過程中隨機將該層的一些輸出特徵捨棄(設定為 0)。假設在訓練過程中,某一層對給定輸入樣本的返回值應該是向量 [0.2, 0.5, 1.3, 0.8, 1.1]。使用 dropout 後,這個向量會有幾個隨機的元素變成 0,比如 [0, 0.5,1.3, 0, 1.1]。dropout 比率(dropout rate)是被設為 0 的特徵所佔的比例,通常在 0.2~0.5 範圍內。測試時沒有單元被捨棄,而該層的輸出值需要按 dropout 比率縮小,因為這時比訓練時有更多的單元被啟用,需要加以平衡。

三、實戰操作減低過擬合

以下使用 IMDB 資料集作為實戰資料集,研究減低模型過擬合方法,它包含來自網際網路電影資料庫(IMDB)的 50 000 條嚴重兩極分化的評論,,IMDB 資料集內置於 Keras 庫。它已經過預處理:評論(單詞序列) 已經被轉換為整數序列,其中每個整數代表字典中的某個單詞。

from keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
# 資料標籤向量化
x_train = np.asarray(train_data).astype('float32')
x_test = np.asarray(test_data).astype('float32')
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')

為了在訓練過程中要監控模型在前所未見的資料上的精度,來調整模型,需要將原始訓練資料留出 10000個樣本作為驗證集。在調整模型過程中,最終形成模型前不能學到任何測試集的資料。

x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]

使用 512 個樣本組成的小批量,將模型訓練 20 個輪次,同時監控在留出的 10 000 個樣本上的損失和精度。

訓練一個網路
from keras import models
from keras import layers
original_model = models.Sequential()
original_model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
original_model.add(layers.Dense(16, activation='relu'))
original_model.add(layers.Dense(1, activation='sigmoid'))
original_model.compile(optimizer='rmsprop',
                       loss='binary_crossentropy',
                       metrics=['acc'])
original_hist = original_model.fit(partial_x_train,
                                   partial_y_train,
                                   epochs=20,
                                   batch_size=512,
                                   validation_data=(x_val, y_val))


可以看到,隨著每次迭代,訓練集損失每輪都在減低。但驗證精度並非如此:它僅在第4輪達到最低值。在第5輪之後,因為對訓練資料過度優化,最終學到的表示僅針對於訓練資料,無法泛化到訓練集之外的資料。

1.減小網路大小

嘗試用下面這個更小的網路來替換它。

smaller_model = models.Sequential()
smaller_model.add(layers.Dense(4, activation='relu', input_shape=(10000,)))
smaller_model.add(layers.Dense(4, activation='relu'))
smaller_model.add(layers.Dense(1, activation='sigmoid'))
smaller_model.compile(optimizer='rmsprop',
                      loss='binary_crossentropy',
                      metrics=['acc'])
smaller_model_hist = smaller_model.fit(partial_x_train,
                                   partial_y_train,
                                   epochs=20,
                                   batch_size=512,
                                   validation_data=(x_val, y_val))
                                   epochs = range(1, 21)
epochs = range(1, 21)
original_val_loss = original_hist.history['val_loss']
smaller_model_val_loss = smaller_model_hist.history['val_loss']
import matplotlib.pyplot as plt
plt.plot(epochs, original_val_loss, 'b+', label='Original model')
plt.plot(epochs, smaller_model_val_loss, 'bo', label='Smaller model')
plt.xlabel('Epochs')
plt.ylabel('Validation loss')
plt.legend()
plt.show()


更小的網路開始過擬合的時間要晚於參考網路而且開始過擬合之後,它的效能變差的速度也更慢。

2.新增權重正則化

在層引數中加入kernel_regularizer引數

from keras import regularizers
l2_model = models.Sequential()
l2_model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
                          activation='relu', input_shape=(10000,)))
l2_model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
                          activation='relu'))
l2_model.add(layers.Dense(1, activation='sigmoid'))


可見,即使兩個模型的引數個數相同,具有 L2正則化的模型(圓點)比參考模型(十字)更不容易過擬合。

3.新增 dropout 正則化

在網路中新增layers.Dropout(0.5)層

dpt_model = models.Sequential()
dpt_model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
dpt_model.add(layers.Dropout(0.5))
dpt_model.add(layers.Dense(16, activation='relu'))
dpt_model.add(layers.Dropout(0.5))
dpt_model.add(layers.Dense(1, activation='sigmoid'))

dpt_model.compile(optimizer='rmsprop',
                  loss='binary_crossentropy',
                  metrics=['acc'])


再次看到,這種方法的效能相比原始網路有明顯提高。
以上就是減低過擬合的常用方法。