大数跨境

彻底讲透!过拟合与欠拟合!

彻底讲透!过拟合与欠拟合! 机器学习和人工智能AI
2026-09-07
9

哈喽,大家好~

大家是否遇到过,为什么训练集准确率这么高,到了测试集却完全不行?

模型越复杂,效果一定越好吗?

我们就用一个简单但非常典型的例子,把机器学习里的欠拟合、正常拟合和过拟合一次讲清楚~

模型到底在学习什么?

我们先假设有一组数据:

  • 输入  :房屋面积、用户年龄、图片像素等特征
  • 输出  :房价、是否购买、图片类别等目标

模型要做的事情,就是找到一个函数:

让预测值   尽量接近真实值 

比如真实数据大致符合一条曲线:

其中   是噪声,表示现实数据中不可避免的误差。

模型训练时,会不断调整参数,让损失函数变小。最常见的均方误差是:

一句话理解:预测值和真实值差得越远,损失就越大。

什么是欠拟合和过拟合?

我们用多项式模型来举例。

一次多项式只能画出一条直线:

如果真实数据是一条弯曲的曲线,直线就太简单了,连训练数据都解释不好,这就是欠拟合

欠拟合通常表现为:

  • 训练误差较高
  • 测试误差也较高
  • 模型过于简单,偏差较大

如果我们把模型改成 20 次多项式,它几乎可以穿过所有训练样本,训练误差非常低。但它也会把噪声当成规律,遇到新数据就表现很差,这就是过拟合

过拟合通常表现为:

  • 训练误差非常低
  • 测试误差较高
  • 模型过度记忆训练数据,方差较大

可以这样理解,欠拟合是“没学会”,过拟合是“背下来了”,正常拟合才是“真正学会了”。

从误差分解角度看:

模型太简单,偏差高;模型太复杂,方差高。我们真正想找的是两者之间的平衡。

Python实现

下面构造一组带噪声的正弦数据,分别训练一次、五次和十八次多项式模型。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error

np.random.seed(42)

# 构造虚拟数据:真实规律是 sin(x),同时加入噪声
X = np.linspace(-3380).reshape(-11)
y = np.sin(X).ravel() + np.random.normal(00.1880)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)

def build_model(degree):
    return make_pipeline(
        PolynomialFeatures(degree=degree),
        StandardScaler(),
        Ridge(alpha=0.01)
    )

degrees = [1518]
x_line = np.linspace(-33500).reshape(-11)

fig, axes = plt.subplots(13, figsize=(164))

for ax, degree in zip(axes, degrees):
    model = build_model(degree)
    model.fit(X_train, y_train)

    train_pred = model.predict(X_train)
    test_pred = model.predict(X_test)
    line_pred = model.predict(x_line)

    train_mse = mean_squared_error(y_train, train_pred)
    test_mse = mean_squared_error(y_test, test_pred)

    ax.scatter(X_train, y_train, color="#00BFFF",
               edgecolor="white", s=45, label="训练数据")
    ax.scatter(X_test, y_test, color="#FF4B5C",
               edgecolor="white", s=45, label="测试数据")
    ax.plot(x_line, line_pred, color="#7B2CBF",
            linewidth=3, label="模型曲线")

    ax.set_title(f"{degree}次多项式\nTrain={train_mse:.3f}, Test={test_mse:.3f}")
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.grid(alpha=0.2)
    ax.legend()

plt.suptitle("欠拟合、正常拟合与过拟合", fontsize=16)
plt.tight_layout()
plt.show()

# 比较不同复杂度下的训练误差和测试误差
all_degrees = range(121)
train_errors, test_errors = [], []

for degree in all_degrees:
    model = build_model(degree)
    model.fit(X_train, y_train)
    train_errors.append(
        mean_squared_error(y_train, model.predict(X_train))
    )
    test_errors.append(
        mean_squared_error(y_test, model.predict(X_test))
    )

plt.figure(figsize=(95))
plt.plot(all_degrees, train_errors, "o-", color="#00C853",
         linewidth=2.5, label="训练误差")
plt.plot(all_degrees, test_errors, "o-", color="#FF6D00",
         linewidth=2.5, label="测试误差")
plt.axvline(5, color="#2962FF", linestyle="--",
            label="较合适的复杂度")
plt.xlabel("多项式阶数")
plt.ylabel("MSE")
plt.title("模型复杂度与误差变化")
plt.xticks(list(all_degrees))
plt.grid(alpha=0.25)
plt.legend()
plt.show()

第一张图里,1 次多项式基本是一条直线,无法跟随真实曲线,这是欠拟合。5 次多项式能够抓住整体趋势,同时没有过度追踪噪声,通常是比较合适的模型。18 次多项式会产生明显的波动,训练点附近表现很好,但泛化能力下降,这就是过拟合。

第二张图展示了一个很重要的规律:随着模型复杂度增加,训练误差通常持续下降;测试误差则会先下降,达到最低点后再次上升。测试误差最低的位置,就是模型复杂度比较合适的区域。

如何解决过拟合?

实际项目中,我们可以从几个方向入手。

第一,减少模型复杂度,比如降低树的深度、减少神经网络层数、降低多项式阶数。

第二,增加训练数据。数据越丰富,模型越不容易只记住少量样本。

第三,使用正则化。以 Ridge 回归为例,它会在原损失后增加参数惩罚:

其中   是正则化强度。  越大,对复杂参数的限制越强,模型曲线也会更平滑。

不过正则化不是越大越好。太强会让模型重新变得过于简单,出现欠拟合,所以需要通过验证集或交叉验证选择参数。

小结

欠拟合说明模型太简单,过拟合说明模型太复杂。我们不能只看训练集效果,而要重点观察模型在新数据上的表现。

实际训练时,可以结合验证集、交叉验证、正则化和学习曲线,找到模型复杂度与泛化能力之间的平衡。接下来大家还可以尝试把 Ridge 换成 Lasso,或者使用决策树、随机森林,对比不同模型的过拟合表现。

【声明】内容源于网络
0
0
机器学习和人工智能AI
让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
内容 382
粉丝 0
机器学习和人工智能AI 让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
总阅读5.3k
粉丝0
内容382