面向 AI 的求和记号:损失如何变成一个训练目标
学习求和与乘积记号,将上下界展开为循环,并使用 NumPy 的求和与平均把逐样本误差变成均方误差目标。
一个模型做出四个预测,并产生四个平方损失:、、 和 。一次训练步骤需要一个目标,而不是四个彼此分离的数字。把损失相加得到 ,取平均得到 。两种归约都有效,但它们回答的是不同的问题,并且在代码中会产生不同的尺度。
求和记号把重复的加法变成一个紧凑表达式。学完本课,你将能够从上下界展开求和或乘积,把它翻译成循环,在 NumPy 中计算均方误差(MSE),并发现上下界、分母或轴错误的归约。
本课建立在第 5 课的带索引值之上:一旦 表示“示例 的损失”,下一步就是组合所有带索引的损失。官方 Mathematics for Machine Learning 配套网站是本课程参考书的来源主页;它的导论章节说明了从数值数据走向模型和学习目标的路径。
上下界告诉求和从哪里开始、在哪里停止
希腊大写字母 sigma,,表示“把一串项相加”。例如,
这个式子有四个部分需要阅读:
- 是不断变化的索引;
- 是下界,即 开始的位置;
- 是上界,即 停止的位置;以及
- 是在每个索引处计算的项。
两个界都包含在内,因此展开为
展开式同时也是一个算法。累计和从零开始,一次吸收一项:
a = [10, 20, 30, 40, 50]
n = 5
total = 0
for i in range(1, n + 1):
total += a[i - 1]
print(total)
150
数学索引从 到 ,而 Python 列表使用从 到 的位置。Python 的停止值不包含在内,所以必须使用 range(1, n + 1) 才能包含 。写成 range(1, n) 会悄悄漏掉最后一项。表达式 a[i - 1] 把数学中的一基索引转换为第 5 课介绍的零基列表位置。
乘积记号改变起始值和运算
希腊大写字母 pi,,表示“把一串项相乘”。它使用同样类型的索引和包含两端的上下界:
当 、、 且 时,
对应的循环从 开始,因为乘以 不会改变数字:
values = [2, 3, 1, 4]
product = 1
for value in values:
product *= value
print(product)
24
把 product 从 0 开始会让之后的每个结果都为零。这个差异也解释了标准的空情况。没有项的和是 ,没有项的积是 :二者都使用不会改变后续加法或乘法的中性起始值。NumPy 为 np.sum 和 np.prod 记录了同样的约定。
| 重复运算 | 记号 | 累加器起始值 | 空结果 |
|---|---|---|---|
| 加法 | |||
| 乘法 |
当课程学习联合概率时,乘积会变得尤其重要。现在最关键的实现习惯,是让累加器及其起始值与记号相匹配。
四个预测误差变成一个均方误差
假设一个回归模型为四个示例分别预测一个数字:
这里 是示例 的预测值, 是其目标值。把每个示例的平方损失定义为
交互式累加器展示了 sigma 记号压缩掉的中间状态。它的表格仍然是完整的文本替代:第 行列出 、、平方损失,以及该损失是否已经进入累计和。
| 示例 | 预测值 | 目标值 | 平方损失 | 累加器状态 |
|---|---|---|---|---|
| 1 | 2 | 1 | 1 | 本步加入 |
| 2 | 0 | 2 | 4 | 等待中 |
| 3 | 4 | 5 | 1 | 等待中 |
| 4 | 5 | 2 | 9 | 等待中 |
- 已纳入的项
- 1
- 当前平均值
- 1 ÷ 1 = 1.00
第 1/4 步:部分和为 1。
四行全部进入累加器后,累计和为 。将它除以纳入的四行,得到 MSE 。动画没有改变数学内容;它让重复更新 total = total + loss 变得可见。
NumPy 与公式相互对应
NumPy 可以在归约前保留每个示例的损失:
import numpy as np
y_true = np.array([1.0, 2.0, 5.0, 2.0])
y_pred = np.array([2.0, 0.0, 4.0, 5.0])
squared_losses = (y_pred - y_true) ** 2
total_squared_error = np.sum(squared_losses)
mse = np.mean(squared_losses)
print("per-example squared losses:", squared_losses)
print("sum:", total_squared_error)
print("mean squared error:", mse)
per-example squared losses: [1. 4. 1. 9.]
sum: 15.0
mean squared error: 3.75
np.sum 文档定义了对数组元素执行的归约。np.mean 文档定义了算术平均值,并说明默认情况下使用展平后的数组。在这个一维示例中,展平不会改变任何内容:四个条目都会被归约。
把 squared_losses 保留为命名数组有助于调试。它能显示某一个示例是否主导了目标,也让测试可以将实现与上面的四个手算结果进行比较。
轴决定哪些索引消失
对于多维损失数组,“取平均”在轴明确之前是不完整的。考虑两个示例,每个示例有三个输出损失:
行是示例,列是输出。NumPy 的 axis 参数指定要归约的轴:
losses = np.array([
[1.0, 4.0, 1.0],
[9.0, 0.0, 4.0],
])
print("all six losses:", np.mean(losses))
print("one mean per example:", np.mean(losses, axis=1))
print("one mean per output:", np.mean(losses, axis=0))
all six losses: 3.1666666666666665
one mean per example: [2. 4.33333333]
one mean per output: [5. 2. 2.5]
| 表达式 | 被归约的方向 | 保留的结果 |
|---|---|---|
np.mean(losses) | 两个轴 | 六个损失的一个标量 |
np.mean(losses, axis=1) | 每行中的列 | 每个示例一个值 |
np.mean(losses, axis=0) | 每列中的行 | 每个输出一个值 |
当模型需要每个示例一个损失时使用 axis=0 是语义错误。代码能够运行,数字看起来也合理,但留下的索引表示的是“输出”而不是“示例”。sum 和 mean 的 NumPy 直接文档说明,axis=None 会归约整个数组,而整数则选择执行归约的轴。
和与平均值携带不同的尺度
对于固定的、非空的 个损失集合,
平均值是重新缩放后的和,但当批次大小不同时,这个尺度很重要。四个损失都为 的示例,其和为 、平均值为 ;八个这样的示例,其和为 ,平均值仍为 。和衡量集合的总损失,平均值衡量每个纳入项目的损失。
在模型训练期间,预测规则为每个示例产生一个或多个损失。归约把这些值变成用于评估当前参数的标量目标。因此,选择和、平均值,还是带掩码和加权的变体,属于模型的数学契约,而不仅仅是代码格式。
第 7 课将使用幂和根来推理模型尺度。在该课上线之前,面向 AI 的数学课程页面是按发布顺序继续学习的可靠位置。本课学到的记号会贯穿整个课程:和构建加权组合与目标,积则构建重复因子。
检查你的理解
问题 1
将从 i = 3 到 i = 6 的和展开:Σᵢ₌₃⁶ (2i − 1),然后计算其值。
显示分步解答
下界是 3,上界是 6,因此两端都出现:
先计算每个带索引的项,再相加:
共有 项。如果只数出三项,就说明某个端点被漏掉了。
问题 2
Python 循环本来要实现 Σᵢ₌₁ⁿ aᵢ,却使用 for i in range(1, n)。缺少哪一项?循环应如何索引零基列表 a?
显示分步解答
Python 不包含 range 的停止值,所以 range(1, n) 产生 。缺少的项是 。
让停止位置为 n + 1 以包含数学上界,然后在访问零基列表时减去 1:
total = 0
for i in range(1, n + 1):
total += a[i - 1]现在 读取 a[0], 读取 a[n - 1],因此从 到 的每一项都恰好出现一次。
问题 3
展开 ∏ₖ₌₂⁴ (k + 1),计算它,并解释为什么循环累加器必须从 1 开始。
显示分步解答
包含两端的上下界给出索引 、 和 :
循环在看到第一项之前需要一个值。从 开始可以保留第一次乘法,因为 。从 开始则会得到 ,抹去所有项。
问题 4
对于目标值 (3, 1, 2) 和预测值 (1, 2, 2),计算每个平方损失、它们的和以及 MSE。
显示分步解答
对每个示例用预测值减去目标值:
将这些误差平方,得到每个示例的损失:
它们的和是 。共有三个示例,因此
小数是近似值; 是精确值。
问题 5
批次 A 的损失为 (2, 2),批次 B 的损失为 (2, 2, 2, 2)。比较它们的和与平均值。哪种归约保留了每个示例的尺度?
显示分步解答
对于批次 A,
对于批次 B,
由于批次 B 包含两倍数量的示例,和翻了一倍。平均值仍是 ,所以在这个比较中它保留了每个示例的损失尺度。
问题 6
空和与空积是什么?如果两个累加器循环都从 0 开始,会出现什么实现错误?
显示分步解答
加法的中性值是 ,所以没有接收到项的和仍为 。乘法的中性值是 ,所以没有接收到项的积仍为 :
和累加器从 0 开始是正确的。积累加器从 0 开始是错误:即使序列非空,也会因为每次更新都是 而变成零。积累加器必须从 1 开始。
问题 7
一个损失数组的形状为 (32, 5),组织方式是示例 × 输出。哪个 NumPy 表达式会为每个示例产生一个平均值?错误的轴会返回什么?
显示分步解答
每一行包含一个示例的五个输出损失。要在每行内组合各列,应归约轴 1:
per_example_loss = np.mean(losses, axis=1)轴 1 的大小是 5,它消失后留下形状 (32,):32 个示例各有一个值。
如果使用 axis=0,归约的则是包含 32 个示例的轴。结果形状为 (5,),包含每个输出位置跨批次的一个平均值。这些数字可能有效,但回答的是错误的问题。