上一篇:(七)用直线去逼近曲线——讲计算机怎么处理连续的东西。 这一篇:讲计算机怎么表示"意思"——这是整个 AI 的数学地基,也是八层的最后一层。

耗时:读完约 55 分钟,动手约 2 小时。


开头:机器不懂"意思",但它能算"角度"

问一个看起来很简单的问题:

怎么让机器知道"我今天很开心"和"我心情不错"意思相近?

机器只认数字。它没有"意思"这个概念。那怎么办?

答案是把每句话变成一串数字——一个向量。

"我今天很开心"  → [0.12, -0.45, 0.88, ..., 0.31]    ← 768 个数
"我心情不错"    → [0.15, -0.41, 0.85, ..., 0.29]    ← 768 个数
"股票跌了"      → [-0.62, 0.71, 0.03, ..., 0.88]    ← 768 个数

然后比较它们。但不是比较"距离",是比较"夹角"。

前两个向量的夹角很小(方向接近),第三个方向完全不同。这就是"意思相近"的数学形式。

这一篇就讲这件事。它是八层里最后一层,也是最直接通向今天 AI 的一层——因为大语言模型里那个叫"注意力机制"的东西,本质就是三个矩阵相乘。


一、向量:一个数串,就是一个方向

先建立最基本的操作。

余弦相似度:用夹角衡量相似

def cos(a, b):
    return (a·b) / (|a| × |b|)

取值范围 [−1, 1]。1 是同方向,0 是垂直(无关),−1 是反方向。

为什么不用距离?实测给你看

三个向量:

a = [1, 2, 0.5]
b = [2, 4, 1]     ← 同方向,长度是 a 的 2 倍
d = [0.5, 1, 4]   ← 不同方向
度量a vs ba vs d
余弦相似度1.0000000.472866
欧氏距离2.29133.6742

看这个对比:

  • 余弦:a 和 b 相似度 = 1.0,完全一样
  • 欧氏距离:a 和 b 相差 2.29,比 a 和 d 的 3.67 只小一点,看起来"也挺远"

为什么?因为 b 只是 a 的"加强版"(同样方向,2 倍强度)。 在语义上它们讲的是同一件事,只是程度不同。

距离把"长度差异"当成了"不同",夹角不会。

所以在语义检索、推荐系统、RAG(知识库问答)里,全用余弦相似度,不用欧氏距离。 你问"怎么学 C++",一本标题是"如何学习 C++ 编程"的书,和一本"Python 入门"的书——距离可能差不多,但夹角差得远。


二、高维反直觉:越高的维度,距离越没用

这里有个事实,几乎所有第一次听到的人都不信。

在高维空间里,任意两个随机点的距离,几乎都一样。

实测(每个维度取 300 对随机点):

维度平均距离标准差变异系数(标准差/均值)
21.7580.89120.5069
104.4520.95020.2134
509.9560.96920.0973
10014.1090.97450.0691
50031.5840.96540.0306
100044.6641.04680.0234
5000100.0390.99680.0100

看最后一列:变异系数从 0.51 一路掉到 0.01。

5000 维时,随机两点的距离全是 100 左右,波动只有 1%。所有点都几乎等距。

这就是"维度诅咒"(curse of dimensionality)最具体的样子: 维度一高,“距离"这个度量就失去分辨力——因为大家都差不多远。

这解释了很多事:

  • 为什么高维检索要用夹角(余弦),不用距离——距离已经被"稀释"了
  • 为什么高维要用降维(PCA/SVD)才能可视化
  • 为什么机器学习里"特征太多"反而是灾难

注意这跟前面几篇是同一条线: 第二篇讲"采样是把连续压成离散”,这一篇讲"维度一高,直觉失效"。都是"低维直觉在数学上不成立"。


三、矩阵:一个变换,不是一个表格

大多数人对矩阵的印象是"一张数字表格"。这是最误导的印象。

矩阵是一个变换。它对向量做的事,是"旋转 + 拉伸 + 压缩"。

比如这个矩阵:

A = [2 1]
    [1 2]

它作用在向量上,就是一次变形。而它的特征值告诉你:这个变形有哪些"不变的方向"。


四、特征值:变形里那些"方向不变"的地方

定义,一句话

如果 A·v = λ·v,那么 v 是一个"特征向量"(方向不变),λ 是对应的"特征值"(拉长多少倍)。

方向不变,长度变。

实测

A = [2 1]
    [1 2]

特征值 λ = 1, 3
特征向量 = [−0.707, 0.707] 和 [0.707, 0.707]

验证 A·v = λ·v:

λ=1: A@v = [−0.7071, 0.7071],  λv = [−0.7071, 0.7071],  差 0
λ=3: A@v = [ 2.1213, 2.1213],  λv = [ 2.1213, 2.1213],  差 0

完全相等。

为什么这件事重要:反复作用,方向会"收敛"

把 A 反复乘一个初始向量,每次归一化:

A^1  @ x → [0.894427, 0.447214]
A^2  @ x → [0.780869, 0.624695]
A^3  @ x → [0.732793, 0.680451]
A^5  @ x → [0.710011, 0.704191]
A^10 @ x → [0.707119, 0.707095]
A^20 @ x → [0.707107, 0.707107]   ← 收敛了
最大特征向量 = [0.707107, 0.707107]

不管从哪开始,反复乘同一个矩阵,方向都会收敛到"最大特征值"的那个特征向量。

为什么?因为每次乘法,那个方向的成分被乘 3 倍,其他方向的成分被乘 1 倍。反复乘下去,3ⁿ 会彻底压过 1ⁿ。

这个性质叫"幂迭代",是 Google 的 PageRank 的核心算法。 整个互联网的链接矩阵,最大的那个特征向量,就是网页的重要性排序。 “谁最重要"这个问题,数学上就是"求最大特征向量”。


五、SVD:所有矩阵都能拆成"旋转·拉伸·旋转"

特征值只对方阵有意义。SVD(奇异值分解)把它推广到任何矩阵:

任何矩阵 A = U · Σ · Vᵀ U、V 是正交矩阵(旋转),Σ 是对角矩阵(拉伸)。

一句话理解:任何线性变换,都是"先转一下、再拉伸、再转一下"。

实测:一个 4×3 矩阵

A = [[ 1,  2,  3],
     [ 4,  5,  6],
     [ 7,  8,  9],
     [10, 11, 12]]

奇异值 S = [25.462, 1.291, 2.5e-15]
秩 = 2

第三个奇异值是 2.5e-15——约等于 0。 这个矩阵虽然写着 4×3,实际只有 2 维的信息(因为每一行都是等差数列,第三行是前两行的线性组合)。

用前 k 个分量重建:

k最大误差保留能量
10.809895.18%
20.000000100.00%
30.000000100.00%

2 个分量就精确重建。第 3 个分量是噪声(数值误差)。

SVD 告诉你一件事:矩阵的"真实维度"可能远小于它的"表面尺寸"。

图像压缩:真实数字

我拿一张 200×200 的图(40000 个数)做 SVD:

k(保留分量)要存几个数压缩比保留能量最大误差
140199.8×27.28%0.9285
280249.9×54.54%0.2296
104,01010.0×58.62%0.2198
5020,0502.0×75.13%0.1415
10040,1001.0×89.17%0.0706
20080,2000.5×100.00%0.0000

看 k=10 那一行:只用 4010 个数(1/10 的数据),保留 58.62% 的能量,最大误差 0.22。

这就是图像压缩、推荐系统降维、数据去噪的数学基础。

一句话:SVD 找出"哪些方向最重要",然后扔掉不重要的。 不是"删数据",是"删方向"。


六、注意力机制:本质就是三次矩阵乘法

现在到这一篇最重要的部分。GPT、BERT 这些大模型的核心,就叫"注意力机制"(Attention)。

大多数人以为它很神秘。它其实就是三步矩阵乘法。

三个矩阵:Q、K、V

每个 token(词)都变成三个向量:

  • Q(Query,查询):我在找什么
  • K(Key,键):我有什么
  • V(Value,值):我的内容

用矩阵表示:Q、K、V。

三步

第一步:算两两相关度

scores = Q · Kᵀ / √d

这就是一个矩阵乘法。 结果是一个 n×n 的表:第 i 行第 j 列 = 第 i 个词有多关注第 j 个词。

第二步:归一化成权重

W = softmax(scores)

每行加起来等于 1——变成"注意力分配比例"。

第三步:加权求和

output = W · V

又是一个矩阵乘法。 每个词的新表示 = 所有词的内容按注意力加权平均。

实测(4 个词,8 维)

scores = Q@Kᵀ/√8  形状 (4,4)

原始打分:
[[-0.654 -0.489  0.468 -1.722]
 [-0.316 -1.142 -0.286 -1.216]
 [-1.244  1.083 -0.044  0.001]
 [-0.667  0.990 -0.283 -0.371]]

softmax 后(每行和为 1):
[[0.179 0.211 0.549 0.061]
 [0.348 0.152 0.358 0.142]
 [0.055 0.568 0.184 0.193]
 [0.110 0.579 0.162 0.148]]
行和: [1. 1. 1. 1.]

最后 W@V → 输出形状 (4, 8)

就这三步。没有第四步。

大模型的"理解",数学上就是: 每个词先算出它该看哪些词(Q·Kᵀ),再按权重把所有词的信息混在一起(W·V)。

为什么这一步这么关键

因为它让每个词都能"看到全局"。

没有注意力之前,模型只能一个个词顺着读(RNN),信息传得越远越模糊。有了注意力,第 1 个词和第 5000 个词可以直接对话——因为它们在同一个矩阵乘法里。

代价:O(n²)

但是注意 Q·Kᵀ 的大小是 n×n。

n = 4      → 16 个分数
n = 4096   → 16,777,216 个分数
n = 100000 → 10,000,000,000 个分数

上下文长度翻倍,注意力的计算量翻四倍。 这就是为什么大模型的"上下文窗口"是稀缺资源、为什么长文本这么贵。

这也把第五篇的复杂度接了回来: 注意力是 O(n²d),而矩阵乘法朴素实现是 O(n³)。 整个 AI 的算力账单,就是矩阵乘法的账单。

实测矩阵乘法的立方增长(朴素实现):

n耗时
5015.6 ms
10086.5 ms
150242.8 ms
200594.1 ms

n 翻倍,耗时约 8 倍(2³)。 这就是为什么全世界的公司都在抢 GPU——GPU 存在的唯一理由,就是把矩阵乘法做得更快。


七、词向量的经典魔术:king − man + woman ≈ queen

最后讲一个让人拍案的结果,它说明了"高维表示"到底能做到什么。

把词变成向量之后,向量的加减对应语义的加减:

king − man + woman ≈ ?

实测(50 维,带噪声):

比较对象余弦相似度
king0.8381
queen0.9535 ← 最高
man0.1515
woman0.0797
apple0.2470

最相似的是 queen,相似度 0.95。

“国王"减去"男人"加上"女人”,得到"女王"。

这不是巧合,也不是程序员写死的规则。这是"把语义映射到向量空间"这件事自然产生的结构——因为"性别"在向量空间里变成了一个方向,你可以沿着这个方向加减。

这就是"表示学习"(representation learning)的全部意义: 不是人去定义规则,而是找到一个空间,让规则变成几何。

这也解释了为什么大模型不需要人教它语法——语法结构在向量空间里变成了几何结构,模型只要学几何,就自动学会了语法。


八、八层走完:整个专题的骨架

八篇,八层,一条线。

层问什么数学篇
一一个数怎么装进机器数值分析(一)
二一堆东西怎么装进机器离散数学(二)
三怎么确认它是对的数理逻辑(三)
四什么根本算不了计算理论(四)
五能算的有多快算法分析(五)
六不确定怎么处理概率论(六)
七连续怎么逼近微积分(七)
八意思怎么表示线性代数本篇

它们在 AI 里怎么合起来

这是我最想让你看到的一张图。一个大语言模型,八层全用上了:

层在 GPT 里对应什么
一 数值权重是 float16/bfloat16,精度直接影响模型能不能训起来
二 离散token 是离散的,词表是有限的,softmax 把连续变回离散
三 证明训练不收敛时,靠不变量思维(梯度该多大、loss 该多低)定位问题
四 可计算有些问题模型永远学不会(停机问题那类)
五 复杂度O(n²d) 的注意力,就是算力账单的来源
六 概率输出是概率分布,采样温度、top-p 全是概率
七 微积分反向传播 = 链式法则,梯度下降 = 学习本身
八 线代整个模型就是矩阵乘法堆起来的

所以"AI 时代的计算机数学"这句话,不是修辞。 一个 GPT,就是这八层数学的一次性总动员。

最后一句收口

整个专题第一句话是:

数学是连续的、无限的、精确的;计算机是离散的、有限的、近似的。这两者之间的翻译工作,就是计算机数学。

八篇走完,这句话可以升级成:

计算机数学,是在"离散、有限、近似"的机器上,把"连续、无限、精确"的数学算到够用—— 而 AI,是把这八层全部押上去,去逼近一件它自己也说不清的事。


九、动手:六个实验

实验 1:为什么用夹角不用距离

import math
def cos(a,b):
    return sum(x*y for x,y in zip(a,b)) / (
        math.sqrt(sum(x*x for x in a)) * math.sqrt(sum(x*x for x in b)))
def dist(a,b):
    return math.sqrt(sum((x-y)**2 for x,y in zip(a,b)))

a=[1,2,0.5]; b=[2,4,1]; d=[0.5,1,4]
print(f"cos(a,b)={cos(a,b):.4f}  cos(a,d)={cos(a,d):.4f}")
print(f"dist(a,b)={dist(a,b):.4f}  dist(a,d)={dist(a,d):.4f}")

a 和 b 是"同方向不同强度",余弦说 1.0,距离说 2.29。

实验 2:维度诅咒

import numpy as np
for dim in [2, 10, 50, 100, 1000, 5000]:
    ds = [np.linalg.norm(np.random.randn(dim) - np.random.randn(dim))
          for _ in range(300)]
    ds = np.array(ds)
    print(f"{dim:>5} 维: 均值 {ds.mean():7.2f} 变异系数 {ds.std()/ds.mean():.4f}")

看变异系数从 0.5 掉到 0.01——高维里所有点几乎等距。

实验 3:特征值和幂迭代

import numpy as np
A = np.array([[2.,1.],[1.,2.]])
w, v = np.linalg.eigh(A)
print("特征值:", w)

x = np.array([1.,0.])
for k in [1,2,3,5,10,20]:
    y = np.linalg.matrix_power(A,k) @ x
    y = y/np.linalg.norm(y)
    print(f"A^{k:<2}: [{y[0]:.6f}, {y[1]:.6f}]")

不管从哪开始,都会收敛到最大特征向量 [0.707, 0.707]。

实验 4:SVD 压缩一张图

import numpy as np
h = w = 200
x = np.linspace(0, 4*np.pi, w)
img = (np.sin(x)[None,:] + np.sin(x)[:,None])/2 + np.random.randn(h,w)*0.05

U, S, Vt = np.linalg.svd(img, full_matrices=False)
for k in [1, 5, 10, 50]:
    Ak = U[:,:k] @ np.diag(S[:k]) @ Vt[:k,:]
    print(f"k={k:2d}: 存 {k*(h+w+1):,} 个数, 误差 {np.abs(img-Ak).max():.4f}")

实验 5:注意力就是矩阵乘法

import numpy as np
np.random.seed(7)
n, d = 4, 8
Q, K, V = [np.random.randn(n, d) for _ in range(3)]

scores = Q @ K.T / np.sqrt(d)          # 第 1 次矩阵乘法
W = np.exp(scores) / np.exp(scores).sum(axis=1, keepdims=True)   # softmax
out = W @ V                             # 第 2 次矩阵乘法

print("打分:\n", scores.round(3))
print("权重行和:", W.sum(axis=1).round(6))
print("输出形状:", out.shape)

实验 6:king − man + woman

import numpy as np
np.random.seed(3)
d = 50
base = {k: np.pad(np.array(v), (0, d-len(v)))
        for k, v in {'king':[1,.9,.2], 'queen':[1,.1,.2],
                     'man':[0,.9,0], 'woman':[0,.1,0],
                     'apple':[.1,0,1]}.items()}
W = {k: v + np.random.randn(d)*0.02 for k,v in base.items()}
cos = lambda a,b: a@b/(np.linalg.norm(a)*np.linalg.norm(b))

target = W['king'] - W['man'] + W['woman']
for k in W: print(f"cos(结果, {k:6s}) = {cos(target, W[k]):.4f}")

十、带走的东西

一句话:

机器不懂"意思",但它能把意思变成向量——然后"意思相近"就变成了"夹角小",“语义加减"就变成了"向量加减”。

三句话:

  1. 高维要用夹角,不用距离。 实测 5000 维时随机两点的距离变异系数只有 0.01——所有点几乎等距,距离失去分辨力。
  2. SVD 找出"哪些方向重要"。 200×200 的图,只存 1/10 的数(k=10),保留 58.62% 能量,最大误差 0.22。不是删数据,是删方向。
  3. 注意力机制 = Q·Kᵀ → softmax → W·V,就三步矩阵乘法。 大模型的"理解",数学上是"每个词算出该看谁,再按权重把信息混起来"。

一个最该记住的判断

AI 不是"理解了语言",是"找到了一个几何空间,让语义变成了方向"。 king − man + woman ≈ queen,不是规则,是几何的副产品。 所以模型不需要人教语法——语法结构在向量空间里变成了几何结构,学几何就学会了语法。


十一、这个专题到这里结束了

八篇,从"0.1 + 0.2 为什么不等于 0.3",一路走到"GPT 的注意力机制"。

这不是八门互不相关的数学。是一条线:

数装不下 → 所以要离散化 → 所以要证明对 → 所以要知道什么算不了
→ 所以要算得快 → 所以要处理不确定 → 所以要逼近连续 → 所以要表示高维

每一层都在解决上一层留下的问题,每一层都留下新的问题给下一层。

如果这个专题你只带走一句话,带这句:

数学不是编程的前置课。数学是"凭什么判断它对"的来源。 而"彻底掌握"的体感,不是会推导,是同一个结构你认出来三次。

后续还想深入的话

八层,八门课,都在这张表里:

层正统学科入门书(按难度)
一数值分析Burden & Faires
二离散数学Rosen / MIT 6.042
三数理逻辑Rosen 第 1 章 / Hoare 1969
四计算理论Sipser
五算法与复杂度Sipser 后半 / CLRS
六概率统计Ross《A First Course in Probability》
七微积分与优化Stewart / Boyd《Convex Optimization》
八线性代数Strang / Goodfellow《Deep Learning》第 2 章

这些我不做页码引用——不同版本页码不同,写了就是骗你。按书名和章节找。


附:本篇所有数字的来源

全部本机跑出来:Ubuntu / Python 3.8 / numpy 1.17 / x86-64。

余弦 vs 距离:a=[1,2,0.5], b=[2,4,1](同方向 2 倍长), d=[0.5,1,4];cos(a,b)=1.000000、cos(a,d)=0.472866;欧氏 dist(a,b)=2.2913、dist(a,d)=3.6742。

维度诅咒(每维 300 对随机点):2/10/50/100/500/1000/5000 维 → 平均距离 1.758/4.452/9.956/14.109/31.584/44.664/100.039,变异系数 0.5069/0.2134/0.0973/0.0691/0.0306/0.0234/0.0100。

特征值:A=[[2,1],[1,2]] → λ=1, 3;幂迭代 A^1/2/3/5/10/20 @ [1,0] 归一化后收敛到 [0.707107, 0.707107]。

SVD(4×3 矩阵):奇异值 25.462/1.291/2.5e-15,秩 2;k=1 误差 0.8098(保留 95.18% 能量),k=2 误差 0(100%)。

SVD 图像压缩(200×200 = 40,000 数):k=1/2/10/50/100/200 → 存 401/802/4010/20050/40100/80200 个数,压缩比 99.8/49.9/10.0/2.0/1.0/0.5 倍,保留能量 27.28/54.54/58.62/75.13/89.17/100%,最大误差 0.9285/0.2296/0.2198/0.1415/0.0706/0。

注意力:4 token × 8 维,scores 形状 (4,4),softmax 后行和全为 1.0,输出 (4,8)。

矩阵乘法:n=50/100/150/200 → 15.6/86.5/242.8/594.1 ms(近似 n³)。

词向量:king−man+woman 与 queen 余弦 0.9535(最高),king 0.8381,apple 0.2470,man 0.1515,woman 0.0797。

你自己机器上跑出来的数字会略有不同(BLAS 库、CPU)。差一点不影响结论——结论是结构性的。