AI 数学课程 · 第 15 课,共 180

面向 AI 的向量加法:残差更新如何组合信号

逐分量学习向量加法,探索其平行四边形几何,并在 NumPy 中识别形状、广播和特征对齐错误。

分享这篇文章

一个神经网络层接收一个包含三个坐标的状态

x=[2−10.5]\boldsymbol{x}=\begin{bmatrix}2\\-1\\0.5\end{bmatrix}

并计算出一个更新量

u=[−0.51.51]\boldsymbol{u}=\begin{bmatrix}-0.5\\1.5\\1\end{bmatrix}

。

残差连接不会在旧状态和更新量之间二选一。它会把对应坐标相加,得到 y=x+u=[1.5,0.5,1.5]T\boldsymbol{y}=\boldsymbol{x}+\boldsymbol{u}=[1.5,0.5,1.5]^\mathsf{T}。 第一个坐标下降,第二个坐标上升,第三个坐标上升;同一条规则处理全部三个变化。

完成本课后,你将能够逐分量相加向量,把同一运算理解为位移或特征更新,使用 NumPy 验证运算,并拒绝那些形状碰巧可用、但坐标含义没有对齐的加法。

**学习成果:**逐分量相加向量,并解释所得的位移或特征混合。

第 14 课:作为 AI 数据的向量说明,向量是一种有序表示:坐标 jj 承载特定特征或学习到的信号。向量加法依赖这种顺序。官方 Mathematics for Machine Learning 配套网站在第 2.2 节介绍行向量、列向量和矩阵逐元素加法。本课把这条规则专门应用于向量;其中的模型状态示例、推导、代码、交互式几何图形和练习题均为原创。

一条坐标规则组合整个向量

设 a,b∈Rn\boldsymbol{a},\boldsymbol{b}\in\mathbb{R}^{n} 是两个具有相同正维度 nn 的实向量。将它们的坐标写作 aja_j 和 bjb_j,其中 jj 从 1 取到 nn。

“对应坐标”这个说法包含两个要求。第一,向量必须具有相同维度。第二,每一对 aj,bja_j,b_j 必须指向同一个坐标系统:相同的特征顺序、单位和表示约定。数学说明维度要求;数据管道还必须执行语义要求。

加法不会把两个向量连接起来。连接 [a1,a2]T[a_1,a_2]^\mathsf{T} 和 [b1,b2]T[b_1,b_2]^\mathsf{T} 会得到一个四坐标向量,而相加仍然保留两个坐标:

[a1a2]+[b1b2]=[a1+b1a2+b2]\begin{bmatrix}a_1\\a_2\end{bmatrix}+\begin{bmatrix}b_1\\b_2\end{bmatrix}=\begin{bmatrix}a_1+b_1\\a_2+b_2\end{bmatrix}

。

不跳过任何坐标地跟踪一次残差更新

回到开头的向量。假设三个位置都属于同一个学习表示空间。逐行计算和:

坐标现有状态 xjx_j更新量 uju_j输出 yj=xj+ujy_j=x_j+u_j
12.0−0.51.5
2−1.01.50.5
30.51.01.5

正更新量会提高坐标,负更新量会降低坐标,零更新量则保持坐标不变。这些是算术描述,而不是对内部神经元坐标“意味着什么”的语义解释。学习到的表示可以把信息分散到许多坐标中,因此单凭这个和不能揭示一个人类可读的概念。

对角线和两步路径抵达同一点

在二维空间中,向量可以画成从原点出发的箭头。要从几何上把 a\boldsymbol{a} 和 b\boldsymbol{b} 相加,可以沿着 a\boldsymbol{a} 走,然后把 b\boldsymbol{b} 的副本放在它的箭头端点。终点是 a+b\boldsymbol{a}+\boldsymbol{b},因为水平位移是 a1+b1a_1+b_1,垂直位移是 a2+b2a_2+b_2。

也可以反过来。先沿着 b\boldsymbol{b} 走,再沿着平移后的 a\boldsymbol{a} 副本走,同样会抵达这个终点。两条路径构成平行四边形的相邻边;从原点出发的对角线就是这个和。移动下面的四个坐标,测试同向、反向、平行以及相互抵消的方向。

交互式向量平行四边形:改变两个向量,并比较两步路径与对角线之和。
选择水平和垂直坐标

每个滑块的范围是 −4 到 4,步长为 1,也可以使用箭头键。

+3
+1
-1
+2

当前计算:a = (+3, +1),b = (-1, +2),所以 a + b = (+2, +3)。

向量 a — 实线箭头向量 b — 虚线箭头和 a + b — 粗对角线

两条首尾相接的路径都在和这一行的坐标处结束。虚线平移 b 箭头从 a 出发;点线平移 a 箭头从 b 出发。

当前平行四边形的数值替代表
向量水平坐标垂直坐标几何角色
向量 a — 实线箭头+3+1第一个位移,也是其中一条边
向量 b — 虚线箭头-1+2第二个位移,也是其中一条边
和 a + b — 粗对角线+2+3最终端点和对角线

图中使用两个坐标,是因为平面可以显示它们。分量规则并不依赖图形:对于 Rn\mathbb{R}^{n} 中的向量,即使 nn 大到无法绘制,也要把全部 nn 个对应坐标相加。

三个代数事实逐坐标成立

向量加法继承实数加法的熟悉性质。原因是局部的:每条向量恒等式都可以在坐标 jj 处检验。

**交换顺序不会改变和。**因为对实数有 aj+bj=bj+aja_j+b_j=b_j+a_j,

(a+b)j=aj+bj=bj+aj=(b+a)j(\boldsymbol{a}+\boldsymbol{b})_j=a_j+b_j=b_j+a_j=(\boldsymbol{b}+\boldsymbol{a})_j

。

所有坐标都相同,所以 a+b=b+a\boldsymbol{a}+\boldsymbol{b}=\boldsymbol{b}+\boldsymbol{a}。这就是交换律,平行四边形也把同一事实显示为通往一个端点的两条路径。

**分组不会改变三个向量的和。**对于另一个向量 c∈Rn\boldsymbol{c}\in\mathbb{R}^{n},

((a+b)+c)j=(aj+bj)+cj=aj+(bj+cj)=(a+(b+c))j\begin{aligned} ((\boldsymbol{a}+\boldsymbol{b})+\boldsymbol{c})_j&=(a_j+b_j)+c_j\\ &=a_j+(b_j+c_j)\\ &=(\boldsymbol{a}+(\boldsymbol{b}+\boldsymbol{c}))_j \end{aligned}

。

这个结合律保证无论怎样分组,得到的数学向量都完全相同。改变许多次加法的顺序时,有限精度代码可能累积略有不同的舍入误差。数学结合律或数值接近,都不允许混用坐标系统。

**零向量什么也不改变。**令 0=[0,…,0]T∈Rn\boldsymbol{0}=[0,\ldots,0]^\mathsf{T}\in\mathbb{R}^{n}。则

(a+0)j=aj+0=aj(\boldsymbol{a}+\boldsymbol{0})_j=a_j+0=a_j

,

所以 a+0=a\boldsymbol{a}+\boldsymbol{0}=\boldsymbol{a}。返回零向量的残差分支在该加法位置不会造成数值变化。

残差连接使用的正是这种加法机制

原始 ResNet 论文把残差块写成 y=F(x)+x\boldsymbol{y}=\mathcal{F}(\boldsymbol{x})+\boldsymbol{x}:学习分支计算 F(x)\mathcal{F}(\boldsymbol{x}),而捷径把 x\boldsymbol{x} 带到加法处。作者说明这是逐元素加法,并且两项需要具有相同维度。维度不匹配时,其架构可以在相加前对捷径使用学习到的投影。

原始 Transformer 提供了另一个具体例子。它的编码器在每个子层周围应用残差连接:

LayerNorm⁡(x+Sublayer⁡(x))\operatorname{LayerNorm}\left(\boldsymbol{x}+\operatorname{Sublayer}(\boldsymbol{x})\right)

。

在该架构中,嵌入和子层输出使用相同的 dmodel=512d_{\text{model}}=512 维度,因此可以定义逐坐标的和。求和之后的归一化是另一种运算;向量加法本身在 x+Sublayer⁡(x)\boldsymbol{x}+\operatorname{Sublayer}(\boldsymbol{x}) 处结束。

这些例子支持一条范围狭窄的实现结论:残差连接用逐元素加法组合兼容的输入和更新量。仅凭这个方程,不能证明每个输入特征都原样保留,不能证明每个坐标都有孤立含义,也不能证明任意两个等长数组都是有效的残差对。

NumPy 加值并广播形状

NumPy 的 add 运算逐元素相加参数。对于形状相同的一维数组,np.add(state, update) 和 state + update 都直接实现分量规则:

import numpy as np

state = np.array([2.0, -1.0, 0.5])
update = np.array([-0.5, 1.5, 1.0])

if state.shape != update.shape:
    raise ValueError("A residual update must match the state shape")

combined = np.add(state, update)
print("state shape:", state.shape)
print("update shape:", update.shape)
print("combined:", combined)

验证后的输出是

state shape: (3,)
update shape: (3,)
combined: [1.5 0.5 1.5]

一个批次的每一行保留一个状态。形状为 (2, 3) 的匹配数组会把每个更新量加到对应状态上:

batch = np.array([
    [2.0, -1.0, 0.5],
    [0.0,  3.0, -2.0],
])
updates = np.array([
    [-0.5,  1.5, 1.0],
    [ 1.0, -1.0, 0.5],
])

print("batch shape:", batch.shape)
print("batch sums:\n", batch + updates)

验证后的输出是

batch shape: (2, 3)
batch sums:
 [[ 1.5  0.5  1.5]
 [ 1.   2.  -1.5]]

第一行输出就是上面的演算示例。第二行来自 [0,3,−2]T+[1,−1,0.5]T=[1,2,−1.5]T[0,3,-2]^\mathsf{T}+[1,-1,0.5]^\mathsf{T}=[1,2,-1.5]^\mathsf{T}。

成功的数组运算仍可能是错误的加法

NumPy 有意支持广播:如果形状不同但满足其兼容规则,较小数组可以作用于较大数组。把形状 (3,) 的更新量加到形状 (2, 3) 的批次上会成功,并把同样的三个值应用到两行:

broadcast_update = np.array([0.5, 0.0, -0.5])
result = batch + broadcast_update

print("broadcast shape:", result.shape)
print("broadcast sums:\n", result)
broadcast shape: (2, 3)
broadcast sums:
 [[ 2.5 -1.   0. ]
 [ 0.5  3.  -2.5]]

这可能正是有意设计的运算,例如让一个共享更新量作用于批次中的每一行。但它不是两个形状相同的单独向量之间的加法;当额外的扩展是意外发生时,它就很危险。应验证模型要执行的契约,而不只是验证 NumPy 接受的形状。

形状匹配也不够。考虑以下数组:

p=[4,1,0]T,q=[2,3,5]T\begin{aligned} \boldsymbol{p}&=[4,1,0]^\mathsf{T},\\ \boldsymbol{q}&=[2,3,5]^\mathsf{T} \end{aligned}

其中 p\boldsymbol{p} 的顺序为 (clicks,purchases,returns)(\texttt{clicks},\texttt{purchases},\texttt{returns}),q\boldsymbol{q} 的顺序为 (returns,clicks,purchases)(\texttt{returns},\texttt{clicks},\texttt{purchases})。

NumPy 返回 [6,4,5]T[6,4,5]^\mathsf{T},但它的第一次计算把 clicks 加到了 returns 上。没有任何数值形状检查能够找回缺失的标签。应在相加前把一个向量重新排列为另一个向量的坐标约定,或者拒绝该运算。

现在,向量加法不只是一个公式:它是坐标契约、几何路径,也是残差块内部的组合运算。下一课将通过标量乘法控制一个向量的强度;今天的和提供了这些缩放信号最终汇合时所需的运算。

检查你的理解

问题 1

将 a = [3, −2, 1]ᵀ 与 b = [−5, 4, 0.5]ᵀ 相加。展示每个坐标的计算,并说明结果的维度。

显示分步解答

两个输入都有三个坐标,所以如果逐坐标计算,它们的和也属于 R3\mathbb{R}^{3}。

(a+b)1=3+(−5)=−2,(a+b)2=−2+4=2,(a+b)3=1+0.5=1.5\begin{aligned} (\boldsymbol{a}+\boldsymbol{b})_1&=3+(-5)=-2,\\ (\boldsymbol{a}+\boldsymbol{b})_2&=-2+4=2,\\ (\boldsymbol{a}+\boldsymbol{b})_3&=1+0.5=1.5 \end{aligned}

。

按原来的顺序收集结果:

a+b=[−221.5]∈R3\boldsymbol{a}+\boldsymbol{b}=\begin{bmatrix}-2\\2\\1.5\end{bmatrix}\in\mathbb{R}^{3}

。

加法改变了数值,但没有插入或删除坐标。

问题 2

对于 a = (2, 1) 和 b = (−1, 3),跟踪先走 a 再走 b 的首尾相接路径。它在哪里结束?为什么这个点是平行四边形的对角线端点?

显示分步解答

从原点 (0,0)(0,0) 出发。沿着 a=(2,1)\boldsymbol{a}=(2,1) 走到 (2,1)(2,1)。然后沿平移后的 b=(−1,3)\boldsymbol{b}=(-1,3) 改变水平坐标 −1-1,改变垂直坐标 33:

(2,1)+(−1,3)=(2−1,1+3)=(1,4)(2,1)+(-1,3)=(2-1,1+3)=(1,4)

。

反过来走则得到

(−1,3)+(2,1)=(−1+2,3+1)=(1,4)(-1,3)+(2,1)=(-1+2,3+1)=(1,4)

。

两条路径是平行四边形的两组平行边。由于两条路径都抵达 (1,4)(1,4),从原点到 (1,4)(1,4) 的箭头就是它的对角线,表示 a+b\boldsymbol{a}+\boldsymbol{b}。

问题 3

对于任意 a, b ∈ ℝⁿ,逐坐标解释为什么 a + b = b + a。不要只依赖二维图形。

显示分步解答

任选坐标 j∈{1,…,n}j\in\{1,\ldots,n\}。根据向量加法的定义,

(a+b)j=aj+bj(\boldsymbol{a}+\boldsymbol{b})_j=a_j+b_j

。

实数加法满足交换律,所以 aj+bj=bj+aja_j+b_j=b_j+a_j。反向应用向量定义得到

bj+aj=(b+a)jb_j+a_j=(\boldsymbol{b}+\boldsymbol{a})_j

。

因此,两个和在任意选定的坐标 jj 上相等。这个论证对每个坐标都成立,所以向量相等:a+b=b+a\boldsymbol{a}+\boldsymbol{b}=\boldsymbol{b}+\boldsymbol{a}。这个推理适用于任何有限维度,包括无法画出的维度。

问题 4

一个残差分支对状态 x ∈ ℝ⁴ 返回 [0, 0, 0, 0]ᵀ。计算 x + 0,并说出在声称该分支没有产生有意义的变化之前所需的假设。

显示分步解答

写作 x=[x1,x2,x3,x4]T\boldsymbol{x}=[x_1,x_2,x_3,x_4]^\mathsf{T}。逐坐标相加得到

x+0=[x1+0x2+0x3+0x4+0]=[x1x2x3x4]=x\boldsymbol{x}+\boldsymbol{0}=\begin{bmatrix}x_1+0\\x_2+0\\x_3+0\\x_4+0\end{bmatrix}=\begin{bmatrix}x_1\\x_2\\x_3\\x_4\end{bmatrix}=\boldsymbol{x}

。

所以该加法位置没有造成数值变化。称其为“没有有意义的变化”还需要假设状态和零更新量使用同一个四坐标表示,并且之后没有其他运算(例如归一化)改变数值。

问题 5

[1, 2]ᵀ + [3, 4, 5]ᵀ 能成为向量和吗?诊断问题,然后说明对于维度不匹配的残差块,一种架构层面的补救方法。

显示分步解答

第一个向量属于 R2\mathbb{R}^{2},第二个属于 R3\mathbb{R}^{3}。逐分量相加需要为每个输出坐标找到对应的第二个输入,但第一个向量没有第三个坐标。因此,按当前写法向量和没有定义。

残差架构可以先把一个分支映射到另一个分支的维度。例如,投影 PP 可以把 x∈R2\boldsymbol{x}\in\mathbb{R}^{2} 变换为 Px∈R3P\boldsymbol{x}\in\mathbb{R}^{3}。随后,只要该投影也建立了预期的坐标语义,就可以定义 Px+uP\boldsymbol{x}+\boldsymbol{u}。填充是另一种依赖具体架构的选择,但默默丢弃或凭空添加坐标都不是向量加法。

问题 6

两个形状为 (3,) 的数组使用特征顺序 (clicks, purchases, returns) 和 (returns, clicks, purchases)。为什么按位置相加无效?应该如何准备第二个数组?

显示分步解答

形状相等会把位置 1 与位置 1 配对,但标签显示这会把 clicks 加到 returns 上。位置 2 和 3 也没有对齐。该运算会产生没有一致特征解释的数字。

先把第二个数组的每个值与其标签绑定。然后按照第一个数组使用的约定

(clicks,purchases,returns)(\texttt{clicks},\texttt{purchases},\texttt{returns})

重新排列这些带标签的值。只有两个数组在维度、顺序和单位上都一致后,才能执行按位置相加。NumPy 无法从不带特征标签的纯数字数组中完成这项语义检查。

问题 7

一个批次的形状为 (4, 3),更新量的形状为 (3,)。NumPy 接受 batch + update。输出形状是什么,执行了什么计算?程序员在信任它之前必须回答哪个问题?

显示分步解答

NumPy 比较末尾维度。最后的维度都是 3,而 update 缺失的前导维度视为 1,并可以扩展到批次的四行。因此输出形状是 (4, 3)。

对于每个行索引 i∈{0,1,2,3}i\in\{0,1,2,3\} 和坐标 j∈{0,1,2}j\in\{0,1,2\},NumPy 计算

result[i,j]=batch[i,j]+update[j]\texttt{result}[i,j]=\texttt{batch}[i,j]+\texttt{update}[j]

。

程序把同一个三坐标更新量应用到全部四行。程序员必须判断:是否确实有意让一个共享更新量复用于每个批次项。广播成功只证明符合 NumPy 规则的形状兼容;它不证明模型契约要求在所有项目间复用一个更新量。

问题 8

一个子层接收 x = [1, −2, 3]ᵀ,并返回 F(x) = [0.5, 2, −1]ᵀ。计算残差和,然后精确解释算术证明了什么、没有证明什么。

显示分步解答

把对应坐标相加:

y=x+F(x)=[1+0.5−2+23+(−1)]=[1.502]\begin{aligned} \boldsymbol{y}&=\boldsymbol{x}+\mathcal{F}(\boldsymbol{x})\\ &=\begin{bmatrix}1+0.5\\-2+2\\3+(-1)\end{bmatrix}\\ &=\begin{bmatrix}1.5\\0\\2\end{bmatrix} \end{aligned}

。

该算术证明兼容的输入和子层输出已按坐标组合为一个三坐标结果。它没有确定任一坐标的人类含义,没有证明原始信息可以完全恢复,也没有说明后续归一化或层将做什么。这些属于学习到的表示和周围架构的性质,而不是加号本身的结果。

资料来源

  1. Mathematics for Machine Learning companion website
  2. Mathematics for Machine Learning book PDF
  3. NumPy documentation: numpy.add
  4. NumPy documentation: broadcasting
  5. Deep Residual Learning for Image Recognition
  6. Attention Is All You Need