机器学习究竟在学什么?从样本、假设到经验风险最小化
从“写规则”和“从数据学习”的区别出发,拆解样本、特征、模型、损失函数与训练循环,并手算一次参数更新。
如果要判断一封邮件是不是垃圾邮件,传统程序会先写规则:标题含“中奖”就加分,正文含大量链接再加分,超过阈值便拦截。
问题是,发送者会换词、改写句子,规则很快失效。机器学习换了一种思路:不直接写出所有判断规则,而是给出许多“邮件及其答案”,让程序从样本中找到一套能推广到新邮件的计算规则。
今天真正要回答的问题是:机器学习中的“学习”究竟发生在哪里?
01 从写规则到选择函数#
一个监督学习任务通常有三样东西:
- 输入(Input) :模型能看到的信息,例如邮件的词频。
- 标签(Label) :希望模型给出的答案,例如“正常邮件”或“垃圾邮件”。
- 模型(Model) :把输入映射为预测结果的函数,其中 是可调整参数。
训练过程可以压缩成一条数据流:
样本 (x, y)
│
▼
模型 fθ(x) ──► 预测值 ŷ
│ │
└──── 标签 y ──┘
▼
损失 L(ŷ, y)
│
▼
计算梯度 ∇θL
│
▼
更新参数 θtext所以,“学习”并不是模型凭空产生知识,而是:在一组候选函数中,根据数据不断调整参数,找到损失较小的那个函数。
这里第一次出现两个重要术语:
Hypothesis(假设):模型可以表示的某个具体函数。例如 是线性模型族中的一个假设。
Hypothesis Space(假设空间):模型所有可能参数对应的函数集合。线性回归的假设空间包含所有 。
模型结构决定“能找哪些函数”,训练算法决定“怎样在这些函数中寻找”。
02 数据怎样进入模型?#
假设我们用房屋面积和房龄预测价格。一个样本可表示为:
有两个特征:80 平方米、房龄 5 年; 是价格,单位可以是万元。
把 个样本放在一起:
- :样本数量。
- :每个样本的特征数量。
- 的第 行是第 个样本。
- 是第 个样本的标签。
若一次送入 32 套房屋、每套有 2 个特征,则:
Input:
X.shape = [32, 2]
Parameters:
w.shape = [2]
b.shape = []
Output:
y_hat.shape = [32]text最简单的线性模型是:
决定每个特征对预测的影响, 是不依赖输入的基础偏移, 是模型预测。
03 为什么需要损失函数?#
模型输出预测后,必须有一个数字回答“错得有多严重”。这个函数叫 Loss Function(损失函数)。
回归任务常用平方误差:
它的输入是一个预测值和一个真实值,输出是非负标量。预测完全正确时损失为 0;误差越大,平方后的惩罚增长越快。
但一个样本损失小,不代表模型整体可靠。训练时通常最小化所有训练样本的平均损失:
这叫 Empirical Risk(经验风险):在已经观察到的有限样本上测得的平均错误。
公式中的变量分别是:
- :模型全部可训练参数;在线性模型中就是 和 。
- :第 个样本的预测。
- :单个样本的损失函数。
- :训练样本数量。
- :一个标量,表示当前参数在训练集上的平均损失。
训练目标写成:
arg min 返回的不是最小损失值,而是“让损失最小的那组参数”。这就是 Empirical Risk Minimization(经验风险最小化,ERM)。
04 手算一次真正的“学习”#
先去掉偏置,只看一个参数:
给定一个样本:
x = 2
y = 5
w = 1text第一步,前向计算:
第二步,计算平方损失:
第三步,求损失对参数 的梯度:
梯度为负,表示略微增大 会让损失下降。设学习率 :
更新后:
一次更新就让损失从 9 降到 0.36。模型“学到”的内容,正是参数从 1 变成了 2.2。
交互检查:如果学习率改成 1,会发生什么?
此时 ,预测变成 26,损失变成 441。梯度方向虽然正确,但步子太大,越过了最低点。学习率控制每次参数更新的幅度。
05 从公式落到代码#
不依赖框架的关键逻辑#
x, y = 2.0, 5.0
w = 1.0
learning_rate = 0.1
for step in range(5):
y_hat = w * x
loss = (y_hat - y) ** 2
grad_w = 2 * (y_hat - y) * x
w = w - learning_rate * grad_w
print(step, round(w, 4), round(loss, 4))python这段代码没有隐藏步骤:前向计算得到预测,损失衡量错误,导数给出局部变化方向,最后更新参数。
PyTorch 怎样表达同一个过程?#
import torch
X = torch.tensor([[1.0], [2.0], [3.0]]) # [batch_size=3, num_features=1]
y = torch.tensor([[2.0], [4.0], [6.0]]) # [3, 1]
model = torch.nn.Linear(in_features=1, out_features=1)
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
for step in range(200):
y_hat = model(X) # [3, 1]
loss = torch.mean((y_hat - y) ** 2) # 标量
optimizer.zero_grad()
loss.backward()
optimizer.step()python三个关键 API 分别做什么:
optimizer.zero_grad():清空上一次迭代留下的梯度。PyTorch 默认会累加梯度。loss.backward():沿计算图反向应用链式法则,把梯度写入每个参数的.grad。optimizer.step():根据梯度和学习率更新参数。
训练阶段需要计算梯度;推理阶段只需要:
with torch.no_grad():
prediction = model(torch.tensor([[4.0]])) # [1, 1]python06 经验风险最小,为什么还不够?#
真正想要的不是记住训练样本,而是在未来数据上仍然准确。这种能力叫 Generalization(泛化)。
现实中至少有四类风险:
| 风险 | 表现 | 原因 |
|---|---|---|
| 模型太简单 | 训练损失也很高 | 假设空间表达能力不足 |
| 模型太复杂 | 训练损失低,新数据误差高 | 把噪声也当成规律 |
| 数据不代表未来 | 离线表现好,上线失效 | 训练分布与实际分布不同 |
| 标签或特征有问题 | 损失下降但目标错误 | 数据定义偏离真实任务 |
因此,训练损失只是证据,不是最终答案。下一篇会建立一套基本实验制度:训练集用于学习参数,验证集用于做选择,测试集只负责最后验收。
07 项目中最常见的错误#
- 把 loss 当成准确率。 损失是优化目标,准确率是评价指标;两者相关但不等价。
- 忽略张量形状。
[batch]和[batch, 1]可能触发广播,代码能运行却计算了错误结果。 - 只看最后一次训练损失。 应同时记录训练曲线、验证指标和随机种子。
- 学习率只凭感觉。 过大会震荡或发散,过小会训练缓慢;先观察损失是否稳定下降。
- 认为模型自动理解现实含义。 模型只优化你提供的损失,不会替你判断目标定义是否合理。
08 它什么时候会失败?#
经验风险最小化依赖一个隐含前提:训练样本能代表未来数据。如果训练邮件全部来自同一家公司,而上线后面对不同语言和用户群体,即使训练损失接近 0,也可能失败。
此外,多个参数都能获得很低的训练损失时,ERM 本身不会告诉我们哪个更能泛化。正则化、模型结构中的归纳偏置、更多数据和可靠验证,都是后续要引入的约束。
09 今天真正需要记住什么?#
- 机器学习的“学习”是根据数据调整参数,而不是凭空理解世界。
- 模型结构定义假设空间,损失函数定义“什么叫错”,优化算法负责寻找低损失参数。
- 经验风险是训练样本上的平均损失,ERM 就是寻找使它较小的参数。
- 梯度指出参数的局部上升方向,梯度下降沿反方向更新。
- 训练损失低不等于能泛化到新数据。
10 思考题与小练习#
练习 1:手算一次更新
令 ,使用 。先计算预测、损失、梯度,再得到新参数。答案:,,梯度为 ,。
练习 2:哪一部分定义了“好模型”?
直接答案是损失函数与评价方案。模型会忠实地优化目标,因此目标写错比优化器选错更危险。
练习 3:修改代码观察学习率
把纯 Python 示例中的学习率依次改成 0.01、0.1、0.5,记录损失。解释为什么“下降方向正确”仍不保证每一步都下降。
相关工作#
- Tom M. Mitchell: Machine Learning ↗:经典教材,从任务、经验和性能度量定义学习问题。
- Vapnik: The Nature of Statistical Learning Theory ↗:统计学习理论与经验风险最小化的重要来源。
- PyTorch Autograd ↗:自动微分和计算图的官方说明。
- PyTorch Optimizers ↗:
zero_grad、backward后的参数更新接口。
11 下一篇预告#
如果同一批数据既用于训练,又用于判断模型好不好,模型很容易“既当运动员又当裁判”。下一篇将解释训练集、验证集和测试集各自负责什么,并用一个只有 10 个样本的例子看清数据泄漏为什么会制造虚假的高分。