Skip to content

元数据卡

  • 前置知识:Vol 12 数据处理与特征工程、Vol 10 概率与统计
  • 预计时间:45 分钟
  • 核心难度:入门到进阶
  • 阅读模式:高度专注
  • 完成标志:能拆分数据集、理解过拟合与欠拟合、完成一个端到端 ML 流程

你的进度

你从模型工坊的前三张工作台抬起头来,发现它们有一个共同点:每一套系统都需要你亲手写规则。搜索的代价函数是你写的,推理的逻辑规则是你写的,强化学习的奖励函数也是你写的。

你拿起阿花的信重新读了一遍:“它要是能自己学就好了。”

你换了个方向——不给规则,给例子。模型工坊的新工作台上摆着一摞标注好的数据。

你的任务

机器学习的本质是从有限样本中学习一个能推广到未见数据的函数。本章建立基础框架:数据拆分、特征工程、训练验证测试三阶段、过拟合与欠拟合的诊断和应对。

本章分层

  • 必读:数据集拆分、偏差-方差困境、过拟合与正则化
  • 选读:学习曲线、AIC/BIC
  • 进阶:PAC 学习理论直观理解

破局 · 溯源

假设你在教一个小孩认识猫。你不会写一本《猫的识别算法手册》——你只是不断指给他看:"这个是猫,这个不是。"慢慢地他自己学会了。

这是机器学习的直觉。但第一个问题立刻出现:你用来教他的"猫"照片和考试时给他的新照片,是同一批吗?如果是同一批,他只是背下了答案——这叫过拟合。如果照片太少、太模糊,他根本学不会——这叫欠拟合。

数据集拆分

所有 ML 项目的第一步:把数据拆成三份。

  • 训练集:模型在上面学习
  • 验证集:调超参数、选模型
  • 测试集:最终评估(绝对不准碰,直到最后一刻)
python
from sklearn.model_selection import train_test_split
import numpy as np

# 生成示例数据
X = np.random.randn(1000, 10)
y = (X[:, 0] + X[:, 1] > 0).astype(int)

# 先拆出测试集
X_temp, X_test, y_temp, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
# 再拆训练和验证
X_train, X_val, y_train, y_val = train_test_split(
    X_temp, y_temp, test_size=0.25, random_state=42
)

print(f"训练: {len(X_train)}, 验证: {len(X_val)}, 测试: {len(X_test)}")

为什么要两个步骤?如果你用同一个 train_test_split 反复看着验证集调参数,验证集的信息就泄露到了模型选择上——验证集不再是"未见数据",你实际上在间接过拟合验证集。

特征工程

原始数据很少直接喂给模型。特征工程是把原始数据转化为模型能有效利用的形式:

  • 标准化(StandardScaler):减去均值除以标准差,让不同尺度的特征可比
  • 归一化(MinMaxScaler):缩放到 [0, 1]
  • 类别编码:One-Hot 或 Label Encoding
  • 特征交叉:组合特征创造新特征
python
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_val_scaled = scaler.transform(X_val)   # 注意:用训练集的统计量
X_test_scaled = scaler.transform(X_test) # 绝对不能调用 fit

过拟合与欠拟合

这是机器学习里最重要的诊断框架。

  • 欠拟合:训练误差高 → 模型不够复杂 / 特征不够
  • 过拟合:训练误差低但验证误差高 → 模型记住了噪声而非模式
python
# 在多项式回归上演示过拟合
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

np.random.seed(42)
X_small = np.sort(np.random.rand(20, 1), axis=0)
y_small = np.sin(2 * np.pi * X_small).ravel() + np.random.normal(0, 0.1, 20)

for degree in [1, 3, 15]:
    poly = PolynomialFeatures(degree)
    X_poly = poly.fit_transform(X_small)
    model = LinearRegression()
    model.fit(X_poly, y_small)
    train_pred = model.predict(X_poly)
    train_mse = mean_squared_error(y_small, train_pred)
    print(f"degree={degree:2d}, train MSE: {train_mse:.4f}")

当 degree=1 时,欠拟合(线形太简单);degree=3 刚好;degree=15 完美通过每个训练点(过拟合),在新数据上表现极差。

对抗过拟合的方法:

  1. 正则化:在损失函数中加惩罚项
  2. 交叉验证:用多组训练/验证拆分评估
  3. 早停(Early Stopping):在验证误差开始上升时停止训练
  4. 数据增强:扩大训练数据规模
  5. Dropout/噪声:随机丢弃单元或加噪声
python
# L2 正则化的线性回归(岭回归)
from sklearn.linear_model import Ridge

ridge = Ridge(alpha=1.0)
ridge.fit(X_train, y_train)
print(f"Ridge train: {ridge.score(X_train, y_train):.3f}, "
      f"val: {ridge.score(X_val, y_val):.3f}")

偏差-方差困境

偏差(bias)衡量模型对真实模式的逼近能力。方差(variance)衡量模型对训练数据变化的敏感度。

  • 高偏差 → 欠拟合(假设太强)
  • 高方差 → 过拟合(假设太弱,对数据敏感)

两者此消彼长:降低偏差升高方差,反之亦然。好模型就是在两者之间找到临界点。

常见陷阱

  • 先看测试集再看测试集再看测试集——当你"不小心"看了测试集上的表现来调参数时,测试集已经不再是评估标准了。
  • 标准化必须用训练集的 fit,不能在整体数据上 fit 再拆分(数据泄露)。
  • 类别不均衡:如果 99% 是负类,一个永远预测负类的模型准确率 99%——但没用。需要改用 F1、PR 曲线或重采样。
  • 时间序列数据不能随机拆分——必须按时间顺序前训练后测试。

通关挑战

  • 热身(10 分钟):用 sklearn.datasets.make_classification 生成一个二分类数据集,手动拆分成训练/验证/测试,运行一个逻辑回归,打印三个集合上的准确率。
  • 挑战(30 分钟):在 10 个不同的多项式度数(1~20)上训练,绘制训练误差和验证误差随度数变化的曲线——观察过拟合从哪个度数开始。
  • 观察:在训练集上加入随机噪声标签(错误标注 10%),看看验证集上的准确率变化趋势。

旅人笔记

机器学习三分模型,七分数据。理解过拟合是理解一切 ML 的核心——从简单的线性回归到万亿参数大语言模型,都在解决同一个问题:从有限样本中学习能推广的知识。

-> 下一站预告

有了数据框架,你开始学习第一个完整的模型家族:线性模型。

Built with VitePress | Software Systems Atlas