1. 程式人生 > >tf.keras入門(4) Explore over-fitting and under-fitting

tf.keras入門(4) Explore over-fitting and under-fitting

探索過擬合與欠擬合

我們將探索兩種常見的正則化技術(權重正則化丟棄),並使用它們改進我們的 IMDB 影評分類筆記本。

我們不會像在上一次那樣使用嵌入,而是對句子進行獨熱編碼。該模型將很快過擬合訓練集。它將用來演示何時發生過擬合,以及如何防止過擬合。

務必謹記:深度學習模型往往善於與訓練資料擬合,但真正的挑戰是泛化,而非擬合。

資料預處理與網路結構

和之前採用嵌入方法不同,這裡我們直接使用one hot 編碼,該模型將很快過擬合訓練集。

從上往下一共有5個模型,我們來對比它們在validation上的結果:

  • baseline_model

img

  • smaller_model

img

  • bigger_model

img

  • baseline_model_l2

img

  • baseline_model_dropout

img

介面解釋

  • 將資料轉為二維矩陣,注意enumerate操作,可同時獲得索引和值
def multi_hot_sequences(sequences, dimension):
    # create an all_zero matrix of shape(len(sequences), dimension)
    results =
np.zeros( (len(sequences), dimension) ) # 引數應該提供一個元組 for i, word_indices in enumerate(sequences): # 可同時獲得索引和值 results[i,word_indices] = 1.0 return results

要緩解過擬合,一種常見方法是限制網路的複雜性,具體方法是強制要求其權重僅採用較小的值,使權重值的分佈更“規則”。這稱為“權重正則化”,通過向網路的損失函式新增與權重較大相關的代價來實現。這個代價分為兩種型別:

  • L1 正則化,其中所新增的代價與權重係數的絕對值(即所謂的權重“L1 範數”)成正比。
  • L2 正則化,其中所新增的代價與權重係數值的平方(即所謂的權重“L2 範數”)成正比。L2 正則化在神經網路領域也稱為權重衰減。不要因為名稱不同而感到困惑:從數學角度來講,權重衰減與 L2 正則化完全相同。
  • 新增L2正則化 表示層的權重矩陣中的每個係數都會將 0.001 * weight_coefficient_value**2 新增到網路的總損失中。請注意,由於此懲罰僅在訓練時新增,此網路在訓練時的損失將遠高於測試時。
  keras.layers.Dense(16,activation=tf.nn.relu,kernel_regularizer=keras.regularizers.l2(0.001),input_shape=(NUM_WORDS, )),

  • 新增dropout層(目前最有效且最常用的神經網路正則化技術之一)。假設某個指定的層通常會在訓練期間針對給定的輸入樣本返回一個向量 [0.2, 0.5, 1.3, 0.8, 1.1];在應用丟棄後,此向量將隨機分佈幾個 0 條目,例如 [0, 0.5, 1.3, 0, 1.1]。“丟棄率”指變為 0 的特徵所佔的比例,通常設定在 0.2 和 0.5 之間在測試時,網路不會丟棄任何單元,而是將層的輸出值按等同於丟棄率的比例進行縮減,以便平衡以下事實:測試時的活躍單元數大於訓練時的活躍單元數。
keras.layers.Dense(16, activation=tf.nn.relu, input_shape=(NUM_WORDS,)),
   				 keras.layers.Dropout(0.5),
    ...
    ...

  • 作圖函式
def plot_history(histories, key='binary_crossentropy'):
    plt.figure(figsize=(16,10))
    for name,history in histories:
        val = plt.plot(history.epoch, history.history['val_'+key],'--
                       ',label=name.title()+' Val')
        plt.plot(history.epoch, history.history[key], color=val[0].get_color(),
                 label=name.title()+' Train')

    plt.xlabel('Epochs')
    plt.ylabel(key.replace('_',' ').title())
    plt.legend()
    plt.xlim([0,max(history.epoch)])

總結

  • 三種不同容量的網路的訓練效果:

實線表示訓練損失,虛線表示驗證損失(謹記:驗證損失越低,表示模型越好)。在此示例中,較小的網路開始過擬合的時間比基準模型晚(前者在 6 個週期之後,後者在 4 個週期之後),並且開始過擬合後,它的效果下降速度也慢得多。

img

  • 加入L2正則化的模型:

可以看到,L2 正則化模型的過擬合抵抗能力比基準模型強得多,雖然這兩個模型的引數數量相同。:

img

  • 新增dropout層可明顯改善基準模型:

img

下面總結一下防止神經網路出現過擬合的最常見方法:

  • 獲取更多訓練資料
  • 降低網路容量
  • 新增權重正則化
  • 新增dropout層

還有兩個重要的方法在本指南中沒有介紹:資料增強批次標準化

Code

import tensorflow as tf 
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt


'''
資料預處理
'''
NUM_WORDS = 10000
(train_data, train_labels), (test_data, test_labels) = keras.datasets.imdb.load_data(num_words=NUM_WORDS)
print(train_data.shape)

def multi_hot_sequences(sequences, dimension):
    # create an all_zero matrix of shape(len(sequences), dimension)
    results = np.zeros(  (len(sequences), dimension)  ) #提供一個元組
    for i, word_indices in enumerate(sequences): # 可同時獲得索引和值
        results[i,word_indices] = 1.0
    return results

train_data = multi_hot_sequences(train_data, NUM_WORDS)
test_data  = multi_hot_sequences(test_data, NUM_WORDS)
# plt.plot(train_data[0])
# plt.show()


'''
建立模型
'''
baseline_model = keras.Sequential([
    # 'input_shape' is only required here so that '.summary' works
    keras.layers.Dense(16, activation=tf.nn.relu, input_shape=(NUM_WORDS, )),
    keras.layers.Dense(16, activation=tf.nn.relu),
    keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
baseline_model.compile( optimizer = 'adam',
                        loss='binary_crossentropy',
                        metrics=['accuracy','binary_crossentropy'])
baseline_model.summary()



smaller_model = keras.Sequential([
    keras.layers.Dense(4,activation=tf.nn.relu, input_shape=(NUM_WORDS,)),
    keras.layers.Dense(4,activation=tf.nn.relu),
    keras.layers.Dense(1,activation=tf.nn.sigmoid)
])
smaller_model.compile(optimizer='adam',
                      loss='binary_crossentropy',
                      metrics=['accuracy','binary_crossentropy'])
smaller_model.summary()



bigger_model =  keras.Sequential([
    keras.layers.Dense(512, activation=tf.nn.relu, input_shape=(NUM_WORDS,)),
    keras.layers.Dense(512, activation=tf.nn.relu),
    keras.layers.Dense(1,   activation=tf.nn.sigmoid)
])
bigger_model.compile(optimizer='adam',
                     loss='binary_crossentropy',
                     metrics=['accuracy','binary_crossentropy'])
bigger_model.summary()



baseline_model_l2 = keras.Sequential([
    keras.layers.Dense(16, activation=tf.nn.relu, kernel_regularizer=keras.regularizers.l2(0.001),
                        input_shape=(NUM_WORDS, )),
    keras.layers.Dense(16, activation=tf.nn.relu, kernel_regularizer=keras.regularizers.l2(0.001)),
    keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
baseline_model_l2.compile(optimizer='adam',
                            loss='binary_crossentropy',
                            metrics=['accuracy','binary_crossentropy'])
baseline_model_l2.summary()



baseline_model_dropout = keras.Sequential([
    keras.layers.Dense(16, activation=tf.nn.relu, input_shape=(NUM_WORDS, )),
    keras.layers.Dropout(0.5),
    keras.layers.Dense(16, activation=tf.nn.relu),
    keras.layers.Dropout(0.5),
    keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
baseline_model_dropout.compile(optimizer='adam',
                                loss='binary_crossentropy',
                                metrics=['accuracy','binary_crossentropy'])
baseline_model_dropout.summary()                                



'''
訓練模型
'''
def train_model(model):
    history = model.fit(train_data,
                        train_labels,
                        epochs=20,
                        batch_size=512,
                        validation_data=(test_data, test_labels),
                        verbose=2) 
    return history

a= train_model(baseline_model)
# b= train_model(smaller_model)
# c= train_model(bigger_model)
# d = train_model(baseline_model_l2)
e = train_model(baseline_model_dropout)


'''
作圖 檢視模型效果
'''
def plot_history(histories, key='binary_crossentropy'):
    plt.figure(figsize=(16,10))
    for name,history in histories:
        val = plt.plot(history.epoch, history.history['val_'+key],'--',label=name.title()+' Val')
        plt.plot(history.epoch, history.history[key], color=val[0].get_color(),
                 label=name.title()+' Train')

    plt.xlabel('Epochs')
    plt.ylabel(key.replace('_',' ').title())
    plt.legend()
    plt.xlim([0,max(history.epoch)])


plot_history([('baseline',a),
              #('smaller_model',b),
              #('bigger_model',c),
              #('baseline_l2',d),
              ('baseline_dropout',e)])
plt.show()