词向量:从 one-hot 到 word2vec、GloVe

面向 NLP 研究生的深度教材 · 改编自 Stanford CS224N Winter 2026 Lecture 2 (Diyi Yang)
覆盖:one-hot · 分布式语义 · Word2Vec · Skip-gram · Negative Sampling · GloVe · 共现矩阵 · 词向量评估
版本 1.0 · 2026 春 · 原 slides 共 44 页 · 读者:具备线代/概率/微积分与 PyTorch 基础

0 · 引言:本讲在 CS224N 中的位置

词向量(word vectors,也叫 word embeddings、neural word representations)几乎是整门 CS224N 第一个真正"动手"的内容。本讲之后的所有模型 — RNN、Transformer、BERT、LLaMA —— 在输入端都依赖某种形式的 embedding 表,因此你对本讲的理解将直接迁移到 Lecture 5(Transformer)的 token embedding、Lecture 7(pretraining)的 input layer、甚至 Lecture 14(tokenization)对子词单位的设计。

原 PPT 的 6 节安排是:

  1. 课程组织(行政性内容,本教材略去)
  2. Word2vec introduction(15 min)
  3. Word2vec objective function gradients(25 min, 本讲最硬核的一段)
  4. Optimization basics(5 min)
  5. Can we capture word meaning more effectively by counting?(10 min, 引入 GloVe)
  6. Evaluating word vectors(10 min)

本教材在保留 PPT 全部要点的前提下,做了三件 PPT 没做、但研究生需要的事:

  • 把 PPT 上一句话带过的"为什么"展开成完整的论证(标记为 🤔 为什么 模块);
  • 把 word2vec / GloVe 联通到 2024–2026 仍在使用的范式(contrastive learning、矩阵分解视角、Transformer 词嵌入层),标记为 🎓 研究生延伸
  • 每节配可推导/可编程的练习,并在 §8 给出 ≤ 40 行的 SGNS 最小复现代码。
本讲的关键目标 (Key Goal, 引 PPT):understand word meaning can be represented by a high-dimensional vector of real numbers, and can read word embeddings papers by the end of class. 也就是说,本讲结束你应当能不查资料地读懂 Mikolov 2013a、Mikolov 2013b、Pennington 2014 三篇原文。

1 · 词义如何表征?

1.1 denotational semantics:符号 ⇔ 所指

语言学中最朴素的词义观叫 denotational semantics(指称语义):词作为 signifier(能指 / 符号),指向 signified(所指 / 事物或观念)。形式上:

$$ \text{tree} \;\Longleftrightarrow\; \{\,🌳, \,🌲, \,🌴, \,\dots\,\} \tag{1.1} $$

这种"符号→对象集合"的映射在词典编纂里非常实用,但计算机无法直接消费一张图片集合或语义网络。我们需要 把"指称"变成"可计算的对象"。这就是本讲要解决的核心问题。

1.2 早期方案:WordNet(语义网络)

2013 年以前最主流的工程方案是 WordNet(Princeton, 1985–):一张人工标注的 synset(同义词集合)+ hypernym(is-a 关系)网络。NLTK 里只需几行 Python 即可访问:

from nltk.corpus import wordnet as wn
panda = wn.synset("panda.n.01")
hyper = lambda s: s.hypernyms()
list(panda.closure(hyper))
# -> [Synset('procyonid.n.01'), Synset('carnivore.n.01'), Synset('mammal.n.01'), ...]

WordNet 的问题在 PPT 中列出了 6 条,但本质上可以归纳为两类失败:

失败类型具体表现
① 缺乏连续度量"proficient" 与 "good" 在不同语境下相似度不同;WordNet 只给二元的"是否同义"。
② 维护成本爆炸新词("wicked, badass, bombest")、新义、跨领域、跨语种都需人工增补;信息工业的速度根本跟不上。

1.3 上一代基线:one-hot 表征

把词表 $V$ 中第 $i$ 个词表示为长度 $|V|$ 的指示向量:

$$ \text{hotel} = [\,0,0,0,0,0,0,0,\underbrace{1}_{i\text{-th}},0,0,0,0,0,0\,]^\top, \quad |V|\approx 5\times10^5 \tag{1.2} $$

这种表示叫 localist representation。它有三大致命缺陷:

  • 正交性:任意两个不同词的内积恒为 0,motel·hotelmotel·banana 都是 0,模型无法学到"汽车旅馆 ≈ 酒店"。
  • 维度爆炸:$|V|$ 通常 $10^5$–$10^7$,下游矩阵参数线性增长。
  • 泛化失败:训练集见过 "Seattle hotel",测试集出现 "Seattle motel" 时无法泛化。

one-hot 与 LLM 的关联

现代 Transformer 的 input embedding layer,从数学上看 就是 一个 $|V|\times d$ 的查表矩阵 $E$,输入 one-hot 后取出对应行,即 $E^\top \mathbf{x}_{\text{one-hot}}$。所以 one-hot 没有消失,它只是被"压缩"到了稠密向量空间——而 word2vec 正是教这个查表矩阵 $E$ 该长什么样的第一种成功方法。

1.4 关键思想:分布式语义(distributional semantics)

本讲(也是现代 NLP)的奠基性 insight 来自语言学家 J.R. Firth (1957):

"You shall know a word by the company it keeps."

形式化地:词 $w$ 的 分布式语义 由它的 上下文分布 $P(\text{context} \mid w)$ 决定。看下面三个真实语料里的"banking":

… government debt problems turning into banking crises as happened in 2009 …
… saying that Europe needs unified banking regulation to replace the hodgepodge …
… India has just given its banking system a shot in the arm …

这些上下文词(debt, crises, regulation, system, …)共同 定义了 banking 的含义。我们的目标就是把这种"上下文分布"压缩成一个稠密向量

$$ \textbf{banking} = \begin{bmatrix}0.286\\0.792\\-0.177\\-0.107\\0.109\\-0.542\\0.349\\0.271\end{bmatrix}\in\RR^{d},\quad d\approx 50\text{–}300 \tag{1.3} $$

这种向量有几个并列的名字,三者指的是同一个东西

  • word vectors(本讲用语)
  • word embeddings(强调"嵌入"到欧氏空间)
  • (neural) word representations(强调神经网络学到)

另一个重要术语:上面的向量是 distributed representation(分布式表示,注意与 distributional semantics 名字像但概念不同)—— "分布式"指的是词义被分摊到所有维度上,没有任何一维独占某个特征;与之相对的 one-hot 是 localist。两个 d 不同:

  • distributional semantics:词义如何定义(由上下文)
  • distributed representation:词义如何编码(分散在多维)

分布式假设的现代回响

2018 年以来的 BERT/GPT 把分布式假设推到了极致:词义不再是一个 静态向量,而是上下文相关的(contextualized)。但请注意:Transformer 的 word embedding layer(输入端的 token embedding)仍然是 word2vec 风格的 静态 向量,self-attention 才是把它"按上下文重新计算"的机制。因此 word2vec 不是被淘汰,而是下沉为基础设施

练习 1.1

(A)证明:两个不同 one-hot 向量的余弦相似度恒为 0。(B)给出一个例子,说明 WordNet 给出"同义"但语境里其实不应该互换的词对。

2 · Word2Vec 框架概览

2.1 skip-gram 的直觉:用中心词"猜"周边词

Word2Vec(Mikolov et al., 2013a, NeurIPS)是第一个 把分布式语义大规模工程化的成功模型。它的 idea 极其简洁——只有 4 句话:

  1. 有一个大语料 corpus,相当于一长串 token。
  2. 词表 $V$ 中每个词 $w$ 分配一个向量 $\mathbf{v}_w\in\RR^d$。
  3. 遍历语料每个位置 $t$(中心词 $c$),考虑窗口内的上下文词 $o$("outside words")。
  4. 用 $c$ 和 $o$ 的向量计算 $P(o\mid c)$,然后不断调整向量 让真正出现过的 $(c,o)$ 概率变高。
problems turning into banking crises as P(w_{t-2}|w_t) P(w_{t-1}|w_t) P(w_{t+1}|w_t) P(w_{t+2}|w_t) outside context words (window=2) center word outside context words (window=2)
图 2.1 · skip-gram 在位置 t = "into" 上的工作方式:以中心词 into 预测窗口大小 m=2 内的 4 个上下文词。每个箭头对应一个 P(w_{t+j}|w_t) 项。

注意三个细节:

  • 窗口是 对称 的,包含左右各 $m$ 个词;本图 $m=2$,实际生产中 $m\in[5,10]$。
  • 预测不考虑相对位置("position-independent"):$P(\text{banking}|\text{into})$ 不区分 banking 是 $t+1$ 还是 $t+2$。这是 word2vec 的简化假设,也是它叫"bag of words 风格"的原因。
  • 所谓"调整向量",本质就是 梯度下降。下一节我们会把目标函数写出来并求导。

2.2 两套向量 u / v:一个工程美学决策

本讲最容易被初学者忽略、但研究生必须掌握的设计:每个词维护两个向量

  • $\mathbf{v}_w$ — 当 $w$ 充当 中心词(center)时使用
  • $\mathbf{u}_w$ — 当 $w$ 充当 上下文词(outside)时使用

训练完毕通常把两个向量平均,或者直接丢弃 $\mathbf{u}$ 只保留 $\mathbf{v}$。整套参数集合:

$$ \theta = \begin{bmatrix}\mathbf{v}_{\text{aardvark}}\\\vdots\\\mathbf{v}_{\text{zebra}}\\\mathbf{u}_{\text{aardvark}}\\\vdots\\\mathbf{u}_{\text{zebra}}\end{bmatrix}\;\in\RR^{2dV} \tag{2.1} $$

为什么要用两套向量?

若只有一套 $\mathbf{v}$,那 $P(w \mid w)$(中心词预测自己)就会变成 $\exp(\mathbf{v}_w^\top \mathbf{v}_w) / Z$,这个内积恒为正的最大值。这会逼模型把每个词的概率"自指",破坏训练动力学。两套向量解耦了"作为中心"和"作为上下文"两种角色,让梯度计算和优化都更平滑(Goldberg & Levy 2014 的 note 给出了详细论证)。工程实践中:一套向量也能 work,但稍逊色一些。
U (|V|×d) outside vectors u_w v_c center vec = U·v_c logits ∈ ℝ^|V| softmax P(·|c) prob. dist. vs y_o (truth) one-hot
图 2.2 · word2vec 的计算图:先用 center 向量 v_c 与全部 outside 向量 U 做点积,再 softmax 得到概率分布,最后与真实上下文词 o 的 one-hot 对比构造损失。本质上是多分类交叉熵,类别数 = |V|。

3 · 目标函数与完整梯度推导

3.1 似然函数 L(θ)

对长度为 $T$ 的语料,每个位置 $t$ 都贡献 $2m$ 个 (中心,上下文) 对。假设这些条件概率条件独立,整体数据似然:

$$ L(\theta) \;=\; \prod_{t=1}^{T}\prod_{\substack{-m\le j\le m\\ j\ne 0}} P\!\left(w_{t+j}\,\big|\,w_t;\theta\right) \tag{3.1} $$

实践中我们最大化对数似然、即最小化 平均负对数似然

$$ J(\theta) \;=\; -\frac{1}{T}\log L(\theta) \;=\; -\frac{1}{T}\sum_{t=1}^{T}\sum_{\substack{-m\le j\le m\\ j\ne 0}} \log P\!\left(w_{t+j}\,\big|\,w_t;\theta\right) \tag{3.2} $$

这就是我们要最小化的 目标函数 / 代价函数 / 损失函数(在不同子领域里这三个词被混用)。一个等价的口号:

Minimizing J(θ) ⟺ Maximizing predictive accuracy.

3.2 用 softmax 实现 P(o|c)

现在的关键:$P(w_{t+j} \mid w_t;\theta)$ 该怎么算? word2vec 给出一个非常优雅的答案:

$$ \boxed{\;P(o \mid c) \;=\; \frac{\exp(\mathbf{u}_o^\top \mathbf{v}_c)}{\sum_{w\in V}\exp(\mathbf{u}_w^\top \mathbf{v}_c)}\;} \tag{3.3} $$

把公式拆成三步看:

  1. ① 点积衡量相似性:$\mathbf{u}_o^\top \mathbf{v}_c = \sum_{i=1}^{d} u_{o,i} v_{c,i}$。两个向量越对齐(同方向、大模长),点积越大。
  2. ② 指数化保证非负:$\exp(\cdot)>0$,避免负概率。
  3. ③ 归一化得到概率分布:除以分母(对全词表的 sum),保证 $\sum_o P(o\mid c)=1$。

这正是 softmax 函数 在多分类中的标准用法:

$$ \text{softmax}(x_i) = \frac{\exp(x_i)}{\sum_{j=1}^{n}\exp(x_j)} \in (0,1),\quad \sum_i \text{softmax}(x_i)=1 \tag{3.4} $$
sigmoid σ(x) = 1/(1+e^{-x}) x σ 1 .5 0 softmax over 5 logits x = [2.0, 1.0, 0.1, -1.0, -2.0] 0.616 x=2.0 0.227 x=1.0 0.092 x=0.1 0.031 x=-1 0.011 x=-2
图 3.1 · 左:sigmoid 函数 σ(x) 在 (0,1) 内取值;这是负采样要用的关键非线性。右:softmax 把任意实数向量"放大最大值、保留其他"的过程——"max" 因为它放大最大值(peak at 0.616),"soft" 因为它依然给小值非零概率(0.011)。

3.3 关键推导:∂/∂v_c log P(o|c)

这是本讲最重要的一段数学,建议自己用纸笔重做一遍。目标:对中心词向量 $\mathbf{v}_c$ 求 $\log P(o\mid c)$ 的梯度。

Step 1:把 log 展开为差。

$$ \log P(o\mid c) \;=\; \mathbf{u}_o^\top \mathbf{v}_c \;-\; \log\sum_{w\in V} \exp(\mathbf{u}_w^\top \mathbf{v}_c) \tag{3.5} $$

Step 2:分别对两项求 $\partial/\partial \mathbf{v}_c$。第一项是线性的:

$$ \frac{\partial}{\partial \mathbf{v}_c} \big(\mathbf{u}_o^\top \mathbf{v}_c\big) \;=\; \mathbf{u}_o \tag{3.6} $$

Step 3:第二项用链式法则。令 $S = \sum_{w} \exp(\mathbf{u}_w^\top \mathbf{v}_c)$,则 $\frac{\partial \log S}{\partial \mathbf{v}_c} = \frac{1}{S}\frac{\partial S}{\partial \mathbf{v}_c}$,而

$$ \frac{\partial S}{\partial \mathbf{v}_c} \;=\; \sum_{x\in V}\exp(\mathbf{u}_x^\top \mathbf{v}_c)\,\mathbf{u}_x \tag{3.7} $$

把 $\exp(\mathbf{u}_x^\top \mathbf{v}_c)/S$ 这个比值认出来:这正是 $P(x \mid c)$!于是:

$$ \frac{\partial}{\partial \mathbf{v}_c}\log \sum_w \exp(\mathbf{u}_w^\top \mathbf{v}_c) \;=\; \sum_{x\in V} P(x\mid c)\,\mathbf{u}_x \tag{3.8} $$

Step 4:合并得到最终梯度:

$$ \boxed{\;\frac{\partial}{\partial \mathbf{v}_c}\log P(o\mid c) \;=\; \mathbf{u}_o - \sum_{x\in V} P(x\mid c)\,\mathbf{u}_x\;} \tag{3.9} $$

这个梯度的几何解释

$\mathbf{u}_o$ 是"真实"的 outside 向量;$\sum_x P(x\mid c)\mathbf{u}_x$ 是"模型当前预测的 outside 向量的期望"。所以 $\nabla_{\vc}\log P = (\text{observed}) - (\text{expected})$。这是所有指数族 / softmax 模型的通用形式——你会在最大熵、CRF、能量模型里反复见到。如果模型预测完美,$\mathbf{u}_o = \mathbb{E}_{P(x|c)}[\mathbf{u}_x]$,梯度为零,停止更新。

3.4 对 ∂/∂u_o 的推导(自己来)

类似地,对 outside 向量 $\mathbf{u}_o$(注意只对 正确 的那个 outside 词 o,不是对所有 $w$)求导。展开 (3.5) 再求导:

$$ \frac{\partial}{\partial \mathbf{u}_o}\log P(o\mid c) \;=\; \mathbf{v}_c \;-\; \frac{\exp(\mathbf{u}_o^\top \mathbf{v}_c)}{\sum_w \exp(\mathbf{u}_w^\top \mathbf{v}_c)}\,\mathbf{v}_c \;=\; \big(1 - P(o\mid c)\big)\,\mathbf{v}_c \tag{3.10} $$

而对错误的 outside 词 $w'\ne o$:

$$ \frac{\partial}{\partial \mathbf{u}_{w'}}\log P(o\mid c) \;=\; -P(w' \mid c)\,\mathbf{v}_c \tag{3.11} $$

合起来:真实词的 outside 向量被往 $\mathbf{v}_c$ 方向,其他词的 outside 向量被往 $\mathbf{v}_c$ 反方向。这就是 word2vec 学习的几何机制。

3.5 参数张量 θ:维度核算

有 $|V|$ 个词,每个词维护 2 个 $d$ 维向量,所以 $\theta\in\RR^{2d|V|}$。常见量级:

设定|V|d|θ|(参数量)
Mikolov 2013 (Google News)3M3001.8 × 10⁹
本课程作业 (a1, 截取语料)~20K50–100~ 2–4 M
GloVe-840B (Common Crawl)2.2M3001.32 × 10⁹

10 亿级参数听起来吓人,但全是embedding 查表,没有 dense MLP/attention,磁盘存储约 4 GB(float32)即可。

练习 3.1 — 推导题

(A)从 (3.5) 出发独立推出 (3.9)。(B)证明 $\sum_{x\in V} P(x\mid c)=1$(提示:softmax 性质)。(C)若把 softmax 改为 max 函数(即 argmax 输出 one-hot),(3.9) 的梯度变成什么形式?说明这种"hard"版本为何无法用 SGD 训练。

练习 3.2 — 数值题

设 $|V|=10000$, $d=100$, 中心词 c = "into",已知 $\mathbf{v}_c = (0.1, 0.2, \ldots)$(虚构数)。若一次梯度更新需要对 (3.9) 的求和遍历全词表,估计单次 SGD 步骤的浮点数运算量(FLOPs),并解释为何这促使了下一节负采样的提出。

4 · 优化:从 GD 到 SGD

4.1 批量梯度下降 (Batch GD)

梯度下降的核心思想:把"目标函数 $J(\theta)$"想成一座山,每一步沿 负梯度(最陡下坡)方向走一小段:

$$ \theta^{\text{new}} = \theta^{\text{old}} - \alpha\, \nabla_\theta J(\theta) \tag{4.1} $$ $$ \theta_j^{\text{new}} = \theta_j^{\text{old}} - \alpha\, \frac{\partial}{\partial \theta_j^{\text{old}}} J(\theta) \tag{4.2} $$

其中 $\alpha$ 称为 step size学习率(learning rate)。伪代码:

while True:
    theta_grad = evaluate_gradient(J, corpus, theta)  # 用全语料计算
    theta = theta - alpha * theta_grad
θ₁ θ₂ θ* minimum θ⁰ random init −α∇J
图 4.1 · 二维损失曲面上的梯度下降:椭圆等高线表示 J(θ);每一步沿 −∇J 走一段长度 α 的小步;几步之后逼近最小点 θ*。注意:word2vec 的目标函数并非 凸函数,因此实际曲面有很多鞍点和局部极小,但 SGD 的随机性帮助它逃逸。

4.2 随机梯度下降 (SGD)

问题:$J(\theta)$ 是整个语料所有窗口 的和($T$ 通常 $10^9$ 级)。一次 batch GD 要算整个 corpus 才能更新一次——谁等得起?解决方案是 SGD:每次只用一个窗口估计梯度:

while True:
    window = sample_window(corpus)
    theta_grad = evaluate_gradient(J, window, theta)
    theta = theta - alpha * theta_grad

更工程化的版本是 mini-batch SGD:每次抽 $B$ 个窗口(典型 $B\in\{32, 64, 128, 256\}$),平均它们的梯度后更新。好处:

  • GPU 并行,吞吐量提升;
  • 梯度方差降低($\propto 1/B$),训练更稳;
  • 仍保持随机性,能逃局部极小/鞍点。

4.3 稀疏梯度更新

关键工程观察:在 (3.9) 的梯度公式里,大部分词的 $\mathbf{u}_w$ 没被这次窗口"看见",它们的梯度为 0;只有窗口内的 $2m+1$ 个词 + 负采样的 $2km$ 个噪声词需要更新。整个梯度向量 $\nabla_\theta J_t(\theta)\in\RR^{2dV}$ 是极度稀疏的

$$ \nabla_\theta J_t(\theta) \;=\; \big[\,0,\ldots,0,\ \nabla_{\mathbf{v}_{\text{like}}},\ 0,\ldots,0,\ \nabla_{\mathbf{u}_{I}},\ \ldots,\ \nabla_{\mathbf{u}_{\text{learning}}},\ldots,0,\ldots,0\,\big]^\top \tag{4.3} $$
工程实践中两条做法都有:(a) 用稀疏矩阵更新接口(如 PyTorch 的 nn.Embedding(..., sparse=True) + SparseAdam)只更新出现的;(b) 用稠密更新但仅在用到的行处梯度非零(实现简单,多算几次 0 即可)。在分布式训练中(a)是必须的——否则每次都要广播 $2d|V|$ 大小的全量梯度。

SGD 在现代 LLM 中的演化

2026 年训练 LLM 时几乎没人直接用 vanilla SGD:人们用 AdamW、Lion、Sophia 等带自适应学习率动量的优化器。但所有这些方法的骨架都是 (4.1):你算出当前梯度,然后按某种方式更新参数。理解 SGD 是理解 Adam 的前提。建议自己实现一遍 SGD(10 行)→ Momentum SGD → AdaGrad → Adam,体会从 5 行到 20 行的复杂度跃迁。

练习 4.1

若学习率 $\alpha$ 过大,梯度下降会出现什么现象?过小呢?画出 1-D 抛物线 $J(\theta)=\theta^2$ 上分别 $\alpha=0.1, 0.5, 1.0, 1.1$ 时连续 5 步迭代的轨迹。

5 · 算法家族:CBOW / SG / 负采样

5.1 两种模型变体

变体预测方向用途偏好
Skip-gram (SG)中心词 c → 多个上下文词 o (本讲推导的就是这个)低频词效果更好;小语料;分析任务
CBOW (Continuous Bag-of-Words)多个上下文词的平均 → 中心词高频词更准;训练更快;当年 Google News 默认

原 PPT 推导的是 SG。CBOW 的 loss 留作练习。

5.2 负采样:把 softmax 分母搞掉

(3.3) 的分母 $\sum_{w\in V}\exp(\mathbf{u}_w^\top \mathbf{v}_c)$ 每步都要遍历全词表,$|V|=10^6$ 时这是计算瓶颈。负采样 (Negative Sampling, Mikolov 2013b) 把"多分类"问题转成"$K+1$ 个二分类"问题:

$$ J_{\text{neg-sample}}(\mathbf{u}_o,\mathbf{v}_c,U) = -\log \sigma(\mathbf{u}_o^\top \mathbf{v}_c) - \sum_{k\in \{K \text{ sampled}\}} \log \sigma(-\mathbf{u}_k^\top \mathbf{v}_c) \tag{5.1} $$

其中 $\sigma(x)=1/(1+e^{-x})$ 是 sigmoid。直观理解:

  • 第一项:让真实的 (c, o) 对的 sigmoid 接近 1;
  • 第二项:让 $K$ 个随机抽样的(噪声)词 $w_k$ 与 $c$ 的 sigmoid 接近 0(即 $-\mathbf{u}_k^\top\mathbf{v}_c$ 大)。

所以现在每步只需要 $K+1$ 次内积,独立于 |V|。典型 $K\in\{5,10,20\}$(小语料用大 K)。

5.3 抽样分布:那个神秘的 3/4 power

负样本不是均匀抽,而是按修正的 unigram 分布:

$$ P(w) = \frac{U(w)^{3/4}}{Z},\quad Z=\sum_{w'\in V}U(w')^{3/4} \tag{5.2} $$

其中 $U(w)$ 是 $w$ 在语料中的频率。

为何 3/4 次幂?

若直接按 $U(w)$ 抽,最高频词(the, of, a)几乎垄断了所有负样本,模型只学会"和 the 不一样"而无法区分中频词。若按均匀分布抽,又会出现大量极罕见词当负样本——它们本来概率就极低,没什么信号。3/4 是 Mikolov 用 grid search 找到的折中:把 0.9 → 0.92、0.01 → 0.032,压扁高频、抬升低频。这是个纯工程经验值,没有理论保证,但事实证明跨语料都很 robust。

负采样 ≈ 矩阵分解:Levy & Goldberg 2014 的重大发现

2014 年 Levy & Goldberg 在 NeurIPS 上证明了:SGNS(skip-gram with negative sampling)实质上在隐式地分解 shifted PMI 矩阵: $$M_{ij}=\text{PMI}(w_i,c_j)-\log K,\quad \text{PMI}(w,c)=\log\frac{P(w,c)}{P(w)P(c)}$$ 这把"神经"方法和"计数"方法(GloVe / LSA)统一在一个矩阵分解框架下,是 word embedding 理论上最重要的论文之一。研究生应当读原文。

5.4 "Bag of Words" 警告

由于 word2vec 对位置不敏感(窗口内任何相对位置都给同一个 P 估计),它本质上是 词袋模型。在一个句子里,"dog bites man" 与 "man bites dog" 学到的中心-上下文统计是一样的。这是 word2vec 的根本局限——也是 Transformer 用 positional encoding + self-attention 试图修复的事。

练习 5.1

推导 CBOW 的目标函数。设上下文词向量为 $\bar{\mathbf{v}} = \tfrac{1}{2m}\sum_{j\ne 0}\mathbf{v}_{w_{t+j}}$,写出 $P(w_t \mid \bar{\mathbf{v}})$ 和负采样版本的损失。

练习 5.2

(理论题)证明 (5.1) 中的 sigmoid 损失等价于:把"是否为真实 (c,o) 对"看作 Bernoulli 变量、做最大似然估计。

6 · 计数法路线:从共现矩阵到 GloVe

6.1 一个困惑:我们为什么要"遍历语料"?

PPT 上 Manning 半开玩笑地说:"There's something weird about iterating through the whole corpus (perhaps many times); why don't we just accumulate all the statistics of what words appear near each other?!?" —— 这其实是 NLP 早 20 年的主流思路:直接统计共现。让我们看看它能走多远。

6.2 共现矩阵 X

给定语料和窗口大小,构造矩阵 $X\in\RR^{|V|\times |V|}$:$X_{ij}$ = 词 $i$ 与词 $j$ 在窗口内共同出现的次数。两种主流变体:

  • 窗口共现:类似 word2vec,捕捉句法+语义(产出"word space");
  • 词-文档:$X_{ij}$ = 词 $i$ 在文档 $j$ 中的频次,捕捉主题(产出"document space",即 LSA, Latent Semantic Analysis)。

例子(PPT 第 38 页, window=1, symmetric):

三个句子:

  • I like deep learning .
  • I like NLP .
  • I enjoy flying .
counts I like enjoy deep learning NLP flying . I 02100000 like 20010100 enjoy 10000010 deep 01001000 learning 00010001 NLP 01000001 flying 00100001 . 00001110
图 6.1 · 三句话语料 + window=1 的对称共现矩阵 X。第 i 行就是词 i 的"共现向量"。注意 X 的高度稀疏性、对称性,以及行/列和等于词频。

把 $X$ 的第 $i$ 行直接当作词 $i$ 的向量?三个问题:

  • 维度爆炸:向量长度随 $|V|$ 增长 → 存储 $O(|V|^2)$;
  • 稀疏:大部分元素为 0,下游模型不 robust;
  • 不平衡:高频词(the, of)共现数极大,主导相似度。

6.3 SVD 降维 → LSA

经典做法是奇异值分解(Singular Value Decomposition):

$$ X = U\,\Sigma\,V^\top,\quad U\in\RR^{|V|\times r},\ \Sigma\in\RR^{r\times r}_{\ge 0},\ V\in\RR^{|V|\times r} \tag{6.1} $$

截断到 top-$k$ 个奇异值得到秩-$k$ 最佳近似 $\hat{X}_k$(Eckart-Young 定理,最小化 Frobenius 范数)。词向量 = $U_k$ 的前 $k$ 列。

为何对原始 X 直接 SVD 不 work?

原始 $X$ 的元素跨度太大:function words ("the", "he", "has") 在每个窗口都出现,共现数动辄上万;内容词只有几次。SVD 倾向于优先解释那些数值最大的元素,结果第一主成分常常是"the-likeness"——这显然不是我们要的语义。解决方案:
  • 对 cell 取对数:$X' = \log(1 + X)$
  • 截顶:$X' = \min(X, 100)$
  • 直接丢弃 stop words
  • 用 ramped window(离中心越远权重越低)
  • 用 PPMI(positive pointwise mutual information)替代原始 count
Rohde 等 (2005) 的 COALS 模型综合了这些 trick,能在二维投影里漂亮地把动词 "swim" 与名词 "swimmer" 用平行向量连起来——这是分布式语义"线性结构"的第一个清晰证据。

6.4 GloVe (Pennington, Socher, Manning, EMNLP 2014)

GloVe(Global Vectors)的设计哲学:结合 word2vec 的"局部窗口梯度学习"和 LSA 的"全局共现统计"。它的灵感来自一个关键观察。

观察:两个词的差异不应反映在绝对共现概率上,而应反映在共现概率的比值上。例如:

x =solidgaswaterrandom
P(x|ice)1.9×10⁻⁴6.6×10⁻⁵3.0×10⁻³1.7×10⁻⁵
P(x|steam)2.2×10⁻⁵7.8×10⁻⁴2.2×10⁻³1.8×10⁻⁵
P(x|ice)/P(x|steam)8.9 (大)0.085 (小)1.360.96

"ice 比 steam 更 solid"、"ice 不如 steam 那么 gas"——这种结构性 信息在比值里。GloVe 的设计目标:让词向量的算术运算线性地编码 log 共现比值

形式上他们假设:

$$ \mathbf{w}_i \cdot \mathbf{w}_j = \log P(i \mid j), \quad\text{从而}\quad \mathbf{w}_x \cdot (\mathbf{w}_a - \mathbf{w}_b) = \log\frac{P(x\mid a)}{P(x \mid b)} \tag{6.2} $$

把 $P(i\mid j)=X_{ij}/X_j$ 代入,整理后得到一个对称化的加权最小二乘损失

$$ J = \sum_{i,j=1}^{V} f(X_{ij}) \Big(\mathbf{w}_i^\top \tilde{\mathbf{w}}_j + b_i + \tilde{b}_j - \log X_{ij}\Big)^2 \tag{6.3} $$

其中 $f$ 是权重函数(避免高频共现 dominate、避免零共现 NaN):

$$ f(x) = \begin{cases} (x/x_{\max})^{\alpha} & x < x_{\max}\\ 1 & x\ge x_{\max}\end{cases},\quad x_{\max}=100, \alpha = 3/4 \tag{6.4} $$
X_ij f 1.0 0 x_max=100 f(X_ij) (α=3/4)
图 6.2 · GloVe 的权重函数:(a) X_ij = 0 时 f = 0,自动跳过;(b) 0 < X_ij < 100 时按 3/4 次幂增长,给中频词加权;(c) X_ij ≥ 100 时饱和于 1,防止 "the / of" 等垄断损失。注意 3/4 与负采样的 3/4 power 同源——都是"压扁高频"的思路。

GloVe 的优势:

  • 训练快:直接对 $X$ 做加权回归,无需扫语料;
  • 规模化:原作者训练到 840B tokens;
  • 线性结构强:在类比任务上 SOTA(见 §7)。

6.5 统一视角:所有 word embedding 都在做矩阵分解

SGNS, GloVe, LSA 的统一解释

方法分解的矩阵近似目标
LSA / SVDX 或 log Xmin ||X − UΣV^⊤||_F
SGNSPMI(w,c) − log K隐式(Levy & Goldberg 2014)
GloVelog X_ijweighted least squares (6.3)
所以 2014 年以后 word embedding 研究的大致共识是:这三种方法在数学上是同一种东西的不同近似。它们的差异主要体现在:(a) 对哪个矩阵做分解;(b) 用什么损失;(c) 工程实现。这种"统一"思路是 Levy, Goldberg & Dagan 2015 这篇 TACL 长文的主旋律,强烈建议研究生通读。

练习 6.1

(推导题)从假设 $\mathbf{w}_i^\top \mathbf{w}_j = \log P(i\mid j)$ 出发,证明它同时 满足 $\mathbf{w}_j^\top \mathbf{w}_i = \log P(j\mid i)$ 当且仅当 $P(i\mid j)P(j) = P(j\mid i)P(i)$(即贝叶斯定理)。这解释了为何 GloVe 需要 (6.3) 中两个偏置 $b_i, \tilde{b}_j$ 来吸收边际概率。

7 · 评测词向量

7.1 内在 vs 外在评测

Intrinsic 内在Extrinsic 外在
对象词向量自身的几何性质下游任务表现
例子类比、相似度、聚类NER, QA, sentiment, MT
速度分钟级小时~天
风险与真实任务相关性未知难判断瓶颈在 embedding 还是模型

7.2 内在评测 ① — 词向量类比

经典任务:man : woman :: king : ?,期望答案 queen。具体做法:

$$ d = \arg\max_{i\in V \setminus \{a,b,c\}} \frac{(\mathbf{x}_b - \mathbf{x}_a + \mathbf{x}_c)^\top \mathbf{x}_i}{\lVert \mathbf{x}_b - \mathbf{x}_a + \mathbf{x}_c\rVert \cdot \lVert\mathbf{x}_i\rVert} \tag{7.1} $$

注意三点:

  • 评价指标是 余弦相似度(除范数),不是纯内积;
  • 必须把输入词 $a,b,c$ 从候选里剔除(否则答案常常是输入词本身);
  • 这个 trick 叫 3CosAdd;改进版 3CosMul(Levy & Goldberg 2014)效果更好。
x₁ x₂ man woman king queen ★ (x_b − x_a) = "feminine" (parallel) "royalty" "royalty"
图 7.1 · 类比的几何:man:woman :: king:queen。"feminine" 方向 = x_woman − x_man ≈ x_queen − x_king。这意味着词向量空间里存在语义维度的近似线性子空间——是 word2vec/GloVe 最神奇的涌现性质。但要注意:这个性质在 BERT 风格的 contextualized embedding 里明显减弱

GloVe 在 Wikipedia + Gigaword 上学到的词向量在二维 PCA 投影下能清晰展示:

  • 性别:man↔woman, king↔queen, uncle↔aunt, nephew↔niece —— 这些对的差向量近似平行;
  • 头衔/地位:sir↔madam, earl↔duchess, king↔queen, emperor↔empress;
  • 家族关系:sister/brother, niece/nephew, heir/heiress;
  • 动词→名词:swim→swimmer, drive→driver, teach→teacher(COALS 图)。

线性结构来自哪里?

若词向量满足 $\mathbf{w}_i\cdot \mathbf{w}_j = \log P(i\mid j)$(GloVe 假设),那么 $\mathbf{w}_x\cdot(\mathbf{w}_a-\mathbf{w}_b) = \log\frac{P(x|a)}{P(x|b)}$,是log 比值,自然满足"对称差"性质:
$\log\frac{P(x|a)}{P(x|b)} = \log\frac{P(x|c)}{P(x|d)}$ ⟺ 上下文相似度比相同。
所以类比可解 不是巧合,而是 GloVe 损失函数直接逼出来的代数性质。SGNS 也大致继承了这个性质(因为它隐式分解 PMI,PMI 也有 log 形式)。

7.3 内在评测 ② — 相似度任务

给定一组人类标注的词对相似度(例如 WordSim-353:tiger-cat=7.35, stock-jaguar=0.92),计算词向量余弦相似度,与人类打分做 Spearman 相关。常用 benchmark:

数据集词对数说明
WordSim-353 (WS353)353最经典;混合 similarity 与 relatedness
MC (Miller-Charles)30极小但极干净
RG (Rubenstein-Goodenough)65名词对,1965 年的标注
SCWS (Huang)2003带上下文,可评 polysemy
RW (Rare Word)2034低频词,挑战 OOV/morphology

原 PPT 给出的一组对照(Spearman ρ × 100):

ModelSizeWS353MCRGSCWSRW
SVD6B35.335.142.538.325.6
SVD-S6B56.571.571.053.634.7
SVD-L6B65.772.775.156.537.0
CBOW6B57.265.668.257.032.5
SG6B62.865.269.758.137.2
GloVe6B65.872.777.853.938.1
GloVe42B75.983.682.959.647.8

三个关键 takeaways:

  1. SVD 的简单版(hack 后的 SVD-L)已经接近神经方法——"counting 不死"。
  2. 语料越大越好:GloVe 从 6B → 42B token,几乎所有指标都跳 8-10 点。
  3. 低频词(RW)是公认难点:所有方法都掉到 30-50 区间,是 fastText 后来引入字符 n-gram 的原因。

7.4 外在评测 — NER 案例

命名实体识别 (Named Entity Recognition):识别句子中的人名、地名、机构名。例 "Chris Manning lives in Palo Alto." → (Chris Manning, PER), (Palo Alto, LOC)。把 word embedding 当 feature 喂给 NER 分类器(CRF 或 BiLSTM),比较 F1。

EmbeddingDevTestACEMUC7
Discrete (one-hot/手工特征)91.085.477.473.4
SVD90.885.777.373.7
HPCA92.688.781.780.7
CBOW93.188.282.281.1
GloVe93.288.382.982.2

结论:在外在任务上 GloVe 略胜 SG/CBOW,是 2015 年前后 NLP pipeline 的默认选择。

2018 年 ELMo / BERT 之后,静态 embedding 在外在任务上被 contextualized embedding 全面超越,但 word2vec/GloVe 仍然作为 (a) 教学起点 (b) 计算受限场景 baseline (c) probing 实验的对照。

练习 7.1

WordSim-353 的问题:tiger-cat 和 plane-car 都是 ~7 分。但前者是 "is-a" 关系,后者是 "co-hyponym"。若你设计一个 NER 系统,哪种关系对你更有用?为什么这暴露了"intrinsic"评测的根本局限?

练习 7.2 — 编程题

下载 GloVe.6B.50d,写一个 Python 函数 analogy(a,b,c,topk=5),实现 (7.1)。在 5 组类比上测试:(king,man,woman)、(Paris,France,Japan)、(big,bigger,small)、(running,run,walk)、(cat,cats,dog)。

8 · 最小化 PyTorch 实现:40 行 SGNS

下面是一个能跑、能验证的 skip-gram + negative sampling 极简实现。删除注释后正好 38 行。建议你 fork 并在 Penn Treebank 子集上跑一次。

import torch, torch.nn as nn, torch.nn.functional as F
from torch.utils.data import DataLoader, Dataset
import random, collections

class SGNS(nn.Module):
    def __init__(self, V, d):
        super().__init__()
        self.v = nn.Embedding(V, d)  # center vectors v_w
        self.u = nn.Embedding(V, d)  # outside vectors u_w
        nn.init.uniform_(self.v.weight, -0.5/d, 0.5/d)
        nn.init.zeros_(self.u.weight)

    def forward(self, c, o_pos, o_neg):
        # c: (B,)  o_pos: (B,)  o_neg: (B, K)
        vc  = self.v(c)               # (B, d)
        uo  = self.u(o_pos)           # (B, d)
        Uk  = self.u(o_neg)           # (B, K, d)
        pos = F.logsigmoid((vc * uo).sum(-1))                # (B,)
        neg = F.logsigmoid(-(Uk * vc.unsqueeze(1)).sum(-1))  # (B, K)
        return -(pos + neg.sum(-1)).mean()                   # scalar

class SGNSData(Dataset):
    def __init__(self, tokens, w2i, window=5, K=5):
        self.tok = [w2i[w] for w in tokens]; self.w = window; self.K = K
        freq = collections.Counter(self.tok)
        p = torch.tensor([freq[i]**0.75 for i in range(len(w2i))])
        self.p = p / p.sum()                                  # 3/4-power dist
    def __len__(self): return len(self.tok)
    def __getitem__(self, i):
        c = self.tok[i]
        j = random.choice([k for k in range(-self.w, self.w+1)
                           if k != 0 and 0 <= i+k < len(self.tok)])
        o = self.tok[i+j]
        neg = torch.multinomial(self.p, self.K, replacement=True)
        return c, o, neg

def train(tokens, w2i, d=100, lr=1e-3, epochs=3, batch=512):
    model = SGNS(len(w2i), d)
    opt = torch.optim.Adam(model.parameters(), lr=lr)
    loader = DataLoader(SGNSData(tokens, w2i), batch_size=batch, shuffle=True)
    for ep in range(epochs):
        for c, o, neg in loader:
            opt.zero_grad(); loss = model(c, o, neg); loss.backward(); opt.step()
        print(f"epoch {ep} loss {loss.item():.4f}")
    return (model.v.weight + model.u.weight).detach() / 2     # 平均两套向量

几个工程细节值得讲:

  • nn.init.uniform_(self.v.weight, -0.5/d, 0.5/d) + zeros_(self.u.weight):原作者的初始化策略,避免初期 $\mathbf{u}^\top\mathbf{v}$ 爆炸;
  • F.logsigmoid 而不是 torch.log(torch.sigmoid(...)):数值稳定,避免 log(0);
  • torch.multinomial(self.p, K, replacement=True):经验上 K=5 对小语料、K=2 对大语料;
  • 最后平均 两套向量 $\tfrac{1}{2}(V+U)$ 作为最终 embedding,是经验上的小提升技巧;
  • 真正的 word2vec C 代码还做了 subsampling:以概率 $1-\sqrt{t/f(w)}$ 跳过高频词($t\approx 10^{-5}$)。这能进一步加速并提升低频词质量。

9 · 通向现代 LLM:word2vec 在 2026 年还有意义吗?

简短答案:,但角色变了。

2013–2017 (静态时代)2018 后 (上下文化时代)
word2vec / GloVe 是 NLP pipeline 的第一道工序是 Transformer 输入层的初始化或对照
下游任务 = embedding + LSTM/CNN + softmax下游任务 = pretrained Transformer + 小幅 fine-tune
每个词一个向量每个词每个上下文一个向量(contextualized)
无法处理 OOVBPE/SentencePiece 子词切分 → 几乎不 OOV

9.1 word2vec → Transformer token embedding

Transformer 的第一层是 nn.Embedding(V_subword, d_model)——本质和 word2vec 的 $U$ 矩阵完全一样。区别在于:

  • 这个矩阵在 LLM 中和后面 12-96 层 self-attention 端到端训练,而不是单独学;
  • 词表是 BPE/sentencepiece 子词单位(GPT-2: 50257, LLaMA-3: 128256),不是 word;
  • 初始化用 Gaussian 而不是 word2vec 的输出(虽然有研究说预训练 word2vec 初始化能加速收敛)。

9.2 word2vec ↔ contrastive learning

仔细看 SGNS 损失 (5.1):

  • "真正"对 (c, o) → 拉近向量(正样本)
  • "随机"对 (c, w_k) → 推远向量(负样本)

这正是 contrastive learning 的范式!2020 年后 SimCLR / InfoNCE / CLIP 在视觉和多模态领域大火,它们的损失函数(InfoNCE)几乎是 SGNS 的连续推广:

$$ \mathcal{L}_{\text{InfoNCE}} = -\log \frac{\exp(\mathbf{q}^\top \mathbf{k}_+/\tau)}{\sum_{i=0}^{K}\exp(\mathbf{q}^\top \mathbf{k}_i/\tau)} \tag{9.1} $$

把 $\mathbf{q}=\mathbf{v}_c$, $\mathbf{k}_+=\mathbf{u}_o$, $\mathbf{k}_i=\mathbf{u}_k$ 代入、温度 $\tau=1$,你就得到了 softmax 风格的 SGNS。所以——word2vec 是 contrastive learning 在文本上最早的 successful instance

9.3 静态 vs 上下文:词义偏置 (bias) 的发现

word2vec 的线性结构带来一个意外副作用:偏置也是线性的。Bolukbasi et al. 2016 著名地证明 "man:computer_programmer :: woman:?" → "homemaker"。这开启了 NLP fairness 的研究方向。静态 embedding 的偏置是数据集偏置的最忠实化石——这对 2026 年的 LLM alignment 工作仍有借鉴意义。

三个建议性扩展阅读

  1. fastText (Bojanowski et al. 2017):用字符 n-gram embedding 之和表示词,OOV-free,多语言效果提升显著。
  2. ELMo (Peters et al. 2018, NAACL best paper):把双向 LSTM 的所有层向量做加权和——第一个真正成功的 contextualized embedding。
  3. BERT (Devlin et al. 2019):用 Transformer + masked LM 取代 LSTM,把 NLP pipeline 推向 fine-tune 范式。

📚 参考文献

  1. Mikolov 2013a — Mikolov, T., Chen, K., Corrado, G., & Dean, J. (2013). Efficient Estimation of Word Representations in Vector Space. ICLR Workshop. arXiv:1301.3781
  2. Mikolov 2013b — Mikolov, T., Sutskever, I., Chen, K., Corrado, G. S., & Dean, J. (2013). Distributed Representations of Words and Phrases and their Compositionality. NeurIPS. arXiv:1310.4546 (此篇含负采样、subsampling、phrase 检测)
  3. Pennington 2014 — Pennington, J., Socher, R., & Manning, C. D. (2014). GloVe: Global Vectors for Word Representation. EMNLP.
  4. Levy & Goldberg 2014 — Levy, O., & Goldberg, Y. (2014). Neural Word Embedding as Implicit Matrix Factorization. NeurIPS. (证明 SGNS ≈ shifted PMI 分解)
  5. Levy, Goldberg & Dagan 2015Improving Distributional Similarity with Lessons Learned from Word Embeddings. TACL. (强烈推荐的统一视角综述)
  6. Bojanowski 2017 — Bojanowski, P., Grave, E., Joulin, A., & Mikolov, T. (2017). Enriching Word Vectors with Subword Information. TACL. (fastText)
  7. Bolukbasi 2016 — Bolukbasi, T., Chang, K.-W., Zou, J., Saligrama, V., & Kalai, A. (2016). Man is to Computer Programmer as Woman is to Homemaker? Debiasing Word Embeddings. NeurIPS.
  8. Rohde 2005 — Rohde, D., Gonnerman, L., & Plaut, D. (2005). An Improved Model of Semantic Similarity Based on Lexical Co-Occurrence. ms. (COALS)
  9. Firth 1957 — Firth, J. R. (1957). A synopsis of linguistic theory, 1930–1955. Studies in Linguistic Analysis, 1–32.
  10. Goldberg & Levy 2014word2vec Explained: Deriving Mikolov et al.'s Negative-Sampling Word-Embedding Method. arXiv:1402.3722 (本讲推导的"教师版",强烈推荐细读)

🔖 术语速查

中文 / 英文定义本讲位置
分布式语义 / distributional semantics词义由其上下文决定§1.4
分布式表示 / distributed representation词义分摊到所有维度(≠ localist one-hot)§1.4
中心词 v_c / 上下文词 u_oword2vec 的两套向量;分别对应词作为"被预测端"和"预测者"的角色§2.2
softmax把实向量映射为概率分布;多分类标配§3.2
负采样 / Negative Sampling用 K 个二分类代替 |V| 分类,绕开 softmax 分母§5.2
3/4 power 采样P(w) ∝ U(w)^{3/4},压扁高频、抬升低频§5.3
共现矩阵 XX_ij = i 与 j 在窗口内共同出现的次数§6.2
SVD / LSAX = UΣV^⊤;截断 → 低秩词向量§6.3
GloVe对 log X_ij 做加权最小二乘回归,得到与窗口梯度等价的全局解§6.4
类比 / analogyman:woman :: king:? → 用向量加减求 cos 最近邻§7.2
WordSim-353353 词对人工相似度评分,最常用的 intrinsic 评测集§7.3
NER命名实体识别;word vector 的标准 extrinsic 评测任务§7.4
SGNSSkip-Gram with Negative Sampling,word2vec 工程默认实现§5.2 §8

教材编纂:由 Claude (Opus 4.7) 基于 CS224N Winter 2026 Lecture 2 by Diyi Yang + 经典文献综合编写 · 整理日期 2026-05-17 · 单文件本地 HTML