Introduction
資料科學專業人員面臨的最常見問題之一是避免過度拟合。 您是否遇到過您的模型在訓練資料上表現異常但無法預測測試資料的情況。 或者你在公共排行榜的競争中處于領先地位,但在最終排名中隻有數百個名額? 好吧 - 這是給你的文章!
避免過度拟合可以單獨提高我們模型的性能。

在本文中,我們将了解過度拟合的概念以及正則化如何幫助克服同一問題。 然後,我們将研究一些不同的正則化技術,并在python中進行案例研究,以進一步鞏固這些概念。
目錄
什麼是正規化?
正規化如何幫助減少過度拟合?
深度學習中的不同正則化技巧
L2和L1正則化
退出
資料增加
提前停止
使用Keras進行MNIST資料的案例研究
#####什麼是正規化?
在我們深入研究這個主題之前,請看一下這個圖像:
你以前見過這張照片嗎? 當我們在此圖像中向右移動時,我們的模型試圖從訓練資料中很好地學習細節和噪聲,這最終導緻對看不見的資料的不良表現。
換句話說,在向右移動時,模型的複雜性增加,使得訓練誤差減小但測試誤差不會。 如下圖所示。
如果你以前建立過神經網絡,你就會知道它們有多複雜。 這使他們更容易過度拟合。
正則化是一種對學習算法進行細微修改以使模型更好地概括的技術。 這反過來又改善了模型在看不見的資料上的性能。
正規化如何幫助減少過度拟合?
讓我們考慮一個過度拟合訓練資料的神經網絡,如下圖所示。
如果您已經在機器學習中研究了正則化的概念,那麼您将有一個公平的想法,即正則化會對系數進行懲罰。 在深度學習中,它實際上會懲罰節點的權重矩陣。
假設我們的正則化系數太高,以至于一些權重矩陣幾乎等于零。
這将導緻更簡單的線性網絡和訓練資料的輕微不足。
正則化系數的這種大值不是那麼有用。 我們需要優化正則化系數的值,以獲得如下圖所示的良好拟合模型。
深度學習中的不同正則化技術
現在我們已經了解了正則化如何幫助減少過度拟合,我們将學習一些不同的技術,以便在深度學習中應用正則化。
L2&L1正則化
L1和L2是最常見的正則化類型。 這些通過添加另一個稱為正則化項的術語來更新一般成本函數。
成本函數=損失(比如二進制交叉熵)+正則化項
由于該正則化項的增加,權重矩陣的值減小,因為它假設具有較小權重矩陣的神經網絡導緻更簡單的模型。 是以,它也将在很大程度上減少過度拟合。
但是,該正則化項在L1和L2中不同。
在L2中,我們有:
這裡,lambda是正則化參數。 它是超參數,其值已針對更好的結果進行了優化。 L2正則化也稱為權重衰減,因為它迫使權重向零衰減(但不完全為零)。
在L1中,我們有:
在這裡,我們懲罰權重的絕對值。 與L2不同,這裡的權重可以減少到零。 是以,當我們嘗試壓縮模型時,它非常有用。 否則,我們通常更喜歡L2而不是它。
在keras中,我們可以使用正則化器直接将正則化應用于任何層。
下面是将L2正則化應用于Dense層的示例代碼。
from keras import regularizers
model.add(Dense(64, input_dim=64,
kernel_regularizer=regularizers.l2(0.01)
注意:這裡值0.01是正則化參數的值,即lambda,我們需要進一步優化。 我們可以使用網格搜尋方法對其進行優化。
同樣,我們也可以應用L1正則化。 我們将在本文後面的案例研究中更詳細地研究這一點。
Dropout
這是最有趣的正則化技術之一。 它還産生非常好的結果,是以是深度學習領域中最常用的正則化技術。
為了解Dropout,我們假設我們的神經網絡結構類似于下圖所示:
那麼辍學者呢? 在每次疊代時,它會随機選擇一些節點并将它們與所有傳入和傳出連接配接一起删除,如下所示。
是以,每次疊代都有一組不同的節點,這會産生一組不同的輸出。 它也可以被認為是機器學習中的內建技術。
集合模型通常比單個模型表現更好,因為它們捕獲更多随機性。 類似地,丢失也比正常的神經網絡模型表現更好。
選擇應丢棄多少節點的這種機率是丢失函數的超參數。 如上圖所示,dropout可以應用于隐藏層和輸入層。
由于這些原因,當我們具有大的神經網絡結構以引入更多随機性時,通常優選丢失。
在keras中,我們可以使用keras核心層實作dropout。 下面是它的python代碼:
from keras.layers.core import Dropout
model = 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定義為丢棄的機率。 我們可以使用網格搜尋方法進一步調整它以獲得更好的結果。
資料擴充
減少過度拟合的最簡單方法是增加訓練資料的大小。 在機器學習中,由于标簽資料成本太高,我們無法增加教育訓練資料的大小。
但是,現在讓我們考慮一下我們正在處理圖像。 在這種情況下,有幾種方法可以增加訓練資料的大小 - 旋轉圖像,翻轉,縮放,移位等。在下圖中,對手寫數字資料集進行了一些轉換。
這種技術稱為資料增強。 這通常會提高模型的準确性。 它可以被視為一個強制性的技巧,以改善我們的預測。
在keras中,我們可以使用ImageDataGenerator執行所有這些轉換。 它有一個很大的參數清單,您可以使用它們來預處理您的訓練資料。
下面是實作它的示例代碼。
from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(horizontal flip=True)
datagen.fit(train)
提早停止
早期停止是一種交叉驗證政策,我們将訓練集的一部分作為驗證集。 當我們發現驗證集上的性能變差時,我們會立即停止對模型的教育訓練。 這被稱為提前停止。
在上圖中,我們将停止在虛線處進行訓練,因為之後我們的模型将開始過度拟合訓練資料。
在keras中,我們可以使用callbacks函數應用早期停止。 下面是它的示例代碼。
from keras.callbacks import EarlyStopping
EarlyStopping(monitor='val_err', patience=5)
這裡,monitor表示需要監視的數量,'val_err’表示驗證錯誤。
耐心表示沒有進一步改進的時期數,之後訓練将被停止。為了更好地了解,讓我們再看一下上面的圖像。在虛線之後,每個時期将導緻更高的驗證錯誤值。是以,在虛線之後的5個時期(因為我們的耐心等于5),我們的模型将停止,因為沒有看到進一步的改善。
注意:有可能在5個時期之後(這是為一般耐心定義的值),模型可能再次開始改進,驗證錯誤也開始減少。是以,在調整這個超參數時我們需要格外小心。
關于使用keras的MNIST資料的案例研究
到目前為止,您應該對我們所經曆的不同技術有一個理論上的了解。我們現在将這些知識應用于我們的深度學習實踐問題 - 識别數字。下載下傳資料集後,請按照以下代碼開始!首先,我們将導入一些基本庫。
%pylab inline
import numpy as np
import pandas as pd
from scipy.misc import imread
from sklearn.metrics import accuracy_score
from matplotlib import pyplot
import tensorflow as tf
import keras
# To stop potential randomness
seed = 128
rng = np.random.RandomState(seed)
載入資料集
root_dir = os.path.abspath('/Users/shubhamjain/Downloads/AV/identify the digits/')
data_dir = os.path.join(root_dir, 'data')
sub_dir = os.path.join(root_dir, 'sub')
## reading train file only
train = pd.read_csv(os.path.join(data_dir, 'Train', 'train.csv'))
train.head()
現在看看我們的一些圖檔。
img_name = rng.choice(train.filename)
filepath = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)
img = imread(filepath, flatten=True)
pylab.imshow(img, cmap='gray')
pylab.axis('off')
pylab.show()
#storing images in numpy arrays
temp = []
for img_name in train.filename:
image_path = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)
img = imread(image_path, flatten=True)
img = img.astype('float32')
temp.append(img)
x_train = np.stack(temp)
x_train /= 255.0
x_train = x_train.reshape(-1, 784).astype('float32')
y_train = keras.utils.np_utils.to_categorical(train.label.values)
建立驗證資料集,以優化我們的模型以獲得更好的分數。 我們将使用70:30的列車和驗證資料集比率。
split_size = int(x_train.shape[0]*0.7)
x_train, x_test = x_train[:split_size], x_train[split_size:]
y_train, y_test = y_train[:split_size], y_train[split_size:]
首先,讓我們從建構一個包含5個隐藏層的簡單神經網絡開始,每個隐藏層有500個節點。
# import keras modules
from keras.models import Sequential
from keras.layers import Dense
# define vars
input_num_units = 784
hidden1_num_units = 500
hidden2_num_units = 500
hidden3_num_units = 500
hidden4_num_units = 500
hidden5_num_units = 500
output_num_units = 10
epochs = 10
batch_size = 128
model = Sequential([
Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu'),
Dense(output_dim=hidden2_num_units, input_dim=hidden1_num_units, activation='relu'),
Dense(output_dim=hidden3_num_units, input_dim=hidden2_num_units, activation='relu'),
Dense(output_dim=hidden4_num_units, input_dim=hidden3_num_units, activation='relu'),
Dense(output_dim=hidden5_num_units, input_dim=hidden4_num_units, activation='relu'),
Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),
])
請注意,我們隻運作了10個時代。 讓我們快速檢查一下我們模型的性能。
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test))
現在,讓我們嘗試L2正則化器,檢查它是否比簡單的神經網絡模型提供更好的結果。
from keras import regularizers
model = Sequential([
Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu',
kernel_regularizer=regularizers.l2(0.0001)),
Dense(output_dim=hidden2_num_units, input_dim=hidden1_num_units, activation='relu',
kernel_regularizer=regularizers.l2(0.0001)),
Dense(output_dim=hidden3_num_units, input_dim=hidden2_num_units, activation='relu',
kernel_regularizer=regularizers.l2(0.0001)),
Dense(output_dim=hidden4_num_units, input_dim=hidden3_num_units, activation='relu',
kernel_regularizer=regularizers.l2(0.0001)),
Dense(output_dim=hidden5_num_units, input_dim=hidden4_num_units, activation='relu',
kernel_regularizer=regularizers.l2(0.0001)),
Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),
])
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test))
請注意,lambda的值等于0.0001。 大! 我們剛剛獲得的精度高于我們之前的NN模型。
現在,讓我們嘗試L1正則化技術。
## l1
model = Sequential([
Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu',
kernel_regularizer=regularizers.l1(0.0001)),
Dense(output_dim=hidden2_num_units, input_dim=hidden1_num_units, activation='relu',
kernel_regularizer=regularizers.l1(0.0001)),
Dense(output_dim=hidden3_num_units, input_dim=hidden2_num_units, activation='relu',
kernel_regularizer=regularizers.l1(0.0001)),
Dense(output_dim=hidden4_num_units, input_dim=hidden3_num_units, activation='relu',
kernel_regularizer=regularizers.l1(0.0001)),
Dense(output_dim=hidden5_num_units, input_dim=hidden4_num_units, activation='relu',
kernel_regularizer=regularizers.l1(0.0001)),
Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),
])
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test))
這并沒有顯示出對以前型号的任何改進。 讓我們跳到dropout技術。
## dropout
from keras.layers.core import Dropout
model = Sequential([
Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu'),
Dropout(0.25),
Dense(output_dim=hidden2_num_units, input_dim=hidden1_num_units, activation='relu'),
Dropout(0.25),
Dense(output_dim=hidden3_num_units, input_dim=hidden2_num_units, activation='relu'),
Dropout(0.25),
Dense(output_dim=hidden4_num_units, input_dim=hidden3_num_units, activation='relu'),
Dropout(0.25),
Dense(output_dim=hidden5_num_units, input_dim=hidden4_num_units, activation='relu'),
Dropout(0.25),
Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),
])
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test))
不錯! Dropout也比我們簡單的NN模型有了一些改進。
現在,讓我們嘗試資料擴充。
from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(zca_whitening=True)
# loading data
train = pd.read_csv(os.path.join(data_dir, 'Train', 'train.csv'))
temp = []
for img_name in train.filename:
image_path = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)
img = imread(image_path, flatten=True)
img = img.astype('float32')
temp.append(img)
x_train = np.stack(temp)
X_train = x_train.reshape(x_train.shape[0], 1, 28, 28)
X_train = X_train.astype('float32')
Now, fit the training data in order to augment.
# fit parameters from data
datagen.fit(X_train)
在這裡,我使用了zca_whitening作為參數,它突出顯示了每個數字的輪廓,如下圖所示。
## splitting
y_train = keras.utils.np_utils.to_categorical(train.label.values)
split_size = int(x_train.shape[0]*0.7)
x_train, x_test = X_train[:split_size], X_train[split_size:]
y_train, y_test = y_train[:split_size], y_train[split_size:]
## reshaping
x_train=np.reshape(x_train,(x_train.shape[0],-1))/255
x_test=np.reshape(x_test,(x_test.shape[0],-1))/255
## structure using dropout
from keras.layers.core import Dropout
model = Sequential([
Dense(output_dim=hidden1_num_units, input_dim=input_num_units, activation='relu'),
Dropout(0.25),
Dense(output_dim=hidden2_num_units, input_dim=hidden1_num_units, activation='relu'),
Dropout(0.25),
Dense(output_dim=hidden3_num_units, input_dim=hidden2_num_units, activation='relu'),
Dropout(0.25),
Dense(output_dim=hidden4_num_units, input_dim=hidden3_num_units, activation='relu'),
Dropout(0.25),
Dense(output_dim=hidden5_num_units, input_dim=hidden4_num_units, activation='relu'),
Dropout(0.25),
Dense(output_dim=output_num_units, input_dim=hidden5_num_units, activation='softmax'),
])
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test))
哇! 我們的準确度得分有了很大的提升。 好的是它每次都有效。 我們隻需根據資料集中的圖像選擇合适的參數。
現在,讓我們試試我們的最終技術 - 提前停止。
from keras.callbacks import EarlyStopping
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
trained_model_5d = model.fit(x_train, y_train, nb_epoch=epochs, batch_size=batch_size, validation_data=(x_test, y_test)
, callbacks = [EarlyStopping(monitor='val_acc', patience=2)])
您可以看到我們的模型僅在5次疊代後停止,因為驗證準确性沒有提高。 如果我們為更大的曆元值運作它,它會給出很好的結果。 你可以說這是一種優化時代數量值的技術。
結束注釋
我希望你現在已經了解了正則化以及在深度學習模型中實作它所需的不同技術。 我強烈建議您在處理深度學習任務時應用它。 它将幫助您擴充視野并更好地了解該主題。
你覺得這篇文章有用嗎? 請在下面的評論部分分享您的意見/想法。