和風網標誌

了解 ConvNet 中的過度擬合

日期:

簡介

卷積網路中的過度擬合是深度學習和神經網路中的一個挑戰,其中模型從訓練資料中學習太多,導致新資料的表現不佳。這種現像在複雜的神經結構中尤其普遍,它可以模擬複雜的關係。解決卷積網路中的過度擬合對於建立可靠的神經網路模型至關重要。本文提供了理解和減輕過度擬合的指南,檢查模型複雜性、有限的訓練資料和雜訊特徵等根本原因。它還討論了防止過度擬合的技術,例如資料增強策略和正則化方法。

我建議閱讀這些文章以獲得基本了解 過擬合、欠擬合偏差方差權衡。

學習目標

  • 了解過度擬合的原因、後果和場景 卷積網絡.
  • 解釋學習曲線以檢測神經網路模型中的過度擬合和欠擬合。
  • 學習各種減輕過度擬合的技術,例如提前停止、dropout、批量標準化、正則化和資料增強。
  • 使用這些技術來實現 TensorFlow 和 Keras 在 CIFAR-10 資料集上訓練 ConvNet。
  • 分析不同技術對模型效能和泛化的影響。

目錄

ConvNet 中過度擬合的常見場景

讓我們來看看 ConvNet 中一些常見的過度擬合場景:

場景一:模型高度複雜,資料不足

在小數據集上使用非常複雜的模型(例如深度神經網路)可能會導致過度擬合。該模型可能會記住訓練範例,而不是學習一般模式。例如,為影像辨識等複雜任務訓練僅使用數百張影像的深度神經網路可能會導致過度擬合。

後果

該模型可能在訓練數據上表現良好,但無法推廣到新的、未見過的數據,導致在實際應用中表現不佳。

如何解決這個問題?

取得更多訓練數據,進行影像增強以概括我們的數據集。從不太複雜的模型開始,如果容量較小,則增加複雜性。 

場景2:過度訓練

連續訓練模型過多的時期可能會導致過度擬合。當模型反覆查看訓練資料時,它可能會開始記住它而不是學習底層模式。

後果

隨著模型對訓練集的專業化程度越來越高,模型的效能可能會在未見過的資料上趨於穩定甚至下降。

如何解決這個問題?

使用早期停止來避免模型過度擬合並保存最佳模型。 

情境3:忽略正規化

正則化技術(例如 L1 或 L2 正則化)用於透過懲罰複雜模型來防止過度擬合。忽略或不正確地調整正規化參數可能會導致過度擬合。

後果

該模型可能會變得過於複雜,並且無法很好地推廣到新數據,從而導致訓練集之外的性能不佳。

如何解決這個問題?

實施正規化、交叉驗證、超參數調整。 

什麼是機型容量?

模型的容量是指它能夠學習的模式的大小和複雜性。對於神經網路來說,這很大程度上取決於它有多少個神經元以及它們如何連接在一起。如果您的網路似乎不適合數據,您應該嘗試增加其容量。

您可以透過加寬網路(為現有層添加更多單元)或使其更深(增加更多層)來增加網路的容量。更寬的網路更容易學習更多的線性關係,而更深的網路更喜歡更多的非線性關係。哪個更好取決於資料集。

學習曲線的解釋

Keras 提供了在訓練時註冊回調的功能 深度學習模型。 訓練所有深度學習模型時註冊的預設回調之一是歷史回調。它記錄每個時期的訓練指標。這包括損失和準確性(對於分類問題)以及驗證資料集的損失和準確性(如果設定了)。

歷史物件是透過呼叫用於訓練模型的 fit() 函數傳回的。指標儲存在傳回物件的歷史成員中的字典中。

例如,您可以在訓練模型後使用以下程式碼片段列出歷史物件中收集的指標:

# list all data in history
print(history.history.keys())

輸出:

['準確度','損失','val_accuracy','val_loss']

信息類型

您可能會認為訓練資料中的資訊有兩種:

  • 信號: 訊號是概括的部分,可以幫助我們的模型根據新數據進行預測。
  • 噪音: 噪音是僅適用於訓練資料的部分;雜訊是來自現實世界中的資料的所有隨機波動,或是所有偶然的、非資訊性的模式,這些模式實際上無法幫助模型進行預測。噪音是該部件可能看起來有用但實際上沒有用。

當我們訓練模型時,我們一直在逐個時期繪製訓練集上的損失。為此,我們還將添加驗證數據圖。這些圖我們稱之為學習曲線。為了有效地訓練深度學習模型,我們需要能夠解釋它們。

學習曲線

在上圖中,我們可以看到訓練損失隨著時期的增加而減少,但驗證損失首先減少,並隨著模型開始捕捉資料集中存在的雜訊而增加。現在我們將了解如何透過各種技術避免卷積網路中的過度擬合。 

避免過度擬合的方法

現在我們已經了解了一些場景以及如何解釋學習曲線以檢測過度擬合。讓我們來看看一些避免神經網路過度擬合的方法:

方法1:使用更多數據

增加資料集的大小可以幫助模型更好地泛化,因為它有更多樣化的範例可供學習。模型將發現資料集中存在的重要模式並忽略噪聲,因為模型意識到所有資料集中並不存在這些特定模式(噪聲)。

方法2:提前停止

提前停止是一種透過在訓練期間監控模型在驗證集上的表現來防止過度擬合的技術。當驗證集的表現開始下降時,訓練就會停止,這表示模型開始過度擬合。通常,使用單獨的驗證集來監視效能,並且當效能在指定的時期數內沒有改善時停止訓練。

欠擬合和過擬合

方法3:輟學

我們知道,過度擬合是由網路學習訓練資料中的虛假模式(雜訊)引起的。為了識別這些虛假模式,網路通常會依賴非常特定的權重組合,這是一種權重的「陰謀」。由於它們如此具體,因此往往很脆弱:刪除其中一個,陰謀就會崩潰。

這就是 dropout 背後的想法。為了打破這些陰謀,我們在訓練的每一步中隨機丟棄層輸入單元的一部分,從而使網路更難學習訓練資料中的那些虛假模式。相反,它必須尋找廣泛、通用的模式,其權重模式往往更穩健。 

您也可以將 dropout 視為建立一種網路集合。預測將不再由一個大網路做出,而是由較小網路組成的委員會做出。委員會中的個人往往會犯不同類型的錯誤,但同時又是正確的,這使得委員會作為一個整體比任何個人都更好。 (如果您熟悉隨機森林作為決策樹的集合,那麼這是相同的想法。)

卷積網路中的過度擬合

方法4:批量歸一化

我們將看到的下一個特殊方法執行「批量歸一化」(或「batchnorm」),這可以幫助糾正緩慢或不穩定的訓練。

對於神經網絡,通常最好將所有資料放在一個通用的尺度上,也許可以使用 scikit-learn 的 StandardScaler 或 MinMaxScaler 之類的東西。原因是 SGD 會根據資料產生的啟動大小而按比例改變網路權重。傾向於產生不同大小活化的特徵可能會導致訓練行為不穩定。

現在,如果在資料進入網路之前對資料進行標準化是件好事,也許在網路內部進行標準化會更好!事實上,我們有一種特殊的層可以做到這一點,即批量歸一化層。批次歸一化層會查看每個批次的數據,首先使用批次本身的平均值和標準差對批次進行歸一化,然後使用兩個可訓練的縮放參數將數據置於新的尺度上。實際上,Batchnorm 對其輸入執行了一種協調的重新調整。

最常見的是,添加批歸一化作為最佳化過程的輔助(儘管有時它也可以幫助預測效能)。具有批量歸一化的模型往往需要更少的時期來完成訓練。此外,batchnorm 還可以修復各種可能導致訓練「卡住」的問題。考慮為您的模型添加批量歸一化,特別是當您在訓練期間遇到問題時。

方法5:L1和L2正規化

L1 和 L2 正則化是透過懲罰神經網路中的大權重來防止過度擬合的技術。 L1 正規化在損失函數中加入了一個與權重絕對值成比例的懲罰項。它鼓勵權重的稀疏性並可以導致特徵選擇。 L2 正則化,也稱為權重衰減,在損失函數中加入與權重平方成比例的懲罰項。它可以防止權重變得太大,並鼓勵權重分佈更均勻。

L1 和 L2 正規化之間的選擇通常取決於特定問題和模型所需的屬性。

L1/L2 正規化值過大將導致模型學習速度不快,並在學習中達到平台期,進而導致模型欠擬合。 

方法6:數據增強

提高機器學習模型效能的最佳方法是使用更多資料對其進行訓練。模型學習的例子越多,它就越能夠辨識影像中哪些差異重要,哪些差異不重要。更多數據有助於模型更好地泛化。

獲取更多數據的簡單方法是使用已有的數據。如果我們能夠以保留類別的方式轉換資料集中的影像(例如:MNIST 數字分類,如果我們嘗試增強 6,則將很難區分 6 和 9),我們可以教導我們的分類器忽略這些類型的轉換。例如,無論汽車在照片中朝左還是朝右,都不會改變它是汽車而不是卡車的事實。因此,如果我們用翻轉影像來增強訓練數據,我們的分類器將了解到「左或右」是它應該忽略的差異。

這就是數據增強背後的整個想法:增加一些看起來相當像真實數據的額外假數據,你的分類器將會得到改進。 

請記住,避免過度擬合的關鍵是確保模型具有良好的泛化能力。始終在驗證集上檢查模型的效能,而不僅僅是訓練集。

上述方法的數據實現

讓我們探討一下上述方法的實作步驟:

Step1:載入必要的函式庫

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.callbacks import ModelCheckpoint
import keras
from keras.preprocessing import image
from keras import models, layers, regularizers
from tqdm import tqdm
import warnings
warnings.filterwarnings(action='ignore')

Step2:載入資料集並預處理

#Here all the images are in the form of a numpy array
cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train / 255.0
x_test = x_test / 255.0

Step3:學習資料集

x_train.shape, y_train.shape, x_test.shape, y_test.shape 

輸出:

產量
np.unique(y_train)

輸出:

產量
#These labels are in the order and taken from the documentaion
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck']

第四步:可視化資料集中的影像

def show_image(IMG_INDEX):
    plt.imshow(x_train[20] ,cmap=plt.cm.binary)
    plt.xlabel(class_names[y_train[IMG_INDEX][0]])
    plt.show()
show_image(20)
卷積網路中的過度擬合
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10))
model.summary()

現在讓我們初始化超參數並使用優化器、損失函數和評估指標編譯模型。


train_hyperparameters_config={'optim':keras.optimizers.Adam(learning_rate=0.001),
                             'epochs':20,
                              'batch_size':16
                             }
model.compile(optimizer=train_hyperparameters_config['optim'],
                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                  metrics=['accuracy'])

Step6:訓練模型

history = model.fit(x_train, y_train, 
                        epochs=train_hyperparameters_config['epochs'], 
                        batch_size=train_hyperparameters_config['batch_size'], 
                        verbose=1,
                        validation_data=(x_test, y_test))

Step7:評估模型

這些將告訴我們歷史物件中包含的信息,我們使用這些資訊來創建資訊曲線。

print(history.history.keys()) 
def learning_curves(history):
# Plotting Accuracy
    plt.figure(figsize=(14, 5))  # Adjust the figure size as needed
    plt.subplot(1, 2, 1)  # Subplot with 1 row, 2 columns, and index 1
    plt.plot(history.history['accuracy'], label='train_accuracy', marker='s', markersize=4)
    plt.plot(history.history['val_accuracy'], label='val_accuracy', marker='*', markersize=4)
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.legend(loc='lower right')

    # Plotting Loss
    plt.subplot(1, 2, 2)  # Subplot with 1 row, 2 columns, and index 2
    plt.plot(history.history['loss'], label='train_loss', marker='s', markersize=4)
    plt.plot(history.history['val_loss'], label='val_loss', marker='*', markersize=4)
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.legend(loc='lower right')

    plt.show()
learning_curves(history)
卷積網路中的過度擬合

從曲線中我們可以看到,驗證精度在第 4 個 epoch 之後達到穩定狀態,模型開始捕捉雜訊。因此,我們將實施提前停止以避免模型過度擬合,並根據 val_loss 恢復最佳權重。當我們的神經網路嘗試使用優化器來減少損失時,我們將使用 val_loss 來監控提前停止。準確性和驗證準確性取決於閾值(分離類別的機率 - 對於二元分類通常為 0.5),因此如果我們的資料集不平衡,在大多數情況下我們應該擔心損失。 

Step8:實施提前停止

因為我們並不擔心我們的模型會過度擬合,因為提前停止將避免我們的模型發生這種情況。選擇較高的epoch數和合適的耐心是一個不錯的選擇。現在我們將使用相同的模型架構並透過提前停止回調進行訓練。 

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10))
model.summary()

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)

def model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config):
    model.compile(optimizer=train_hyperparameters_config['optim'],
                      loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                      metrics=['accuracy'])
    ht = model.fit(x_train, y_train, 
                            epochs=train_hyperparameters_config['epochs'], 
                            batch_size=train_hyperparameters_config['batch_size'],
                            callbacks=[callback],
                            verbose=1,
                            validation_data=(x_test, y_test))
    return ht

ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
卷積網路中的過度擬合

了解模型所採用的最佳權重。 

print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

第 9 步:增加模型複雜性

由於我們的模型表現不佳且擬合不足,因為它無法捕捉足夠的數據。我們應該增加模型的複雜性並進行評估。 

model = models.Sequential()

model.add(layers.Conv2D(128, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
model.summary()
產量

我們可以看到總參數增加。這將有助於在我們的模型中找到更複雜的關係。注意:我們的資料集是 32X32 影像;這些是相對較小的圖像。因此,一開始使用更複雜的模型肯定會過度擬合模型,因此我們傾向於緩慢增加模型的複雜性。

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)

learning_curves(ht)
卷積網路中的過度擬合
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

從上圖我們可以清楚地看出模型過度擬合,因此我們將使用另一種稱為 Drop out 歸一化和 Batch 歸一化的方法。

第 10 步:使用 Dropout Layers 和 Batch Normalization Layers

model = models.Sequential()

model.add(layers.Conv2D(128, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.3))

model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.3))

model.add(layers.Dense(10, activation='softmax'))
model.summary()
產量
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
卷積網路中的過度擬合
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

從學習圖中我們可以看到,即使使用批量歸一化和 dropout 層,模型也會過度擬合。因此,不是增加複雜性而是增加濾波器的數量。我們將添加更多的捲積層來提取更多的特徵。

Step11:增加卷積層數

減少可訓練參數但增加卷積層以提取更多特徵。

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))

model.add(layers.Dense(10, activation='softmax'))

model.summary()
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
產量
learning_curves(ht)
卷積網路中的過度擬合
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

從上面的輸出和學習曲線我們可以推斷模型表現得非常好並且避免了過度擬合。訓練精度和驗證精度非常接近。在這種情況下,我們不需要更多的方法來減少過度擬合。然而我們將探索 L1/L2 正規化。 

Step12:使用L1/L2正規化

from tensorflow.keras import regularizers

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l2(0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu', kernel_regularizer=regularizers.l1_l2(0.0005, 0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(10, activation='softmax'))

model.summary()
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 7,
    'epochs': 70,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
卷積網路中的過度擬合
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

現在我們可以看到,即使使用 1 的低懲罰分數,L2/L0.0001 正則化也會使我們的模型欠擬合 4%。因此,建議謹慎地同時使用所有方法。由於批量歸一化和正則化以類似的方式影響模型,因此我們不需要 L1/L2 正則化。 

Step13:資料增強

我們將使用來自tensorflow keras的ImageDataGenerator。

# creates a data generator object that transforms images
datagen = ImageDataGenerator(
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')

# pick an image to transform
test_img = x_train[20]
img = image.img_to_array(test_img)  # convert image to numpy arry
img = img.reshape((1,) + img.shape)  # reshape image

i = 0

for batch in datagen.flow(img, save_prefix='test', save_format='jpeg'):  # this loops runs forever until we break, saving images to current directory with specified prefix
    plt.figure(i)
    plot = plt.imshow(image.img_to_array(batch[0]))
    i += 1
    if i > 4:  # show 4 images
        break

plt.show()
卷積網路中的過度擬合

這是四張增強影像和一張原始影像。

# Create an instance of the ImageDataGenerator
datagen = ImageDataGenerator(
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest'
)

# Create an iterator for the data generator
data_generator = datagen.flow(x_train, y_train, batch_size=32)

# Create empty lists to store the augmented images and labels
augmented_images = []
augmented_labels = []

# Loop over the data generator and append the augmented data to the lists
num_batches = len(x_train) // 32
progress_bar = tqdm(total=num_batches, desc="Augmenting data", unit="batch")

for i in range(num_batches):
    batch_images, batch_labels = next(data_generator)
    augmented_images.append(batch_images)
    augmented_labels.append(batch_labels)
    progress_bar.update(1)

progress_bar.close()

# Convert the lists to NumPy arrays
augmented_images = np.concatenate(augmented_images, axis=0)
augmented_labels = np.concatenate(augmented_labels, axis=0)

# Combine the original and augmented data
x_train_augmented = np.concatenate((x_train, augmented_images), axis=0)
y_train_augmented = np.concatenate((y_train, augmented_labels), axis=0)
產量

我們使用 tqdm 函式庫來了解增強的進度。

x_train_augmented.shape, y_train_augmented.shape
產量

這是我們增強後的資料集。現在讓我們使用這個資料集並訓練我們的模型。

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))

model.add(layers.Dense(10, activation='softmax'))

model.summary()

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 10,
    'epochs': 70,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)

ht=model_train(model, x_train_augmented, y_train_augmented, x_test, y_test, train_hyperparameters_config)


learning_curves(ht)
卷積網路中的過度擬合
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

我們可以看到模型更加通用並且損失減少了。我們也獲得了更好的驗證準確性。因此,數據增強提高了我們的模型準確性。 

結論

過度擬合是深度學習中的常見問題,尤其是對於 ConvNet 等複雜的神經網路架構。從業者可以透過了解其根本原因並識別其發生的場景來防止卷積網路中的過度擬合。早期停止、遺失、批量標準化、正則化和資料增強等技術可以幫助緩解這個問題。在 CIFAR-10 資料集上實施這些技術顯示出模型泛化和效能的顯著改進。掌握這些技術並理解其原理可以產生強大且可靠的神經網路模型。

常見問題解答

Q1.什麼是過度擬合? 

答:當模型對訓練資料(包括其雜訊和不相關模式)學習得太好時,就會發生過度擬合,導致在新的、未見過的資料上表現不佳。這是一個問題,因為過度擬合的模型無法有效地泛化,限制了它們的實際用途。

Q2。如何偵測神經網路模型中的過度擬合?

答:您可以透過解釋學習曲線來偵測 ConvNet 中的過度擬合,該曲線繪製了曆元內的訓練和驗證指標(例如損失、準確性)。如果驗證指標停止改善或開始下降,而訓練指標繼續改善,則這是過度擬合的跡象。

Q3。什麼是早期停止,它如何幫助防止過度擬合?

答:早期停止是一種在訓練期間監控模型在驗證集上的表現的技術,並在驗證集上的表現開始下降(表明過度擬合)時停止訓練過程。它透過在正確的時間停止訓練來幫助防止模型過度擬合。

Q4。數據增強如何幫助減輕過度擬合? 

答:資料增強是透過對現有資料套用變換(例如,翻轉、旋轉、縮放)來產生新的合成訓練資料的過程。它透過將模型暴露在更多樣化的範例中來幫助模型更好地泛化,從而降低 ConvNet 中對有限訓練資料的過度擬合風險。

現貨圖片

最新情報

現貨圖片