B. 数学符号与常用推导
公式读不下去时,不一定是数学太难。有时只是同一个字母在两页里换了工作:上一页的 是温度,下一页又成了序列长度。
查符号时,先看当前章节给它的定义,再看形状和单位。下面是一份常用约定,也保留几个容易混淆的地方。
1. 符号不是密码:先把它读成一句话
| 写法 | 可以怎样读 |
|---|---|
| 是一个实数 | |
| 一个有 个实数分量的向量 | |
| 行、 列的矩阵 | |
| 、 | 第 个分量;第 行第 列 |
| 对应分量相乘再相加 | |
| 矩阵转置,交换行列 | |
| 向量的欧几里得长度 | |
| 预测或估计出来的量 | |
| 某个约定目标下的最优参数 | |
| 从第 1 项到第 项相加 | |
| 从第 1 项到第 项相乘 | |
| 让损失最小的参数,而不是最小损失值 | |
| 只改变第 个参数时,损失怎样变化 | |
| 把各个参数的偏导数排成向量 | |
| 已知 时, 的条件概率 | |
| 随机变量 服从分布 | |
| 、 | 期望与方差 |
| 条件成立为 1,否则为 0 | |
| 、 | 成比例;近似相等 |
数学下标常从 1 开始,Python 下标从 0 开始,所以“第一个类别”的编号在代码里往往是 0。这个差别在交叉熵目标中尤其值得检查。
本书通常用 表示样本数, 表示特征或表示维度, 表示批量大小, 表示类别数。序列长度、训练步数、温度等含义需要看局部定义。Transformer 中的 又分别是查询、键、值,不能机械地把每一个 都解释成类别数。
上标也要看语境: 是转置, 是平方,而 常表示第 层的表示。后者不是乘方。
2. 用形状检查一个式子有没有接对
设每行是一条样本,输入 的形状为 ,权重 的形状为 。那么:
中间两个长度 对上,输出保留样本数 和输出宽度 。若再加一个长度为 的偏置,程序通常沿每行广播。
第一章的单输出模型可以写成:
是长度为 的全 1 向量,说明每条样本都加同一个偏置。纸上的列向量在 NumPy 中可能用 (n,) 保存;转换为 (n,1) 时要让目标也遵守同一约定,见附录 A。
单位也能帮忙检查。若输入是页数,输出是秒,权重就是秒/页,偏置是秒。把秒数平方后取平均,MSE 的单位是秒的平方;RMSE 再开平方,才回到秒。
3. 均方误差的梯度:把链式法则展开一次
设第 条样本的残差为 :
对第 个权重,先对平方求导,再乘上残差对该权重的变化率:
在打印例子里,三行输入是 [10,2]、[4,6]、[8,0],残差是 −2、2、−1。第一个权重的梯度于是为:
它为负,表示在当前点稍微增大第一个权重,损失会下降。实际更新还要乘学习率,不能把局部变化率当成“大步走到哪里都有效”的承诺。
把各分量合起来,便得到常见的矩阵写法:
是 ,乘长度为 的残差,得到长度为 的梯度。这个形状检查也解释了为什么这里需要转置。
有些资料把平方损失定义成前面带 。那样梯度中的 2 会消掉。两个版本并不矛盾,但比较公式或学习率时要先看损失定义。
4. softmax 与交叉熵:概率和梯度接在一起
给一条样本的类别分数 ,softmax 定义为:
所有分数一起加一个常数,概率不变。因此实际计算时可以先减去最大分数 :
这样最大的指数项是 1,避免直接计算 之类的大数。它不是随意剪掉概率,而是同一个公式的等价写法。
若真实类别是 ,单条交叉熵可改写为:
对任意分数 求导,第一项给出 ,第二项只在 时给出 1,于是:
正确类别的概率如果还很小,它对应的梯度就明显为负;梯度下降会倾向于抬高这个分数。其他类别的梯度为正,分数倾向于降低。这里说的是对分数的局部梯度,网络参数还要继续沿计算图求导。
本书没有特别说明时, 指自然对数。交叉熵因此以 nat 计量,困惑度是 ;若改用以 2 为底的对数,对应的单位与反变换也要一起改变。
5. 期望与方差:平均什么,按什么权重
随机变量取离散值 ,各自概率为 ,且概率和为 1。期望是:
方差看的是相对期望的平方偏差:
第二行来自展开平方,再用 。例如取 0、1、2 的概率分别是 1/4、1/2、1/4,期望为 1,方差为 1/2。
概率分布的方差和有限样本的方差估计不是同一个定义。对独立同分布、方差有限且数量大于 1 的样本,用样本均值估计总体均值时,常见的无偏样本方差除以 ;如果只把当前数组当作一个经验分布来描述,则通常除以 。不能只看函数名叫 var,还要检查自由度参数。数字很大但差异很小时,直接计算“平方均值减均值平方”还可能损失精度,程序通常采用更稳定的算法。
6. 用计算核对推导
完整脚本只依赖 Python 标准库。它计算稳定概率,并用中心差分核对交叉熵梯度。运行:
python3 src/examples/math_reference.py
实际输出:
稳定 softmax: [0.244728, 0.665241, 0.090031]
真实类别为 1 时的交叉熵:0.407606
交叉熵梯度: [0.244728, -0.334759, 0.090031]
中心差分核对:通过
打印例子的权重梯度: [-13.333333, 5.333333]
离散例子的期望:1.000,方差:0.500
代码里的真实类别编号 1 指第二个分数。数值核对能发现不少下标和系数错误,但有限个输入上的检查不能代替一般证明。
展开完整代码
"""附录数学核对:稳定概率、均方误差梯度、离散期望;仅标准库。"""
from math import exp, log
def logsumexp(values):
largest = max(values)
return largest + log(sum(exp(v - largest) for v in values))
def cross_entropy(logits, label):
return logsumexp(logits) - logits[label]
def softmax(logits):
shifted = [exp(z - max(logits)) for z in logits]
total = sum(shifted)
return [v / total for v in shifted]
def mse(weights, bias, inputs, targets):
return sum((sum(a * b for a, b in zip(row, weights)) + bias - y) ** 2
for row, y in zip(inputs, targets)) / len(inputs)
def main():
z = [1000.0, 1001.0, 999.0]
p = softmax(z)
gradient = [v - int(j == 1) for j, v in enumerate(p)]
eps = 1e-4
finite = []
for j in range(len(z)):
plus, minus = z.copy(), z.copy()
plus[j] += eps
minus[j] -= eps
finite.append((cross_entropy(plus, 1) - cross_entropy(minus, 1)) / (2 * eps))
assert max(abs(a - b) for a, b in zip(gradient, finite)) < 1e-8
outcomes, probabilities = [0, 1, 2], [0.25, 0.5, 0.25]
mean = sum(x * q for x, q in zip(outcomes, probabilities))
variance = sum((x - mean) ** 2 * q for x, q in zip(outcomes, probabilities))
x, w, y = [[10, 2], [4, 6], [8, 0]], [3.0, 5.0], [62, 60, 45]
residual = [sum(a * b for a, b in zip(row, w)) + 20 - t for row, t in zip(x, y)]
dw = [2 * sum(row[j] * e for row, e in zip(x, residual)) / len(x) for j in range(2)]
print("稳定 softmax:", [round(v, 6) for v in p])
print(f"真实类别为 1 时的交叉熵:{cross_entropy(z, 1):.6f}")
print("交叉熵梯度:", [round(v, 6) for v in gradient])
print("中心差分核对:通过")
print("打印例子的权重梯度:", [round(v, 6) for v in dw])
print(f"离散例子的期望:{mean:.3f},方差:{variance:.3f}")
if __name__ == "__main__":
main()
自测
- 把 从平均平方误差改成平方误差总和,梯度改变多少?
- 给全部 logits 加 100,交叉熵和它对各 logits 的梯度是否改变?
- 为什么本页交叉熵梯度的各分量加起来为 0?
- 随机变量总是等于 5,它的期望和方差各是多少?
答案
- 乘以样本数 。比较训练时也要检查批量大小与损失归约方式。
- 数学上不变;稳定实现更容易在浮点计算中保留这个性质。
- softmax 概率之和是 1,真实类别指示量之和也是 1。这也对应共同平移分数不会改变损失。
- 期望为 5,方差为 0。“随机变量”这个名称不要求每次一定出现不同值。
需要更慢的推导时,回到第一章、第二章、第三章和第五章。这些基础关系也可对照 《Deep Learning》第 2、3、6 章阅读。