首页/在线代码库/demo24_overfit.py

demo24_overfit.py

在多项式回归(真实为 3 次函数, 30 个样本)上: - 欠拟合: 1 次模型; - 恰好: 3 次模型; - 过拟合: 15 次模型(训练误差小, 泛化差); - 正则化: 15 次模型 + L2 weight decay 抑制过拟合。 画出训练/验证误差随训练进程与最终拟合曲线。

Python 69 行 系列:工程实践 🔒 源码仅供在线学习,不提供文件下载
完整源码 · 在线查看demo24_overfit.py
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
"""演示24: 过拟合 vs 欠拟合 + 正则化对比。

在多项式回归(真实为 3 次函数, 30 个样本)上:
- 欠拟合: 1 次模型;
- 恰好: 3 次模型;
- 过拟合: 15 次模型(训练误差小, 泛化差);
- 正则化: 15 次模型 + L2 weight decay 抑制过拟合。
画出训练/验证误差随训练进程与最终拟合曲线。
"""
import os
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import Ridge, LinearRegression
from figure_style import figures_dir, configure

configure()

OUT = figures_dir()
os.makedirs(OUT, exist_ok=True)

rng = np.random.default_rng(0)
X = rng.uniform(-3, 3, 30)
y = np.sin(X) + rng.normal(0, 0.15, 30)
X = X.reshape(-1, 1)
Xt = np.linspace(-3, 3, 300).reshape(-1, 1)

models = {
    "欠拟合 (degree=1)": make_pipeline(PolynomialFeatures(1), LinearRegression()),
    "恰好 (degree=3)": make_pipeline(PolynomialFeatures(3), LinearRegression()),
    "过拟合 (degree=15)": make_pipeline(PolynomialFeatures(15), LinearRegression()),
    "过拟合+L2 (degree=15, α=0.01)": make_pipeline(PolynomialFeatures(15), Ridge(alpha=0.01)),
}

fig, axes = plt.subplots(1, 2, figsize=(12, 4.8))
ax1 = axes[0]
ax1.scatter(X, y, c="k", s=12, label="训练样本")
for name, model in models.items():
    model.fit(X, y)
    train_err = ((model.predict(X) - y) ** 2).mean()
    ax1.plot(Xt, model.predict(Xt), lw=1.8,
             label=f"{name} (训练MSE={train_err:.3f})")
ax1.plot(Xt, np.sin(Xt), "k--", lw=1.2, label="真实 sin(x)")
ax1.set_title("多项式回归: 模型复杂度 vs 泛化")
ax1.legend(fontsize=8); ax1.grid(alpha=0.3)

# 训练/验证误差曲线(用 15 次模型演示过拟合轨迹)
from sklearn.model_selection import train_test_split
Xtr, Xva, ytr, yva = train_test_split(X, y, test_size=0.4, random_state=0)
model = make_pipeline(PolynomialFeatures(15), LinearRegression())
tr_err, va_err = [], []
for i in range(1, 30):
    model.fit(Xtr[:i], ytr[:i])
    tr_err.append(((model.predict(Xtr[:i]) - ytr[:i]) ** 2).mean())
    va_err.append(((model.predict(Xva) - yva) ** 2).mean())
ax2 = axes[1]
ax2.plot(tr_err, "tab:blue", lw=2, label="训练误差")
ax2.plot(va_err, "tab:red", lw=2, label="验证误差")
ax2.set_xlabel("训练样本数(递增)"); ax2.set_ylabel("MSE")
ax2.set_title("过拟合诊断: 训练误差降、验证误差升 = 过拟合")
ax2.legend(); ax2.grid(alpha=0.3)

fig.suptitle("过拟合 vs 欠拟合: 诊断信号与正则化解法", fontsize=14)
fig.tight_layout(rect=(0, 0, 1, 0.93))
out = os.path.join(OUT, "fig_train_overfit.png")
fig.savefig(out, dpi=150)
print(f"-> {out}")

输出图

fig_train_overfit.png
输出图 · fig_train_overfit.png