深度學習中的正則化技術(附Python程式碼)
資料科學家面臨的常見問題之一是如何避免過擬合。你是否碰到過這樣一種情況:你的模型在訓練集上表現異常好,卻無法預測測試資料。或者在一個競賽中你排在public leaderboard的頂端,但是在最終排名中卻落後了幾百名?那麼這篇文章就是為你而準備的!
(譯者注: 在kaggle這樣的資料競賽中, public leaderboard排名是根據一部分測試集來計算的,用於給選手提供及時的反饋和動態展示比賽的進行情況;而private leaderboard是根據測試集的剩餘部分計算而來,用於計算選手的最終得分和排名。 通常我們可以把public LB理解為在驗證集上的得分,private LB為真正未知資料集上的得分,這樣做的目的是提醒參賽者,我們建模的目標是獲取一個泛化能力好的模型)
避免過擬合可以提高我們模型的效能。
目錄
1. 什麼是正則化?
2. 正則化如何減少過擬合?
3. 深度學習中的各種正則化技術:
L2和L1正則化
Dropout
資料增強(Data augmentation)
提前停止(Early stopping)
4. 案例:在MNIST資料集上使用Keras的案例研究
1. 什麼是正則化?
在深入該主題之前,先來看看這幾幅圖:
之前見過這幅圖嗎?從左到右看,我們的模型從訓練集的噪音資料中學習了過多的細節,最終導致模型在未知資料上的效能不好。
換句話說,從左向右,模型的複雜度在增加以至於訓練誤差減少,然而測試誤差未必減少。如下圖所示:
Source: Slideplayer
如果你曾經構建過神經網路,你就知道它們有多複雜。這也使得它們更容易過擬合。
正則化技術是對學習演算法做輕微的修改使得它泛化能力更強。這反過來就改善了模型在未知資料上的效能。
2. 正則化如何減少過擬合?
我們來看一個在訓練資料上過擬合的神經網路,如下圖所示:
如果你曾經學習過機器學習中的正則化,你會有一個概念,即正則化懲罰了係數。在深度學習中,它實際上懲罰了節點的權重矩陣。
假設我們的正則化係數很高,以至於某些權重矩陣近乎於0:
這會得到一個簡單的線性網路,而且在訓練資料集上輕微的欠擬合。
如此大的正則化係數並不是那麼有用。我們需要對其進行優化從而得到一個擬合良好的模型,正如下圖所示:
3. 深度學習中的各種正則化技術
我們已經理解了正則化如何幫助減少過擬合,現在我們將學習一些把正則化用於深度學習的技術。
L1和L2正則化
L1和L2是最常見的正則化型別。它們通過增加一個被稱為正則項的額外項來更新成本函式:
Cost function = Loss (say, binary cross entropy) + Regularization term
由於增加了這個正則項,權重矩陣的值減小了,因為這裡假定了具有較小權重矩陣的神經網路會導致更簡單的模型。因此,它也會在相當程度上減少過擬合。
然而,該正則項在L1和L2中是不同的。
L2中,我們有:
這裡,lambda是正則引數。它是一個超引數用來優化得到更好的結果。L2正則化也叫權重衰減(weight decay),因為它強制權重朝著0衰減(但不會為0)
在L1中,我們有:
這裡,我們懲罰了權重的絕對值。不像L2, 這裡的權重是有可能衰減到0的。因此,當我們想壓縮模型的時候, L1非常有用,否則會偏向使用L2.
在Keras中,我們可以使用regularizers直接在任意層上應用正則化。
下面是一段樣例程式碼,把L2正則化用於一層之上:
from keras import regularizers
model.add(Dense(64, input_dim=64,
kernel_regularizer=regularizers.l2(0.01)
注意:這裡的值0.01是正則化引數的值,即lambda, 它需要被進一步優化。可以使用grid-search的方法來優化它。
同樣的,我們也可以採用L1正則化。後文中的案例研究會看到更多細節。
Dropout
這是一種非常有趣的正則化技術。它的效果非常好因此在深度學習領域中最常被使用。
為了理解dropout,假設我們的神經網路結構如下所示:
dropout做什麼呢?每次迭代,隨機選擇一些節點,將它們連同相應的輸入和輸出一起刪掉,如下圖:
所以,每一輪迭代都有不同的節點集合,這也導致了不同的輸出。它也可以被認為是一種機器學習中的整合技術(ensemble technique)。
整合模型(ensemble models)通常比單一模型表現更好,因為捕獲了更多的隨機性。同樣的,比起正常的神經網路模型,dropout也表現的更好。
選擇丟棄多少節點的概率是dropout函式的超引數。如上圖所示,dropout可以被用在隱藏層以及輸入層。
由於這些原因,當我們有較大的神經網路時,為了引入更多的隨機性,通常會優先使用dropout。
在Keras中,可以使用Keras core layer來實現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。我們可以使用grid search方法來微調它以獲得更好的結果。
資料增強(Data augmentation)
減少過擬合最簡單的方式其實是增加訓練集的大小。在機器學習中,由於人工標註資料成本過高所以很難增加訓練集的大小。
但是,考慮一下如果我們處理的是影像。在這種情況下,有一些方法可以增加訓練集的大小——旋轉、翻轉、縮放、移動等等。下圖中,對手寫數字資料集進行了一些轉換:
這種技術叫做資料增強。通常會明顯改善模型的準確率。為了提高模型預測能力,這種技術可以被視為一種強制性技巧。
在Keras中,我們使用ImageDataGenerator來執行所有這些轉換。它有一大堆引數,你可以用它們來預處理你的訓練資料。
下面是示例程式碼:
from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(horizontal flip=True)
datagen.fit(train)
提前停止(Early stopping)
提前停止是一種交叉驗證的策略,即把一部分訓練集保留作為驗證集。當看到驗證集上的效能變差時,就立即停止模型的訓練。
在上圖中,我們在虛線處停止模型的訓練,因為在此處之後模型會開始在訓練資料上過擬合。
在Keras中,使用callbacks函式來應用提前停止。下面是程式碼:
from keras.callbacks import EarlyStopping
EarlyStopping(monitor=`val_err`, patience=5)
這裡的monitor是表示需要監視的量,‘val_err’代表驗證集錯誤.
Patience表示在該數量的epochs內沒有進一步的效能改進後,就停止訓練。為了更好地理解,我們再看看上面的圖。在虛線之後,每個epoch都會導致一個更高的驗證集錯誤。因此,在虛線之後的5個epoch(因為我們設定patience等於5),由於沒有進一步的改善,模型將停止訓練。
注意:可能在5個epoch之後(這是一般情況下為patience設定的值)模型再次開始改進,並且驗證集錯誤也開始減少。因此,在調整這個超引數的時候要格外小心。
在MNIST資料集上使用Keras的案例研究
至此,你應該對我們提到的各種技術有了一個理論上的理解。現在我們把這些知識用在深度學習實際問題上——識別數字。下載完資料集之後,你就可以開始下面的程式碼。首先,我們匯入一些基本的庫。
%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)
現在,載入資料。
現在拿一些圖片來看看。
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個epoch,快速檢查一下模型的效能。
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`)
現在,擬合訓練資料以便增強。
# 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輪迭代後就停止了,因為驗證集準確率不再提高了。當我們使用更大值的epochs來執行它時,它會給出好的結果。你可以說它是一種優化epoch值的技術。
結語
我希望現在你已經理解了正則化以及在深度學習模型中實現正則化的不同技術。 無論你處理任何深度學習任務,我都強烈建議你使用正則化。它將幫助你開闊視野並更好的理解這個主題。
原文釋出時間為:2018-05-24
本文作者:SHUBHAM JAIN
本文來自雲棲社群合作伙伴“資料派THU”,瞭解相關資訊可以關注“資料派THU”。
相關文章
- 學習筆記:深度學習中的正則化筆記深度學習
- 深度學習——正則化深度學習
- Python學習之正則Python
- 《深度學習Python》核心技術實戰深度學習Python
- 深度學習+符號智慧,硬核公司「深度好奇」正在將新技術正規化商業化深度學習符號
- 深度學習、強化學習核心技術實戰深度學習強化學習
- 深度強化學習核心技術實戰強化學習
- 機器學習之簡化正則化:L2 正則化機器學習
- Python中的正則Python
- [深度學習]L2正則化和權重衰退(Weight Decay)深度學習
- 機器學習之稀疏性正則化:L1 正則化機器學習
- 「機器學習速成」稀疏性正則化:L1正則化機器學習
- 基於深度學習的圖書管理推薦系統(附python程式碼)深度學習Python
- 深度學習技術研究會深度學習
- Python TensorFlow深度學習迴歸程式碼:DNNRegressorPython深度學習DNN
- 技術 | 強化學習入門以及程式碼實現強化學習
- 萬字長文概述NLP中的深度學習技術深度學習
- 機器視覺軟體中的深度學習技術視覺深度學習
- 深度強化學習技術開發與應用強化學習
- 機器學習中的正則化到底是什麼意思?機器學習
- 機器學習之簡化正則化:Lambda機器學習
- 【深度學習】深度學習md筆記總結第1篇:深度學習課程,要求【附程式碼文件】深度學習筆記
- JS 正則學習JS
- 推薦系統中的前沿技術研究與落地:深度學習、AutoML與強化學習 | AI ProCon 2019深度學習TOML強化學習AI
- 將強化學習引入NLP:原理、技術和程式碼實現強化學習
- 正則學習小記
- 深度學習在攝影技術中的應用與發展深度學習
- 深度學習技術在網路入侵檢測中的應用深度學習
- 深度學習DeepLearning核心技術實戰深度學習
- 深度解析Android APP加固中的必備手段——程式碼混淆技術AndroidAPP
- 深度學習程式碼積累深度學習
- 利用深度學習和機器學習預測股票市場(附程式碼)深度學習機器學習
- 深度學習中的優化方法(二)深度學習優化
- 深度學習中的優化方法(一)深度學習優化
- 用Python進行機器學習(附程式碼、學習資源)Python機器學習
- 深度學習 | 分類任務中類別不均衡解決策略(附程式碼)深度學習
- 深度學習之影象超分辨重建技術深度學習
- 實戰 | 基於深度學習模型VGG的影象識別(附程式碼)深度學習模型