线性代数 入门
读懂任何 Transformer 图示之前所需的最少线性代数。9 个短小主题,涵盖向量、矩阵、它们的基本运算、 点积、矩阵乘法、余弦相似度、范数,以及在注意力中无处不在的转置 / reshape。 除高中代数外,不假定任何先验数学。
向量(Vector)
一串有序的数字 —— 而且指向某个方向。
其实你早就知道向量是什么 —— 只是没这么叫它。当你给别人留地址 (中山路 123 号 4B 室) 时,这就是一串有序的列表。顺序很关键:"中山路 123 号的 4B 室"说得通, 把字段打乱就完全讲不通了。顺序本身就编码了含义。
屏幕上选颜色时,(255, 0, 0) 是亮红色,(0, 255, 0) 是亮绿色。同样三个数字, 换个顺序就是完全不同的颜色。GPS 坐标 (37.78, −122.42) 是旧金山;两个数字交换一下, 你就跑到南极去了。顺序就是含义。
数学里的向量,就是把这个思路形式化:一串有序的数字,每个位置都有特定含义。 每个数叫一个 分量(component);分量的个数叫向量的 维度(dimension)。
教科书记法:向量写作 v = (v₁, v₂, …, vₙ) 或 v = [v₁, v₂, …, vₙ] —— 也常用粗体 v。一个 n 维实数向量, 生活在 ℝⁿ 这个空间里,写作 v ∈ ℝⁿ。我们例子里的 [3, 4] 就在 ℝ² 中。
v = [3, 4]。每个格子是一个分量;格子的位置很重要。但向量不只是带标签的盒子 —— 它还有几何意义。把每个分量当作坐标,向量就变成了空间中的一个点。 按惯例,我们再把它画成 从原点出发的箭头,指向那个点。
箭头这种表示揭示了向量的魔法。一个向量同时携带 方向(指向哪)和 大小(多长) —— 这就是有方向的量。说"走了 100 米"只是个数字, 没人知道你到了哪;说"向东走了 100 米"就是一个向量,精确告诉别人你的终点。
稍加留意,向量在物理和日常生活中无处不在:
- 风:从西南方向吹来的 20 mph = 一个二维速度向量。
- 力:朝门口推沙发的 50 N = 一个三维力向量。
- RGB 颜色:屏幕上每个像素都是一个三维向量(红、绿、蓝)。
- 你的一天:
(睡眠小时数, 工作小时数, 锻炼小时数)—— 一个三维向量。 - 一首歌:每秒 44100 个音频采样 = 一个 N 维向量(N 大得吓人)。
同一平面上还可以有第二个向量:
v 和 w 共享同一个坐标系。比较它们、相加、缩放 —— 后续每一节都是在这样成对(或成网格)的向量之上做运算。向量真正的威力体现在高维。我们这里画二维只是为了直观,但本节涉及的所有运算 —— 加法、缩放、点积 —— 都可以推广到 任意 维度。线性代数让我们能对看不见的高维空间做几何推理。
在 Transformer 里:每个 token 都对应一个几百到几千维的向量。 单词 cat 可能变成 [0.42, −0.17, 0.88, …] (GPT-2 用 768 个数,最大的开源模型超过 16000)。dog 对应一个不同但相似的向量。模型从来不"看见"这个词 —— 它只看见这些数字,并通过它们之间的规律学习。cat 和 dog 在高维空间中彼此靠近,正是模型"知道"它们语义相关的方式。一个 LLM 全部的智能, 都藏在每个 token 在这个向量空间里的位置上。
矩阵(Matrix)
一张二维数字网格 —— 或者等价地说,一摞向量。
和向量一样,你早就用过矩阵 —— 每张电子表格都是矩阵:学生为行、考试成绩为列的成绩表; 星期为行、时间段为列的周历。把数码照片放到最大,也是一个矩阵 —— 每个格子保存一个像素的亮度(RGB 的话就是三个矩阵叠在一起)。
正式定义:矩阵是一张二维数字网格。向量只有一个维度(长度),矩阵有两个:行 和 列。记号上始终是"行 × 列" —— 一个 2 × 3 矩阵有 2 行 3 列,不能颠倒过来说。
教科书记法:一个 R × C 的实矩阵写作 M ∈ ℝR×C。 位于第 i 行、第 j 列的元素记作 Mij (在代码里通常是 M[i][j])。
这种"既能按行读、也能按列读"的特性,就是矩阵的隐藏超能力。同一张网格,从不同方向切, 含义可以截然不同。
想象一家小电商,它的销售矩阵长这样:
周一 周二 周三 T 恤 4 7 2 马克杯 1 0 5 书 3 2 1
按行读,每一行是一种商品一周的销量(T 恤 4 + 7 + 2 = 13 件)。 按列读,每一列是某一天所有商品的销量(周一 4 + 1 + 3 = 8 件)。 一张网格,两种视角都对;具体用哪种,取决于你想回答什么问题。
矩阵还能做向量做不到的事:表示变换。本质上,一个矩阵是一个 函数 —— 喂给它一个向量,它返回另一个向量。具体怎么算我们在第 06 节(矩阵乘法)细说, 但关键直觉是:旋转、缩放、错切、投影 —— 几乎任何你想对向量做的线性操作, 都可以编码成一张整整齐齐的数字网格。
所以矩阵在图形学、物理、机器学习里到处都是:
- 3D 游戏里旋转模型 = 把每个顶点乘上一个 3×3 旋转矩阵。
- 调整照片大小 = 把它的像素矩阵乘上一个缩放矩阵。
- 神经网络的一层 = 把输入向量乘上一个学到的权重矩阵。
- 搜索引擎把上百万篇文档存成一个巨大矩阵的若干行。
- 马尔可夫链用"转移矩阵"预测下一状态。
在 Transformer 里:几乎每个学到的参数都在某个矩阵里。embedding 矩阵的形状是 V × D(V ≈ 50000 行,每行 D 个学到的数); 注意力中的投影矩阵 WQ、WK、 WV 各自是 D × D,负责把 token 向量 投影到 Query / Key / Value 三个空间。 一个 70 亿参数的模型,本质上就是若干张大矩阵 + 它们如何相乘的规则。
加减(Add / Subtract)
把两个"有方向的量"合并成一个。
两个向量相加,和你直觉里想的一模一样。
想象你向东走 3 个街区,然后转弯向北走 4 个街区。 你最后到了哪里?不是分开的"东 3 街区 + 北 4 街区",而是一个具体的新位置: 相对起点,东 3 北 4。这个唯一的终点,就是两个步行向量的和。
规则很简单:逐分量相加。两个向量必须有相同的维度 (你不能把二维步行向量加到三维上,就像不能把纬度加到价格上)。 若 u = [3, 1] 而 v = [1, 2],则它们的和是 [3+1, 1+2] = [4, 3]。
教科书记法:对任意下标 i, (u + v)i = ui + vi。 两个向量必须在同一个空间 —— 形式化地说,u, v ∈ ℝn 意味着 u + v ∈ ℝn。
算术是容易的部分,真正给你直觉的是几何。把 v 的"尾巴"接在 u 的"头"上,从原点指到 v 新箭头尖端的箭头,就是 u + v —— 这就是"首尾相接"法则,和你在地图上描自己走过的路线完全一样。
u 和 v;按分量相加得 [3+1, 1+2] = [4, 3]; 再把 v 的尾巴接在 u 的头上 —— 从原点指到新尖端的箭头就是 u + v。 两支原箭头加这个和,组成一个三角形(若四支都从原点画出,则是平行四边形)。这不只是抽象的数学游戏。水手、飞行员、气象员每天都在用向量加法: 一艘以 10 节朝东北航行的帆船,遇到 3 节朝南的洋流,它实际的航向和航速, 正是这两个向量"首尾相接"之后的合向量。飞行员把这叫"对地航迹 vs 机首航向", 搞错了飞机就会飞到错误的国家。
减法是同一套思路,翻个符号而已。u − v 可以理解为 "从 v 出发,要加上什么向量才能到 u" —— 等价地,"两者所指方向的差"。
u − v = [3−1, 1−2] = [2, −1]。逐分量,允许负数。减法在哪里悄悄地做着大事?任何时候你想问"这两个东西差多少?", 答案都是一个减法。两种 RGB 颜色?相减,结果告诉你每个通道差多少。 两个玩家的技能画像?相减。差向量的长度告诉你这两个玩家到底有多不一样。
在 Transformer 里:残差连接(residual connection) —— 现代深度学习里最重要的架构技巧之一 —— 就是向量加法。一层的输出会被加回到它的输入: xout = xin + Layer(xin)。 这就是上面讲的逐分量相加。正因如此,信息才能干净地传过 100+ 层而不被噪声淹没。 没有残差连接,深层 Transformer 根本训练不起来。
标量乘法(Scalar Multiplication)
拉长、翻转、缩短 —— 同向不同大小。
标量(scalar) 就是一个数(相对的,向量是一串数)。 英文 scalar 来自 "scale"(缩放):标量告诉你把向量缩放多少倍。 把一个向量乘 2,它就变长一倍(方向不变);乘 0.5 变成一半;乘 −1 翻转方向、长度不变。
规则非常直接:每个分量都乘上这个标量。以 v = [2, 1] 为例:2v = [4, 2]、−v = [−2, −1]、0.5v = [1, 0.5]。
教科书记法:对任意标量 α ∈ ℝ 和向量 v ∈ ℝn, 乘积按分量定义为 (αv)i = α · vi。
2v 长度变两倍;−v 方向翻转;0.5v 长度减半。方向保持(或完全反向),只有长度在变。其实你已经做过几千次标量乘法,只是没这么叫它:
- 加倍菜谱。原料向量
(2 个鸡蛋, 1 杯面粉, 0.5 杯牛奶)乘 2, 就成了(4 个鸡蛋, 2 杯面粉, 1 杯牛奶)。所有分量一起缩放。 - 调节音量。音频波形就是一长串采样值。整个向量乘 0.5 = 变小声; 乘 2 = 变大声(忽略削顶)。
- 照片缩放。每个像素的坐标都乘上缩放系数。放大 2 倍,每个像素离中心都远了 2 倍。
- 方向反向。汽车的速度向量乘 −1 = 同样速度但反向(以同样的速度倒车)。
三个要点:
- 正标量保持方向;长度按标量大小缩放。
- 负标量反转方向;长度按绝对值缩放。
- 乘 0 把向量塌缩到原点(长度 0);乘 1 不变。
标量乘法是这篇入门里最简单的运算,但别因此小看它的重要性。 训练每个神经网络的算法 —— 梯度下降 —— 每一次迭代,本质都是一次标量乘法。 机器学习里反复出现的"学习率(learning rate)",就是把梯度向量按比例缩放、然后从权重中减去的那个标量:
new_weights = old_weights − learning_rate · gradient
学习率太大,训练就会震荡发散;太小,就慢得像爬。 调好这一个标量,是机器学习实践中最常见的任务之一。
在 Transformer 里:除上面提到的学习率更新之外,标量乘法还在多个关键步骤中工作。softmax 温度会在归一化前缩放 logits(我们会在 Transformer 章节遇到 softmax);注意力缩放把点积分数除以 √dk,以便模型维度变大时分数依然稳定;梯度裁剪那种"按最大范数缩放"的操作,也是一次标量乘法。 任何你看到"把整个东西乘上一个数"的地方,都是标量乘法在干活。
点积(Dot Product)
深度学习里用得最多的运算 —— 没有之一。
这篇入门里如果你只能记住一件事,就记住点积。 后面的所有概念 —— 注意力、相似度、投影、矩阵乘法 —— 都不过是点积换了张脸。
做法非常简单:取两个相同维度的向量,对应位置相乘,然后求和。 结果是一个数。这就是全部定义。
教科书记法:用求和符号写
u · v = ∑i=1n ui vi
部分教材会把点积写成 ⟨u, v⟩ —— 一回事,也叫内积(inner product)。
u = [3, 4]、v = [2, 1]。配对相乘得 [6, 4],求和 6 + 4 = 10。 所以 u · v = 10。点积乍看没什么特别。但它悄悄回答了一个非常有力的问题:这两个向量有多"对齐"?
想象你和一个朋友各自填一份"电影偏好"问卷,每种类型打 0–5 分:
动作 喜剧 爱情 恐怖 科幻 你 5 3 1 2 4 A 朋友 5 3 0 3 4 ← 跟你合拍 B 朋友 0 2 5 0 1 ← 跟你相反
分别算你和两位朋友的点积。正例 —— 和 A:5·5 + 3·3 + 1·0 + 2·3 + 4·4 = 56。反例 —— 和 B:5·0 + 3·2 + 1·5 + 2·0 + 4·1 = 15。
A 的数大,是因为他/她的高分类型和你的高分类型几乎重合(同一行同时是大数字)。 B 的数小,是因为他/她打高分的类型恰好是你不在意的,而你打高分的类型他/她基本没兴趣。同一个运算,两种结果:一个数,一步就告诉你"谁能跟你看同一部电影"。 推荐系统的最初版本,基本就是这么干的。
为什么这个简单的运算能做到这件事?关键全藏在乘法里。 乘积 a · b 只有在 两个数都大、而且同号 的时候才会大 —— 乘法在每个维度上就像一个"与门(AND-detector)"。看一下"动作"那一列:你和 A 都打 5,5 · 5 = 25,大大贡献了总和;B 打 0,5 · 0 = 0,毫无贡献。 只有双向一致才能加进很多。
把所有维度一加,你其实是在数出"两人意见一致的次数"。 数越大,两个向量越对齐。整个机制就这么简单 —— 而且能推广到任意维度, 这就是为什么同样一个运算,既能给你"电影合拍度"打分,也能给 LLM 里的注意力打分。
点积还有一个干净的几何含义:
u · v = ‖u‖ · ‖v‖ · cos(θ)
双竖线 ‖v‖ 表示"v 的长度" —— 我们在第 08 节会正式给出它的名字,叫范数(norm)。
所以点积把三件事打包成一个数:u 多长、v 多长、它们方向有多接近。 同向 → 一个大正数。垂直 → 正好是 0。反向 → 一个大负数。
垂直的情况最值得记:两个垂直向量的点积恰好为零, 它们在"方向"层面没有任何共同信息 —— 如果你的偏好向量和某人完全垂直, 你俩的口味就毫无交集。这个结论是几次乘法和几次加法直接推出来的, 在数学上非常强。
一旦你把点积理解成"两个东西有多对齐",它的应用就豁然开朗:
- 3D 图形里的光照。一个表面的亮度 = "光线方向"和"表面法向"的点积。 正对光源 = 亮;侧着 = 暗;背着 = 黑(或 0)。
- Google 搜索相关度。你的查询是一个向量,每篇文档也是一个向量。 点积给每篇文档打一个"和查询有多对齐"的分。
- 垃圾邮件过滤。邮件特征是向量,模型学了一个"垃圾方向"向量。 点积大正 = 大概率是垃圾邮件。
在 Transformer 里:这就是注意力分数(attention score)。 每个"这个 token 应该对那个 token 关注多少?"的问题,都用一次点积回答 —— 具体是 Query 向量和 Key 向量之间的点积。点积大 → "大力关注这个 token"; 接近 0 → "忽略";负 → "主动压制"。把数十亿次这种点积横跨很多层、很多头堆起来, 就是 GPT。
矩阵乘法(Matrix Multiplication)
一次性做好多个点积 —— 现代 AI 的主力运算。
理解了点积之后,矩阵乘法基本就是记账。把一个 R 行的矩阵 M 乘以一个向量 v, 得到一个有 R 个分量的新向量 —— 每个分量都是 M 某一行和 v 的一次点积。就这么简单。
M · v:逐行点积。第 0 行 → 17;第 1 行 → 39。所以 Mv = [17, 39]。这有什么用?这正是让一个矩阵能把一个向量变成另一个向量的机制 —— 旋转、拉伸、投影到某个平面、或者任意其它线性变换。 第 02 节提到过矩阵相当于一个函数,具体怎么算的就是这一节的乘法。
举个具体的例子。你还经营着第 02 节的小电商(T 恤、马克杯、书),每个商品有单价:
prices = [ T 恤 20, 马克杯 8, 书 15 ] # 一个向量 sales = 周一 周二 周三 T 恤 4 7 2 马克杯 1 0 5 书 3 2 1 # 一个 3×3 矩阵
要算每天的营业额,只需把 prices 乘上销量矩阵:
周一营业额: 20·4 + 8·1 + 15·3 = 133 周二营业额: 20·7 + 8·0 + 15·2 = 170 周三营业额: 20·2 + 8·5 + 15·1 = 95
三次点积,三个答案,一次操作。这就是矩阵乘法 —— 它让你一口气做完一整批点积,不管行和列代表什么(对齐度、加权求和、投影,都行)。
矩阵 × 矩阵。同一个套路,只是右边并排放了多个"v"。 若 M 是 R × K、N 是 K × C,则 MN 是 R × C, 由 R · C 次独立的点积算出。两个 K 必须相等 —— 这就是新手最容易踩的"形状规则"。
教科书记法:MN 在第 i 行、第 j 列的元素为
(MN)ij = ∑k=1K Mik Nkj
—— 正是 M 的第 i 行与 N 的第 j 列的点积。
矩阵乘法在很多地方都在,只是常常看不见:
- 3D 游戏。每一帧,每个模型的所有顶点都要乘上"相机矩阵", 以决定它们最终出现在屏幕的哪里。每秒几千万次矩阵乘法。
- PageRank。Google 最初的算法,本质就是把一个巨大的"链接矩阵" 自乘很多次直到收敛。
- 图像滤镜。锐化、模糊、边缘检测 —— 都是用一个小矩阵(滤波核) 和图像的局部相乘。
- 卷积神经网络。每一层都把图像的局部块和学到的滤波器矩阵相乘, 逐层检测边缘、纹理、形状。识别一张照片要用掉万亿次乘法。
GPU 之所以那么快。现代 GPU 几乎是专门为矩阵乘法造的。 因为输出矩阵的每个格子都独立 —— 完美适合并行硬件 —— 它们可以同时跑成千上万次点积。一块消费级 GPU 一秒能做几万亿次乘加。 我们能训练得起万亿参数模型,就是因为矩阵乘法完美映射到 GPU 硬件上。
在 Transformer 里:矩阵乘法就是 Transformer。 一层至少包含:一个 matmul 把 token 投影成 Q、一个投影成 K、一个投影成 V、 一个把注意力加权的输出合并起来、两个是 feed-forward 块。 最后再加一个 matmul 把隐藏向量投影回 vocab logits。把这样的层堆叠 32 到 80 个 —— 就是一个现代 LLM。训练和推理的加速,基本就是工程上"如何从同样的硬件里榨出更多 matmul" 的故事。
余弦相似度(Cosine Similarity)
只看方向,把长度归一化掉。
回忆一下点积。它混合了两件事:向量有多长和它们有多对齐。 很多时候我们只关心"对齐",想把长度因素去掉。这就是余弦相似度要做的事。
让我们把电影例子改得更现实一点。假设你是个电影发烧友,用 0–10 评分; 而朋友 A 和 B 仍然沿用第 05 节的 0–5 评分。三个向量是:
动作 喜剧 爱情 恐怖 科幻 你 (0–10) 10 6 2 4 8 A 朋友 5 3 0 3 4 B 朋友 0 2 5 0 1
基于这份数据算点积:
你 · A = 10·5 + 6·3 + 2·0 + 4·3 + 8·4 =112你 · B = 10·0 + 6·2 + 2·5 + 4·0 + 8·1 =30
和第 05 节一样,A 还是更合拍的那位 —— 但每个数的一半都来自你的尺度更宽, 而不是口味真的发生了变化(对比第 05 节的 56 和 15:都翻了一倍)。 原始点积把"口味多对齐"和"打分多大"混在一起,我们分不清哪部分是哪部分。
办法:把点积除以两个向量各自的长度。
cos_sim(u, v) = (u · v) / (‖u‖ · ‖v‖)
这就把尺度消掉了,只剩下方向。因为 u · v = ‖u‖ · ‖v‖ · cos(θ),除以两个长度 恰好把它们消掉,剩下 cos(θ) —— 两个向量夹角的余弦。 结果永远落在 [−1, 1] 之间,跟评分尺度无关。
算出三个向量各自的范数(也就是第 05 节里说的长度):
‖你‖ = √(10² + 6² + 2² + 4² + 8²) = √220 ≈ 14.83 ‖A‖ = √(5² + 3² + 0² + 3² + 4²) = √59 ≈ 7.68 ‖B‖ = √(0² + 2² + 5² + 0² + 1²) = √30 ≈ 5.48
代入:
cos_sim(你, A) = 112 / (14.83 × 7.68) ≈0.98 —— 几乎相同的口味。cos_sim(你, B) = 30 / (14.83 × 5.48) ≈0.37 —— 有点交集,但相差挺大。
现在数字本身就有含义:0.98 是"口味几乎一致",0.37 是"勉强合拍"。 更妙的是:如果你换回 0–5 评分(每个分量除以 2,回到第 05 节的数据), 这两个余弦会落在完全相同的 0.98 和 0.37。 余弦把尺度扔掉,只保留方向 —— 这个性质叫 尺度不变性(scale-invariance)。
实际应用几乎无处不在:
- 文档检索。每篇文档和每条查询都被表示成一个向量(词分布或语义 embedding)。 余弦相似度按"主题对齐程度"给文档排序。长文档和短查询之间也能公平比较, 因为长度被归一化掉了。
- Spotify 和 Netflix 推荐。你的口味画像是一个向量,每首歌或每部电影 也是一个向量。推荐大致就是"你还没看过/听过、余弦相似度最高的那些"。
- 抄袭检测。两份文档的向量若 cos_sim 接近 1,说明它们的词汇结构非常相似。 完美改写抓不到,但其它情况大多能命中。
- 人脸识别。每张脸都被转成 128 维或 512 维的"人脸 embedding"。 比较两张脸 = 算它们 embedding 的余弦相似度。超过阈值就判定匹配。
在 Transformer 里:注意力分数严格来说是点积、不是余弦, 但网络在训练中学会把注意力向量保持在相似尺度,所以这些点积在表现上跟余弦相似度非常接近。 模型就是靠它来"判断"哪些先前 token 跟当前新 token 最相关。 余弦相似度也撑起了 RAG(检索增强生成)的"检索"那一半: 一整个产业(Pinecone、Weaviate、Qdrant、pgvector、Chroma 等等)就是为了存几十亿个 embedding 向量、并大规模回答"哪个最像这个查询?"而诞生的。
范数 / 长度(Norm)
勾股定理 —— 推广到任意维度。
小问题:你向东走 3 个街区,再向北走 4 个街区,你离起点多远?不是 7 个 —— 你没有绕远。你走的是一个直角边为 3 和 4 的直角三角形的斜边,长度 √(3² + 4²) = √25 = 5 个街区。这条斜边就是向量 [3, 4] 的范数(norm)(也叫长度),记作 ‖v‖(双竖线)。
全部思想就在这。一个向量的范数就是它的几何长度 —— 从原点到箭头尖端的距离。 公式就是把勾股定理用到你恰好有多少个分量上。
D 维的情况公式扩展得很顺手:
‖v‖ = √(v₁² + v₂² + … + vD²)
把每个分量平方,加起来,开方。完事。2D、3D、100D、12288D —— 完全一样的流程。
放到第 05–07 节的电影偏好上,这个数字意味着什么?用原始的 0–5 评分数据, 算出每个人的范数:
‖你‖ = √(5² + 3² + 1² + 2² + 4²) = √55 ≈7.42‖A‖ = √(5² + 3² + 0² + 3² + 4²) = √59 ≈7.68‖B‖ = √(0² + 2² + 5² + 0² + 1²) = √30 ≈5.48
这些数字可以理解成整体偏好强度 —— 一个人在所有类型上的"打分总能量"。A 的范数最大,因为他/她在好几个类型上都打了高分;B 的范数最小:大多数都是 0,只有"爱情"那一项很高,总和就小;你夹在中间。同一份向量,换个视角看:余弦问的是"这两个向量对齐 吗",范数问的是"这个向量有多大"。
换回第 07 节里那个 0–10 评分的"电影发烧友"版的你,范数刚好翻倍 —— 不是因为口味变了,而是评分尺度变了。范数捕捉的是大小,余弦忽略大小。
为什么这么重要?因为"这个东西有多大?"无处不在, 而范数就是回答它的方式:
- GPS 距离。两个坐标之间的直线距离,就是它们差向量的范数。
- 音量大小。一段音频信号的"响度"大致就是其采样向量的范数。
- 比较两张照片。把两张图的像素矩阵相减,差向量的范数告诉你它们视觉上差多少。
- 物理中的速度。速度向量的范数就是速率;力向量的范数就是力的大小。
还有个常用技巧叫归一化(normalization):把向量除以它自己的范数, 得到的新向量方向不变、长度刚好为 1(称为"单位向量")。 当你只关心方向时非常有用;余弦相似度用的是同样的思路 —— 只不过它是直接用两个范数同时去除点积,而不是先把每个向量归一化。
严格说,我们刚定义的是 L2 范数(也叫欧几里得范数),有时为明确而写成 ‖v‖2。还有其它范数 —— ‖v‖1 是分量绝对值之和;‖v‖∞ 是分量绝对值最大者;等等。 但在深度学习里,如果没有特别说明,"范数"几乎都默认是 L2。
在 Transformer 里:LayerNorm 和 RMSNorm —— 现代 Transformer 里几乎层层都有的归一化模块 —— 都在计算一个范数然后除以它, 把每个 token 向量保持在大致一致的尺度上,让后续运算更稳定。梯度裁剪(每个 PyTorch 训练循环里的常客 torch.nn.utils.clip_grad_norm_) 把梯度向量的范数限到一个上限,以防 loss 曲面陡峭时训练爆炸。还有, 刚学过的余弦相似度,字面上就是"点积除以两个范数"。
转置与 reshape(Transpose & Reshape)
同一组数,换个形状。
想象一张"学生 × 试卷成绩"的电子表格。有时你想要某个学生在所有考试中的成绩 —— 那是一行; 有时你想要某次考试所有学生的成绩 —— 那是一列。数据没变,变的只是你怎么读它。转置(transpose) 就把这件事正式化:让矩阵沿对角线翻转, 行变列、列变行。记作 MT。
具体例子。回到第 02 节的店铺销售矩阵:
周一 周二 周三 T 恤 4 7 2 马克杯 1 0 5 书 3 2 1
行 = 商品,列 = 日期。要回答"哪天销售总额最大?",得一列一列扫、边扫边加,挺别扭。把它转置之后,同样这些数变成 行 = 日期、列 = 商品:
T 恤 马克杯 书 周一 4 1 3 周二 7 0 2 周三 2 5 1
现在"周二销量"就是一整行,横着加就完事。同样 9 个数字,两种访问方式; 下一步运算需要哪种,转一下就到位。
教科书记法:若 M ∈ ℝR×C,则 MT ∈ ℝC×R,元素满足 (MT)ij = Mji—— 行和列下标互换。一个常用恒等式:(AB)T = BTAT(乘积转置,顺序反过来)。
M(2 × 3) ⇄ MT(3 × 2)。M 的每一行变成 MT 的对应列。追一个元素验证一下:M 中 2 在第 0 行第 1 列,转置后那个 2 跑到了 MT 的第 1 行第 0 列 —— 行下标和列下标对调了。每个元素都同时执行这种对调。 对角线上的元素(行下标等于列下标)位置不变,其余的都沿对角线翻折。
为什么我们常常要转置?两个原因。
第一,形状对齐。矩阵乘法只有在内层维度相等时才成立:(R × K) 乘 (K × C) 可以,但 (R × K) 乘 (R × C) 不行。如果两个矩阵差一点就能相乘、只是方向不对,把其中一个转置就修好了。
实际例子:注意力分数。Transformer 里,Q(queries)和 K(keys)都是 (N 个 token × D 个特征) 的形状。模型想算的是: 每个 query 行 和 每个 key 行 之间的点积 —— 形成一张 N × N 的"对齐度"网格。 但矩阵乘法做的是 行 · 列,不是 行 · 行。把 K 转置, 让它的行变成列,然后一次 Q · KT matmul 就把全部 N × N 个点积一口气算完。没有转置,形状根本对不上。
第二,转置基本上是免费的。数字并不真的搬来搬去 —— GPU 只是给"行轴"和"列轴"换了个标签。零成本就能修好一大类"形状不匹配"错误,堪称白送。
Reshape 更一般。它把一段数字按"总数不变"的前提,重排成任意新形状。 2×3 矩阵(6 个数)可以变成 1×6 的行、6×1 的列、3×2 的矩阵,或者任何元素数相同的其它形状。 数据一点没变,只是"如何寻址"变了。
想象一副 52 张牌。摆成 4 个花色 × 13 个点数,你查一张牌的方式是"红桃,7"。 换成 13 个点数 × 4 个花色,就是"7,红桃"。牌本身没动 —— 变的只是"先按哪个维度查"。Reshape 对一组数做的就是这件事。
[1, 2, 3, 4, 5, 6],两种 reshape 结果:1×6 行 与 6×1 列。Reshape vs. 转置 —— 一个微妙但很重要的差别。 Reshape 保留数字的线性顺序,只重新分组。矩阵 M = [[1, 2, 3], [4, 5, 6]] reshape 成 6 个元素是 [1, 2, 3, 4, 5, 6](按行读),再 reshape 成 3×2 是 [[1, 2], [3, 4], [5, 6]] —— 不等于 MT(M 的转置应该是 [[1, 4], [2, 5], [3, 6]])。Reshape 只是重新分组, 转置真的把元素挪了位置。混淆这两者是机器学习代码里最常见的 bug 之一。
Reshape 听起来似乎没什么了不起 —— 不就是换个标签吗?但它是现代 AI 里一个核心思想的关键:多头注意力(multi-head attention)。
诀窍就是一行代码:x.reshape(N, 12, 64)。GPT-2 里每个 token 的向量是 D = 768 维。这一行把这 768 个数重新解释为 12 组、每组 64 个 —— 这些组就是"头(head)"。 模型在 12 个头上并行跑 12 个独立的注意力。每个头可以专注不同方向: 有的关注语法,有的追长距离依赖,有的负责单复数一致性。 12 个头算完之后,x.reshape(N, 768) 再把它们拼回单一的 768 维向量,继续往下走。
Reshape 一个数据都没改,只改变了"下一步操作怎么切分它"。 但就是这一个小动作 —— 在 12 个"切片视图"上跑注意力而不是在整体上 —— 贡献了 Transformer 能力的相当一部分。
Reshape 还活跃在很多地方:
- 图像处理。28×28 灰度图就是 784 个数。要喂给全连接层时, 把
28 × 28reshape 成784 × 1。同样的像素,不同的排布。 - 批量训练。16 张 28×28 的图被堆成
16 × 28 × 28的张量 (即 3D 矩阵),好让 GPU 并行训练。 - 卷积。图像的一个"patch"被 reshape 成向量,再和滤波器矩阵相乘。 Reshape → matmul → reshape 回去,周而复始。
在 Transformer 里:除了上面那个标志性的多头注意力之外, 转置在任何需要让形状对齐的 matmul 中都会出现 —— 包括注意力的核心步骤 Q · KT,其中 K 的行必须变成列,点积才能算起来。