观文听傑

返回

如果要判断一封邮件是不是垃圾邮件,传统程序会先写规则:标题含“中奖”就加分,正文含大量链接再加分,超过阈值便拦截。

问题是,发送者会换词、改写句子,规则很快失效。机器学习换了一种思路:不直接写出所有判断规则,而是给出许多“邮件及其答案”,让程序从样本中找到一套能推广到新邮件的计算规则。

今天真正要回答的问题是:机器学习中的“学习”究竟发生在哪里?

01 从写规则到选择函数#

一个监督学习任务通常有三样东西:

  • 输入(Input) xx:模型能看到的信息,例如邮件的词频。
  • 标签(Label) yy:希望模型给出的答案,例如“正常邮件”或“垃圾邮件”。
  • 模型(Model) fθf_\theta:把输入映射为预测结果的函数,其中 θ\theta 是可调整参数。

训练过程可以压缩成一条数据流:

样本 (x, y)


模型 fθ(x) ──► 预测值 ŷ
    │              │
    └──── 标签 y ──┘

              损失 L(ŷ, y)


              计算梯度 ∇θL


              更新参数 θ
text

所以,“学习”并不是模型凭空产生知识,而是:在一组候选函数中,根据数据不断调整参数,找到损失较小的那个函数。

这里第一次出现两个重要术语:

Hypothesis(假设):模型可以表示的某个具体函数。例如 y=2x+1y=2x+1 是线性模型族中的一个假设。

Hypothesis Space(假设空间):模型所有可能参数对应的函数集合。线性回归的假设空间包含所有 wx+bwx+b

模型结构决定“能找哪些函数”,训练算法决定“怎样在这些函数中寻找”。

02 数据怎样进入模型?#

假设我们用房屋面积和房龄预测价格。一个样本可表示为:

x=[80,5],y=160x=[80, 5],\qquad y=160

xx 有两个特征:80 平方米、房龄 5 年;yy 是价格,单位可以是万元。

nn 个样本放在一起:

XRn×d,yRnX\in\mathbb{R}^{n\times d},\qquad y\in\mathbb{R}^{n}
  • nn:样本数量。
  • dd:每个样本的特征数量。
  • XX 的第 ii 行是第 ii 个样本。
  • yiy_i 是第 ii 个样本的标签。

若一次送入 32 套房屋、每套有 2 个特征,则:

Input:
X.shape = [32, 2]

Parameters:
w.shape = [2]
b.shape = []

Output:
y_hat.shape = [32]
text

最简单的线性模型是:

y^i=wxi+b\hat y_i=w^\top x_i+b

ww 决定每个特征对预测的影响,bb 是不依赖输入的基础偏移,y^i\hat y_i 是模型预测。

03 为什么需要损失函数?#

模型输出预测后,必须有一个数字回答“错得有多严重”。这个函数叫 Loss Function(损失函数)

回归任务常用平方误差:

L(y^i,yi)=(y^iyi)2L(\hat y_i,y_i)=(\hat y_i-y_i)^2

它的输入是一个预测值和一个真实值,输出是非负标量。预测完全正确时损失为 0;误差越大,平方后的惩罚增长越快。

但一个样本损失小,不代表模型整体可靠。训练时通常最小化所有训练样本的平均损失:

R^(θ)=1ni=1nL(fθ(xi),yi)\hat R(\theta)=\frac{1}{n}\sum_{i=1}^{n}L(f_\theta(x_i),y_i)

这叫 Empirical Risk(经验风险):在已经观察到的有限样本上测得的平均错误。

公式中的变量分别是:

  • θ\theta:模型全部可训练参数;在线性模型中就是 wwbb
  • fθ(xi)f_\theta(x_i):第 ii 个样本的预测。
  • LL:单个样本的损失函数。
  • nn:训练样本数量。
  • R^(θ)\hat R(\theta):一个标量,表示当前参数在训练集上的平均损失。

训练目标写成:

θ=argminθR^(θ)\theta^*=\arg\min_\theta\hat R(\theta)

arg min 返回的不是最小损失值,而是“让损失最小的那组参数”。这就是 Empirical Risk Minimization(经验风险最小化,ERM)

04 手算一次真正的“学习”#

先去掉偏置,只看一个参数:

y^=wx\hat y=wx

给定一个样本:

x = 2
y = 5
w = 1
text

第一步,前向计算:

y^=wx=1×2=2\hat y=wx=1\times2=2

第二步,计算平方损失:

L=(y^y)2=(25)2=9L=(\hat y-y)^2=(2-5)^2=9

第三步,求损失对参数 ww 的梯度:

Lw=2(wxy)x=2(25)×2=12\frac{\partial L}{\partial w} =2(wx-y)x =2(2-5)\times2 =-12

梯度为负,表示略微增大 ww 会让损失下降。设学习率 η=0.1\eta=0.1

wnew=wηLw=10.1×(12)=2.2w_{new}=w-\eta\frac{\partial L}{\partial w} =1-0.1\times(-12)=2.2

更新后:

y^new=2.2×2=4.4,Lnew=(4.45)2=0.36\hat y_{new}=2.2\times2=4.4,\qquad L_{new}=(4.4-5)^2=0.36

一次更新就让损失从 9 降到 0.36。模型“学到”的内容,正是参数从 1 变成了 2.2。

交互检查:如果学习率改成 1,会发生什么?

此时 wnew=11×(12)=13w_{new}=1-1\times(-12)=13,预测变成 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]
python

06 经验风险最小,为什么还不够?#

真正想要的不是记住训练样本,而是在未来数据上仍然准确。这种能力叫 Generalization(泛化)

现实中至少有四类风险:

风险表现原因
模型太简单训练损失也很高假设空间表达能力不足
模型太复杂训练损失低,新数据误差高把噪声也当成规律
数据不代表未来离线表现好,上线失效训练分布与实际分布不同
标签或特征有问题损失下降但目标错误数据定义偏离真实任务

因此,训练损失只是证据,不是最终答案。下一篇会建立一套基本实验制度:训练集用于学习参数,验证集用于做选择,测试集只负责最后验收。

07 项目中最常见的错误#

  1. 把 loss 当成准确率。 损失是优化目标,准确率是评价指标;两者相关但不等价。
  2. 忽略张量形状。 [batch][batch, 1] 可能触发广播,代码能运行却计算了错误结果。
  3. 只看最后一次训练损失。 应同时记录训练曲线、验证指标和随机种子。
  4. 学习率只凭感觉。 过大会震荡或发散,过小会训练缓慢;先观察损失是否稳定下降。
  5. 认为模型自动理解现实含义。 模型只优化你提供的损失,不会替你判断目标定义是否合理。

08 它什么时候会失败?#

经验风险最小化依赖一个隐含前提:训练样本能代表未来数据。如果训练邮件全部来自同一家公司,而上线后面对不同语言和用户群体,即使训练损失接近 0,也可能失败。

此外,多个参数都能获得很低的训练损失时,ERM 本身不会告诉我们哪个更能泛化。正则化、模型结构中的归纳偏置、更多数据和可靠验证,都是后续要引入的约束。

09 今天真正需要记住什么?#

  1. 机器学习的“学习”是根据数据调整参数,而不是凭空理解世界。
  2. 模型结构定义假设空间,损失函数定义“什么叫错”,优化算法负责寻找低损失参数。
  3. 经验风险是训练样本上的平均损失,ERM 就是寻找使它较小的参数。
  4. 梯度指出参数的局部上升方向,梯度下降沿反方向更新。
  5. 训练损失低不等于能泛化到新数据。

10 思考题与小练习#

练习 1:手算一次更新

x=3,y=7,w=1,η=0.05x=3,y=7,w=1,\eta=0.05,使用 L=(wxy)2L=(wx-y)^2。先计算预测、损失、梯度,再得到新参数。答案:y^=3\hat y=3L=16L=16,梯度为 24-24wnew=2.2w_{new}=2.2

练习 2:哪一部分定义了“好模型”?

直接答案是损失函数与评价方案。模型会忠实地优化目标,因此目标写错比优化器选错更危险。

练习 3:修改代码观察学习率

把纯 Python 示例中的学习率依次改成 0.010.10.5,记录损失。解释为什么“下降方向正确”仍不保证每一步都下降。

相关工作#

11 下一篇预告#

如果同一批数据既用于训练,又用于判断模型好不好,模型很容易“既当运动员又当裁判”。下一篇将解释训练集、验证集和测试集各自负责什么,并用一个只有 10 个样本的例子看清数据泄漏为什么会制造虚假的高分。

机器学习究竟在学什么?从样本、假设到经验风险最小化
https://zwjcode.cn/blog/ml-what-is-learning
作者
发布于 2026年8月18日
版权协议 CC BY-NC-SA 4.0
评论加载似乎遇到了问题,请尝试刷新页面。