AI/ML 详解:表示与嵌入几何

Day 30 · 2026-06-16 · 难度 ★★★★☆
面向:有编程经验的非 AI 方向工程师
工程对应 → super-individual D11: RAG 实战工程(embedding 质量直接决定召回)

"嵌入(embedding)"是把一个词、一句话、一张图压成一串数字——一个高维向量。但这些向量在空间里怎么排布,本身就编码了语义。今天不讲某个具体编码模型(那是 Day 24),而讲贯穿所有表示学习的几何规律:为什么"国王−男人+女人≈王后"成立、现代 embedding 靠什么训练目标、为什么 BERT 的句向量"挤成一团"、以及怎么用一个向量同时服务快查询和精排。看懂几何,你才知道一个 embedding 为什么好或为什么坑。

词向量的线性结构Linear Structure of Word Embeddings

几何Word2Vec / GloVe
一句话类比

词向量之间的差值(diff)本身是有意义的——就像 git 里两个 commit 之间的 patch:你可以把"性别 diff"从一个词上抽出来,再 apply 到另一个词上。国王 − 男人 = 一段"王权但去掉男性"的位移向量,把它加到"女人"身上,就落在了"王后"附近。语义关系被编码成了可复用的、方向一致的平移操作

它解决什么问题 + 工作机制

2013 年前,词的表示是 one-hot:每个词一个孤立维度,"猫"和"狗"的相似度严格为 0,机器完全不知道它们都是动物。Word2Vec(Mikolov 等, 2013)和 GloVe(Pennington 等, 2014)的突破:让"意思相近的词,向量也相近",而且关系被编码成方向。

机制核心是一句老话——"一个词由它的上下文定义"。Word2Vec 用一个浅层网络做"完形填空":给中心词预测周围词(Skip-gram)。要把这个预测做好,模型被迫把常出现在相似上下文里的词放到相邻位置。训练完,"语义"就沉淀成了几何:同类词聚成簇,而"单复数""国家→首都""动词时态"这类系统性关系表现为平行且等长的位移向量

类比 = 向量平移(两条 diff 近似平行)

男人──「+王权」──▸国王
 │           │
「+女性」      「+女性」
 ▼           ▼
女人──「+王权」──▸王后

国王 − 男人 + 女人 ≈ 王后 (找最近邻即得)

注意"≈":这不是精确等式,而是"加完之后最近邻恰好是王后"。这种线性结构只在高频、规整的关系上稳定,生僻词和复杂关系常常失灵——别把它当成可靠的推理引擎。

代码示例
import gensim.downloader as api
# 加载预训练 GloVe 词向量(首次会下载约 65MB)
wv = api.load("glove-wiki-gigaword-100")  # 100 维

# 经典类比:king - man + woman ≈ ?  用向量加减后找最近邻
result = wv.most_similar(positive=["king", "woman"],
                        negative=["man"], topn=3)
print(result)  # → [('queen', 0.78), ('throne', ...), ...]

# 几何验证:相似词余弦相似度高,无关词低
print(wv.similarity("cat", "dog"))      # ~0.6,都是宠物
print(wv.similarity("cat", "democracy"))# ~0.1,几乎无关
常见误区 + 实践场景
误区:"Word2Vec 是静态的,所以过时了"。确实它给每个词一个固定向量("bank"无论指银行还是河岸都同一个),而 BERT/GPT 给的是随上下文变化的向量。但静态词向量至今仍是理解嵌入几何最干净的样本,而且在轻量召回、关键词扩展、可视化里依然实用——不是过时,是分工不同。
📌 BigCat 场景:做跨学科笔记时,把"佛学""复杂性科学""分布式系统"等概念词投到同一向量空间,用余弦相似度找意想不到的邻居——有时这些"几何近邻"正是你下一个跨界灵感的种子。
Takeaway + 思考题
💡 嵌入的威力不在"单个向量是什么",而在"向量之间的几何关系"——方向和距离都有语义。
🤔 如果"语义=方向",那是否存在一个词,它的向量恰好是另外两个概念向量的"中点"?这对应什么样的认知?

对比学习目标与表示坍缩Contrastive Objective & Representation Collapse

训练目标InfoNCE坍缩
一句话类比

对比学习 = 训练一个"语义指纹函数":同一份内容的不同表面形式(改写、翻译、加噪)要哈希到相邻位置(正样本拉近),不相关内容要哈希到远处(负样本推开)。表示坍缩就是这个哈希函数退化成"永远返回同一个值"——所有 key 撞进一个桶,相似度全是 1,等于没学。负样本就是防坍缩的那条约束。

它解决什么问题 + 工作机制

Word2Vec 给词,但我们更想要整句的好向量(用于检索、聚类)。问题:没有现成标签说"这两句意思一样"。对比学习的巧思——自己造正样本:对同一句话做两次轻微扰动(如两次不同 dropout),它俩天然是一对正样本;一个 batch 里的其他句子就当负样本。这正是 SimCSE(Gao 等, 2021)的做法。

训练目标是 InfoNCE 损失(van den Oord 等, 2018)。直觉:它是一道"N 选 1 的选择题"——给定锚点 query,在"1 个正样本 + 多个负样本"里,让模型给正样本最高相似度。公式:

L = −log [ exp(sim(q,k⁺)/τ) / Σⱼ exp(sim(q,kⱼ)/τ) ]

  • q, k⁺:锚点与它的正样本;kⱼ:遍历正样本+所有负样本;
  • sim:余弦相似度;τ(温度):缩放因子。τ 小→放大难负样本的惩罚,迫使边界更锐利;
  • 整体就是一个 softmax 分类:分子是"正确答案"的得分,分母是所有候选。最小化它=把 q 拉向 k⁺、推开其余。

为什么需要负样本?只拉正样本不推负样本,模型会发现一个偷懒解:把所有句子都映射到同一个点——正样本距离瞬间归零,损失却没真正学到区分。这就是表示坍缩。负样本在分母里制造"互斥张力",逼着表示铺满空间(均匀性),才不会塌缩。

对比学习:拉近正样本 + 推开负样本

正常:q←拉近→k⁺  k⁻←推开→ k⁻
    均衡张力 → 表示铺满空间(均匀)

坍缩(无负样本): qk⁺AB ← 全挤成一点
    相似度恒为 1,等于没学
代码示例
import torch, torch.nn.functional as F

def info_nce(z1, z2, tau=0.05):
    # z1[i] 与 z2[i] 是一对正样本;同 batch 其余皆为负样本
    z1, z2 = F.normalize(z1, dim=1), F.normalize(z2, dim=1)
    sim = z1 @ z2.T / tau          # [B,B] 相似度矩阵 / 温度
    labels = torch.arange(z1.size(0), device=z1.device)
    # 对角线 = 正样本,等价于"每行选对角"的分类题
    return F.cross_entropy(sim, labels)

# 同一 batch 过两次带 dropout 的编码器 → 得到两套表示(SimCSE 思路)
z1 = encoder(batch)   # dropout 随机性 → 天然正样本对
z2 = encoder(batch)
loss = info_nce(z1, z2)  # 拉近 (z1[i],z2[i]),推开其余
常见误区 + 实践场景
误区:"batch 越小越省,效果差不多"。对比学习里 batch size = 负样本数量,负样本越多,InfoNCE 这道"选择题"越难、信号越强——这是它对显存敏感的根本原因(不是工程调参癖好,是目标函数本身的几何要求)。负样本太少→均匀性不足→又向坍缩漂移。
📌 BigCat 场景:理解你的语义搜索/RAG 为何召回不稳,根子常在 embedding 模型的对比训练数据——若它没见过你的专业领域(分布式、佛学术语),这些词在它空间里区分度低,向量挤在一起,余弦相似度集体虚高。
Takeaway + 思考题
💡 对比学习的本质是"用关系当监督":不需要标签,只需要"谁和谁是一对、谁和谁不是"。负样本不是配角,是防坍缩的结构性约束。
🤔 InfoNCE 是"N 选 1 分类"。如果把人类学习新概念也看成"在干扰项里认出正确关联",这和对比学习有多像?
工程对应 → super-individual D11: RAG 实战工程

各向异性与白化Anisotropy & Whitening

几何病理后处理
一句话类比

各向异性(anisotropy)= 数据倾斜(data skew)的几何版。一个糟糕的哈希函数会把所有 key 挤进少数几个桶——余弦相似度因此被系统性抬高,所有句向量看起来"都挺像"。白化(whitening)就是给这堆挤成锥形的向量做"再均衡":线性变换一下,让它们重新均匀铺满空间,相似度才重新可信。

它解决什么问题 + 工作机制

直接拿 BERT 最后一层做句向量,效果意外地差。原因被诊断为各向异性:向量不是均匀分布在球面上,而是全部挤在一个狭窄的圆锥里(都指向相近方向)。后果:任取两个不相关句子,余弦相似度也有 0.7+,因为它们方向本来就接近——相似度失去了区分力

白化(Su 等, 2021,即 BERT-whitening)是个纯线性的后处理,不用重训。两步:

  • ① 去均值(center):把整堆向量平移到原点为中心——消除"集体偏向某方向"的偏置;
  • ② 去相关并缩放(decorrelate & scale):用协方差矩阵把各维度变成互不相关、方差为 1。几何上=把"被压扁的椭球"拉回"正圆球",即各向同性(isotropic)。

本质就是统计里的 PCA 白化:对协方差矩阵 Σ 求变换 W 使 WᵀΣW = I。变换后,余弦相似度重新有意义,STS 语义相似度任务上往往直接涨点;还能顺手做降维(只保留方差大的方向)。

各向异性 → 白化 → 各向同性

病理(锥形): //// ← 全挤一个方向
     任意两向量夹角都很小 → cos 虚高

── 去均值 + 去相关 + 缩放 ──▸

健康(球形): ↖ ↑ ↗ → ↘ ↓ ↙ ← 均匀铺满
     夹角分布合理 → cos 重新可信
代码示例
import numpy as np

def whitening_fit(embs):           # embs: [N, d] 一批句向量
    mu = embs.mean(axis=0, keepdims=True)  # ① 去均值
    cov = np.cov((embs - mu).T)         # 协方差矩阵 [d,d]
    u, s, _ = np.linalg.svd(cov)        # 特征分解
    # ② 变换矩阵 W:把协方差白化成单位阵
    W = u @ np.diag(1.0 / np.sqrt(s + 1e-8))
    return mu, W

mu, W = whitening_fit(train_embs)
# 应用:任何新向量都先减均值再乘 W,之后再算余弦相似度
def transform(x): return (x - mu) @ W
print(np.linalg.norm(transform(query)))  # 白化后再 normalize 检索
常见误区 + 实践场景
误区:"白化是万能后处理,套上必涨"。它假设"好的表示=各向同性",但白化会抹掉维度间的方差差异——若某些高方差方向本身携带重要语义,无差别拉平反而掉点。现代专门的 embedding 模型(经良好对比训练)各向异性已轻得多,白化收益就小。它是救场补丁,不是默认管线。
📌 BigCat 场景:自建知识库检索若发现"什么 query 都返回一堆 0.8+ 相似度、根本拉不开档次",先别急着换模型——这是各向异性的典型症状,用一批语料拟合一次白化矩阵,常常立竿见影。
Takeaway + 思考题
💡 "相似度高"不等于"真的相似"——若整个空间各向异性,高相似度可能只是几何假象。判断 embedding 好坏,要看相似度的区分度,不是绝对值。
🤔 白化让分布均匀,对比学习的"均匀性"也追求均匀。两者是同一目标的不同手段吗?一个后处理、一个训练时,哪个更治本?

Matryoshka 套娃表示Matryoshka Representation Learning

表示结构弹性维度
一句话类比

渐进式 JPEG:先传前一小段就能看到一张低清整图,数据越多越清晰。Matryoshka 训练出的向量,前缀的前 k 维就是一个能独立用的低清版本。也像复合索引——只读前几列就能做粗筛。存一次 1536 维,粗排截前 256 维(快),精排用全维(准),不必为不同精度各训一套。

它解决什么问题 + 工作机制

痛点:高维向量(1536 维)又准又贵——存储、内存、检索都随维度涨。但低维向量(快、省)又不够准。传统做法是为不同场景各训各的,或事后硬截断高维向量——而普通向量被截断后信息散落各维,前缀根本不能用,精度崩。

Matryoshka 表示学习(MRL,Kusupati 等, 2022)的巧思:训练时就逼着信息按重要性"由粗到细"地排进前缀。机制只是改了损失——不再只对完整维度算一次损失,而是对一组嵌套前缀(如 [64,128,256,512,1024,1536])各算一次损失再求和:

L = Σ_{k∈{64,128,...,1536}} Loss( embedding[:k] )

直觉:既然每个前缀都被要求独立完成任务,梯度就会把最关键的信息往最前面挤(因为前 64 维要独自顶用),后面的维度只做精细化补充。训练完,一个向量天然分层:截到哪一维都是该精度下"尽量好"的表示,而非随机碎片。开销几乎为零——只是多算几次损失,推理时一个前向。

一个向量,多种精度(信息由粗到细嵌套)

[0:64] 粗排/快查 — 省 24×,够用
[0:64][64:256] 中等精度
[0:64][64:256][256:1536] 全精度/精排

检索两段式:先用 64 维粗筛海量候选 → 全维精排 Top-K
代码示例
from openai import OpenAI
import numpy as np
client = OpenAI()  # 需要 OPENAI_API_KEY

# text-embedding-3 系列原生支持 Matryoshka:用 dimensions 截维
def embed(text, dim):
    r = client.embeddings.create(model="text-embedding-3-large",
                                 input=text, dimensions=dim)
    v = np.array(r.data[0].embedding)
    return v / np.linalg.norm(v)   # 截断后务必重新归一化

full  = embed("分布式系统的最终一致性", 3072)  # 全精度,精排
small = embed("分布式系统的最终一致性", 256)   # 低维,粗排快查
print(full.shape, small.shape)  # (3072,) (256,) — 同模型,一截即得
常见误区 + 实践场景
误区:"任何 embedding 都能随便截前几维省空间"。只有用 MRL 训练过的模型(如 OpenAI text-embedding-3、部分开源模型)才有这种前缀可用性。把普通模型的向量硬截,信息分散在各维、前缀语义残缺,精度会显著掉。还有:截断后一定要重新归一化,否则余弦相似度算错。
📌 BigCat 场景:个人知识库越攒越大时,用 Matryoshka 做两段式检索——256 维向量先从数万条里秒筛出几百候选,再用全维精排 Top-10。存储和首段延迟省一大截,精度几乎无损,是个人级"省钱不降质"的杠杆。
Takeaway + 思考题
💡 Matryoshka 把"精度 vs 成本"从训练期的二选一变成了推理期的滑块:一个向量,按预算自由截取。关键是它训练时就强制信息分层,不是事后硬切。
🤔 "重要信息排前面"这个归纳偏置,除了 embedding,还能用在哪?(模型权重?数据传输?注意力?)
工程对应 → super-individual D11: RAG 实战工程

深入资源Further Reading

深入思考Deep Questions

1. 词向量的"线性类比"(国王−男人+女人≈王后)到底是 Word2Vec 训练目标显式追求的,还是涌现出来的副产品?这区别为什么重要?
它基本是涌现的,不是显式优化目标。Word2Vec 的损失只要求"预测上下文",从没写过"让性别关系成为平行向量"。线性结构之所以出现,是因为语言里的语义关系本身具有系统性(每个国家都有首都、每个动词都有过去式),而模型用"上下文共现统计"拟合时,这些规律最经济的编码方式恰好是近似线性的位移。这区别极重要:(a) 涌现意味着它不可靠——没有约束保证它处处成立,生僻关系常失灵,别当推理引擎用;(b) 它揭示了:把"预测"做到极致,结构会自己长出来——这正是后来 GPT"下一词预测涌现出能力"的同源直觉。看清"涌现 vs 显式设计",你才不会过度信任某个看似神奇的几何性质。
2. 对比学习的"均匀性"(铺满球面)和白化的"各向同性"(把锥形拉回球形),看起来都在追求"向量别挤一起"。它们是一回事吗?
目标方向一致,手段和层次不同。白化事后线性变换:不改模型,只对已有向量做一次去均值+去相关,把全局的二阶统计(协方差)强行拉成单位阵——治标,且假设"各向同性=好",可能误伤携带语义的高方差方向。对比学习的均匀性训练时的非线性塑形:通过负样本的互斥张力,在学习过程中就把表示往球面均匀推,同时还保住"正样本贴近"(alignment)。SimCSE 论文正是用 alignment / uniformity 这对指标解释了它为何同时改善二者。哪个治本?对比学习——它在表示生成的源头调形,且能权衡"拉近正样本"与"铺开整体";白化只能在既成事实上做线性补救。现代好 embedding 模型各向异性已轻,正说明"源头治理"胜过"末端补丁"。但白化胜在零成本、即插即用,作为旧模型救场仍实用。
3. Matryoshka 的"重要信息排前面"是一种归纳偏置(inductive bias)。它和 PCA 降维的"保留方差最大的方向"在哲学上一样吗?
精神相通,但机制和适用性不同。PCA无监督、线性、事后的:它按"方差大小"排序方向,假设"方差大=信息多"——这在数据高斯、任务与方差对齐时成立,但方差大不一定语义重要(可能只是噪声幅度大)。Matryoshka有监督、非线性、训练内的:它不按方差排,而是按"对下游任务的有用性"排——因为前 64 维被损失直接逼着"独立完成任务",梯度自然把任务相关信息往前挤。所以 MRL 的"重要性"是任务定义的,PCA 的"重要性"是方差定义的,后者可能与任务南辕北辙。更深的共性:两者都体现了"表示应该有序、可截断"这一信念——拒绝"所有维度同等重要、不可分割"的默认假设。这个偏置一旦接受,你会发现它能推广到很多地方:渐进式传输、early-exit 网络、甚至课程学习(先学粗后学细)。
4. 如果 embedding 几何把"语义"编码成"距离和方向",那它能编码的语义有没有天花板?哪些东西注定塞不进一个向量?
有明确天花板,根源是固定维度几何本身的容量与拓扑限制。几个塞不进去的典型:(a) 组合爆炸的关系——单向量是"压缩态",当一句话涉及多实体多重关系("A 在 B 之前但在 C 之后,且依赖 D"),平铺成一个向量必丢结构,这也是 RAG 里长复杂文档召回差的几何原因;(b) 层级/树状结构——欧氏空间塞树会"指数级拥挤"(这正是双曲空间 embedding 的动机:负曲率天然适配层级);(c) 非对称关系——余弦相似度对称,但"A 蕴含 B"≠"B 蕴含 A",对称几何表达不了方向性蕴含;(d) 多义与语境——静态向量无法同时是"银行"和"河岸",这正是上下文化表示(BERT/GPT)取代静态词向量的根本原因。意涵:别迷信"万物皆可 embedding"。当任务依赖结构、方向、层级时,单一向量是有损投影——该上图结构、检索+推理或保留原始结构,而非压成一个点。
5. 把"理解一个新概念"类比成"在向量空间里给它找位置",这对你(BigCat)的跨学科学习有什么操作性启发?
这个类比比看上去更实用。如果"理解=在已有概念网里定位",高效学习就有了几何操作指南:(a) 用类比=向量平移——学新概念时主动找"它相当于我已懂领域里的什么+什么 diff",本 routine 一直用的"分布式类比 AI",本质就是在你熟悉的子空间里给新词找坐标,远比孤立记忆牢固;(b) 追求各向同性=避免认知坍缩——若所有新知识都往同一框架(比如全用分布式解释)挤,就会"各向异性":什么都觉得差不多,丧失区分力。刻意问"这个概念像什么"能拉开间距;(c) Matryoshka=分层理解——先用最粗的"一句话本质"抓住概念,再按需补全细节,符合认知负荷规律;(d) 跨学科创新=寻找异常近邻——佛学的"无常"与分布式的"最终一致性"在某个抽象空间里可能是近邻,这种跨域几何邻接往往是原创洞见的来源。把学习当成"维护一个各向同性、关系丰富的个人概念嵌入空间",是 AI 超级个体的元技能。