理解神经网络中的过拟合(TensorFlow - CNN)

发布: (2025年12月12日 GMT+8 00:17)
4 分钟阅读
原文: Dev.to

Source: Dev.to

过拟合是开发神经网络时的一个根本性挑战。一个在训练数据集上表现极佳的模型可能无法对未见数据进行泛化,从而导致真实世界中的性能不佳。本文使用 Fashion‑MNIST 数据集对过拟合进行结构化调查,并评估了几种缓解策略,包括 Dropout、L2 正则化和提前停止(Early Stopping)。

Fashion‑MNIST 数据集

  • 60,000 张训练图像
  • 10,000 张测试图像
  • 28 × 28 灰度格式
  • 10 个输出类别

有意使用了显著更小的训练子集,以使过拟合行为更加明显。

CNN 架构

def create_cnn_model(l2_lambda=0.0, dropout_rate=0.0):
    model = keras.Sequential([
        layers.Conv2D(32, (3, 3), activation='relu',
                      kernel_regularizer=regularizers.l2(l2_lambda)),
        layers.MaxPooling2D((2, 2)),
        layers.Conv2D(64, (3, 3), activation='relu',
                      kernel_regularizer=regularizers.l2(l2_lambda)),
        layers.MaxPooling2D((2, 2)),
        layers.Flatten(),
        layers.Dense(64, activation='relu',
                     kernel_regularizer=regularizers.l2(l2_lambda)),
        layers.Dropout(dropout_rate),
        layers.Dense(10, activation='softmax')
    ])

    model.compile(
        optimizer="adam",
        loss="sparse_categorical_crossentropy",
        metrics=["accuracy"]
    )
    return model

所有实验均使用此架构,可选的 L2 正则化和 Dropout。

绘制训练历史的工具函数

def plot_history(history, title_prefix=""):
    hist = history.history
    plt.figure(figsize=(12, 5))

    plt.subplot(1, 2, 1)
    plt.plot(hist["loss"], label="Train Loss")
    plt.plot(hist["val_loss"], label="Val Loss")
    plt.title(f"{title_prefix} Loss")
    plt.legend()

    plt.subplot(1, 2, 2)
    plt.plot(hist["accuracy"], label="Train Accuracy")
    plt.plot(hist["val_accuracy"], label="Val Accuracy")
    plt.title(f"{title_prefix} Accuracy")
    plt.legend()

    plt.tight_layout()
    plt.show()

基线模型(无正则化)

baseline_model = create_cnn_model(l2_lambda=0.0, dropout_rate=0.0)
history_baseline = baseline_model.fit(
    x_train_small, y_train_small,
    validation_split=0.2,
    epochs=20
)
plot_history(history_baseline, title_prefix="Baseline (no regularisation)")

观察结果

  • 训练准确率持续稳步上升。
  • 验证准确率在早期达到峰值后开始下降。
  • 训练损失下降,而验证损失上升。

这些模式表明出现了明显的过拟合。

Dropout(率 = 0.5)

dropout_model = create_cnn_model(dropout_rate=0.5)
history_dropout = dropout_model.fit(
    x_train_small, y_train_small,
    validation_split=0.2,
    epochs=20
)
plot_history(history_dropout, title_prefix="Dropout (0.5)")

观察结果

  • 训练准确率上升较慢(这是 Dropout 的预期效果)。
  • 验证准确率与训练曲线更为接近。
  • 训练与验证损失之间的差距显著缩小。

Dropout 在本实验中效果显著,显著提升了泛化能力。

L2 正则化(λ = 0.001)

l2_model = create_cnn_model(l2_lambda=0.001)
history_l2 = l2_model.fit(
    x_train_small, y_train_small,
    validation_split=0.2,
    epochs=20
)
plot_history(history_l2, title_prefix="L2 Regularisation")

观察结果

  • 由于权重惩罚,训练损失明显更高。
  • 与基线相比,验证损失趋势更为平稳。
  • 验证准确率有适度提升。

L2 正则化平滑了学习动态,缓解了过拟合,但其影响相较于 Dropout 较为温和。

提前停止(Early Stopping)

earlystop_model = create_cnn_model()
early_stop = keras.callbacks.EarlyStopping(
    monitor='val_loss',
    patience=3,
    restore_best_weights=True
)

history_early = earlystop_model.fit(
    x_train_small, y_train_small,
    validation_split=0.2,
    epochs=20,
    callbacks=[early_stop]
)
plot_history(history_early, title_prefix="Early Stopping")

观察结果

  • 当验证损失不再提升时,训练提前终止。
  • 避免了基线模型在后期出现的过拟合。
  • 在所有模型中产生了最干净的验证曲线之一。

提前停止是一种简单且有效的泛化技术。

模型转换(可选)

converter = tf.lite.TFLiteConverter.from_keras_model(baseline_model)
tflite_model = converter.convert()
print("Quantised model size (bytes):", len(tflite_model))

此步骤演示了用于部署的模型体积缩减;它并非正则化策略。

结果汇总

  • 基线:出现明显过拟合。
  • Dropout:在验证表现上提升最大。
  • L2 正则化:帮助稳定训练动态。
  • 提前停止:防止后期发散并提升泛化。
  • 组合(Dropout + 提前停止):在缩减后的 Fashion‑MNIST 数据集上实现最稳健的性能。
Back to Blog

相关文章

阅读更多 »