02 课 · 向量与张量
LESSON 02 · 卷二 数学地基

万物皆可向量

计算机不懂「猫」,也不懂「国王」——它只认识数字。 这一课,我们从零开始发明「向量」:现代 AI 描述万物的通用语言。 你会亲手算出「国王 − 男人 + 女人 = 女王」, 再学会向量的加减乘除、点积、余弦相似度和归一化——「像不像」的完整尺子, 以及检索系统的起手式。 最后认识大模型世界的集装箱——张量。全程只需要加减乘除。

STAGE 01
一根数轴
只用一个数描述世界,以及它的翻车现场
STAGE 02
一张地图
两个数:向量诞生,「像不像」变成距离
STAGE 03
词的地图
语义变成几何,算术搬出女王
STAGE 04
向量运算
加减乘除、点积、余弦、归一化——「像不像」的完整尺子
STAGE 05
张量集装箱
从一个数,到 7B 模型的一整箱张量
第 1 站

计算机眼里没有猫

假设你在写一个程序,希望它能回答:「猫、老虎、仓鼠——这三种动物,哪两个更像?」

你脱口而出:猫和老虎。理由也许是「都是猫科」「都毛茸茸」「都会扑猎物」。 但问题来了:计算机不懂「猫科」,不懂「毛茸茸」,它只认识数字。你没法把一只猫塞进 CPU——你只能给它数字。

如果只允许用数字向计算机描述一只动物,你打算怎么描述?先别往下翻,真的想 30 秒。
第 2 站

最小方案:一根数轴

能偷的最大懒,是只用一个数字:给每种动物打一个「体型分」,从 0 到 1。 仓鼠 0.04,猫 0.25,狗 0.45,鳄鱼 0.50,狼 0.72,老虎 0.90。 它们全部住进一根数轴:

动手实验体型数轴 —— 点选两只动物,看它们的距离
体型0.000.250.500.751.00仓鼠0.040.250.45鳄鱼0.500.72老虎0.90
👆 点选两只动物,数轴会告诉你在「只看体型」的世界里它们差多远。(选满两只后再点第三只,会替换最早选的那只)
先试试 猫 vs 老虎,再试试 猫 vs 仓鼠。按「只看体型」的规则,哪个更近?
图 2-1 · 把每种动物压成一个「体型分」,它们就住在同一根数轴上

神奇的事情发生了:「像不像」突然变成了可以计算的东西。两个数离得越近,就越「像」。整个判断只需要一次减法。

但等一下——按这个算法,猫和仓鼠(差 0.21)比猫和老虎(差 0.65)更像? 好像哪里不对,但先别急着推翻它。这个方案至少做对了一件大事:它把「比较两个事物」变成了「比较两个数字」。这个思想我们要保留。真正的问题出在——维度不够。

第 3 站

狗和鳄鱼成了近亲

再看一眼数轴:狗 0.45,鳄鱼 0.50,只差 0.05——全场最小。

在「一个数字」的世界里,狗和鳄鱼是天造地设的一对——只因为它们体重差不多。 把一只动物压缩成一个数字时,性格、习性、危险程度……全被扔掉了。

不推翻「用数字描述」这个思路,你能想到的最小修补是什么?
第 4 站

再加一根轴:从数轴到地图

给每只动物两个(体型, 凶猛):第一个当横坐标,第二个当纵坐标。 它们就从一根线,搬到了一张平面地图上。

动手实验动物地图 —— 拖动「?」,看谁离它最近
0.000.000.250.250.500.500.750.751.001.00体型 →凶猛 ↑横差 0.25纵差 0.15仓鼠鳄鱼老虎?
神秘动物 = [0.70, 0.35] —— 离它最近的是 (距离 ≈ 0.29
勾股分解:距离 = √( 横差² + 纵差² ) = √( 0.25² + 0.15² ) ≈ 0.29
拖动「?」:你把它放在哪,就等于宣布了它的两个数字;「谁最像」,只是两点间的距离。
「?」是一只神秘动物。你把它拖到哪里,就等于宣布了它的两个数字—— 而「谁和它最像」,只是两点之间的距离。注意狗(0.45, 0.20)和鳄鱼(0.50, 0.90): 横轴上还贴着,纵轴把它们狠狠拽开了。
图 2-2 · 加上第二个维度后,狼、鳄鱼、老虎自然聚成了右上角的「猛兽区」
数学小白也不怕 · 两点的距离怎么算?

横差记作 a,纵差记作 b,距离 = √(a² + b²)——读作「根号下 a 方加 b 方」。 拿狗和鳄鱼练手:

横差 a = 0.45 − 0.50 = 0.05  纵差 b = 0.20 − 0.90 = 0.70
距离 = √(0.05² + 0.70²) = √(0.0025 + 0.49) ≈ 0.70

√(开根号)就是找一个「自己乘自己等于它」的数:因为 0.7 × 0.7 = 0.49,所以 √0.49 = 0.7。 这个套路有个大名:勾股定理。三维?三项平方相加。一万维?一万项相加—— 公式一个模子,数学从不挑食。

现在,请注意你刚刚做了什么——你把一只动物写成了一组有顺序的数字

狗 = [ 0.45 , 0.20 ] // 第 1 个数是体型,第 2 个数是凶猛

这组有顺序的数字,数学上就叫向量(vector)。数字的个数叫维度(dimension)——我们刚造的是 2 维向量。 同一个向量有三副面孔,说的是同一件事:

一列数字
计算机看到的:[0.45, 0.20],两个数,仅此而已。
平面上的一个点
我们画地图时看到的:横 0.45、竖 0.20 的那个位置。
一支箭头
从原点 (0, 0) 指向那个点。下一站做加减法时,它是最好用的视角。
第 5 站

国王 − 男人 + 女人 = ?

动物能变成向量,那「词」呢?别忘了我们的目的地是大语言模型——它的原料是词。 照样打分,两个维度:性别(男 0 → 女 1)、皇家气质(平民 0 → 皇家 1):

核心实验词的地图 —— 一步一步算出「国王 − 男人 + 女人」
0.000.000.250.250.500.500.750.751.001.00性别(男 → 女)→皇家 ↑男人女人王子公主国王女王
选好一个算式,点「下一步」—— 我们把每一步都画在这张词的地图上。横轴是性别(男 → 女),纵轴是皇家气质(平民 → 皇家)。
国王 − 男人 + 女人 = ?
换一个算式再玩一遍。你会发现:不管哪组词,减法提取箭头,加法平移箭头—— 向量的加减法,是特征的搬运工。
图 2-3 · 横轴是性别,纵轴是皇家气质;箭头=差向量=「从一个词走到另一个词」的路径

看清楚刚才发生的事,一共只有三步:

  • 相减,提箭头:「国王 − 男人」= [0.00, +0.82]——性别纹丝不动,皇家气质 +0.82。这支箭头就是「封王」的方向。
  • 平移,再相加:把「封王箭头」原封不动搬到「女人」脚下,一加——
  • 落地,找近邻:落点 [0.90, 0.92],全地图最近的词是:女王

向量加减从来不是玄学:减去「男人」,是把男性方向抵消掉;加上「女人」,是把女性方向搬进来。语义的每一步移动,背后都是数字的加减。

动物的「像」是距离,
词语的「义」是几何
真相时刻 · 这不是玩具,这是 2013 年的真发现

2013 年,Google 的 Mikolov 团队在 word2vec 模型里发现: 用真实语料训练出的词向量空间中,king − man + woman 的最近邻真的是 queen。 没有任何人手工打分——所有数字都是模型从海量文本里自己学出来的

那「自己学」到底是怎么操作的?机制朴素得惊人:模型不停地猜下一个词, 猜错了就回头把每个数字朝「猜对的方向」拧一丁点。拧上几百万次,数字自己排好了队。 这一「拧」就是训练——拧多大、往哪拧,我们留到第 10 课「损失函数与梯度」亲手算。

真实空间也不是 2 维,而是 300 维起步;GPT-3 里每个词是 12288 维。 维度画不出来,但公式和我们今天用的一模一样。

第 6 站

乘和除:把一支箭头拉长

加减说完了,向量还剩乘和除。但这里得先澄清一件事:「向量乘法」不是两个向量相乘,而是「一个数乘一个向量」——数学上叫数乘

规则简单到没有惊喜:2 × [0.45, 0.20] = [0.90, 0.40]——每个坐标都乘 2。 字面意思是「这只动物的每个特征都翻倍」。但翻倍之后,箭头会往哪儿跑?

动手实验缩放实验 —— 拖动倍数 k,看箭头怎么动
挑一只 
0.000.000.500.501.001.001.501.502.002.00体型 →凶猛 ↑鳄鱼原 [0.90, 0.85]× 1.00 → [0.90, 0.85]
倍数 k× 1.00
老虎 = [0.90, 0.85] |a| = √(0.90² + 0.85²) = 1.238
0.90 × 1.00 = 0.90  0.85 × 1.00 = 0.85
k·a = [0.90, 0.85] |k·a| = 1.238 × 1.00 = 1.238
两个坐标乘的是同一个数 1.00,横的竖的一个都没落下;所以箭头只能沿那条虚线走,绝不会歪。
慢慢拖 k,你会发现一件怪事:箭头只会沿着那条虚线变长变短,绝不会歪。 为什么?看读数框——两个坐标乘的是同一个数,横的竖的一个都没落下。
图 2-4 · 数乘:k > 1 加强,0 < k < 1 减弱,k = 0 归零,k < 0 掉头指反面

所以「数乘」的语义特别好记:k 就是放大器的档位。 k = 2 是「特征加倍」,k = 0.5 是「特征减半」,k = −1 是「彻底反过来」(箭头掉头指向反面)。 方向不动的原因朴素得可爱:横竖都被乘了同一个数,纵比横的比值自然没变。

除法呢?除以一个数 = 乘以它的倒数

[0.90, 0.20] ÷ 2[0.90, 0.20] × 0.5 是同一件事, 结果都是 [0.45, 0.10]。向量世界里没有真正的「除法」,只有「乘以倒数」。

那为什么还要给除法留个位置?因为它有一个了不起的用途:求平均

猫 = [0.25, 0.30]  老虎 = [0.90, 0.85]
先相加:[0.25 + 0.90, 0.30 + 0.85] = [1.15, 1.15]
再除以 2:[1.15 ÷ 2, 1.15 ÷ 2] = [0.58, 0.58]

算出来的 [0.58, 0.58] 正好落在猫和老虎的正中点——一只体型中等、凶猛中等的动物。 这就是「平均向量」:加法把大家合到一起,除法把结果拉回中间。 大模型训练时有成千上万条梯度要取平均,用的就是这个动作。

不许画图,只给你两个向量:[0.45, 0.20][0.90, 0.40]。谁更长?它们方向一样吗?
第 7 站

点积:把两个向量压成一个数

第 4 站我们说「像不像就是距离」。这句话一路用到现在都很好使,但它有一个致命的盲区。

想象你在给文章打分:A 文里「科技」出现 1 次。现在把 A 文原样复制一遍、词频全部翻倍,得到 C 文。C 文和 A 文分明是同一种文章——可按距离算,它们的差距却被翻了一倍。 长度一掺进来,「像不像」就被污染了。

我们需要的是一把只看方向、不看长度的尺子。它的起点叫点积: 两个向量逐项相乘、再全部加起来,把两个向量压成一个数

核心实验点积与余弦 —— 转动 b,看数字怎么变
-3-2-1123-2-112a(不动)b夹角 18.1°b 的影子 1.52
b 的朝向45.0°
b 的长度 
a = [1.80, 0.90] |a| = √(1.80² + 0.90²) = 2.01
b = [1.13, 1.13] |b| = 1.60
点积 a·b = 1.80 × 1.13 + 0.90 × 1.13 = 2.04 + 1.02 = 3.06
影子长 = 点积 ÷ |a| = 3.06 ÷ 2.01 = 1.52
余弦 = 点积 ÷ (|a| × |b|) = 3.06 ÷ 3.22 = 0.95
夹角 = 18.1° —— 余弦 1 是完全同向、0 是垂直、−1 是掉头反向。它只认角度,不认长度。
点积 a·b(长度一变,它跟着变)
3.06
b 长 1.60 时:3.06
b 长 1.60 时:3.06
余弦相似度(只看方向,纹丝不动)
0.95
b 长 1.60 时:0.95
b 长 1.60 时:0.95
先转角度,试出三种极端:同向时点积最大且为正、垂直时点积恰好是 0、反向时点积变成负数(图里的绿色粗线就是 b 投在 a 上的影子)。 然后再玩「b 的长度」那排按钮——看见了吗?长度一变,点积跟着变,余弦一动不动。
图 2-5 · 绿色粗线是 b 的影子;点积 = |a| × 影子长,余弦 = cos(夹角)
面试常问

点积有三种读法,记住任意一种都够用:

  • 算式读法a·b = a₁b₁ + a₂b₂ + a₃b₃ + …——逐项相乘再全部相加,一万维也是这个套路。
  • 几何读法a·b = |a| × |b| × cos(夹角);图里那条绿色影子就是 |b| × cos
  • 直觉读法:点积是一台「方向一致度」探测器——同向合作得正分,互不相干得 0,反着来得负分。

上面第二条读法凭什么等于第一条?答案就一个字:。 第 6 站那台放大器(k × a,把 a 放大 k 倍)在这里派上了用场—— 只不过档位不是人挑的,是 b 指定的:把 b 拆成「顺着 a 的一截」和「垂直于 a 的一截」,档位就现形了。

图解推导点积的来历 —— 六步把两条读法算成同一个数
11223344ab
先把两个向量按它们真实的坐标画上去:a = [3.00, 4.00],b = [4.00, 3.00]——数格子就能对上。两支箭头都挺长,方向却只差一点点。
a = [3.00, 4.00]  |a| = √(3.00² + 4.00²) = 5.00
b = [4.00, 3.00]  |b| = √(4.00² + 3.00²) = 5.00
算式读法:a·b = 3.00 × 4.00 + 4.00 × 3.00 = 12.00 + 12.00 = 24.00
一步点点看,图会一层层长出来,每步的账都印在图下面,不用你动手算。 一路点完你会发现:垂直的那一截档位是 0,一个子儿都进不了账; 真正结账的只有顺着 a 的那一截,它记的是 |a| × 影子长——而影子长正是 |b| cos θ,两条读法到这里合上了。
图 2-6 · b = 顺着 a 的一截 + 垂直 a 的一截;点积只给顺着的那截结账

换任何一对 a、b,这六步一步都不用改。二维能把图画出来,三维、一万维画不出来, 但「横着的档位归 0、顺着的那截报出 |a| × 影子长、影子长就是 |b| cos θ」这三件事在多少维都成立。 不信就回到上面的演示:把 b 转到跟 a 垂直,绿色影子会缩成一个点——那正是「档位 0」,点积同时掉到 0.00。

但点积也有毛病,就是你刚才在演示里亲手碰到的那个:把 b 拉长到 1.5 倍,方向一点没变,点积却跟着变成 1.5 倍。它到底还是被长度污染了。最小修补是什么?把两个长度都除掉:

余弦相似度 = a·b ÷ ( |a| × |b| )
点积真正的用武之地 · 先归一化,再点积

单看点积,它被长度污染,看着有点鸡肋。但在真实系统里,点积几乎总是「算余弦」的第一步—— 而且工程师会偷一个更大的懒:与其每比一次都除一遍,不如提前把向量的长度都改成 1。

这个动作叫归一化(normalize):把向量除以它自己的长度,v̂ = v ÷ |v|。 长度变成 1 之后,a·b 里的 |a|、|b| 都是 1,分母压根不用写——归一化之后,点积直接就是余弦相似度。

于是整条「找相似」的流水线短得惊人:库里的向量提前归一化好 → 查询也归一化 →一次点积就拿到全部相似度分数 → 排序,取前 k 个(top-k)的分数和 ID → 拿着 ID 回词表取词。搜索引擎、推荐、RAG 检索,骨架上都是这四步。

名字里为什么带个「余弦」?就藏在这个除法的结果里——下一站把它摊开看; 而「把长度拧成 1」这个偷懒动作,第 9 站我们亲手做一遍,再把它变成一条打分流水线。

第 8 站

余弦相似度:这把尺子的刻度从哪来

上一站最后那一下除法,凭什么能把两个长度消得干干净净?

就凭第 7 站那条放大器公式可以反过来用a·b = |a| × |b| × cos θ。 把它原样代进分子:

一步就到底
余弦相似度 = a·b ÷ (|a| × |b|)
      = (|a| × |b| × cos θ) ÷ (|a| × |b|)
      = cos θ

分子分母里的 |a| × |b| 一模一样,约掉之后什么都不剩——余弦相似度不是什么新发明,它算出来的就是 cos θ 本身

那何必绕这一圈写成除法?因为真实场景里你手上只有两个向量的坐标,不知道夹角是几度; 而除法用坐标就能算,算完顺手把 cos θ 免费送给你。 名字里的「余弦」两个字,就是这么来的。

θ 到底是什么?它是两支箭头之间的夹角,只有两种量法:

  • 角度制:0° 到 180°,日常说话用这个。
  • 弧度制:0 到 π(约 3.14),程序里用这个——np.arccos()torch.acos() 吐出来的都是弧度,180° 就是 π 弧度。
  • 为什么封顶 180°:两支箭头的夹角最多就是掉头相对,再转下去等于绕回来,所以 cos θ 被牢牢锁在 −1 到 1 之间。

注意 cos θ 是个标量小数:没有方向、没有维度,就是一个小数。 两个 12288 维向量比出来的相似度,也还是这么一个小数。 它凭什么是那个数?下面这个三角形会告诉你。

动手实验邻边与斜边 —— 拖动夹角,看 cos 从哪儿冒出来
a 的方向θ斜边 = 1.00邻边 = 0.50对边
夹角 θ60°
夹角 θ = 60° = 1.05 弧度 (斜边 b 的长度固定 1.00,所以只看得见角度的影响)
邻边 = 斜边 × cos θ = 1.00 × 0.500.50 —— 它就是 b 落在 a 方向上的影子
对边 = 0.87 —— b 垂直于 a 的那一截,它跟 a 一点关系都没有,cos 完全不理它
余弦相似度 = 邻边 ÷ 斜边 = 0.50 ÷ 1.00 = 0.50
拖到底看看:0° 时邻边跟斜边一样长(cos = 1.00),90° 时邻边缩成一个点(cos = 0.00),180° 时邻边整个掉头(cos = −1.00)。
把斜边(也就是 b)的长度定成 1.00,那条绿色邻边就正好是 b 的影子, 它的长度不是别的,就是 cos θ 的数值。 拖到 0° 邻边跟斜边一样长(1.00),拖到 90° 邻边缩成一个点(0.00), 再拖过 90°——邻边越过顶点跑到反面,cos 开始变负数。
图 2-7 · 斜边固定 1.00;绿邻边 = cos θ,红点就是直角顶点(影子落点)
面试常问 · 这把尺子的刻度表
θ = 0° → cos θ = 1.00 邻边与斜边重合,完全同向
θ = 30° → cos θ = 0.87
θ = 60° → cos θ = 0.50
θ = 90° → cos θ = 0.00 邻边缩成一个点,互不相干
θ = 120° → cos θ = −0.50 邻边已经跑到反面
θ = 180° → cos θ = −1.00 邻边整个掉头,完全反向

和距离不一样:余弦永远是一个 −1 到 1 之间的小数,不管你在 2 维还是 12288 维。 这就是为什么今天几乎所有「找相似」的系统——搜索引擎、推荐、RAG 检索—— 报出来的相似度都是一个小数:那不是别的,就是余弦。

现在你手里有两把尺子:距离(第 4 站)和余弦(刚才)。要判断「这两句歌词是不是同一种情绪」,你用哪一把?
第 9 站

归一化:把向量长度变成 1

第 7 站最后那个除法「余弦 = 点积 ÷ (|a| × |b|)」有个小麻烦:每比一次,都要除一次。 工程上没人这么干——大家换了个更省事的做法:提前把向量的长度都改成 1。

这个动作叫归一化(normalize),做法只有一步除法:v̂ = v ÷ |v|—— 把向量的每个坐标,都除以它自己的长度。看个例子:

一步除法,长度归 1
v = [0.30, 0.40] → |v| = √(0.30² + 0.40²) = √0.25 = 0.50
v̂ = v ÷ |v| = [0.30 ÷ 0.50, 0.40 ÷ 0.50] = [0.60, 0.80]
|v̂| = √(0.60² + 0.80²) = √1.00 = 1.00 ✓

方向一点没动:两个坐标除以的是同一个数,纵比横还是 0.40 ÷ 0.30 = 1.33。 变的只有长度——从 0.50 变成 1.00。这其实还是第 6 站那台放大器:乘以一个小于 1 的数,箭头沿原路缩短,只不过这里的档位 k 换成了 1 ÷ |v|。

归一化之后,所有向量都站在同一个圆上。这个半径 1 的圆叫单位圆—— 圆上的点再多,长度也全是 1,于是「谁和谁像」只剩下一件事:角度

回头看点积:a·b = |a| × |b| × cos θ。既然 |a| = |b| = 1, 公式当场塌缩成 a·b = cos θ——归一化之后,点积就是余弦相似度,一个除号都不用写。第 7 站那一步除法没有消失,它只是被提前做掉了。

核心实验归一化 + 打分 —— 调长度、转方向,看谁被选出来
蓝色虚线圆 = 单位圆(长度 1)老虎汽车飞机|q| = 0.60|q̂| = 1.00
q̂ 归一化后的查询(长度恒为 1)q 原始查询(长度随「×」按钮变)库里的词(入库前已归一化)
q 朝向108°
top-k3
q 的原始长度 
q(原始)= [−0.19, 0.57] |q| = √(−0.19² + 0.57²) = 0.60 ← 长度随按钮变
q̂ = q ÷ |q| = [−0.19, 0.57] ÷ 0.60 = [−0.31, 0.95] |q̂| = 1.00 ← 长度恒为 1
分数 = q̂ · w = −0.31 × w₁ + 0.95 × w₂ —— 两边都归一化了,点积就是余弦
分数最高的是「老虎」:1.00(它在词表里的 ID = 0
top-k = 3 交出来的是 k 个「分数 + ID」: 老虎 1.00 / ID 0  猫 0.97 / ID 1  狗 0.83 / ID 2—— 拿 ID 回词表一查,取出来的才是词。
q 的原始长度 |q|(按「×」它就变)
0.60
× 1 档:0.60
当前档位:× 1
top-1 的分数(归一化之后纹丝不动)
1.00
长度 0.60 时:1.00
长度 0.60 时:1.00
#1 ID 0 老虎w = [−0.31, 0.95]
1.00
#2 ID 1 w = [−0.53, 0.85]
0.97
#3 ID 2 w = [−0.79, 0.62]
0.83
#4 ID 3 w = [−0.95, −0.31]
0.00
#5 ID 5 飞机w = [0.96, −0.28]
−0.56
#6 ID 4 汽车w = [0.53, −0.85]
−0.97
👆 先按「× 0.7 / × 1 / × 2.5」:虚线(原始 q)忽长忽短,蓝色实线(q̂)死死踩在单位圆上, 长度永远是 1.00。再拖「q 的朝向」:下面这张表跟着重排,前 k 名(琥珀色)就是这次检索选中的词。 负分表示方向相反——条形画不出来,看数字就行。
先按「× 0.7 / × 1 / × 2.5」:虚线(原始 q)忽长忽短,蓝色实线(q̂)死死踩在单位圆上, 长度永远是 1.00。再拖「q 的朝向」:下面那张分数表跟着重排, 前 k 名(琥珀色)就是这次检索选中的词。注意 top-1 的分数:q 的长度怎么变,它都不动。
图 2-8 · 虚线是没归一化的 q,实线是归一化后的 q̂;琥珀色箭头是库里已经归一化好的词向量

分数 → top-k → ID → 词

「归一化 + 一次点积」这套组合,在工程上有个固定的用法。四步走完,就是一次检索

  • 入库先归一化:把库里的每一个向量都除以自己的长度,统一成 1,存好。以后不用再除。
  • 打分:查询向量也归一化,然后拿它跟库里每个向量做一次点积——一个点积就是一个余弦分数,一次算完全部分数。
  • 排序取 top-k:分数从高到低排,取前 k 个。这一步交出来的不是词,是 k 个分数 + k 个 ID
  • 用 ID 取词:ID 就是词表里的座位号。拿着 ID 回词表一查,取出来的才是「老虎」「猫」这些真正的词。

为什么非要绕 ID 这一道?因为模型从头到尾只跟数字打交道:算相似度算出的是分数,排序排出来的是 ID,最后一步才把 ID 翻译回人能看懂的字。下一课那本词表,就是「ID ↔ 字」的对照册。

既然「点积 ÷ (|a| × |b|)」也能得到余弦,为什么还要先归一化?
第 10 站

第三根轴:年龄——以及画不出来的第四根

两维也有装不下的时候:「王子」和「国王」怎么区分?都挺皇家、都男性——他们差在年龄。 那就加第三根轴。

3D 实验三维词空间 —— 拖动旋转,点击词语看坐标与近邻
性别 · 男→女皇家年轻女人女王女孩男人公主国王婴儿男孩王子
偏男性向 偏女性向点越大越深 = 离你越近
👆 拖动旋转整个空间;点击任一个词,看它的三个数字,以及三维空间里谁离它最近。
转一圈你会发现:「国王—王子—男孩」几乎排在年龄轴的一条线上; 而「王子—公主」之间永远隔着性别轴。三个数,就是三维空间里的一个点。
图 2-9 · 三根轴:性别(蓝→琥珀)、皇家、年龄。第四维「时代」?画不出来,但照样算

想再区分「古代的国王 / 现代的国王」?加第四维「时代」。四维的空间人类画不出来——没关系,数学完全不在乎:距离公式照样一项项加,「谁和谁更像」照样比。 12288 维也一样。在 GPT-3 眼里,每个词是一张 12288 个数字的照片, 没有人说得清第 7042 维是什么意思——但每一维,都可能是一个我们叫不出名字的「角度」。

第 11 站

张量:AI 世界的集装箱

一个数,是标量;一排数,是向量;一张数表,是矩阵; 一摞表、一箱表……统统叫张量(tensor)。 它是大模型世界里的集装箱:所有输入、所有中间结果、所有参数,都以张量的形状流动。

图解张量升级器 —— 从一个数,一路加维到 4D
0.001学习率 η —— 一个孤零零的数
标量
scalar · 0 维
torch.Size([])
取一个数t = 0.001(它自己就是那个数,一个下标都不用)
大模型里的它学习率 0.0001、温度 0.7、某一步的损失值 2.31 —— 都是一个数。
比喻比喻:一粒豆子。
规律:需要 几个下标 才能锁定一个数,它就是 几维 张量。
图 2-10 · 每升一维,就多一层「摞」:向量是数的排,矩阵是向量的排,张量是矩阵的排

真实的模型里还有一层最关键的「摞」:一句话要被切成若干个 token, 每个 token 查出一支向量,这些向量按顺序摞成一张表——这才是模型眼中的「一句话」。 一次训练同时喂 32 句话,表再摞成一摞。下一课我们就去造这张表的入口:词表与分词。

它是什么(Qwen2.5-7B)形状维数
学习率标量0
一个 token 的词向量[3584]1
嵌入矩阵(躺在 safetensors 文件里)[151936, 3584] ≈ 5.4 亿个数2
一句话 10 个 token 的向量序列[10, 3584]2
一个批次:32 句话[32, 10, 3584]3
32 句 × 28 个注意力头的「谁关注谁」表[32, 28, 10, 10]4
面试常问 · 「7B」这个外号是怎么加出来的

上面那张表里最大的形状就是嵌入矩阵。把整个模型的参数量从头加一遍你会发现:「7B」不是厂家规定的,就是加出来的——而且每一步都只是乘法:

第 1 块 · 嵌入矩阵(每个 token 一个 3584 维向量)
 151936 × 3584 = 544,538,624 ≈ 5.45 亿

第 2 块 · 一层里的两个大件
 注意力:Q、O 各 3584 × 3584 = 12,845,056;K、V 各 3584 × 512 = 1,835,008
  四个加起来 = 29,360,128 ≈ 0.29 亿
  (K、V 只有 Q、O 的 1/7 宽——28 个头共用 4 组 K、V,这就是 GQA)
 FFN:3 个矩阵,每个 3584 × 18944 = 67,895,296,加起来 = 203,685,888 ≈ 2.04 亿
 一层合计 = 29,360,128 + 203,685,888 = 233,046,016 ≈ 2.33 亿,光 FFN 就占了 87%

第 3 块 · 一共 28 层
 233,046,016 × 28 = 6,525,288,448 ≈ 65.2 亿

第 4 块 · 输出层(把向量换算回词表上的分数)
 151936 × 3584 = 544,538,624 ≈ 5.45 亿 —— 有的模型让它和嵌入矩阵共用一份参数,那就省掉这 5.45 亿

合计 544,538,624 + 6,525,288,448 + 544,538,624 = 7,614,365,696 ≈ 76.1 亿 → 业界写作 7.6B,口语里就叫「7B」

三个面试常问的观察就藏在这几行里:参数的大头不在注意力、在 FFN(一层里 87%);K、V 可以做得比 Q、O 小得多(28 个头共用 4 组,省掉 6/7 的 K、V 参数);嵌入矩阵和输出层能不能共用一份参数,直接差出 5.45 亿。

那要多大显存才装得下?除了参数量,还得知道每个参数在显存里占几个字节

只做推理(fp16,每个参数 2 字节):76.1 亿 × 2 ≈ 15.2 GB —— 一张 16GB 的 T4 刚好塞下,别的就别想开了
全参数训练(fp16 权重 + fp16 梯度 + fp32 优化器状态,每个参数约 16 字节):
 76.1 亿 × 16 ≈ 122 GB —— 差得远,免费卡想都别想

Kaggle 给的是 16GB,装不下这个 76 亿。所以我们要训的第一个模型得自己造小一点: 层数砍下来、宽度收窄、词表缩到只剩算术要用的那几个字—— 参数量一下子小了几千倍,小到一张免费卡就能装下。但张量的形状规则、注意力的算法、训练的动作,一个都没变。这就是我们敢在免费 GPU 上从零手写一个 Transformer 的底气。

(具体小到多少、显存占多少,等第 14 课真的把那个模型造出来、跑起来,我们再把它自己的数字算给你看。)

动手实验模型规格 / 显存计算器 —— 拖一拖,看这 76.1 亿是怎么长出来的
层数28
宽度 d3,584
FFN 宽度= 18,944
词表大小
输出层
嵌入矩阵151,936 × 3,584
7.2%
注意力(Q + O + K + V)28 层 × 29,360,128
10.8%
FFN28 层 × 203,685,888
74.9%
输出层151,936 × 3,584
7.2%
条长 = 占总参数的百分比(各留一位小数,加起来约 100%)。一层里 FFN 占 87%(203,685,888 ÷ 233,046,016) —— 参数的大头不在注意力,在 FFN。
嵌入矩阵 = 151,936 × 3,584 = 544,538,6245.45 亿
一层注意力 = 2 × 3,584² + 2 × 3,584 × 512 = 29,360,1280.29 亿  (K、V 只有 512 宽,省掉 6/7)
一层 FFN = 3 × 3,584 × 18,944 = 203,685,8882.04 亿  ← 一层里 87% 都是它
28 层 = (29,360,128203,685,888)× 28 = 6,525,288,448
输出层 = 151,936 × 3,584 = 544,538,6245.45 亿
合计 = 544,538,6246,525,288,448544,538,624 = 7,614,365,69676.1 亿
总参数量
76.1 亿
精确值 7,614,365,696
换成 B:7.6B
fp16 推理显存(每个参数 2 字节)
15.2 GB
76.1 亿 × 2 字节 ≈ 15.2 GB
一张 16GB 的卡刚好塞得下
全参训练显存(每个参数约 16 字节)
122 GB
76.1 亿 × 16 字节 ≈ 121.8 GB → 约 122 GB
一张 16GB 的卡装不下 —— 差了 7.6
光 FFN 就占一层
87%
203,685,888 ÷ 233,046,016
注意力只占 13%
👆 先拖宽度 d:FFN 那条占比条一路暴涨(它是 d² 级的),注意力几乎不动, 嵌入和输出层反而被挤小 —— 这就是「参数的大头在 FFN」。 再拖层数:总量老老实实线性涨,每多一层就多一份 233,046,016。 最后把输出层切成「与嵌入共用一份」,立刻少掉 5.45 亿。 默认档(28 层 / d = 3584 / FFN 18944 / 词表 151936 / K·V 512)算出来正是页面上的 76.1 亿 —— 也就是大家口中的「7B」。
先拖宽度 d:FFN 那条占比条一路暴涨(它按 d² 涨),注意力几乎不动 —— 所以参数的大头永远是 FFN,不是注意力。 再拖层数:总量老老实实线性涨。 想省显存?把输出层切成「与嵌入共用一份」,立刻少掉一大块。 下面四个读数跟着滑块实时变,可以拿来试「多大才塞得进一张 16GB 的卡」。
图 2-11 · 拖宽度 d:FFN 占比迅速膨胀(d² 级)、注意力几乎不动;拖层数:总量线性增长

一个 7B(刚才加出来的 76 亿参数)的大模型,本质就是一堆形状各异的张量, 整整齐齐躺在 .safetensors 文件里——那不是什么神秘格式, 就是「带形状标签的大数字数组」。第 4 课我们会真的打开一个模型文件,把嵌入矩阵挖出来给你看。

猜一猜:为什么注意力分数表 [32, 28, 10, 10] 要比词向量序列 [32, 10, 3584] 多出一个「28 个头」的维度?
第 12 站 · 收官

把这一课钉在墙上

本课核心 · TAKEAWAY

向量,是给万物拍的数字照片:一组有顺序的数字捕捉特征, 「像不像」第一次变成可以计算的距离。 加减是搬运特征,数乘是调强度,点积探测方向,再除以长度就得到余弦——一副只认方向、不认长短的尺子; 而把长度统一拧成 1(归一化)之后,一次点积就是余弦——检索系统靠它给全库打分, 取 top-k 的分数和 ID,再拿 ID 回词表取词。 词也有向量,语义于是变成几何——国王 − 男人 + 女人 = 女王不是魔法,是箭头的搬运。 而张量,是数字的集装箱:大模型里流动的一切——词、句、注意力、76 亿参数——全是张量。

这一课你亲手发明了

  • 向量:一组有顺序的数字,例如 [0.45, 0.20];它同时是数列、点、箭头,数字的个数就是维度
  • 加减 = 搬运特征:减法抵消方向、加法叠加方向;数乘 = 调强度——每个坐标乘同一个数,方向不动;除以 k 就是乘 1/k,用来求平均
  • 距离:√(横差² + 纵差² + …),几维都一个公式,回答「差多少」。
  • 点积与余弦a·b = a₁b₁ + a₂b₂ + …,同向为正、垂直为 0、反向为负;再除以 |a||b| 得到余弦——约掉长度之后,它算出来的就是 cos θ 本身,永远在 −1 到 1 之间,回答「像不像」。
  • 归一化v̂ = v ÷ |v|——每个坐标除以自己的长度,长度变成 1、方向一点不动。归一化之后 a·b = cos θ,点积直接就是余弦;检索的四步(归一化入库 → 一次点积打分 → 取 top-k 的分数和 ID → 用 ID 回词表取词)全靠它。
  • 张量:需要几个下标锁定一个数,就是几维;标量 → 向量 → 矩阵 → 一摞一箱。

学习小测验

已完成 0 / 80.0%答对 0
还没提交过 —— 每题先选一个选项,再点「提交」,答完就能看到诊断。
Q1计算机本身只认识数字。我们用「向量」来表示「猫」「老虎」这样的事物,最根本的目的是什么?
Q2在「国王 − 男人 + 女人 = 女王」这个算式里,「国王 − 男人」这一步实际上在做什么?
Q3Qwen2.5-7B 的嵌入矩阵形状是 [151936, 3584]。下面哪句话是对的?
Q4如果词向量的维度从 2 维加到 12288 维(GPT-3 的规格),会发生什么?
Q5把向量 [0.40, 0.20] 乘以 2,得到 [0.80, 0.40]。下面哪句话是对的?
Q6把向量 b 拉长到 3 倍(方向一点不变),点积 a·b 和余弦相似度分别怎么变?
Q7余弦相似度 = a·b ÷ (|a| × |b|)。它算出来的这个数,本质上是什么?
Q8把向量 v 除以它自己的长度(v̂ = v ÷ |v|),这个动作叫归一化。归一化之后,点积 a·b 变成了什么?
NEXT · 第 3 课

词与词表:文字怎么变成数字

我们已经会用向量描述「猫」和「国王」了——可那些数字是我们手工打的。 大模型面对的是几百万条句子,谁来打分?下一课,我们自己造一套中文算术语料, 亲手切出词表、编好号,再让模型把每个字的向量从随机数里「跑」出来。

从零手册 —— 下一课:词与词表:文字怎么变成数字