在机器学习中,我们的目标是发现泛化(Generalization)模式,即在未见过的数据上也能预测准确。然而,模型往往会陷入两个极端:要么学得太浅(欠拟合),要么记住了噪音(过拟合)。
1. 核心概念:训练误差 vs 泛化误差
- 训练误差(Training Error):模型在训练数据集上的误差。
- 泛化误差(Generalization Error):模型应用在同样分布的无穷多新数据上的误差期望。
- 验证集(Validation Set):由于我们无法提前知道泛化误差,通常预留一部分不参与训练的数据来模拟测试环境,进行模型选择。
2. 三种典型的模型状态
① 欠拟合 (Underfitting)
- 表现:模型无法在训练集上获得足够低的误差。
- 原因:模型容量(复杂度)太低,无法捕捉数据的基本模式。
- 对策:增加网络深度、增加神经元数量或减少正则化。
② 过拟合 (Overfitting)
- 表现:训练误差远低于验证误差。
- 原因:模型容量太高,它不仅学到了规律,还死记硬背了训练集中的随机噪音。
- 对策:增加数据量、使用权重衰减(L2 正则化)、Dropout 或简化模型。
③ 最佳状态 (Best Fit)
- 表现:训练误差和验证误差都很低,且两者差距较小。
3. 代码实战:通过多项式回归观察拟合状态
文件通过多项式函数拟合的例子,生动展示了模型容量如何影响结果。
准备:多项式特征生成
我们将真实标签设为三阶多项式,然后尝试用不同阶数的模型去拟合它。
Python
import math
import torch
from torch import nn
from d2l import torch as d2l
# 假设真实函数为 y = 5 + 1.2x - 3.4x^2 / 2! + 5.6x^3 / 3! + noise
max_degree = 20 # 多项式的最大阶数
n_train, n_test = 100, 100 # 训练和测试数据集大小
true_w = torch.zeros(max_degree) # 分配大量的空间
true_w[0:4] = torch.tensor([5, 1.2, -3.4, 5.6])
# 生成特征和标签
features = torch.randn((n_train + n_test, 1))
poly_features = torch.pow(features, torch.arange(max_degree).reshape(1, -1))
for i in range(max_degree):
poly_features[:, i] /= math.gamma(i + 1)
labels = torch.matmul(poly_features, true_w)
labels += torch.randn(labels.shape) * 0.1 # 加入噪音
情况一:正常拟合(使用三阶多项式)
模型容量与真实数据匹配,泛化性能最好。
Python
# 取前4个特征(0~3阶)进行训练
train(poly_features[:n_train, :4], poly_features[n_train:, :4],
labels[:n_train], labels[n_test:])
情况二:欠拟合(只用一阶多项式)
模型太简单,无法描述高阶曲线。
Python
# 只取前2个特征(0~1阶)
train(poly_features[:n_train, :2], poly_features[n_train:, :2],
labels[:n_train], labels[n_test:])
情况三:过拟合(用十九阶多项式)
模型尝试穿过每一个噪点,导致验证误差激增。
Python
# 使用所有特征(0~19阶)
train(poly_features[:n_train, :], poly_features[n_train:, :],
labels[:n_train], labels[n_test:])
4. 关键结论:VC 维与数据量
文件探讨了影响拟合程度的两个主要维度:
- 模型容量:参数越多、隐藏层越深、非线性越强,容量越大。
- 数据量:数据越多,模型越难"背诵"所有样本,泛化能力越强。
5. 总结:如何打磨一个完美的模型?
- 先跑通简单模型:观察是否存在欠拟合。
- 逐渐增加复杂度:直到模型能够很好地拟合训练集。
- 引入正则化技术:如 Dropout 或 L2 惩罚,拉近训练集与测试集的距离,压制过拟合。
💡 学习小结
深度学习不仅是构建模型的艺术,更是控制误差的艺术。掌握了过拟合与欠拟合的原理,你就拥有了"对症下药"调试模型的能力。