和风网标志

了解 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 中对有限训练数据的过度拟合风险。

现货图片

最新情报

现货图片