歡迎光臨
每天分享高質量文章

深度學習中的正則化技術(附Python程式碼)

作者:SHUBHAM JAIN  翻譯:和中華  校對:丁楠雅

本文約3500字,建議閱讀20鐘。

本文簡單介紹了什麼是正則化以及在深度學習任務中可以採用哪些正則化技術,並keras程式碼具體講解了一個案例。

簡介

資料科學家面臨的常見問題之一是如何避免過擬合。你是否碰到過這樣一種情況:你的模型在訓練集上表現異常好,卻無法預測測試資料。或者在一個競賽中你排在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. 深度學習中的各種正則化技術


我們已經理解了正則化如何幫助減少過擬合,現在我們將學習一些把正則化用於深度學習的技術。

  • L1L2正則化


L1L2是最常見的正則化型別。它們透過增加一個被稱為正則項的額外項來更新成本函式:

Cost function = Loss (say, binary cross entropy) + Regularization term


由於增加了這個正則項,權重矩陣的值減小了,因為這裡假定了具有較小權重矩陣的神經網路會導致更簡單的模型。因此,它也會在相當程度上減少過擬合。

 

然而,該正則項在L1L2中是不同的。

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都會導致一個更高的驗證集錯誤。因此,在虛線之後的5epoch(因為我們設定patience等於5),由於沒有進一步的改善,模型將停止訓練。

 

註意:可能在5epoch之後(這是一般情況下為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)

建立一個驗證集,以便最佳化我們的模型以獲得更好的分數。我們將採用7030的訓練集、驗證集比例。

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值的技術。

結語

我希望現在你已經理解了正則化以及在深度學習模型中實現正則化的不同技術。 無論你處理任何深度學習任務,我都強烈建議你使用正則化。它將幫助你開闊視野並更好的理解這個主題。

原文連結:

https://www.analyticsvidhya.com/blog/2018/04/fundamentals-deep-learning-regularization-techniques/

譯者簡介:和中華,留德軟體工程碩士。由於對機器學習感興趣,碩士論文選擇了利用遺傳演演算法思想改進傳統kmeans。目前在杭州進行大資料相關實踐。加入資料派THU希望為IT同行們盡自己一份綿薄之力,也希望結交許多志趣相投的小夥伴。

END

版權宣告:本號內容部分來自網際網路,轉載請註明原文連結和作者,如有侵權或出處有誤請和我們聯絡。


關聯閱讀:

原創系列文章:

1:從0開始搭建自己的資料運營指標體系(概括篇)

2 :從0開始搭建自己的資料運營指標體系(定位篇)

3 :從0開始搭建自己的資料運營體系(業務理解篇)

4 :資料指標的構建流程與邏輯

5 :系列 :從資料指標到資料運營指標體系

6:   實戰 :為自己的公號搭建一個資料運營指標體系

7:  從0開始搭建自己的資料運營指標體系(運營活動分析)

資料運營 關聯文章閱讀:  

運營入門,從0到1搭建資料分析知識體系    

推薦 :資料分析師與運營協作的9個好習慣

乾貨 :手把手教你搭建資料化使用者運營體系

推薦 :最用心的運營資料指標解讀

乾貨 : 如何構建資料運營指標體系

從零開始,構建資料化運營體系

乾貨 :解讀產品、運營和資料三個基友關係

乾貨 :從0到1搭建資料運營體系

資料分析、資料產品 關聯文章閱讀:

乾貨 :資料分析團隊的搭建和思考

關於使用者畫像那些事,看這一文章就夠了

資料分析師必需具備的10種分析思維。

如何構建大資料層級體系,看這一文章就夠了

乾貨 : 聚焦於使用者行為分析的資料產品

如何構建大資料層級體系,看這一文章就夠了

80%的運營註定了打雜?因為你沒有搭建出一套有效的使用者運營體系

從底層到應用,那些資料人的必備技能

讀懂使用者運營體系:使用者分層和分群

做運營必須掌握的資料分析思維,你還敢說不會做資料分析

贊(0)

分享創造快樂