chapter 01 / ml-foundations · 预计学习时间 90-120 分钟
传统编程是这样的:人类理解问题,把解法写成规则,计算机执行规则。
规则 + 数据 ──▶ 计算机 ──▶ 答案
但有一类问题,人类自己也说不清规则。你能认出一张照片里有猫,但你写不出「猫的 if-else 判定规则」——耳朵尖?折耳猫呢?有毛?无毛猫呢?规则爆炸了。机器学习把流程反过来:
数据 + 答案 ──▶ 计算机 ──▶ 规则(模型)
我们不告诉计算机「怎么做」,而是给它大量「输入 → 正确输出」的例子,让它自己找出从输入映射到输出的函数。这就是机器学习的本质:从数据中自动寻找函数。
更严格的定义来自 Tom Mitchell(1997):
数据集是成对的 $(x, y)$:输入 $x$ 配有人类标注的正确答案 $y$(标签)。模型学习映射 $f(x) \approx y$。按 $y$ 的类型分两类:
只有 $x$,没有标签。模型自己发现数据内部的结构:
没有现成的标签,但有奖励信号。智能体(agent)在环境中行动,环境反馈奖励,目标是学会最大化长期累积奖励的策略。AlphaGo、机器人控制、以及——划重点——训练 ChatGPT 的 RLHF 和训练推理模型(OpenAI o 系列、DeepSeek-R1)的核心技术,第 8 章详讲。
| 范式 | 数据形态 | 学到什么 | 典型例子 |
|---|---|---|---|
| 监督学习 | $(x, y)$ 成对 | 映射 $f(x)\to y$ | 图像分类、房价预测 |
| 无监督学习 | 只有 $x$ | 数据的结构/分布 | 聚类、PCA、生成模型 |
| 强化学习 | 状态、动作、奖励 | 最优策略 $\pi(a|s)$ | AlphaGo、RLHF |
先统一术语,后面 11 章都用它们:
开始推导前,先看一个把「学习的直觉」讲得最好的视频。它名义上讲神经网络,但「网络在学什么、参数是什么」的画面会给你接下来所有数学一个直觉锚点:
现在用最简单的模型把「三件套」完整走一遍。别因为它简单就跳过——梯度下降、损失面、学习率这些概念在这里看得最清楚,到了第 6 章的 Transformer,数学骨架还是这一套。
假设输出和输入是线性关系。单特征时:
$$\hat{y} = f_\theta(x) = wx + b$$$w$(权重/斜率)和 $b$(偏置/截距)就是参数,$\theta = (w, b)$。帽子 $\hat{y}$ 表示「预测值」,区别于真实值 $y$。多特征时写成向量内积 $\hat{y} = \mathbf{w}^\top \mathbf{x} + b$。
对每个样本,误差是 $\hat{y}^{(i)} - y^{(i)}$。把所有样本的误差平方后取平均:
$$L(w, b) = \frac{1}{n} \sum_{i=1}^{n} \left( wx^{(i)} + b - y^{(i)} \right)^2$$$L(w,b)$ 是参数的二次函数——一个碗形曲面,存在唯一最低点。微积分告诉我们:最低点处偏导数为零。直接求:
$$\frac{\partial L}{\partial w} = \frac{2}{n}\sum_{i=1}^n \left(wx^{(i)} + b - y^{(i)}\right)x^{(i)} = 0$$ $$\frac{\partial L}{\partial b} = \frac{2}{n}\sum_{i=1}^n \left(wx^{(i)} + b - y^{(i)}\right) = 0$$两个方程两个未知数,解出(推导:由第二式得 $b = \bar{y} - w\bar{x}$,代入第一式整理):
$$w^* = \frac{\sum_i (x^{(i)} - \bar{x})(y^{(i)} - \bar{y})}{\sum_i (x^{(i)} - \bar{x})^2} = \frac{\text{Cov}(x, y)}{\text{Var}(x)}, \qquad b^* = \bar{y} - w^*\bar{x}$$多特征情形用矩阵写法更优雅。把所有样本堆成矩阵 $X \in \mathbb{R}^{n \times d}$(每行一个样本,并入一列全 1 来吸收 $b$),标签堆成 $\mathbf{y} \in \mathbb{R}^n$,则 $L(\mathbf{w}) = \frac{1}{n}\|X\mathbf{w} - \mathbf{y}\|^2$。对 $\mathbf{w}$ 求梯度并令其为零:
$$\nabla_\mathbf{w} L = \frac{2}{n} X^\top (X\mathbf{w} - \mathbf{y}) = 0 \;\;\Longrightarrow\;\; \boxed{\mathbf{w}^* = (X^\top X)^{-1} X^\top \mathbf{y}}$$这就是著名的正规方程(Normal Equation)。
换一种思路:不直接解方程,而是从随机位置出发,一小步一小步走下坡,走到坑底。
梯度 $\nabla_\theta L$ 是一个向量,指向损失上升最快的方向。所以反着走:
$$\theta_{t+1} = \theta_t - \eta \, \nabla_\theta L(\theta_t)$$$\eta$ 是学习率(learning rate)——每步迈多大。对我们的线性回归,把上一节算好的偏导拿来用,每一步更新就是:
$$w \leftarrow w - \eta \cdot \frac{2}{n}\sum_i (\hat{y}^{(i)} - y^{(i)})\, x^{(i)}, \qquad b \leftarrow b - \eta \cdot \frac{2}{n}\sum_i (\hat{y}^{(i)} - y^{(i)})$$| 变体 | 每步用多少数据 | 特点 |
|---|---|---|
| 批量梯度下降 BGD | 全部 $n$ 个 | 梯度准、方向稳,但每步贵;数据大时不可行 |
| 随机梯度下降 SGD | 1 个 | 每步极快但方向抖动大;噪声反而帮助跳出局部坑 |
| 小批量 Mini-batch | $B$ 个(如 32~512) | 实践标准。GPU 并行友好,噪声适中。LLM 训练的 batch 可达数百万 token |
不要只看文字——去下面的实验室把学习率滑块拉到最右边,亲眼看一次发散。这个画面比任何公式都难忘。
先看两个视频巩固。第一个用「下山」把梯度下降讲透;第二个是 StatQuest,把每一步算术掰开揉碎,适合想跟着手算一遍的你:
点击画布添加数据点(或选预设数据集)→ 点「开始训练」看蓝色直线被梯度下降一步步拉到位。红色竖线 = 每个点的残差(误差),黄色曲线 = 损失随迭代的下降轨迹。必做实验:①把学习率拉到最大看发散;②加两个离群点看 MSE 被绑架;③切到 SGD 看抖动。
到目前为止我们只让损失在训练数据上变小。但训练的真正目的,是模型在没见过的数据上表现好——这叫泛化(generalization)。这是 ML 与单纯的优化的本质区别。
| 集合 | 用途 | 典型占比 |
|---|---|---|
| 训练集 train | 更新参数 $\theta$ | ~80% |
| 验证集 validation | 调超参数(学习率、模型大小)、早停 | ~10% |
| 测试集 test | 只在最后用一次,报告最终性能 | ~10% |
为什么验证和测试要分开?因为你会根据验证集表现反复调超参——调多了,超参就「偷偷拟合」了验证集,验证分数虚高。测试集是从未参与任何决策的"裁判"。数据少时用 K 折交叉验证:分 K 份,轮流留一份验证,取平均。LLM 时代这个纪律同样存在,变成了「评测集污染(benchmark contamination)」问题——评测题混进预训练数据,分数就不可信了。
欠拟合/过拟合可以写成精确的数学。设真实关系 $y = g(x) + \varepsilon$,噪声 $\varepsilon$ 均值 0 方差 $\sigma^2$。在固定点 $x$ 处,对「换不同训练集训练出的模型 $\hat{f}$」求期望,期望平方误差可以严格分解为三项(推导:加减 $\mathbb{E}[\hat{f}(x)]$ 后展开平方,交叉项期望为零):
$$\mathbb{E}\big[(y - \hat{f}(x))^2\big] = \underbrace{\big(g(x) - \mathbb{E}[\hat{f}(x)]\big)^2}_{\text{偏差}^2} + \underbrace{\mathbb{E}\big[(\hat{f}(x) - \mathbb{E}[\hat{f}(x)])^2\big]}_{\text{方差}} + \underbrace{\sigma^2}_{\text{不可约误差}}$$把本章全部数学翻译成 30 行代码。逐行读,每一行都能对应到前面的公式:
python · linear_regression_from_scratch.py# 从零实现线性回归:正规方程 vs 梯度下降
import numpy as np
rng = np.random.default_rng(42)
# ---- 造数据:y = 2.5x + 1.0 + 高斯噪声 ----
n = 200
x = rng.uniform(0, 10, size=n)
y = 2.5 * x + 1.0 + rng.normal(0, 1.5, size=n)
# ---- 方法一:正规方程 w* = (XᵀX)⁻¹Xᵀy ----
X = np.column_stack([x, np.ones(n)]) # 拼一列 1 吸收偏置 b
w_closed = np.linalg.solve(X.T @ X, X.T @ y) # 比显式求逆更稳定
print(f"解析解 w={w_closed[0]:.4f}, b={w_closed[1]:.4f}")
# ---- 方法二:批量梯度下降 ----
w, b = 0.0, 0.0 # 随机起点(这里取 0)
eta = 0.01 # 学习率
for step in range(2000):
y_hat = w * x + b # 前向:预测
err = y_hat - y # 误差向量
grad_w = 2 * np.mean(err * x) # ∂L/∂w = (2/n)Σ err·x 「误差×输入」
grad_b = 2 * np.mean(err) # ∂L/∂b = (2/n)Σ err
w -= eta * grad_w # 沿负梯度走一步
b -= eta * grad_b
if step % 500 == 0:
print(f"step {step:4d} MSE={np.mean(err**2):.4f} w={w:.3f} b={b:.3f}")
print(f"梯度下降 w={w:.4f}, b={b:.4f} # 与解析解一致 ✓")
生产环境一行搞定(但现在你知道这一行背后发生了什么):
python · sklearn 版本from sklearn.linear_model import LinearRegression
model = LinearRegression().fit(x.reshape(-1, 1), y)
print(model.coef_[0], model.intercept_) # ≈ 2.5, 1.0
np.linalg.solve 而不是 np.linalg.inv 再乘?inv 显式求逆再相乘,误差会被条件数放大两次;solve 用 LU 分解直接解线性方程组,更快也更稳。当特征高度相关(多重共线性)时 $X^\top X$ 接近奇异,这个差别会变成天壤之别。工程细节也是技术实力的一部分。最后,看一眼这一切通向哪里。Karpathy(前 OpenAI 创始成员、前特斯拉 AI 总监)这个演讲是「从本章到 LLM」最好的桥梁——你会发现他讲的「训练就是压缩」「下一词预测」,骨架正是你刚学完的 模型+损失+优化: