如果说 softmax 是把分数变概率的桥,embedding 就是把词变向量的桥。所有现代 NLP 模型的第一步都是 embedding,但它的设计直觉、历史演化和工程取舍很少被系统讲清楚。本文试着把这条线一次讲透。
写这篇时我特意翻了一下 1957 年 Firth 的那篇老论文,又翻了 2013 年 Mikolov 的 word2vec、2014 年 Pennington 的 GloVe、2018 年 Peters 的 ELMo 和 Devlin 的 BERT。半个多世纪的时间跨度,不同时代的人用截然不同的方法在解决同一个问题:「怎么用一组数字来表示一个词」。这个问题的答案变了几次,但核心思路始终在「分布假设」上。
读完本文,你应该能理解:embedding 不是一个孤立的工程技巧,而是一条从语言学假设、统计学方法、神经网络训练一路演化的概念线。理解了这条线,再看 Transformer 的 embedding 层、tied weights、subword 这些细节,就不会觉得它们是「凭空出现」的设计。
一、为什么需要 embedding:从 one-hot 的痛苦说起
1.1 计算机眼里的词是什么
打开任何 NLP 任务的代码,第一件事都是「分词 +
建词表」。设词表大小是 \(V\)(典型值:\(3 \times 10^4\) 到 \(1 \times
10^5\))。每个词获得一个唯一整数 ID,比如
cat = 1234。
但神经网络不能直接处理整数
ID。整数有大小关系(1234 < 1235),但词语之间没有这种「序」(cat
不该比 cap 大)。把 ID
当数字喂进去会让网络学到错误的归纳偏置。
最朴素的解决办法是 one-hot 编码:把 ID
1234 变成长度 \(V\) 的向量,第 1234 位是
1,其他都是
0。这样每个词都是一个独立的「方向」,没有大小关系,互相正交。
1.2 one-hot 的三个致命问题
第一个问题是维度灾难。\(V = 5 \times 10^4\) 时,每个词是 5 万维的向量。一句话 100 个词就是 100 个这样的向量。存储和计算都很恐怖。
第二个问题是稀疏性。one-hot 向量 99.998% 都是 0。把这种稀疏向量送进神经网络的全连接层,等价于「查矩阵的某一列」——这个操作不需要乘以一整个矩阵,只需要查表。这意味着 one-hot 把神经网络的功能浪费了。
第三个问题,也是最致命的:所有词之间距离相同。任意两个
one-hot 向量的欧氏距离都是 \(\sqrt{2}\),余弦相似度都是
0。模型完全不知道 cat 和 dog
应该比 cat 和 apple
更相似。这意味着 one-hot 抹掉了所有语义信息。
1.3 我们到底想要什么样的表示
把上面的问题翻一翻,「想要的表示」就清楚了:
第一,低维。用几百维(典型 256~1024)就够,不用几万维。
第二,稠密。每一维都有非零值,每一维都参与计算,矩阵乘法发挥作用。
第三,语义距离有意义。相似的词在向量空间里靠近,不同的词远离。这是核心。
满足这三条的表示叫「分布式表示」(distributed representation)或「dense embedding」。这个目标是清楚的,但怎么训出这种表示?这就是 70 年来的故事。
1.4 一个历史小注
「embedding」这个词本身在数学里有「嵌入」的含义:把一个对象嵌入到另一个空间。比如群嵌入到群、流形嵌入到欧氏空间。NLP 借用了这个词:把离散的词「嵌入」到连续的向量空间。
最早系统化用「embedding」这个词的论文之一是 Bengio 2003 的「A Neural Probabilistic Language Model」。在他的框架里,词被嵌入到一个低维向量空间,再用神经网络做语言建模。这是后来所有 word embedding 工作的祖宗。
但「分布式表示」这个想法可以追溯到更远——Hinton 1986 的「Learning representations by back-propagating errors」就在讨论分布式表示的概念。那时候还没有大规模训练 NLP 模型的能力,但思想已经在了。
1.5 一个直觉性的目标
设想我们最终成功训出了 embedding。那它应该满足这种性质:
embedding("king") - embedding("man") + embedding("woman") ≈ embedding("queen")
这是 Mikolov 2013 那篇 word2vec
论文里最经典的结果。它告诉我们:embedding
不仅捕捉了「相似性」,还捕捉了「类比关系」。king - man + woman = queen
不是巧合,而是 embedding 空间内禀的几何结构。
第一次看到这个结果的时候人们是震惊的——一个用「预测周围词」训练出来的向量空间,竟然天然包含了「性别」「皇室」等抽象概念,而且这些概念表现为线性子空间。这是 NLP 研究范式的一次转折。
1.6 从 LSA 到 dense embedding 的过渡
在神经网络之前,NLP 用过另一条路径:Latent Semantic Analysis(LSA / LSI,Deerwester 1990)。LSA 的做法是:构建词-文档共现矩阵,做奇异值分解(SVD),保留前 \(k\) 个奇异值方向,得到每个词的 \(k\) 维向量。
LSA 的向量也是低维稠密的,也能表达语义相似(比如「car」和「automobile」会靠近)。本质上 LSA 是一种线性的 embedding 方法。
word2vec 出来之后大家发现:word2vec 的非线性优势其实不大,很多场景下 LSA 也能做。Levy & Goldberg 2014 的工作甚至证明 SGNS(Skip-gram + negative sampling)在数学上等价于 PMI 矩阵的隐式分解——也是一种矩阵分解。
这给我们一个 humbling 的启示:很多看起来「神经网络才能做」的事情,其实 30 年前的线性方法已经在做了。神经网络的优势在 scale 和灵活性,不在「线性 vs 非线性」这个对立。
1.7 embedding 与 representation learning
更广义地,embedding 是 representation learning(表征学习)的一个特例:把原始数据(离散符号、图像像素、音频波形)映射到一个适合下游任务的向量空间。
图像有 image embedding(CNN/ViT 的中间层输出),音频有 audio embedding(wav2vec),代码有 code embedding,分子有 molecular embedding,蛋白质有 protein embedding。所有这些都是同一个思路的具体化:把「原始的、离散的、不规则的」对象变成「连续的、规则的、可计算的」向量。
这个统一的视角让 embedding 不再仅仅是 NLP 概念,而是机器学习里普遍的「数字化」工具。
二、Distributional Hypothesis:一切的起点
2.1 Firth 那句著名的话
1957 年,英国语言学家 J. R. Firth 写下:
“You shall know a word by the company it keeps.”
直译:你能从一个词的伙伴中认识它。意思是:一个词的意义,由它经常和哪些词一起出现来决定。
这听起来朴素,但是一个深刻的转折。在 Firth 之前,主流的语义学走的是「哲学定义」路线——给「猫」下定义需要列出「四足、有毛、会捉鼠、家养」等属性。Firth 说:你不需要定义,只要观察这个词周围出现什么词就行。
「猫」周围常出现「miaow」「fur」「mouse」「pet」;「狗」周围常出现「bark」「fur」「bone」「pet」;两者周围词重叠很多,所以「猫」「狗」语义相近。「苹果」周围常出现「fruit」「red」「eat」,跟「猫」「狗」重叠少,所以语义远。
Firth 这句话之所以被引用了无数遍,是因为它把一个抽象的语义学问题变成了一个可计算的统计学问题。在那个没有大数据、没有大算力的年代,他的话更像是一种远见,要再过 50 年才被工程化兑现。
2.2 这个假设有多大胆
Firth 的假设把「语义」从「内涵」转成了「外延的统计」。这是一个非常大胆的简化——它实际上把语义全部还原为词的共现统计。
这种还原在哲学上有问题:「上帝」这个词的语义能从它的共现词学出来吗?「自由」呢?「正义」呢?显然,对这些抽象概念,单纯的共现统计可能学到的只是这些词在某种文本里的「使用模式」,而不是它们的「真实语义」。
但实践上,Firth 的假设非常好用。计算机能处理大量文本,能精确统计共现,能把这些统计变成向量空间。这个假设把「语义建模」变成了「统计学习」,从此 NLP 不再依赖人工词典和规则。
2.3 Harris 的版本
跟 Firth 几乎同时代,美国语言学家 Zellig Harris 在 1954 年也提出了类似的假设。他用更数学化的语言:
「在相同的语言环境里出现的词,倾向于有相似的意义。」(Distributional structure)
Harris 还指出可以用矩阵代数来分析这种共现关系——在 60 年前他就预见了用线性代数处理语义的可能。这条线后来催生了「distributional semantics」整个研究领域,最终在 word2vec、GloVe 这些工作里开花结果。
2.4 共现矩阵的雏形
把分布假设具体化成算法的第一步是「共现矩阵」。建一个 \(V \times V\) 的矩阵 \(M\),\(M_{ij}\) 是词 \(i\) 和词 \(j\) 在某个上下文窗口里共现的次数。
这个矩阵每一行可以看作那个词的向量表示。但它有问题:太大、太稀疏、被高频词主导(比如「the」和所有词都共现,没区分度)。
后续工作的核心都是想办法让这个共现矩阵「变好」:用 PMI(pointwise mutual information)替代原始计数解决高频词主导问题;用 SVD 降维解决稀疏性和维度问题;用预测式的损失(word2vec)替代统计式的拟合(共现矩阵)。
2.5 PMI 与 PPMI
直接用共现次数有偏。比如 (the, cat) 共现 1000 次,(cat, miaow) 共现 100 次。表面上前者更紧密,但「the」太常见了,跟所有词共现都多。真正的「关联强度」要看「比独立期望多多少」。
PMI(pointwise mutual information):
\[ \mathrm{PMI}(i, j) = \log \frac{P(i, j)}{P(i) P(j)}. \]
正值表示「共现比独立期望多」,越大关联越强;负值表示「共现比独立期望少」,几乎不重要。负值常常被截断为 0,得到 PPMI(positive PMI)。
PPMI 矩阵是早期分布式表示的基石。Levy & Goldberg 2014 证明了 word2vec 在某种意义下等价于隐式分解 PMI 矩阵。这条线索把现代神经词向量和经典统计语义连接到了一起。
2.6 SVD 降维
有了 PPMI 矩阵 \(M \in \mathbb{R}^{V \times V}\),再做 SVD:\(M = U \Sigma V^\top\),取 \(U\) 的前 \(d\) 列就是词向量。这就是 LSA(Latent Semantic Analysis,1990 年提出)的思路。
LSA 在 word2vec 之前是分布语义的主流方法。它的优点是数学清晰、有理论保证;缺点是 SVD 计算昂贵、在线更新困难、捕捉不到非线性关系。
2.7 共现矩阵的稀疏挑战
理论上 \(V \times V\) 共现矩阵有 \(V^2\) 项,但绝大多数是 0。\(V = 100k\) 时有 \(10^{10}\) 个项,存不下;幸好实际非零项可能只有 \(10^7\) 量级,存稀疏矩阵就够了。
但稀疏矩阵的 SVD 比稠密 SVD 麻烦得多:你需要 Lanczos 迭代或类似的稀疏特征值方法,工程实现复杂。这是 1990 年代信息检索领域的一个研究方向,催生了 ARPACK 这样的稀疏特征值库。
word2vec 的优势之一就是绕过这个问题:不显式构建共现矩阵,直接用滑窗在语料上跑 SGD,每步只看一对词。这在工程上简单得多。
2.8 PMI 的对称性与 directionality
PMI(x, y) = PMI(y, x),对称。这意味着「以 x 为中心、y 在窗口内」和「以 y 为中心、x 在窗口内」的统计是同一回事。
但有些语言任务关心方向:「主语 → 谓语」和「谓语 → 主语」语义不同。简单 PMI 抹掉这个信息。后来一些工作(比如 dependency-based embedding)显式引入方向,但主流仍然是无向的——大数据下,方向信息可以靠 word order 在更大模型里学回来。
word2vec 出现后 LSA 一度淡出,但近年又回归——人们意识到 word2vec 训出来的向量其实跟 SVD 分解 PPMI 的结果惊人相似,只是 word2vec 用了更高效的训练方法。
三、Word2Vec:神经网络方法的开端
3.1 Mikolov 的工作
2013 年,Tomas Mikolov 等人在 Google 发布了 word2vec。这是 NLP 的一次范式革命:第一次有了一个能在大规模语料(几十亿词)上高效训练高质量词向量的方法。
word2vec 不是一个模型,是两个模型 + 两个加速技巧的组合:
- 两个模型:CBOW(Continuous Bag-of-Words)和 Skip-gram。
- 两个加速:Hierarchical Softmax 和 Negative Sampling。
我们逐个讲。
值得提的是,Mikolov 在做 word2vec 之前已经在 RNN 语言模型上做了好几年研究(他的博士论文就是 RNNLM)。word2vec 的诞生不是凭空冒出来的,而是他从 RNN 语言模型的「副产物」(中间层的词向量)反思而来——「能不能不要这么复杂的模型,直接用一个最简单的目标训词向量?」
这种「极简化」的思路在工程上特别有价值。后来人们意识到,word2vec 的成功很大程度上来自「简单到足以在大数据上跑起来」,而不是模型本身有多巧妙。这是工程实践里反复出现的教训:能 scale 的简单方法,往往比 scale 不上去的精巧方法赢。
3.2 CBOW:上下文预测中心词
CBOW 的训练目标:给定上下文窗口 \(\{w_{t-k}, \ldots, w_{t-1}, w_{t+1}, \ldots, w_{t+k}\}\),预测中心词 \(w_t\)。
具体做法:把上下文每个词的 embedding 求平均得到 \(\mathbf{h}\),再用 softmax 预测中心词:
\[ P(w_t | \text{context}) = \frac{\exp(\mathbf{v}_{w_t}^\top \mathbf{h})}{\sum_{w \in V} \exp(\mathbf{v}_w^\top \mathbf{h})}. \]
minimum cross-entropy 训练。
CBOW 的特点:训练速度快(每个样本只有一个目标),高频词学得好(出现次数多,被预测多次),但低频词信号弱。
3.3 Skip-gram:中心词预测上下文
Skip-gram 反过来:给定中心词 \(w_t\),预测上下文里的每个词。
\[ P(w_{t+j} | w_t) = \frac{\exp(\mathbf{v}_{w_{t+j}}^\top \mathbf{u}_{w_t})}{\sum_{w \in V} \exp(\mathbf{v}_w^\top \mathbf{u}_{w_t})}. \]
注意这里有两套 embedding:\(\mathbf{u}\) 是中心词的,\(\mathbf{v}\) 是上下文词的。最终用 \(\mathbf{u}\)(或两者平均)作为词向量。
Skip-gram 训练慢(每个中心词产生多个上下文目标),但低频词学得好(每出现一次,所有上下文都来给它一次梯度信号)。
3.4 选择哪个
经验上,Skip-gram 在大多数任务上比 CBOW 略好(特别是稀有词的语义),但 CBOW 训练更快。Mikolov 论文里两者都用,主要给出 Skip-gram 的结果。
实际上后来主流做法是用 Skip-gram 加 negative sampling。这是 2013 年第二篇 word2vec 论文(NIPS 版本)介绍的「SGNS」(Skip-gram with Negative Sampling),是 word2vec 的「标准实现」。
3.5 全词表 softmax 太慢
注意 3.2 和 3.3 的公式分母都是「在整个词表上求和」。\(V = 10^5\) 时每次都要算 \(10^5\) 次 exp 和归一化,慢得不能忍受。
这就需要加速技巧。第一种是 Hierarchical Softmax:把词表组织成二叉树(霍夫曼树),每个词对应从根到叶的路径,预测词等价于在路径上做 \(O(\log V)\) 次二分类。把 \(O(V)\) 降到 \(O(\log V)\)。
第二种是 Negative Sampling(NEG):不算完整 softmax,而是对每个正样本(真正的上下文词),随机采几个「负样本」(不是上下文的词),用 binary cross-entropy 训练区分正负。这把 \(O(V)\) 降到 \(O(k+1)\),\(k\) 是负样本数(典型 5~20)。
NEG 比 Hierarchical Softmax 简单且经验上效果略好,是 word2vec 的标准选择。
3.6 SGNS 的目标函数
具体写一下 SGNS。中心词 \(c\),上下文词 \(w\),用 sigmoid 替代 softmax:
\[ \mathcal{L} = -\log \sigma(\mathbf{v}_w^\top \mathbf{u}_c) - \sum_{i=1}^{k} \mathbb{E}_{w_i \sim P_n}[\log \sigma(-\mathbf{v}_{w_i}^\top \mathbf{u}_c)]. \]
第一项让正样本的点积大;第二项让 \(k\) 个负样本(从噪声分布 \(P_n\) 采样)的点积小。\(P_n\) 通常是 unigram 分布的 0.75 次幂——这个 0.75 是经验调出来的,能让低频词被采样到的概率比 unigram 更高,从而学得更好。
3.7 经典结果:king - man + woman = queen
word2vec 论文里最让人印象深刻的实验是「类比测试」。给定 (a, b, c),要找 d 使得 a:b = c:d。比如 (man, king, woman) → queen。
具体做法:在 embedding 空间里算 \(\mathbf{v}_b - \mathbf{v}_a + \mathbf{v}_c\),然后找词表里向量与之最接近的词。在 word2vec 训出来的 embedding 上,这种类比有 60-70% 的正确率。
这意味着 embedding 空间里「king - man」这个差向量大致代表「皇室身份」这个抽象概念,跨词类型保持稳定。这是「线性结构」的强证据,也是 word2vec 让世界为之惊艳的根源。
3.8 word2vec 的其他八卦
word2vec 一开始是 Mikolov 在 Google 实验室里做的内部项目,2013 年放出来时整个 NLP 社区都疯了——因为它训得又快又好,远超之前所有方法。
有一段时间,「跑 word2vec 训词向量」成了几乎每个 NLP 项目的开头第一步。直到 2018 年 ELMo/BERT 出现,预训练词向量的范式才被「预训练上下文表示」取代。但即使到今天,很多简单任务(比如基于规则的关键词匹配)仍然用 word2vec,因为它便宜、快、够用。
Mikolov 后来跳槽到 Facebook,做了 fastText(subword embedding),是 word2vec 的延伸。这是另一段故事,跟 BPE 那一篇有关。
3.9 word2vec 等价于矩阵分解
Levy & Goldberg 2014 证明了一个让人惊讶的结论:SGNS 在某种意义下等价于隐式分解 shifted PMI 矩阵。
具体说:当 SGNS 收敛时,\(\mathbf{u}_w^\top \mathbf{v}_c \approx \mathrm{PMI}(w, c) - \log k\),其中 \(k\) 是负样本数。也就是说 word2vec 在对 PMI 矩阵做低秩近似。
这个结论把 word2vec 和经典分布语义(PPMI + SVD)联系起来。两条看似完全不同的路径,本质上做的是同一件事。这是 NLP 领域里少见的「不同方法的惊人统一」。
3.10 word2vec 的工程参数
实际跑 word2vec 时常见的超参:词向量维度 100-300;窗口大小 5(CBOW)或 10(Skip-gram);负样本数 5-15;学习率 0.025(线性衰减);下采样阈值 \(10^{-3}\) 到 \(10^{-5}\)(高频词被随机丢弃);最少出现次数 5(频率太低的词不学)。
这些参数有「最佳实践」但没有最优解。Mikolov 的开源代码 word2vec.c 是一个 C 实现,跑 6B 词只要几小时(多核 CPU)。这种「能在 CPU 上几小时跑完」的便利性是它能流行的工程基础。
3.11 word2vec 后的衍生
word2vec 之后衍生出无数变体:
- Doc2Vec(Le & Mikolov 2014):扩展到段落和文档级 embedding。
- node2vec(Grover 2016):把 Skip-gram 思路用到图节点上,做随机游走作为「上下文」。
- item2vec:用户行为序列里的物品,类比词序列里的词。推荐系统大量使用。
- prod2vec、event2vec、anything2vec:「2vec 现象」,几乎每个领域都套用了一遍。
这些工作把 word2vec 的核心方法(局部窗口预测 + negative sampling)迁移到各种离散对象上,是 representation learning 跨领域扩散的一个早期范例。
四、GloVe:全局共现的回归
4.1 word2vec 的局限
word2vec 是「局部窗口」方法:每次只看一个词及其几个邻居。这意味着它没有显式利用「整个语料里的全局共现统计」。
在 SVD/LSA 那条路径里,全局共现矩阵是出发点;在 word2vec 那条路径里,全局信息被「分散在数百万次局部样本」中间接学到。两条路各有优劣。
Pennington、Socher、Manning 在 2014 年提出 GloVe(Global Vectors),尝试把两条路结合起来:直接用全局共现矩阵作为目标,但用神经网络的方法去拟合它。
4.2 GloVe 的目标函数
设 \(X_{ij}\) 是词 \(i\) 和词 \(j\) 的共现次数。GloVe 的目标:
\[ \mathcal{L} = \sum_{i,j} f(X_{ij}) (\mathbf{w}_i^\top \tilde{\mathbf{w}}_j + b_i + \tilde{b}_j - \log X_{ij})^2. \]
直观解释:用两个向量的点积(加偏置)拟合共现次数的对数。\(f(X_{ij})\) 是一个权重函数,对很高的共现做截断,避免被「the the」这种统计主导。
4.3 它和 word2vec 的关系
实践上,GloVe 和 word2vec 在很多下游任务上效果接近,没有压倒性优势。但 GloVe 的训练直接用全局矩阵,可以并行化得更彻底,工程上很有吸引力。
斯坦福放出来的 GloVe 预训练向量(300 维、840B 词)一度是几乎所有 NLP 项目的默认起点。直到 ELMo / BERT 替代它们。
4.4 GloVe 的几何意义
GloVe 论文里有一个非常漂亮的推导:从「词向量应该满足某些函数关系」的需求出发,反推出「点积加偏置等于 log 共现」这个目标函数。这种「从性质推目标」的推导很值得学习——相比 word2vec 的「先有模型再说」,GloVe 的推导更有数学风味。
但这种数学优雅没有在效果上带来质的差异。事实证明,分布式语义这个领域,方法的细节没有大家想象的那么关键,关键是数据规模和训练 trick。
4.5 加权函数 \(f(X_{ij})\)
GloVe 的加权函数典型形式是:
\[ f(x) = \begin{cases} (x/x_{\max})^{\alpha}, & x < x_{\max} \\ 1, & x \ge x_{\max} \end{cases} \]
\(x_{\max}=100\),\(\alpha=3/4\) 是论文里的取值。它做了两件事:第一,过滤掉 \(X_{ij}=0\) 的项(log 0 没定义);第二,截断高频对的权重,避免「the of」、「of the」这种泛滥的共现支配整个目标函数。
这个 \(\alpha=3/4\) 不是巧合——和 word2vec negative sampling 里采样分布的 3/4 次方是同一个数。这个数在分布式语义里反复出现,背后大概有共通的频率分布解释,但没有理论上的精确推导。
4.6 GloVe 的训练实现
GloVe 第一步要扫一遍语料,构建共现矩阵 \(X_{ij}\)。这一步需要决定窗口大小(典型 5 或 10)和加权方式(距离衰减常用 \(1/d\))。结果是一个稀疏矩阵,可能有几百万到几千万个非零项。
第二步是优化。GloVe 用 AdaGrad 在非零项上跑 SGD。每个非零项独立计算梯度,所以训练高度并行。300 维 GloVe 在 6B 词上训练大概几小时(多核 CPU),比 word2vec 还快。
GloVe 还有一个细节:每个词学两套向量 \(\mathbf{w}\) 和 \(\tilde{\mathbf{w}}\),对应「中心词」和「上下文词」。最后用的时候把两套向量相加。这个 trick 比单套效果略好,估计是某种集成效应。
4.7 GloVe 训练好的预训练词向量
斯坦福放出的 GloVe 预训练向量有几个版本:6B(Wikipedia + Gigaword,6B token,词表 400k,50/100/200/300 维),42B(Common Crawl,42B token,词表 1.9M,300 维),840B(Common Crawl,840B token,词表 2.2M,300 维)。
工业界默认用 840B 的版本,因为它见过的语料最大、覆盖最广。但有时小一点的(6B)反而在某些 benchmark 上表现更好——更小的词表 + 更干净的数据可能让向量更聚焦。
这些预训练向量从 2014 年到 2018 年(ELMo / BERT 出来之前)一直是 NLP 的事实标准基础设施。
4.8 GloVe 与 word2vec 的对比
实证 benchmark 上两者难分高下。GloVe 在某些类比任务(语义类比)上略好,word2vec 在某些 syntactic 任务上略好。具体差异往往在百分点以下,被超参选择和数据预处理的差异淹没。
实践上选择常常是工程驱动的:训练资源少用 GloVe(一次扫语料即可),训练资源多用 Skip-gram(更精细的局部建模)。多数项目用预训练向量,所以选哪个其实就是「下载哪个文件」的问题。
4.9 GloVe 与近年的复兴
近年来一些工作回到 GloVe 类的「显式共现矩阵」思路:LLM 时代的 context 越来越长,embedding 训练数据越来越多,全局共现矩阵反而再次有可行的 scale。但这些尝试多数还在研究阶段,没有变成工业主流。
历史经常重复:一个老方法在新条件下复活。GloVe 思路是否会在 LLM embedding 时代再次复兴,值得观察。
4.10 GloVe 与 Transformer 训练目标的对比
GloVe 用 log-bilinear 损失拟合共现 log 计数,是「在共现统计上做线性回归」。Transformer 的 next-token prediction 是「在条件分布上做最大似然」。表面上完全不同,但都从语料里学语义。
近年一些理论工作尝试统一这两条线:用 statistical interpretation 来分析 Transformer,发现它隐式建模了某种共现统计的高阶版本。但这是开放领域,没有结论。
五、静态 embedding 的局限
5.1 一词多义的问题
word2vec、GloVe 都是「静态 embedding」:一个词对应一个向量,永远不变。这有一个明显问题:一词多义。
「bank」可以是「银行」也可以是「河岸」;「apple」可以是「水果」也可以是「公司」;「Java」可以是「编程语言」也可以是「咖啡」也可以是「印尼岛」。静态 embedding 把这些含义平均成一个向量,结果是它对每种含义都不准。
5.2 上下文应该改变 embedding
人类读「I deposited money at the bank」和「I sat by the bank」时,「bank」激发的是不同的语义。embedding 应该跟随上下文变化。
ELMo(Peters 2018)是第一个系统做「上下文化 embedding」的工作。它的思路:用一个双向 LSTM 在大语料上做语言模型预训练,然后把 LSTM 的隐藏状态作为词的「上下文 embedding」。同一个词在不同句子里有不同的向量。
5.3 ELMo 到 BERT
ELMo 是 LSTM 的,BERT(Devlin 2018)把这一套搬到 Transformer 上,用 masked language model 训练,得到了更强的上下文 embedding。BERT 之后所有现代 NLP 模型都是「上下文 embedding」的范式。
GPT 系列也是。GPT 用单向 Transformer 做自回归 LM,得到的 hidden state 同样是上下文 embedding。本质上 GPT 是个语言模型,它的中间层每个位置的向量就是那个 token 在那个位置的「上下文 embedding」。
我们后面第 25 篇 BERT、第 26 篇 GPT 会展开。
5.4 static 还有用吗
虽然现在主流是上下文 embedding,但 static embedding 没死。一些场景仍然适用:
资源受限场景(移动端、嵌入式):BERT 太大,跑 word2vec 加余弦相似度就够。
简单关键词任务(搜索、推荐):很多工业场景不需要那么强的语义建模。
热启动场景:训练 BERT 时偶尔会用 word2vec 初始化某些层,加速收敛。
5.5 contextual embedding 的层间差异
BERT 的不同层学到的 embedding 性质不同。Tenney et al. 2019 的 “BERT Rediscovers the Classical NLP Pipeline” 系统分析过这个:底层学到的是 surface 特征(词性、词形),中层是句法(依存、constituency),高层是语义(共指、逻辑)。
这意味着「BERT 的 embedding」不是一个东西,而是一个层次结构。下游任务用 BERT 时究竟该用哪一层的输出?取决于任务性质。POS tagging 用底层就够;问答更适合高层。
ELMo 的标准做法是把所有层的输出做加权平均(权重学得到),让下游任务自己选。这是一种灵活但参数多的方案。
5.6 BERT [CLS] token 的故事
BERT 在每个输入序列前面加一个特殊 token
[CLS],预训练时让它的 embedding
学习「整个序列的全局表示」。这样下游分类任务可以直接用
[CLS] 的输出向量。
但实践发现 [CLS]
不一定是「最佳的句子表示」——SBERT 等工作显示 mean pooling
经常更好。这又一次说明 embedding
的「最佳用法」需要任务驱动调研,没有 one-size-fits-all。
5.7 ELMo 的双向 LSTM 细节
ELMo 用的是「双向语言模型」:从左到右一个 LSTM 跑 \(P(w_t | w_{<t})\),从右到左一个 LSTM 跑 \(P(w_t | w_{>t})\),两个独立训练。最终的 embedding 是两个方向各层的拼接。
注意 ELMo 不是真正的「双向」——左右两个方向是独立的,没有交互。BERT 才是真正双向的(masked language model 让任意位置可以同时看左右)。这是 ELMo 到 BERT 的关键升级。
5.8 上下文 embedding 的「身份问题」
哲学性问题:同一个词在两个不同句子里的 embedding 是否还是「同一个词」?严格说,contextual embedding 把「词」打散成「词在某语境下的实例」,每个实例有自己的向量。
这意味着传统词典里那种「词的定义」其实在 contextual 模型里不存在——只有大量上下文实例的统计分布。这是语义观的一次哲学转向,从「词的本质」到「词在语境里的使用」。
六、Transformer 的 embedding 矩阵
6.1 输入端的 embedding
Transformer 的输入流程:token IDs → embedding lookup → embedding 向量 → 加位置编码 → 送进 transformer block。
embedding lookup 是一个矩阵 \(E
\in \mathbb{R}^{V \times d}\),给定 token ID
i,输出 \(E[i,
:]\),即矩阵的第 i 行。这本质上是「one-hot
向量乘以矩阵」,但工程实现就是查表,比真的做矩阵乘快得多。
\(E\) 是可学习的参数。训练时它从随机初始化开始,通过反向传播逐步学到「让任务表现最好的词向量」。这跟 word2vec 不同——word2vec 是先单独训练词向量再用,Transformer 是端到端学。
6.2 维度选择
\(d\)(embedding 维度)的典型值:BERT-base 是 768,BERT-large 是 1024,GPT-3 是 12288,GPT-4 推测在万级别。
\(d\) 越大表达能力越强但参数越多。对于词表 5 万、\(d=1024\) 的模型,embedding 矩阵有 \(5 \times 10^7\) 个参数。这通常占整个模型参数的 5-10%,不算大头但也不小。
6.3 tied embedding:输入和输出共享
观察:模型最后一层把隐藏状态投影回词表得到 logits,需要一个 \(W \in \mathbb{R}^{V \times d}\) 矩阵。这个矩阵的形状跟输入 embedding 矩阵 \(E\) 一样。
是否可以让它们共享参数?\(W = E\)?答案是肯定的,叫「tied weights」或「weight tying」(Press & Wolf 2017)。
为什么有用:第一,省一半 embedding 参数;第二,实证上略提升泛化性。直觉解释:「这个词在输入时的语义」和「这个词作为输出时的概率分布」应该是一致的,强制共享可以利用这种对称性。
GPT-2、GPT-3、LLaMA 都用 tied embedding。BERT 没有完全 tie。这是工程上的一个非默认但常见选择。
6.4 token + position
embedding 不仅是 token 的,还有位置的。Transformer 没有 RNN 的天然顺序信息,需要显式注入位置。
最简单的:每个位置 \(p\) 也对应一个向量 \(E_p^{\text{pos}}\),把它加到 token embedding 上:
\[ \mathbf{x}_p = E[\text{tokenID}(p), :] + E_p^{\text{pos}}. \]
后面第 21 篇专门讲位置编码(绝对、相对、RoPE 等)。这里只先把「embedding 包含 token + position」的图建起来。
6.5 normalization 的位置
很多实现里 embedding 之后会立刻接一个 LayerNorm(或 RMSNorm)。这是为了让初始的输入分布稳定,避免某些 token 的 embedding 范数特别大主导后面所有计算。
LLaMA 用 RMSNorm 替代 LayerNorm,位置仍然是 embedding 之后。这些细节看似无关紧要,但在大规模训练时影响稳定性。第 23 篇 LayerNorm 会展开。
6.6 embedding 与 logit 共享导致的尺度问题
tied embedding 还有一个微妙的副作用。embedding 表达「token 的语义」,logit 表达「下一个 token 是它的对数几率」。这两者的「自然尺度」未必一样。
具体说,embedding 范数适合大约 1(让点积稳定),logit 适合范围 \(\pm\) 几(softmax 之前)。tied weights 让它们必须共用同一矩阵,可能在某一面是次优的。
实践中常见的修正:在 logit 端额外乘一个可学习的缩放因子,或者在 logit 前再过一个 LayerNorm。这些细节在 GPT-Neo、LLaMA 的源代码里都能看到。
6.7 vocabulary 的扩展
预训练完成后,有时需要添加新词到词表(新领域、新语言)。怎么处理?
最简单:新词用「相近词的 embedding」初始化,然后微调。实际上有时简单的随机初始化也能工作,因为 fine-tune 会快速学到合适的值。
更复杂的扩展(添加几千个新词)需要先在新词表上跑一段时间的预训练,让 embedding 收敛。这是 LLM 多语言扩展时的工程挑战。
6.8 embedding 的 freeze 与 unfreeze
迁移学习时一个常见问题:是冻结 embedding 还是让它继续训练?
冻结的好处:保留预训练知识、防止小数据下灾难性遗忘。坏处:限制了模型适应新领域的能力。
实践上常见的策略是「分层学习率」:embedding
用很小的学习率(让它微调但不剧烈变动),上层 transformer
用正常学习率。HuggingFace Transformers 库的
discriminative_learning_rate 就是这种思路。
6.9 embedding 在量化部署里
LLM 部署时常做量化(INT8、INT4)。embedding 矩阵作为最大的单一参数块,是量化的重点。
INT8 量化通常对 embedding 影响很小(几百维向量的小数值精度差异不致命)。但 INT4 / INT2 这种激进量化下,embedding 量化误差会传播到所有下游层,要谨慎。GPTQ、AWQ 等量化方法都有针对 embedding 的特殊处理。
七、subword:让 embedding 处理无限词表
7.1 OOV 问题
预训练词向量永远会遇到「词表外」(out-of-vocabulary, OOV)问题:训练时没见过的词,inference 时怎么办?
word2vec 一般给个 <UNK> token
兜底。这是粗糙的处理——所有没见过的词都映射到同一个向量,丧失区分度。
更优雅的做法是「subword」:把词拆成更小的单位(字符、字符 n-gram、BPE pieces)。即使整个词没见过,组成它的 subword 大概率见过,于是可以从 subword 组合出词的表示。
7.2 BPE 的引入
BPE(Byte-Pair Encoding)是 GPT、LLaMA 等模型用的子词切分算法。它从字符级别开始,逐步合并最常出现的 bigram,直到达到目标词表大小。
BPE 让词表里同时包含完整词(the, cat)、词根(un-, -ing)、单字符(a, b, c),覆盖几乎所有可能输入。即使是没见过的新词(比如人名),也能被切成已知的 subword。
第 29 篇专门讲 tokenization(BPE、WordPiece、SentencePiece 等),这里只先说:subword 是现代 LLM 处理无限词表的标准方案。
7.3 fastText:在 embedding 层做 subword
fastText(Bojanowski 2016)把 subword 思想直接放到 embedding 层:每个词的 embedding 是它所有字符 n-gram embedding 的平均。
好处:未见词也能算 embedding(只要字符 n-gram 见过);morphologically 相关的词(cat, cats, catlike)共享部分 embedding,参数效率高。
fastText 主要在静态 embedding 时代用。Transformer 时代主流改用 BPE 类的子词切分,但思路是延续的。
7.4 字符级 vs 子词级 vs 词级
历史上有过三种切分粒度:字符级、子词级、词级。
字符级(character-level):词表小(几百个字符)、长序列、零 OOV。早期工作 Kim 2016 的 char-CNN 用这个。缺点:序列长,\(O(N^2)\) attention 开销大。
词级(word-level):词表大(几万到几十万)、序列短、有 OOV。早期 word2vec、GloVe 用这个。缺点:OOV 问题、词表参数多。
子词级(subword-level):折中。词表中等(一万到十万)、序列中等长、几乎无 OOV。BPE 类算法。现代 LLM 的标准。
每种切分都有适用场景,但近几年 BPE 几乎一统天下。
7.5 byte-level fallback
GPT-2 之后流行 byte-level BPE:词表的最底层不是字符而是字节。这能保证「任何 UTF-8 字符串都能被 tokenize」,零失败率。
代价是对 ASCII 之外的字符(中文、emoji)需要多个 token,显得「贵」一些。但鲁棒性是值得的。第 29 篇 BPE 那一篇会展开。
7.6 WordPiece 与 SentencePiece
BPE 不是唯一的子词算法。Google 在 BERT 里用了 WordPiece:和 BPE 类似,但合并准则不是「频率」而是「合并后能最大化 likelihood」。原理上它是一种贪心的 EM。
SentencePiece 是 Google 开源的工具,把 tokenization 和 detokenization 都做得「端到端」:直接处理原始 Unicode 字符串,不需要预先分词(对中文、日文友好),还能跑 unigram language model 做切分。
T5、ALBERT、mT5、LLaMA-1 都用 SentencePiece。它的优势是支持多语言、对空白符号统一处理,缺点是依赖额外的库。
7.7 Unicode 与字节对齐
中文一个字符在 UTF-8 里通常是 3 字节。如果用 byte-level BPE,「中」会被切成「e4 b8 ad」三个字节 token,效率低。如果用 character-level,「中」就是一个 token,效率高但词表大。
LLaMA-2/3 的解法是「在 byte-level BPE 上预处理常见 Unicode 块」,让常见字符占一个 token。这种 trade-off 是多语言模型设计里的反复出现的工程问题。
八、训练 embedding 的工程细节
8.1 初始化
embedding 的标准初始化:从均值 0、方差 \(1/d\)(或 \(1/\sqrt{d}\))的正态分布或均匀分布采样。这是 Xavier/He 初始化的应用。
为什么这样?让 embedding 后续的点积、求和等操作的输出方差大约为 1,避免初始训练时数值爆炸或消失。
具体到 LLaMA:embedding 用 \(\mathcal{N}(0, 1)\) 初始化(不是 \(1/d\)),然后通过 RMSNorm 归一化。这是设计选择上的细节差异。
8.2 学习率
embedding 层在训练初期会收到非常多的梯度(每个词出现就更新一次),但是绝大多数词更新次数远低于其他层。这可能让 embedding 层学得比其他层慢。
一个常见做法是给 embedding 层一个稍大的学习率(比如全局学习率的 10x)。但现代 LLM 通常不这么搞,依赖 Adam 的自适应学习率自动平衡。
8.3 梯度 sparse 更新
每个 batch 里只用到了词表里的一小部分 token,所以每次反向传播只有这部分 embedding 收到非零梯度。框架会做 sparse update 以提升效率。
PyTorch 里 nn.Embedding(sparse=True) 启用
sparse 梯度,配合 optim.SparseAdam
使用。在大词表场景能显著加速训练。
8.4 正则化
embedding 层常常不加 weight decay。理由:embedding 是查表,不是「线性变换」语义,weight decay 会把 embedding 拉向零,损害语义。
但也有人会对 embedding 加 dropout(随机置零某些 embedding 维度)来正则化。这个做法是经验性的,不一定都有效。
8.5 词表越大,embedding 越大
GPT-4 的词表大约 10 万词,\(d \approx 12288\),embedding 矩阵就有 \(10^9\) 量级参数。这是模型总参数里相当可观的一块。
LLaMA 通过 BPE 把词表压到 32K 来节省 embedding 参数。这是「词表大小 vs 序列长度」的权衡:词表小则单 token 信息密度低、序列变长;词表大则反之。
8.6 ALBERT 的因子分解
ALBERT(Lan 2020)注意到 BERT 的 embedding 矩阵 \(V \times d\) 是巨大的浪费:\(V\) 很大但 embedding 学到的真实秩远小于 \(d\)。
ALBERT 把 embedding 分成两步:先 \(V \times e\)(\(e\) 较小,比如 128),再 \(e \times d\)。前者是真正的「词到语义」的映射,后者是「语义到模型隐层维度」的投影。
参数量:原本 \(Vd = 30000 \times 768 = 23M\),分解后 \(Ve + ed = 30000 \times 128 + 128 \times 768 = 3.94M\)。节省 80%。
这是「embedding 矩阵其实是低秩的」这个观察的工程兑现。后来很多大模型都借鉴了类似的思路,尤其是多语言模型(词表特别大)。
8.7 LLM 训练里的 embedding 分片
数千亿参数的 LLM 训练时,embedding 矩阵不能放在单卡上。常见做法:把 vocab 维度切分(vocab parallel),让每张卡持有词表的一部分,前向时用 all-gather 汇集 logit。
这种切分细节在 Megatron-LM、DeepSpeed 等框架里有专门的实现。你不需要自己写,但需要知道为什么 embedding 是分布式训练里的关键瓶颈。
8.8 embedding 的稀疏激活
token embedding lookup 本质上是稀疏的:每个 batch 只激活若干行。如果 embedding 矩阵是 sparse 的(大部分词从不出现),lookup 是高效的;但梯度更新需要小心——只更新激活过的行,否则浪费计算。
PyTorch 的 nn.Embedding(sparse=True)
选项对应这种语义。但 Adam 优化器和 sparse gradient
配合得不好(Adam 需要全量统计),所以现代实现常常用 dense
更新 + 稀疏 lookup 的组合。
8.9 embedding 的 weight decay
embedding 矩阵是否要加 weight decay?
实践上的争论:加 decay 让所有 embedding 趋零,会模糊罕见词;不加 decay 让高频词的 embedding 自由膨胀,可能 dominate。
折中方案:embedding 不加 decay,但加 LayerNorm 让范数受控。或者只对部分维度加。这些细节是 LLM 训练的工程经验,没有定论。GPT-2 / LLaMA 的具体实现各有侧重。
8.10 embedding 的 dropout
经典 NLP 模型里有时会对 embedding 加 dropout:随机把某一维度置零,作为正则化。BERT 在 embedding 之后用了 0.1 的 dropout rate。
但现代大模型对 embedding dropout 的态度复杂。GPT-3 之后很多模型不再用 dropout(数据量足够大,过拟合不是主要问题)。这又是一个「随时代变化」的工程默认值。
九、嵌入空间的几何
9.1 各向异性问题
研究发现现代 contextual embedding(BERT、GPT)有一个「各向异性」(anisotropy)问题:embedding 集中在一个细长的「锥」里,几乎所有词的余弦相似度都偏高。
这意味着「相似性」的区分度被压缩了。Ethayarajh 2019 的论文 “How Contextual are Contextualized Word Representations?” 系统研究了这个现象。
9.2 isotropy 修正
针对各向异性,有几个修正方法:减去均值(PCA centering)、whitening、对比学习目标。SimCSE(Gao 2021)就是一个用对比学习显著改善 sentence embedding 各向异性的工作。
理解:embedding 的几何不是天然完美的,不同的训练目标会塑造不同的几何形态。如果你做 retrieval,要特别关注 embedding 几何的均匀性。
9.3 embedding 的可解释性
word2vec 时代有不少工作分析 embedding 各维度的语义:「这一维好像对应性别」、「那一维好像对应数量」等等。
但 contextual embedding 时代这种分析变难了——每一维不再有清晰的语义对应。embedding 变成黑盒。这是表达力增强的代价。
第 47 篇可解释性会再回到这个话题。
9.4 embedding 的探针实验
「probing」是一种系统研究 embedding 学到了什么的方法:在固定 embedding 上训练一个简单分类器(线性或浅层 MLP),看它能不能预测某种语言学属性。
如果一个简单分类器能从 BERT 第 X 层 embedding 准确预测 POS 标签,说明那一层的 embedding 编码了 POS 信息。这种实验设计让我们能说「BERT 在第 5 层学到了语法」。
probing 的局限:它只说明信息「存在」,不说明信息「被使用」。可能存在但下游任务从未利用。
9.5 embedding 的对齐
不同模型、不同训练数据训出来的 embedding 在不同的坐标系下。要把它们对齐(比如把 word2vec 和 GloVe 对齐到同一空间),需要 Procrustes 变换或类似的正交变换。
跨语言 embedding 对齐是这个问题的应用:把英文 embedding
和中文 embedding 对齐到同一空间,让 cat 和
猫
的向量靠近。这是早期机器翻译里的一个研究方向。
9.6 embedding 的 norm 分布
观察一个有趣现象:高频词的 embedding 范数往往比低频词小。直觉解释:高频词在很多上下文出现,需要在 embedding 空间里「居中」(任意上下文都能容易和它做点积)。低频词只在少数上下文出现,可以在远离中心的「角落」里。
这种范数差异有时被作为「informativeness」的代理:低频词常常更 informative,它们的 embedding 范数更大。
9.7 t-SNE 与 UMAP 可视化
embedding 是高维(数百维)对象,肉眼看不见。可视化常用工具是 t-SNE(van der Maaten & Hinton 2008)和 UMAP(McInnes 2018),它们把高维点映射到 2D 或 3D,保持局部邻近关系。
t-SNE 的优点是聚类清晰,缺点是「全局结构」不可信(远距离两个聚类的相对位置不反映真实距离);UMAP 在保持全局结构上更好,速度也快。
可视化时一个常见误区:把 t-SNE/UMAP 上的距离当作「真实语义距离」。这是错的,可视化只是定性的,定量比较仍要回到原始 embedding 空间用余弦相似度。
9.8 球面投影:normalize 后的余弦
在很多场景下,会先把 embedding 归一化(除以自己的范数),让所有 embedding 落在单位球面上。这样 dot product 就等价于余弦相似度。
球面归一化的好处:去掉范数差异(避免高频词 dominate),比较纯粹的「方向相似性」;几何上让 embedding 集中在固定区域,方便检索结构(HNSW 等数据结构对单位向量更友好)。
在很多 retrieval 场景,embedding 直接发布的就是单位向量。OpenAI text-embedding-3 等都是这样。
9.9 hubness 现象
高维 embedding 空间里有一个反直觉现象:「中心化」的某些点会成为「枢纽」(hub),即很多查询的最近邻都是同一批点。这种现象在维度 \(d > 100\) 后开始显现,并且越高维越严重。
hubness 影响 retrieval 的多样性:top-k 检索经常被几个 hub 主导,导致结果重复。Suzuki 2013 等工作提出过修正方法(mutual k-NN、reciprocal rank 等)。
这也是「球面归一化 + 余弦相似度」流行的原因之一:在归一化的空间里,hubness 比纯欧氏距离弱一些。
9.10 embedding 的稳定性与可重复性
同一份训练数据,相同超参数,不同随机种子下训练的 embedding 在数值上完全不同。但「邻居关系」是稳定的——「dog」最近邻总是「cat、puppy、pet」之类。
这是一个深刻的现象:embedding 的「绝对坐标」无意义,只有「相对关系」有意义。这跟物理学里的「坐标系不变性」类似——重要的是观察者无关的几何性质,不是具体数值。
十、关键概念回顾(散文式)
embedding 是把离散的词翻译成连续向量的桥。从 70 年前 Firth 的「You shall know a word by the company it keeps」开始,分布假设奠定了一切的基础。
word2vec 把分布假设变成了高效的神经网络训练算法,CBOW 和 Skip-gram 两个模型,加上 negative sampling 加速,定义了 2013-2018 年的 NLP 主流范式。GloVe 在同一时期提出全局共现的视角,与 word2vec 殊途同归。
ELMo、BERT 把 embedding 推进到「上下文化」的新阶段:同一个词在不同句子里有不同向量,解决了一词多义问题。Transformer 时代,embedding 不再是预训练的产物,而是端到端学习的一部分。tied weights、subword tokenization、位置编码等机制让现代 LLM 的 embedding 既高效又灵活。
理解了这条 70 年的演化线,再看 Transformer 的输入端,就不只看到「一个矩阵 + 一个查表」,而能看到「分布假设 → 共现统计 → 神经网络 → 端到端学习」这一路的演变。每一步都是对前一步的回应和改进。
十一、常见误解
11.2 「上下文 embedding 一定比静态好」
不一定。在某些低资源、对延迟敏感的任务里,静态 embedding 仍然是更好的选择。BERT 跑一次比 word2vec 慢百倍,准确率提升不一定值。
11.5 「embedding 一旦训好就不该改」
不对。下游任务往往要 fine-tune embedding。一刀切「冻结 embedding」会丧失任务自适应能力。LLM 时代的标准做法是端到端微调,包括 embedding 层。
11.6 「embedding 是模型的一部分独立模块」
不准确。在端到端训练的现代模型里,embedding 和其他层是耦合的——embedding 的最佳取值依赖于后面的 transformer 层。把 embedding 单独抽出来用,效果不如端到端训练。
很多人会想「我能不能拿别人 BERT 训好的 embedding 直接用」。可以用作初始化,但通常需要 fine-tune 才能发挥作用。
11.10 「embedding 维度越高越好」
不一定。维度太高会导致:参数量爆炸、点积统计性质漂移、下游小数据微调容易过拟合。维度选择和模型其他超参(heads、layers)必须协调。
经验上:word2vec 50-300,BERT 768,GPT-3 12288。维度选择往往跟随 embedding 之外的整体宽度,而不是独立旋钮。
11.13 「越大模型 embedding 越好」
不一定。LLM 内部的 hidden state 维度往往很大(几千),但用作 embedding 时不一定就更优秀。专门做 retrieval 的 embedding 模型(如 BGE、E5)虽然小(几百兆参数),但在 retrieval benchmark 上经常打败 GPT-4 hidden state。
原因:LLM 训练目标是「next-token prediction」,不是「相似性检索」。两者用到的「中间表征」性质不同。embedding 任务需要专门的对比学习目标。
十二、embedding 的偏见与公平性
15.1 embedding 学到了人类的偏见
word2vec 和 GloVe 训练完之后人们发现一些尴尬的结果:
embedding("man") - embedding("woman") ≈ embedding("computer programmer") - embedding("homemaker")
也就是说 embedding 把「程序员」和「男性」、「家务」和「女性」连到了一起。这不是偶然——训练语料(Wikipedia、新闻)里就这样写,分布假设忠实地把它学了下来。
Bolukbasi 2016 的 “Man is to Computer Programmer as Woman is to Homemaker?” 系统揭示了这种偏见。这是 ML 公平性研究的标志性论文之一。
15.2 偏见来源
embedding 偏见的根源是训练数据本身的统计。如果你在 1900 年的英语语料上训 embedding,「医生」会强烈关联男性;2020 年的语料就会更平衡。embedding 是社会语言习惯的镜子。
这给我们一个 warning:embedding(以及更下游的 LLM)会继承训练数据里的所有偏见——种族、性别、地域、宗教。「中性技术」不存在,数据有 bias,模型就有 bias。
15.3 debiasing 的尝试
研究界提出过多种「去偏见」方法:在 embedding 空间里找出「性别方向」并投影掉、用对抗训练让 embedding 不可被分类器分出性别等。
但这些方法多被后续工作证明只是「藏起来」了偏见,本质并没有消除——简单的 probe 仍然能从「去偏见」后的 embedding 里恢复性别信息。这告诉我们:bias 不是 embedding 的某个独立维度,而是渗透在整个表征里的统计模式。
真正的解决途径是从数据源头改。但语言数据反映社会,社会本身有 bias,这就成为一个棘手的循环。
15.4 偏见的下游放大
embedding 的偏见会在下游任务里被放大。例如简历筛选系统用 embedding 算「候选人 ↔︎ 职位」相似度,如果 embedding 里「程序员 → 男性」的关联强,女性候选人就会被系统性低估。
Caliskan 2017 的 “Semantics Derived Automatically from Language Corpora Contain Human-Like Biases”(发表在 Science)系统地展示了这种放大。这是 ML 公平性研究里最有影响力的论文之一。
这给所有用 embedding 的工程师一个责任:你的系统不是中立的,它继承了训练数据的所有偏见,并且在决策里放大它们。设计 ML 系统时把这条放在心上。
15.5 持续的开放问题
偏见处理仍是 NLP 公平性研究的 open question。包括:怎么定量度量 embedding 偏见?多种偏见(性别、种族、地域)能否同时去除?去偏见和保持下游任务效果之间的 trade-off 在哪?
这些问题没有定论。embedding 既是技术工件,也是社会工件。它的研究越深入,对社会的影响也越深入——这是 NLP 研究者必须正视的一个面向。
十三、下一步
embedding 把词变成向量后,下一个问题是:怎么处理由这些向量构成的序列? 答案历史上是 RNN,未来是 Transformer。我们先讲 RNN。
09 RNN 与序列建模 会从 Vanilla RNN 开始,讲 BPTT、梯度消失爆炸,引出 LSTM 和 GRU,最后到 Seq2Seq 框架。这是 Transformer 出现之前所有序列建模的故事。
10 RNN 的根本局限 会讲 RNN 的「三难」(长程依赖、梯度稳定、训练并行),解释为什么需要一个新范式来取代它,以及 Vaswani 2017 是怎么解决这个问题的。
十四、参考文献
- Firth, J. R. (1957). A Synopsis of Linguistic Theory, 1930-1955. Studies in Linguistic Analysis. 「分布假设」的语言学起源。
- Harris, Z. S. (1954). Distributional Structure. Word. 与 Firth 同期的另一版本。
- Bengio, Y., Ducharme, R., Vincent, P., Janvin, C. (2003). A Neural Probabilistic Language Model. JMLR. 神经网络词向量的鼻祖。
- Mikolov, T., Chen, K., Corrado, G., Dean, J. (2013). Efficient Estimation of Word Representations in Vector Space. ICLR Workshop. word2vec 第一篇。
- Mikolov, T., Sutskever, I., Chen, K., Corrado, G., Dean, J. (2013). Distributed Representations of Words and Phrases and their Compositionality. NeurIPS. word2vec 第二篇 (NEG)。
- Pennington, J., Socher, R., Manning, C. D. (2014). GloVe: Global Vectors for Word Representation. EMNLP. GloVe 论文。
- Bojanowski, P., Grave, E., Joulin, A., Mikolov, T. (2017). Enriching Word Vectors with Subword Information. TACL. fastText。
- Levy, O., Goldberg, Y. (2014). Neural Word Embedding as Implicit Matrix Factorization. NeurIPS. word2vec 与矩阵分解的等价。
- Peters, M. E. et al. (2018). Deep Contextualized Word Representations. NAACL. ELMo。
- Devlin, J., Chang, M.-W., Lee, K., Toutanova, K. (2019). BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. NAACL. BERT。
- Press, O., Wolf, L. (2017). Using the Output Embedding to Improve Language Models. EACL. Tied embedding。
- Ethayarajh, K. (2019). How Contextual are Contextualized Word Representations? Comparing the Geometry of BERT, ELMo, and GPT-2 Embeddings. EMNLP. 各向异性。
- Gao, T., Yao, X., Chen, D. (2021). SimCSE: Simple Contrastive Learning of Sentence Embeddings. EMNLP.
上一篇:07 Softmax 与概率分布
下一篇:09 RNN 与序列建模
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【Transformer 与注意力机制】04. 函数与神经网络:从 y=f(x) 到一台可学习的拟合机器
如果你问我「神经网络到底是什么」,我会先把所有教材合上,然后给你一句朴素得近乎敷衍的话——神经网络就是一个函数。
【Transformer 与注意力机制】05. 激活函数:让网络「弯下来」的非线性魔法
上一篇我们论证了一件事——纯线性的网络再深,也只是一个线性变换。把 $W2(W1\mathbf{x} + \mathbf{b}1) + \mathbf{b}2$ 展开就是 $W'\mathbf{x} + \mathbf{b}'$。线性的复合还是线性,这是线性代数的铁律。
【Transformer 与注意力机制】03 矩阵乘法的两种视角
把矩阵乘法掰开成两种等价但风格不同的视角——『行 × 列』的点积视角和『列的线性组合』视角,最终落到 QK^T 的形状分析。
【Transformer 与注意力机制】02 向量与点积的几何直觉
从二维平面上的箭头开始,把『向量、内积、夹角、相似度』这几个概念用几何方式串起来,最后落到注意力公式里那个 QK^T 的来历。