10-1 线性模型的扩展(一):多项式回归与过拟合

🎯 教学目标与重难点…

【三维目标】

  • 📚 知识目标
    1. 理解为什么线性模型无法拟合非线性数据(欠拟合)。
    2. 掌握多项式回归的核心思想:将非线性问题转化为线性问题
    3. 理解过拟合的含义和危害——模型“背答案”而不是“真学习”。
    4. 掌握 PolynomialFeatures 的使用方法。
  • ⚙️ 能力目标
    1. 能使用 PolynomialFeatures + LinearRegression 实现多项式回归。
    2. 能通过调整多项式阶数(degree)观察模型从欠拟合到过拟合的变化过程。
  • 💡 素养目标
    1. 建立“好模型不是训练集得分越高越好”的工程思维。
    2. 理解“模型复杂度”与“泛化能力”之间的权衡关系。

【重点与难点】

  • 🟢 教学重点
    1. 多项式回归的代码实现(PolynomialFeatures + 管道)。
    2. 过拟合的可视化识别:训练集完美、测试集崩盘。
  • 🟡 教学难点
    1. 理解“非线性问题线性化”的思想——把 \( x^2 \) 当作一个新特征。
    2. 理解过拟合的本质:模型记住了噪声,而不是规律。

📌 一、 课程导入(5 分钟)

复习回顾:上一章我们学了线性回归,它用一条直线 \( y = wx + b \) 来拟合数据。

情景模拟:直线不够用怎么办?

假设我们要预测房价与房屋面积的关系。真实情况往往是:面积很小的时候价格低,面积适中的时候价格涨得最快,面积特别大的时候价格增速又放缓了——这是一个弯弯曲曲的曲线,不是一条直线。

问题:线性回归只会画直线,强行画一条直线去拟合曲线,结果就是欠拟合——训练集和测试集的误差都很大,模型太“笨”了。

思考:我们能不能让线性回归“学会”画曲线?

引出方案:可以!把 \( x^2 \)、\( x^3 \) 当作新的特征加进去,线性回归就能拟合曲线了。这就是 多项式回归

  • 👨‍🏫 教师活动:在 PPT 上展示一张散点图(呈现明显的曲线趋势),再画一条直线穿过它,问学生:“这条直线拟合得好不好?”(学生答:不好)。
  • 🧑‍🎓 学生活动:学生观察图形,直观感受“直线拟合曲线”的别扭感,产生“有没有办法让线变弯”的好奇。

用“直线 vs 曲线”的直观对比,让学生立刻理解线性回归的局限性。再抛出“把 x² 当新特征”的思路,为多项式回归做铺垫。


📖 二、 解决问题过程(一):多项式回归——让直线“学会拐弯”

1. 核心思想(一句话)

把“非线性”变成“线性”——把 \( x^2, x^3, \dots \) 当作新的特征,再用线性回归去拟合。

2. 公式对比

模型 公式 本质
线性回归 \( y = w_0 + w_1 x \) 一条直线
二次多项式回归 \( y = w_0 + w_1 x + w_2 x^2 \) 一条抛物线
三次多项式回归 \( y = w_0 + w_1 x + w_2 x^2 + w_3 x^3 \) 更复杂的曲线

关键理解:从模型的角度看,它根本不关心 \( x^2 \) 是“平方”出来的——它只知道现在有 3 个特征:\( x \)、\( x^2 \)、\( x^3 \)。它仍然在用线性回归的公式 \( y = w_0 + w_1 \cdot \text{特征1} + w_2 \cdot \text{特征2} + \dots \) 去算。这就是 “非线性问题线性化” 的精髓。

3. 代码实现(PolynomialFeatures)

 1import numpy as np
 2import matplotlib.pyplot as plt
 3from sklearn.preprocessing import PolynomialFeatures
 4from sklearn.linear_model import LinearRegression
 5
 6# 造一些非线性数据(二次函数 + 噪声)
 7np.random.seed(42)
 8X = np.random.uniform(-3, 3, size=100).reshape(-1, 1)
 9y = 0.5 * X**2 + X + 2 + np.random.normal(0, 0.5, size=(100, 1))
10
11# 生成多项式特征(把 x 变成 x 和 x²)
12poly = PolynomialFeatures(degree=2, include_bias=False)
13X_poly = poly.fit_transform(X)
14
15# 用线性回归拟合多项式特征
16model = LinearRegression()
17model.fit(X_poly, y)
18
19# 预测并绘图
20X_plot = np.linspace(-3, 3, 200).reshape(-1, 1)
21X_plot_poly = poly.transform(X_plot)
22y_plot = model.predict(X_plot_poly)
23
24plt.scatter(X, y, alpha=0.5, label='原始数据')
25plt.plot(X_plot, y_plot, 'r-', label='二次多项式回归')
26plt.legend()
27plt.show()
  • 👨‍🏫 教师活动
    1. 在黑板写下:[x]PolynomialFeatures(degree=2)[x, x²],强调“多了个新特征”。
    2. 运行代码,展示拟合曲线如何“变弯”。
  • 🧑‍🎓 学生活动
    1. 观察 X_poly.shape,看看原来 1 列变成了几列。
    2. 修改 degree=3,观察曲线变得更复杂。

用最简代码展示多项式回归的核心——特征升维。让学生看到“特征变多了,直线就变弯了”的直观效果。


💻 三、 解决问题过程(二):过拟合——当模型“太聪明”反而坏事

1. 情景模拟:学霸的“背答案”困境

有个学生把题库里所有题的答案都背下来了——训练集考试 100 分。但高考出了新题(测试集),一道都不会——测试集 0 分

这就是 过拟合:模型在训练集上表现完美,但在未见过的数据上表现极差。

2. 多项式回归中的过拟合(核心实验)

我们用同样的数据,分别用 degree=1(直线)、degree=4(适度)、degree=15(过高)来拟合,观察发生了什么。

 1from sklearn.model_selection import train_test_split
 2from sklearn.preprocessing import PolynomialFeatures
 3from sklearn.linear_model import LinearRegression
 4from sklearn.metrics import mean_squared_error
 5import numpy as np
 6import matplotlib.pyplot as plt
 7
 8# 生成数据(只有 30 个点,容易过拟合)
 9np.random.seed(42)
10X = np.random.uniform(-3, 3, size=30).reshape(-1, 1)
11y = 0.5 * X**2 + X + 2 + np.random.normal(0, 0.5, size=(30, 1))
12
13# 划分训练集和测试集
14X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
15
16# 分别用不同 degree 拟合
17degrees = [1, 4, 15]
18plt.figure(figsize=(15, 4))
19
20for i, d in enumerate(degrees):
21    poly = PolynomialFeatures(degree=d, include_bias=False)
22    X_train_poly = poly.fit_transform(X_train)
23    X_test_poly = poly.transform(X_test)
24    
25    model = LinearRegression()
26    model.fit(X_train_poly, y_train)
27    
28    # 计算训练集和测试集误差
29    train_pred = model.predict(X_train_poly)
30    test_pred = model.predict(X_test_poly)
31    train_mse = mean_squared_error(y_train, train_pred)
32    test_mse = mean_squared_error(y_test, test_pred)
33    
34    # 绘图
35    plt.subplot(1, 3, i+1)
36    plt.scatter(X_train, y_train, alpha=0.7, label='训练集')
37    plt.scatter(X_test, y_test, alpha=0.7, label='测试集')
38    X_plot = np.linspace(-3, 3, 200).reshape(-1, 1)
39    X_plot_poly = poly.transform(X_plot)
40    plt.plot(X_plot, model.predict(X_plot_poly), 'r-', label=f'degree={d}')
41    plt.title(f'degree={d}\n训练MSE={train_mse:.3f}, 测试MSE={test_mse:.3f}')
42    plt.legend()
43    plt.xlim(-3, 3)
44
45plt.tight_layout()
46plt.show()

3. 三个 degree 的结果解读(课堂重点)

degree 训练集误差 测试集误差 现象
1(直线) 欠拟合——太简单,两边都学不好
4(适中) 刚好——抓住了真实规律
15(过高) 几乎为 0 巨大 过拟合——曲线疯狂扭曲,记住了每一个训练点的噪声

4. 核心结论(学生必记)

模型不是越复杂越好。太简单的模型学不到规律(欠拟合),太复杂的模型记住了噪声(过拟合)。我们的目标是找到 “刚刚好” 的复杂度。

  • 👨‍🏫 教师活动
    1. 运行三段代码,让学生观察三张图的变化——从一条直直的线,到平滑曲线,到最后疯狂扭曲的“蛇形线”。
    2. 指着 degree=15 的图问:“这条扭曲的线,你们觉得它能预测新数据吗?”(学生:不能)。
  • 🧑‍🎓 学生活动
    1. 观察三个子图的 MSE 数值变化,记录“训练集误差越来越小,测试集误差先降后升”的规律。
    2. 讨论:“degree=15 的模型在训练集上几乎零误差,这算‘好模型’吗?”(答案:不算,因为它没有学到真正的规律)。

这是整节课的核心实验。通过三张图的对比,让学生亲眼看到“过拟合”长什么样——那条疯狂扭曲的曲线会深深印在他们脑子里,为下一节课的“正则化”打下强烈的问题意识。


✍️ 四、 解决问题过程(三):课堂练习——自己动手“造”过拟合

📝 任务一:观察过拟合的演变过程…

背景与题目: 使用下面的代码框架,尝试不同的 degree 值(从 1 到 20),观察训练集和测试集误差的变化趋势。

 1import numpy as np
 2import matplotlib.pyplot as plt
 3from sklearn.preprocessing import PolynomialFeatures
 4from sklearn.linear_model import LinearRegression
 5from sklearn.model_selection import train_test_split
 6from sklearn.metrics import mean_squared_error
 7
 8# 生成数据
 9np.random.seed(42)
10X = np.random.uniform(-3, 3, size=30).reshape(-1, 1)
11y = 0.5 * X**2 + X + 2 + np.random.normal(0, 0.5, size=(30, 1))
12X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
13
14# 请完成:循环 degree 从 1 到 20,记录每个 degree 的训练集 MSE 和测试集 MSE
15# 画两条曲线:x轴是degree,y轴是MSE
🔍 查看参考代码与解析…
 1train_errors = []
 2test_errors = []
 3degrees = range(1, 21)
 4
 5for d in degrees:
 6    poly = PolynomialFeatures(degree=d, include_bias=False)
 7    X_train_poly = poly.fit_transform(X_train)
 8    X_test_poly = poly.transform(X_test)
 9    
10    model = LinearRegression()
11    model.fit(X_train_poly, y_train)
12    
13    train_errors.append(mean_squared_error(y_train, model.predict(X_train_poly)))
14    test_errors.append(mean_squared_error(y_test, model.predict(X_test_poly)))
15
16# 绘图
17plt.figure(figsize=(10, 6))
18plt.plot(degrees, train_errors, 'b-', label='训练集误差')
19plt.plot(degrees, test_errors, 'r-', label='测试集误差')
20plt.xlabel('多项式阶数 (degree)')
21plt.ylabel('均方误差 (MSE)')
22plt.legend()
23plt.grid(True)
24plt.show()

结果解读

  • 蓝色曲线(训练集误差)一直下降,最终趋近于 0。
  • 红色曲线(测试集误差)先降后升——在 degree≈4 时最低,之后急剧上升。
  • 结论:degree 超过某个值后,模型开始“背答案”,测试集表现越来越差。

📝 五、 课堂小结(5 分钟)

flowchart LR
    root["📈 多项式回归与过拟合"]

    subgraph C1 ["📐 多项式回归"]
        direction TB
        A1["把 x², x³ 当作新特征"]
        A2["代码: PolynomialFeatures"]
        A3["让线性模型拟合曲线"]
    end

    subgraph C2 ["⚠️ 过拟合"]
        direction TB
        B1["训练集完美 ✅"]
        B2["测试集崩盘 ❌"]
        B3["模型记住了噪声"]
    end

    subgraph C3 ["🎯 核心结论"]
        direction TB
        C1_node["degree 太低 → 欠拟合"]
        C2_node["degree 太高 → 过拟合"]
        C3_node["需要找到『刚刚好』"]
    end

    root --> C1
    root --> C2
    root --> C3

    style root fill:#4b6cb7,stroke:#253b6e,color:#fff
    style C1 fill:#e3f2fd,stroke:#2196f3
    style C2 fill:#ffebee,stroke:#ef5350
    style C3 fill:#e8f5e9,stroke:#4caf50

✏️ 随堂检测与互动练习

点击展开:随堂测试题(带解析)

一、 单选题

  1. 多项式回归的核心思想是什么?
  • A. 用曲线直接拟合数据,不经过线性回归
  • B. 将原始特征 \( x \) 扩展为 \( x, x^2, x^3, \dots \),再用线性回归拟合
  • C. 删除所有非线性特征,只保留线性特征
  • D. 用神经网络代替线性回归
【答案】

【解析】B。多项式回归的本质是“特征升维 + 线性回归”,不是换算法,而是换数据。

  1. 以下哪个现象最能说明模型发生了过拟合
  • A. 训练集 MSE=0.5,测试集 MSE=0.6
  • B. 训练集 MSE=0.01,测试集 MSE=10.5
  • C. 训练集 MSE=10.2,测试集 MSE=10.8
  • D. 训练集 MSE=8.5,测试集 MSE=2.3
【答案】

【解析】B。过拟合的典型特征就是训练集误差极小、测试集误差极大——模型“背答案”而不是“学规律”。

二、 代码填空题

题目:使用 PolynomialFeatures 将特征 X 扩展为包含 \( x \) 和 \( x^2 \) 的 2 列特征。

1from sklearn.preprocessing import PolynomialFeatures
2
3X = np.array([[1], [2], [3]])
4
5poly = _________________________________
6X_poly = _________________________________
7print(X_poly)  # 期望输出: [[1, 1], [2, 4], [3, 9]]
【答案】
  1. PolynomialFeatures(degree=2, include_bias=False)
  2. poly.fit_transform(X)

📮 六、 课后作业与拓展

📮 课后作业…
  1. 基础代码题:用上节课的糖尿病数据集或自己生成的数据,尝试 degree=2, 5, 10, 20,记录每个模型的训练集 R² 和测试集 R²,观察变化趋势。
  2. 思考题:过拟合的模型在训练集上得分很高,为什么我们不能直接用它做预测?(答案:因为它学到的是训练集的“噪声”而不是“规律”,对新数据无效)。
  3. 预习任务:既然过拟合这么可怕,有没有办法“惩罚”模型不要那么扭曲?预习下一节课:岭回归与 Lasso 回归。
  4. 职高衔接拓展:你在工作中训练了一个模型,训练集准确率 99%,但上线后实际准确率只有 60%。老板问你怎么回事,你怎么解释?(答案:模型过拟合了,需要加正则化或简化模型)。

📋 七、 板书设计

🛠️ 板书设计…
 1第十章 线性模型的扩展
 2第一课时:多项式回归与过拟合
 3
 4一、多项式回归
 5   问题:线性回归只会画直线 → 拟合不了曲线
 6   解法:把 x 变成 [x, x², x³, ...]
 7   代码:PolynomialFeatures(degree=n) + LinearRegression
 8   本质:非线性问题 → 线性化
 9
10二、过拟合 (Overfitting)
11   症状:训练集完美,测试集崩盘
12   原因:模型太复杂,记住了噪声
13   对比:
14   - degree=1  → 欠拟合(太笨)
15   - degree=4  → 刚好(聪明)
16   - degree=15 → 过拟合(太聪明,聪明反被聪明误)
17
18三、核心结论
19   “模型不是越复杂越好”

本课用到的单词

单词 发音 专业英语解释(中文)
Polynomial Regression /ˌpɑːliˈnoʊmiəl rɪˈɡreʃən/ 多项式回归。通过增加特征的高次项来拟合非线性关系的回归方法。
Overfitting /ˌoʊvərˈfɪtɪŋ/ 过拟合。模型在训练集上表现极好,但在新数据上表现很差的现象。
Underfitting /ˌʌndərˈfɪtɪŋ/ 欠拟合。模型过于简单,无法捕捉数据中的规律。
Degree /dɪˈɡriː/ 阶数。多项式回归中最高次项的次数。
Generalization /ˌdʒenərəlaɪˈzeɪʃən/ 泛化。模型在新数据上的表现能力。
Noise /nɔɪz/ 噪声。数据中的随机波动或误差。