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

面向 AI 的线性组合:加权向量如何构建新表示

缩放并组合向量,使用 NumPy 检查每个坐标,并理解为什么注意力输出是一种特殊的加权表示。

分享这篇文章

AI 系统经常会有多个候选向量,却需要得到一个结果。只选一个候选向量会丢弃其余信息;原样相加则会让每个候选向量拥有相同的强度。线性组合提供了更灵活的运算:用各自的系数缩放每个向量,再把缩放后的贡献相加。

例如,两个向量可能编码上下文的不同部分。系数 0.750.75 和 0.250.25 让第一个向量的贡献是第二个的三倍。结果仍然是同一坐标系中的一个向量,可以用于下一步计算。

学完本课后,你将能够逐坐标构造线性组合,解释每个系数改变了什么,使用带形状检查的 NumPy 实现该运算,并识别出把一般线性组合变成类似平均值的注意力输出所需的额外条件。

**学习成果:**构造并解释向量的加权和。

第 16 课:面向 AI 的标量乘法展示了一个标量如何控制一个向量的强度和方向。第 15 课介绍了向量加法。本课把这两个运算结合起来。官方 Mathematics for Machine Learning 配套网站在第 2.2 节介绍标量和矩阵运算,并在该书第 2.5 节定义线性组合。下面的专题故事、计算、代码、可视化和评估题均为原创。

为每个候选向量分配自己的系数

设 v1,v2,…,vk∈Rn \boldsymbol{v}_1,\boldsymbol{v}_2,\ldots,\boldsymbol{v}_k\in\mathbb{R}^n 为 kk 个向量。符号 Rn\mathbb{R}^n 表示每个向量都有 nn 个实坐标。设 a1,a2,…,ak∈Ra_1,a_2,\ldots,a_k\in\mathbb{R} 为实数系数。系数是分配给某个向量的标量。

把它称为“加权和”描述的是同一算术过程:系数就是权重。但这里的“权重”不一定是学习得到的模型参数或概率;它的作用只是缩放一个贡献。

所有输入向量必须属于同一个向量空间,这样它们的和才有意义。在数组计算中,这通常意味着形状相同且坐标含义相同。一个表示 [temperature, humidity] 的二坐标向量,不能仅仅因为形状都是 (2,),就与表示 [height, age] 的二坐标向量有意义地相加。

追踪两个贡献直到结果

假设一个小型表示使用坐标 [topic, urgency]。两个候选向量为

u=[31],v=[−13].\boldsymbol{u}=\begin{bmatrix}3\\1\end{bmatrix}, \qquad \boldsymbol{v}=\begin{bmatrix}-1\\3\end{bmatrix}.

选择系数 a=0.75a=0.75 和 b=0.25b=0.25。所要求的线性组合是 r=au+bv\boldsymbol{r}=a\boldsymbol{u}+b\boldsymbol{v}。

这个例子还说明,“系数更大”不等于“最终坐标更大”。系数缩放的是整个向量;每个向量坐标的符号和大小仍然决定该贡献会使相应的结果坐标升高还是降低。

分量规则解释了这个运算

按坐标写出输入向量:

vi=[vi1vi2⋮vin].\boldsymbol{v}_i= \begin{bmatrix} v_{i1}\\v_{i2}\\\vdots\\v_{in} \end{bmatrix}.

符号 vijv_{ij} 表示向量 ii 的坐标 jj。缩放和加法逐坐标进行,因此结果的坐标 jj 为

rj=∑i=1kaivij.r_j=\sum_{i=1}^{k}a_i v_{ij}.

这个恒等式正是两步做法可行的原因。标量乘法让第 ii 个贡献变为 [aivi1,…,aivin]T[a_i v_{i1},\ldots,a_i v_{in}]^\mathsf{T}。随后,向量加法把所有第一个坐标相加得到 r1r_1,把所有第二个坐标相加得到 r2r_2,依此类推。因为每个坐标计算都是实数计算,结果仍然有 nn 个实坐标。

同一规则也给出有用的代数检查。重新分组贡献不会改变结果,因为实数加法满足结合律;重新排序也不会改变结果,因为加法满足交换律:

au+bv=bv+au.a\boldsymbol{u}+b\boldsymbol{v} =b\boldsymbol{v}+a\boldsymbol{u}.

这不意味着可以在向量之间交换系数。通常 au+bv≠bu+ava\boldsymbol{u}+b\boldsymbol{v}\ne b\boldsymbol{u}+a\boldsymbol{v},因为不同的向量得到了不同的缩放。

移动系数,不要移动坐标含义

这个探索器固定 u=[3,1]T\boldsymbol{u}=[3,1]^\mathsf{T} 和 v=[−1,3]T\boldsymbol{v}=[-1,3]^\mathsf{T},同时让你调整 aa 和 bb。实线和虚线箭头展示从原点出发的两个缩放贡献。bvb\boldsymbol{v} 的平移副本从 aua\boldsymbol{u} 的尖端开始,展示相加的路径。点线结果箭头直接从原点连到两者之和。

线性组合交互演示:改变两个系数,跟踪两个缩放贡献如何得到它们的和。
选择两个系数

每个范围从 −1.5 到 2,步长为 0.25。使用方向键移动一步,或使用 Page Up 和 Page Down 进行较大幅度的调整。

当前计算:a = +0.75,b = +0.25,au = (+2.25, +0.75),bv = (-0.25, +0.75),r = au + bv = (+2, +1.5)。

aubvr = au + bv

线型和标签区分所有箭头,不依赖颜色。平移后的虚线箭头与 bv 长度和方向相同;移动起点可以展示相加,但不会改变贡献。如果结果为零,点线圆环会标记原点。

当前系数、向量、贡献和结果的数值替代视图
量系数原始向量缩放后的贡献
第一个候选向量 u+0.75(+3, +1)(+2.25, +0.75)
第二个候选向量 v+0.25(-1, +3)(-0.25, +0.75)
结果 r不适用不适用(+2, +1.5)

在服务器渲染的初始状态中,a=0.75a=0.75、b=0.25b=0.25,因此点线箭头终点为 (2,1.5)(2,1.5),与上面的示例一致。把一个系数设为零,它的贡献就会消失。把系数调到零以下,箭头就会反向。把两个系数都设为零,所有贡献都会收缩为零向量。这些变化都不会重命名或重新排列坐标。

线性组合比平均值更广泛

几个相关术语使用同一个缩放并相加的运算,但对系数施加了不同规则。

组合类型系数条件两个输入点的几何结果
线性组合ai∈Ra_i\in\mathbb{R}可以沿由这些向量生成的整条直线或平面移动
仿射组合∑iai=1\sum_i a_i=1保持在经过输入点的仿射直线或平面上,但可以延伸到输入点之外
凸组合ai≥0a_i\ge 0 且 ∑iai=1\sum_i a_i=1保持在输入之间或它们的凸包内部

在本课中,加权平均指具有归一化非负权重的常见形式,因此它是凸组合。有些领域用“加权均值”时遵循不同约定,应明确说明。这里的起始例子符合该约定,因为 0.75+0.25=10.75+0.25=1 且两个系数都非负。相反,1.5u−0.5v1.5\boldsymbol{u}-0.5\boldsymbol{v} 是有效的线性组合和仿射组合,但不是凸组合:它的负系数可能使结果越过两个向量之间的线段。

把所有系数都设为零,线性组合总能到达零向量。这个事实并不表示输入向量本身是零;它只表示该运算允许没有活跃贡献的平凡组合。

注意力使用受约束的值向量加权和

原始 Transformer 论文将注意力输出定义为值向量的加权和。每个值向量都是具有相同特征维度的候选表示。注意力机制计算当前位置中每个值应贡献多少,然后执行线性组合。论文的缩放点积注意力方程用矩阵运算紧凑地写出了许多这样的和。

对于一个单独的示例输出,假设已经计算出的注意力权重是 0.60.6、0.30.3 和 0.10.1,值向量为

x1=[20],x2=[04],x3=[−12].\boldsymbol{x}_1=\begin{bmatrix}2\\0\end{bmatrix},\quad \boldsymbol{x}_2=\begin{bmatrix}0\\4\end{bmatrix},\quad \boldsymbol{x}_3=\begin{bmatrix}-1\\2\end{bmatrix}.

那么,注意力风格的表示为

z=0.6x1+0.3x2+0.1x3=[1.20]+[01.2]+[−0.10.2]=[1.11.4].\begin{aligned} \boldsymbol{z} &=0.6\boldsymbol{x}_1+0.3\boldsymbol{x}_2+0.1\boldsymbol{x}_3\\ &=\begin{bmatrix}1.2\\0\end{bmatrix} +\begin{bmatrix}0\\1.2\end{bmatrix} +\begin{bmatrix}-0.1\\0.2\end{bmatrix}\\ &=\begin{bmatrix}1.1\\1.4\end{bmatrix}. \end{aligned}

在论文的注意力运算中,用于生成这些权重的归一化会使它们非负,并使每一组权重的和为一。因此,对于该注意力输出,最终的和是值向量的凸组合。这些限制属于这个注意力机制,而不是线性组合的定义。

本课有意从权重已经产生之后开始。它暂不讲解查询、键、兼容性分数、归一化、多头或之后的输出投影。需要记住的数学要点更窄:一旦有了系数和兼容的值向量,注意力就通过缩放每个值并把贡献相加来构建表示。

NumPy 应该揭示向量轴

把两个候选向量作为一个数组的两行存储。数组 vectors 的形状于是为 (2, 2):两个向量,每个有两个坐标。系数数组的形状为 (2,),每个向量对应一个系数。

import numpy as np

vectors = np.array([
    [3.0, 1.0],   # u
    [-1.0, 3.0],  # v
])
coefficients = np.array([0.75, 0.25])

if vectors.ndim != 2:
    raise ValueError("vectors must have shape (number_of_vectors, coordinates)")
if coefficients.shape != (vectors.shape[0],):
    raise ValueError("need exactly one coefficient per vector")

scaled = coefficients[:, np.newaxis] * vectors
result = scaled.sum(axis=0)

print(scaled)
print(result)

验证后的输出为

[[ 2.25  0.75]
 [-0.25  0.75]]
[2.  1.5]

coefficients[:, np.newaxis] 把系数形状从 (2,) 改为 (2, 1)。这样 NumPy 就可以沿每个向量行广播一个系数。官方广播规则从右侧比较数组维度,并允许匹配或大小为一的维度。相乘后,sum(axis=0) 沿向量行轴求和,并保留两个坐标列。

只写 coefficients * vectors 在这个例子中会运行成功:尾部形状 (2,) 和 (2, 2) 恰好把两个系数对齐到坐标列,于是 NumPy 返回一个看似合理但含义错误的数组。第一行会变成 [2.25, 0.25],而不是 [2.25, 0.75]。广播成功并不能证明系数轴正确。

形状兼容是必要条件,但仍然不充分。如果一个候选向量有三个坐标、另一个有两个坐标,就没有一个普通数值矩形 vectors 数组可以用这种方式归约。如果两个向量都有两个坐标,却使用了不同的特征定义,代码可以运行,但结果在语义上仍然无效。要同时检查数组形状和坐标含义。

系数回答“多少”,不回答“这代表什么”

三个习惯可以避免大多数错误:

  1. 计算前,先把每个系数与特定向量配对。
  2. 缩放完整向量,然后相加对应坐标。
  3. 明确说明任何额外的系数规则——例如非负或总和为一——不要假定“加权和”这个词自动包含这些规则。

一般线性组合中的负系数不是错误;它们会反转一个贡献。大于一的系数也有效,它们可以让结果超出输入箭头。贡献彼此相反时可能发生抵消;零系数会移除一个候选向量而不改变向量形状。

下一步满足先修关系的课程步骤是第 18 课,它将数字组织成矩阵。第 19 课随后会把矩阵–向量乘法展示为多个加权和。请把当前的配方——配对、缩放、相加——放在手边;矩阵记号会压缩它,而不会取代它。面向 AI 的数学课程页面保留了有序学习路径和已发布的先修关系。

检查你的理解

问题 1

设 u = [2, −1]ᵀ、v = [4, 3]ᵀ。计算 1.5u − 0.5v,并解释第二个贡献的符号。

显示分步解答

将系数与向量配对,先缩放再相加:

1.5u=1.5[2−1]=[3−1.5],1.5\boldsymbol{u} =1.5\begin{bmatrix}2\\-1\end{bmatrix} =\begin{bmatrix}3\\-1.5\end{bmatrix},−0.5v=−0.5[43]=[−2−1.5].-0.5\boldsymbol{v} =-0.5\begin{bmatrix}4\\3\end{bmatrix} =\begin{bmatrix}-2\\-1.5\end{bmatrix}.

现在相加对应坐标:

1.5u−0.5v=[3+(−2)−1.5+(−1.5)]=[1−3].1.5\boldsymbol{u}-0.5\boldsymbol{v} =\begin{bmatrix}3+(-2)\\-1.5+(-1.5)\end{bmatrix} =\begin{bmatrix}1\\-3\end{bmatrix}.

负系数会在相加前反转 v\boldsymbol{v}。它不是“只减去负坐标”的意思;v\boldsymbol{v} 的每个坐标都乘以 −0.5-0.5。

问题 2

对 au + bv,将系数对 (0.4, 0.6)、(1.2, −0.2) 和 (2, 1) 分别分类。哪些是线性、仿射或凸组合?

显示分步解答

每一对都包含实数系数,因此三者都是线性组合。

  • 对于 (0.4,0.6)(0.4,0.6),系数和为 11 且非负,因此既是仿射组合也是凸组合。
  • 对于 (1.2,−0.2)(1.2,-0.2),系数和同样为 11,所以是仿射组合。负系数使它不是凸组合。
  • 对于 (2,1)(2,1),系数和为 33 而不是 11。它既不是仿射组合也不是凸组合,但仍是有效的线性组合。

分类取决于系数约束,而不是 u\boldsymbol{u} 和 v\boldsymbol{v} 的具体坐标。

问题 3

在探索器中,当 a 从 0.75 变为 0,再变为 −0.75,而 u 保持不变时,au 必须发生什么变化?

显示分步解答

当 a=0.75a=0.75 时,贡献为 0.75u=[2.25,0.75]T0.75\boldsymbol{u}=[2.25,0.75]^\mathsf{T}。当 a=0a=0 时,

0u=[00],0\boldsymbol{u}=\begin{bmatrix}0\\0\end{bmatrix},

所以第一个贡献收缩到原点,不再有方向。当 a=−0.75a=-0.75 时,

−0.75u=[−2.25−0.75].-0.75\boldsymbol{u}=\begin{bmatrix}-2.25\\-0.75\end{bmatrix}.

最终箭头与 a=0.75a=0.75 时的贡献长度相同,但方向相反。向量 u\boldsymbol{u} 及其坐标含义没有改变;改变的只有系数。

问题 4

一个注意力风格的运算使用权重 (0.2, 0.5, 0.3),值为 x₁ = [1, 2]ᵀ、x₂ = [3, 0]ᵀ 和 x₃ = [−1, 4]ᵀ。计算输出,并说明为什么它是凸组合。

显示分步解答

计算三个贡献:

0.2x1=[0.20.4],0.5x2=[1.50],0.3x3=[−0.31.2].0.2\boldsymbol{x}_1=\begin{bmatrix}0.2\\0.4\end{bmatrix},\quad 0.5\boldsymbol{x}_2=\begin{bmatrix}1.5\\0\end{bmatrix},\quad 0.3\boldsymbol{x}_3=\begin{bmatrix}-0.3\\1.2\end{bmatrix}.

逐坐标相加:

z=[0.2+1.5−0.30.4+0+1.2]=[1.41.6].\boldsymbol{z} =\begin{bmatrix}0.2+1.5-0.3\\0.4+0+1.2\end{bmatrix} =\begin{bmatrix}1.4\\1.6\end{bmatrix}.

三个权重都非负,且 0.2+0.5+0.3=10.2+0.5+0.3=1。这两个事实使该加权和成为值向量的凸组合。它们是题述注意力风格设置的额外条件,并非每个线性组合的要求。

问题 5

开发者想组合形状为 (3,) 和 (2,) 的向量。为什么表达式无效?为什么用零填充较短向量不一定能自动修复含义?

显示分步解答

向量加法要求坐标匹配。形状为 (3,) 的向量有三个坐标位置,而形状为 (2,) 的向量只有两个,因此第二个向量没有可相加的第三坐标。按原样写出的 au+bva\boldsymbol{u}+b\boldsymbol{v} 在同一个 Rn\mathbb{R}^n 中没有定义。

用零填充可以创建匹配的数组形状,但会默默添加一个坐标声明:它表示较短向量缺失的第三个特征值恰好为零。只有在编码规范定义了同一个第三特征,并定义缺失值为零时,这才可能有效。修复形状不能代替兼容的坐标含义。

问题 6

为什么 coefficients * vectors 可能在 NumPy 中悄悄地给坐标而不是向量行加权?什么显式变形可以修复计算?

显示分步解答

当 vectors.shape == (2, 2)、coefficients.shape == (2,) 时,NumPy 从右侧比较维度。系数长度 2 与最后的坐标维度 2 匹配,因此广播会把两个数字应用到各列。运算成功了,但系数 1 加权坐标 1,系数 2 加权坐标 2。

插入一个大小为一的坐标维度:

scaled = coefficients[:, np.newaxis] * vectors
result = scaled.sum(axis=0)

现在 coefficients[:, np.newaxis].shape == (2, 1)。第一维把一个系数与每个向量行配对,大小为一的维度则把该系数广播到这一行的每个坐标。随后沿轴 0 求和,逐坐标相加缩放后的行。

问题 7

对两个向量 u、v ∈ ℝ²,逐坐标证明 a(u + v) = au + av。

显示分步解答

写作 u=[u1,u2]T\boldsymbol{u}=[u_1,u_2]^\mathsf{T}、v=[v1,v2]T\boldsymbol{v}=[v_1,v_2]^\mathsf{T}。左侧为

a(u+v)=a[u1+v1u2+v2]=[a(u1+v1)a(u2+v2)].a(\boldsymbol{u}+\boldsymbol{v}) =a\begin{bmatrix}u_1+v_1\\u_2+v_2\end{bmatrix} =\begin{bmatrix}a(u_1+v_1)\\a(u_2+v_2)\end{bmatrix}.

利用实数的分配律可得

[au1+av1au2+av2]=[au1au2]+[av1av2]=au+av.\begin{bmatrix}au_1+av_1\\au_2+av_2\end{bmatrix} =\begin{bmatrix}au_1\\au_2\end{bmatrix} +\begin{bmatrix}av_1\\av_2\end{bmatrix} =a\boldsymbol{u}+a\boldsymbol{v}.

第一个坐标相同,第二个坐标也相同,所以两个向量相等。相同论证适用于任意有限个坐标。

问题 8

模型有兼容的向量 u 和 v。比较 r = 2u + 0v 与拼接 u 和 v。每种运算产生什么信息和形状?

显示分步解答

线性组合先缩放再相加:

r=2u+0v=2u.\boldsymbol{r}=2\boldsymbol{u}+0\boldsymbol{v}=2\boldsymbol{u}.

如果 u,v∈Rn\boldsymbol{u},\boldsymbol{v}\in\mathbb{R}^n,结果仍属于 Rn\mathbb{R}^n。由于系数为零,向量 v\boldsymbol{v} 没有贡献,而 u\boldsymbol{u} 的每个坐标都加倍。

拼接并不把对应坐标相加;它把 u\boldsymbol{u} 的全部 nn 个坐标放在 v\boldsymbol{v} 的全部 nn 个坐标旁边,产生一个有 2n2n 个坐标的对象。拼接保留两个输入块并改变形状;线性组合则把它们的缩放贡献合并到原坐标空间中。它们回答的是不同的表示问题。

资料来源

  1. Mathematics for Machine Learning companion website
  2. Mathematics for Machine Learning book PDF
  3. Attention Is All You Need
  4. NumPy documentation: numpy.sum
  5. NumPy documentation: broadcasting