机器学习进阶(15):过拟合

第十五篇:过拟合到底是怎么发生的------为什么训练集表现很好,模型却不一定真懂了

学机器学习的人,几乎都会在某个时刻碰到一种非常熟悉的场景。

你训练了一个模型。

训练集上的效果特别好,甚至好得让你有点兴奋。

比如:

  • 准确率 99%
  • 损失几乎降到底
  • 看起来模型已经把任务吃透了

然后你把它拿去跑验证集或者测试集。

结果一下子掉下来。

有时候是从 99% 掉到 85%。

有时候甚至更夸张。

这个时候很多人第一反应是:

是不是验证集太难了?

是不是运气不好?

是不是这次数据切得有问题?

这些可能性当然不是完全没有。

但更常见的情况其实是:

模型过拟合了。

这个词你可能已经见过很多次。

但如果只把它记成"训练集好,测试集差",其实还不够。

因为这只是现象,不是理解。

这一篇我们就把它拆开讲清楚:

  • 过拟合到底是什么
  • 它为什么会发生
  • 模型到底是在"学规律"还是"记细节"
  • 你怎么判断自己是不是过拟合了
  • 为什么模型越复杂,有时反而越危险

1. 先别急着背定义,先看一个特别像学生复习的类比

如果你把机器学习模型看成一个学生,过拟合这件事其实特别好理解。

假设这个学生明天要考试。

他手里有一套练习题。

如果他真正理解了知识点,那他面对新题也能做出来。

这对应的是模型真正学到了规律,有泛化能力。

但如果他只是把练习题和答案背下来了,会发生什么?

练习题他会做得特别好。

可一换题型,成绩马上掉。

这就是过拟合最接近本质的地方:

模型不是没有学,而是学得太贴着训练数据了。

它学到的不只是规律,还把训练数据里的很多局部细节、偶然模式、甚至噪声,也一起当成了"规律"。

所以一旦换到新数据上,这些"假规律"就开始失效。


2. 过拟合最表面的现象是什么

过拟合最典型的表面现象,就是:

  • 训练集表现很好
  • 验证集 / 测试集表现明显差很多

比如分类任务里,你可能看到:

  • 训练集准确率:99%
  • 验证集准确率:84%

回归任务里,你可能看到:

  • 训练集 MSE 很低
  • 验证集 MSE 明显更高

这时候你就应该警觉:

模型可能不是在真正学习可以推广的规律,而是在记住训练集本身。

注意,这里不是说"训练集好"本身有问题。

训练集效果好当然是我们想要的。

问题在于:

训练集好到什么程度,和验证集之间差了多少。

如果差距很大,才说明你可能碰到了过拟合。


3. 模型到底是在学什么,为什么会"学偏"

这是理解过拟合最关键的一步。

任何一个模型在训练时,本质上都在做同一件事:

尽量让自己在训练数据上的误差变小。

注意,是"训练数据"。

模型本身并不知道你真正想要的是"新数据也表现好"。

它只知道:

当前损失函数是根据训练集算出来的,所以它会拼命优化训练集上的结果。

如果模型能力不够强,它可能连训练集都学不好。

这是欠拟合。

但如果模型能力很强,而且你又没有限制它,它就可能会走向另一头:

它不只是学到整体规律,还会把训练集里那些很偶然、很局部的东西也吃进去。

比如:

  • 某几个异常样本
  • 某些偶然同时出现的特征组合
  • 样本里的噪声
  • 训练集特有的一些小偏差

这些东西在训练集上当然能帮它把分数做高。

但它们对新数据并不一定成立。

这时候模型看起来很努力,实际上却在往错误的方向"钻细节"。


4. 一个特别直观的例子:拟合曲线时,线太弯了

过拟合在回归问题里特别容易想象。

假设你有一批数据点,整体趋势大概是一条平滑上升的曲线。

如果你画一条合适的曲线,它大概能抓住整体走势。

但如果你给模型太大的自由度,它可能会做什么?

它会想尽办法穿过每一个训练点。

最后画出来的线,可能弯弯绕绕,像在"追着点跑"。

你看起来会觉得:

  • 哇,训练点都贴得好准

但换一批新点,这条线的预测可能反而很差。

因为它拟合的不是"整体趋势",而是"这些点刚好长成的样子"。

这就是过拟合很经典的画面:

模型为了把训练集做到极致,学得太细了。


5. 在分类问题里,过拟合又会长什么样

分类任务里,过拟合虽然不像回归那样容易"看到一条奇怪的曲线",但本质是一样的。

假设两类样本大致能被一个比较平滑的边界分开。

如果模型过拟合,它可能会做什么?

它会把分类边界弄得特别弯、特别碎、特别贴着训练样本走。

哪怕只是为了把几个边缘点、几个异常点也分对。

这样做的结果是:

  • 训练集几乎全对
  • 但边界变得很脆弱
  • 稍微来一点新样本分布变化,就容易出错

所以过拟合在分类里,本质上就是:

边界开始服务于训练集的局部细节,而不再服务于整体规律。


6. 为什么模型越复杂,越容易过拟合

这个结论你可能已经听过很多次:

模型越复杂,越容易过拟合。

但这句话如果只停在口号层面,其实帮助不大。

我们还是得把它说成人话。

所谓模型复杂,不一定只是"算法名字更高级"。

它更像是:

模型有多大的自由度,去贴合训练数据。

比如:

  • 一条直线,自由度比较低
  • 一个高阶多项式,自由度更高
  • 一棵很深的决策树,自由度很高
  • 一个参数很多的模型,自由度也更高

自由度高的好处是,它更有能力捕捉复杂关系。

但问题也在这里:

如果你不给它约束,它也更有能力去记住噪声、例外、偶然模式。

所以复杂模型不是原罪。

真正的问题是:

当模型复杂度超过了数据真正需要的程度,它就开始用多出来的能力去"记训练集"。

这时候就容易过拟合。


7. 这也是为什么"训练得越久"有时反而越危险

很多人刚接触训练过程时,会下意识觉得:

训练轮数越多,模型应该越好吧?

不一定。

特别是一些可以持续迭代优化的模型,比如神经网络、GBDT、甚至某些 boosting 类模型,训练初期通常会先学到比较稳定的整体规律。

但如果你让它一直继续学,它后面可能会开始去适应训练集里的噪声和细枝末节。

结果就是:

  • 训练集损失继续下降
  • 但验证集表现不升反降

这其实也是过拟合的一种典型轨迹。

所以并不是"训练得越狠越好",而是:

训练要刚好停在模型已经学到主要规律、但还没开始迷恋训练集细节的位置。

这也是为什么后面你会看到"早停(early stopping)"这种方法。


8. 数据少的时候,为什么特别容易过拟合

这个点也特别现实。

如果数据量很少,模型能看到的世界本来就有限。

在这种情况下,训练集里的偶然性会显得特别强。

比如你只有几百条数据。

这几百条里某些巧合、偏差、异常点,本来只是样本太少带来的随机波动。

但模型可能会误以为它们是规律。

所以数据少的时候,模型更容易:

  • 把偶然现象当规律
  • 对训练集学得过于具体
  • 在验证集上掉分

这也是为什么:

  • 小数据场景下要更小心模型复杂度
  • 更要依赖交叉验证
  • 更要警惕"训练集太漂亮"的结果

9. 噪声为什么会加剧过拟合

过拟合很多时候不是模型"太笨",反而是模型"太认真"。

只要训练集里有噪声,比如:

  • 标签标错了
  • 某些特征值填错了
  • 样本本身就是异常点
  • 数据采集过程有误差

模型如果太有能力,它就可能会去努力解释这些噪声。

问题是,噪声本来就不是真规律。

你越努力把它学进去,越会损害对新数据的泛化能力。

所以有时候你会看到一种很微妙的情况:

  • 一个复杂模型在训练集上比简单模型更好
  • 但验证集却反而不如简单模型

这往往不是因为复杂模型"不够强",而是因为它强到连噪声都没放过。


10. 过拟合不是某一种算法的问题,而是一种普遍风险

很多人一开始会误以为:

  • 决策树容易过拟合
  • 神经网络容易过拟合
  • 所以是不是某些算法天生就"坏一点"

其实不是这样。

过拟合不是某个特定算法的专利,而是几乎所有模型都会面临的风险。

只不过不同算法表现形式不同,严重程度不同。

比如:

决策树

树太深的时候,特别容易把训练样本切得特别细,直接记住数据。

随机森林

相比单棵树稳很多,但如果数据有问题、特征很多、设置不合理,也不是完全不会过拟合。

GBDT

因为它是一棵棵树不断纠错,所以如果树太多、学习率太激进,也会慢慢贴训练集太紧。

SVM

如果参数设置得过于激进,比如 Cgamma 太大,也会把边界搞得非常复杂。

神经网络

参数量大、训练轮次多时,更是典型的高过拟合风险模型。

所以你最好建立一个意识:

过拟合不是"哪个算法有问题",而是"模型能力、数据规模、噪声水平、训练方式"共同作用下的一种结果。


11. 怎么判断自己是不是过拟合了

这部分一定要写,因为它最贴近读者真实操作时的困惑。

最常见的判断方法有几个。

第一,看训练集和验证集的差距

这是最直接的。

比如:

  • 训练集 99%,验证集 84%
  • 或训练损失很低,但验证损失明显更高

这种明显差距,通常就是危险信号。


第二,看模型复杂度变化后结果怎么变

比如你不断增加:

  • 树深
  • 树数量
  • 特征维度
  • 多项式阶数

如果发现训练集持续变好,但验证集在某个点之后开始变差,那通常就是开始过拟合了。


第三,看训练过程中的曲线

如果你记录训练集和验证集的损失/准确率曲线,常见的过拟合迹象是:

  • 训练集持续变好
  • 验证集先变好,后变差

这通常说明模型一开始学到了规律,后面开始记细节了。


12. 欠拟合和过拟合,到底差在哪

这两个词特别容易一起出现,所以最好放在一块讲清楚。

欠拟合(underfitting)

模型太简单,或者学得不够。

结果是:

  • 训练集表现都不好
  • 验证集也不好

这说明模型连训练数据里的主要规律都没抓住。

过拟合(overfitting)

模型太贴训练集。

结果是:

  • 训练集很好
  • 验证集明显差

这说明模型抓住了太多训练集特有的细节,泛化能力下降。

你可以把它们理解成两个极端:

  • 欠拟合:学不进去
  • 过拟合:学得太进去

真正理想的状态,是模型复杂度刚刚好。

它既能抓住主要规律,又不至于把噪声当真。


对训练集来说

模型越复杂,训练误差通常越低。

因为它越来越有能力去贴训练数据。

对验证集来说

一开始模型变复杂,验证误差会下降。

因为模型学到了更多真实规律。

但到某个点之后,验证误差会开始上升。

因为模型开始过拟合。

所以验证误差曲线通常像一个 U 型。

这个最低点,就是你真正想找到的那个"复杂度刚刚好"的位置。

这也是为什么调参不是越大越好、越深越好,而是要找到一个平衡点。


举个直观的例子

我们用一个非常经典的方式来展示过拟合:

用多项式回归去拟合一条本来很简单的曲线。

直观现象会是:

模型复杂度低 → 拟合不够(欠拟合)

模型复杂度刚好 → 拟合合理

模型复杂度过高 → 开始贴着训练点乱拐(过拟合)

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline

# 构造数据
np.random.seed(42)
X = np.linspace(0, 10, 20)
y = np.sin(X) + np.random.normal(0, 0.2, 20)

X = X.reshape(-1, 1)

# 创建三种复杂度模型
models = {
    "degree=1 (欠拟合)": make_pipeline(PolynomialFeatures(1), LinearRegression()),
    "degree=3 (较好)": make_pipeline(PolynomialFeatures(4), LinearRegression()),
    "degree=12 (过拟合)": make_pipeline(PolynomialFeatures(12), LinearRegression())
}

# 画图
X_plot = np.linspace(0, 10, 200).reshape(-1, 1)

plt.figure(figsize=(8,6))

plt.scatter(X, y)

for label, model in models.items():
    model.fit(X, y)
    y_plot = model.predict(X_plot)
    plt.plot(X_plot, y_plot, label=label)

plt.legend()
plt.title("欠拟合 vs 正常拟合 vs 过拟合")
plt.show()

这个例子说明:

模型复杂度越高,不一定越好。

当模型复杂度超过数据本身需要的程度,它就开始用额外的自由度去拟合噪声。

所以过拟合不是模型不努力,而是努力过头了。

14. 为什么过拟合本质上是"泛化能力出了问题"

说到底,机器学习真正关心的,从来都不是:

你在训练集上能不能做到接近满分。

真正关心的是:

你学到的东西,能不能推广到没见过的新数据上。

所以过拟合本质上不是一个"训练技巧问题",它是一个泛化问题

模型一旦过拟合,就说明它把太多精力花在了训练集的专属细节上,而没有学到足够稳定、可迁移的规律。

这也是为什么你后面会发现,很多看起来不一样的方法------比如:

  • 正则化
  • 交叉验证
  • 剪枝
  • Dropout
  • 数据增强
  • 早停

它们最后都在服务同一件事:

降低过拟合,提高泛化能力。


15. 这一篇讲到这里,你最该建立起来的感觉是什么

我觉得过拟合最容易被误解的一点是:

很多人会把它理解成"模型犯了个错"。

但其实不是。

过拟合更像是模型过于忠诚地执行了训练目标。

它拼命想把训练集做到最好,结果好过头了。

所以你可以说:

过拟合不是模型偷懒,而是模型太认真,认真到把训练集里不该学的东西也学进去了。

这个理解一旦建立起来,后面很多东西都会自然很多。

比如你再去看:

  • 为什么树要剪枝
  • 为什么 SVM 的 C 不能太大
  • 为什么 GBDT 不能一味堆树
  • 为什么神经网络会用早停和正则化

你就会发现,它们其实都在回答同一个问题:

怎么让模型别对训练集太上头。

相关推荐
colus_SEU2 小时前
SVM 的终极视角:合页损失函数 (Hinge Loss) 与正则化
算法·机器学习·支持向量机
大连好光景2 小时前
回顾机器学习几个模型(监督+分类任务)
决策树·随机森林·机器学习·逻辑回归·svm
X journey2 小时前
机器学习进阶(14):交叉验证
人工智能·算法·机器学习
B博士3 小时前
科研进展 | JAG: 大光斑高光谱激光雷达遥感辐射传输模型从垂直视角解锁森林叶绿素分布密码
人工智能·jag·高光谱激光雷达·森林分层叶绿素诊断
Yao.Li4 小时前
PVN3D ORT CUDA Custom Ops 实现与联调记录
人工智能·3d·具身智能
诺伦4 小时前
LocalClaw 在智能制造的新机会:6部门AI+电商政策下的工厂AI升级方案
人工智能·制造
小陈工6 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
墨染天姬10 小时前
【AI】端侧AIBOX可以部署哪些智能体
人工智能
AI成长日志10 小时前
【Agentic RL】1.1 什么是Agentic RL:从传统RL到智能体学习
人工智能·学习·算法