在机器学习中,常常会遇到模型在训练数据集上的准确率经过一些周期的训练后达到峰值,然后停滞不前或开始下降的情况。
过拟合理解如何处理过拟合至关重要。尽管在训练集上获得高准确率通常是可行的,但实际上你想要构建的是能够有效泛化到测试集(或未见过的数据)的模型。当模型在样本数据上训练过久(超过所需的迭代次数)或者模型过于复杂时,它可能开始学习数据集中的“噪声”或无关信息,并记住这些噪声,从而与训练集过度吻合,这时模型就变得“过拟合”,无法很好地泛化到新数据上。
欠拟合欠拟合是过拟合的完全相反。当训练数据仍有改进空间而模型训练时间不足,或者输入变量不足以(需要更多数据)确定输入和输出变量之间的有意义的关系时,就会发生欠拟合。这表明网络没有识别出训练数据中的相关模式。
两种情况的共同点模型无法识别训练数据集中的主导趋势。因此,欠拟合对未见数据的泛化能力差。与过拟合相比,欠拟合的模型在其预测中具有高偏差和低方差。在拟合模型时,目标是找到欠拟合和过拟合之间的“甜蜜点”,以便建立并普遍应用到新的数据集上。
平衡过拟合/欠拟合条件的方法减少ANN模型结构的复杂性:使用简单的架构代替高度复杂的神经网络有助于摆脱过拟合。复杂的结构可能会使网络的学习能力感到困惑。早停法:这种技术旨在在模型开始学习其自身的噪声之前停止训练。这种策略存在过早停止训练过程的风险,这会导致欠拟合,即相反的问题。最终目标是找到欠拟合和过拟合之间的“甜蜜点”。使用更多数据进行训练:通过将更多数据包含到训练集中,可以增加模型的准确性,为解析输入和输出变量之间的主导关系提供更多机会。也就是说,将干净的数据注入模型是一种更有效的方法。否则,你可能会不断增加模型的复杂性,导致过拟合。数据增强:虽然是将干净数据注入训练数据,但有时也会添加噪声数据以使模型更稳定。然而,这种方法应该谨慎使用。正则化:正则化通过对复杂模型施加惩罚来优化模型,从而最小化损失和复杂性。这样就迫使神经网络变得更简单。最常用的正则化是L1和L2正则化。L1正则化,也称为套索回归,将在损失函数中添加系数的“绝对值大小”作为惩罚项。L2正则化,也称为岭回归,将在损失函数中添加系数的“平方大小”作为惩罚项。随机失活:随机失活是机器学习中用于防止过拟合并提高模型性能的强大技术。它通过随机“丢弃”模型中输入层和隐藏层的神经元来实现。这比正则化方法要好得多,并且还可以与最大范数归一化结合使用,这样可以比仅使用随机失活获得显著提升。举例加载模型
#Load Librariesimport tensorflow astf
from tensorflow importkeras
from tensorflow.keras importlayers
import numpy asnp
import pandas aspd
加载图像并划分训练集和测试集
image_size = (180, 180)
batch_size = 128train_ds, val_ds = tf.keras.utils.image_dataset_from_directory(
"/kaggle/input/dataset/training_set",
validation_split=0.2,
subset="both",
seed=1337,
image_size=image_size,
batch_size=batch_size,
)
构建模型
def make_model(input_shape, num_classes):inputs = keras.Input(shape=input_shape)
# Entry block x = layers.Rescaling(1.0 / 255)(inputs)
x = layers.Conv2D(128, 3, strides=2, padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.Activation("relu")(x)
previous_block_activation = x # Set aside residual for size in [256, 512, 728]:
x = layers.Activation("relu")(x)
x = layers.SeparableConv2D(size, 3, padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.Activation("relu")(x)
x = layers.SeparableConv2D(size, 3, padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.MaxPooling2D(3, strides=2, padding="same")(x)
# Project residual residual = layers.Conv2D(size, 1, strides=2, padding="same")(
previous_block_activation
)
x = layers.add([x, residual]) # Add back residual previous_block_activation = x # Set aside next residual x = layers.SeparableConv2D(1024, 3, padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.Activation("relu")(x)
x = layers.GlobalAveragePooling2D()(x)
if num_classes == 2:
activation = "sigmoid" units = 1 else:
activation = "softmax"units = num_classes
x = layers.Dropout(0.5)(x) #only use while overfittingoutputs = layers.Dense(units, activation=activation)(x)
returnkeras.Model(inputs, outputs)
model = make_model(input_shape=image_size + (3,), num_classes=2)
keras.utils.plot_model(model, show_shapes=True)
训练模型
epochs = 10callbacks = [
keras.callbacks.ModelCheckpoint("/kaggle/working/save_at_{epoch}.keras"),
]
model.compile(
optimizer=keras.optimizers.Adam(1e-3),
loss="binary_crossentropy",
metrics=["accuracy"],
)
model.fit(
train_ds,
epochs=epochs,
callbacks=callbacks,
validation_data=val_ds,
输出表明模型过拟合了。因此,我们将使用数据增强来减少过拟合问题。与此同时,我们还在最后的密集层之前使用了随机失活。
数据增强
当你没有大量的图像数据集时,通过应用随机但现实的变换来人为引入样本多样性,例如对训练图像进行随机水平翻转或小幅度随机旋转。这有助于让模型接触到训练数据的不同方面,同时减缓过拟合。
data_augmentation = keras.Sequential(
[
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.1),
]
)
同步增强训练数据
# Apply data_augmentation to the training images.train_ds = train_ds.map(
lambdaimg, label: (data_augmentation(img), label),
num_parallel_calls=tf.data.AUTOTUNE,
)
# Prefetching samples in GPU memory helps maximize GPU utilization.train_ds = train_ds.prefetch(tf.data.AUTOTUNE)
val_ds = val_ds.prefetch(tf.data.AUTOTUNE)
重新训练模型,比较增强图片前后的训练曲线图(左图和右图分别是图片增强前后的训练曲线图,蓝色是训练曲线,黄色是验证曲线):
有很多方法可以改进这个模型。你可以在隐藏层中使用正则化,早停法来处理过拟合情况。根据你数据集的情况,调整超参数和其他变量输入以获得最佳拟合线。