2026-07-29
Efficient Estimation of Word Representations in Vector Space 图解
Mikolov 等人的 Word2Vec 论文把词向量训练从昂贵神经语言模型中抽离出来,用 CBOW 和 Skip-gram 让大规模词表示训练成为基础工具。
论文:Efficient Estimation of Word Representations in Vector Space,Tomas Mikolov, Kai Chen, Greg Corrado, Jeffrey Dean,ICLR 2013 Workshop Poster。
代码状态:论文 v3 的 Follow-Up Work 指向 Google Code word2vec。这篇笔记使用其 GitHub 导出镜像 tmikolov/word2vec,本地克隆 commit 为 20c129a。代码是已公开状态,包含 CBOW、Skip-gram、hierarchical softmax,以及后续扩展的 negative sampling、subsampling 和 phrase 工具。
资料说明:我读取了 arXiv 摘要页、arXiv PDF 与 HTML 全文;旧论文在 arxiv.org/html/1301.3781 没有可用转换页,因此用 ar5iv HTML 全文辅助校对。
图解速览

这篇论文的核心不是“第一次提出词向量”,而是把词向量训练做成足够轻量的工程工具:输入文本序列,经过 CBOW / Skip-gram 两个预测任务,输出能保留语义方向的向量空间。

传统 NNLM / RNNLM 的瓶颈在隐藏层和大词表输出。本文的关键取舍是去掉昂贵的非线性隐藏层,保留共享词向量矩阵,再用 hierarchical softmax 把输出层成本压到树路径长度。

CBOW 用上下文预测中心词。它把窗口里的上下文词查表成向量,求和或平均成一个上下文表示,再预测中间词。这个机制忽略词序,但训练快,句法规律表现尤其稳定。

Skip-gram 用中心词预测周围词。一个中心词会展开成多个监督信号,因此比 CBOW 慢一些,但能给语义关系和稀有词更多训练机会。

论文最有说服力的评测,是把语义和句法关系变成向量类比题:先做向量运算,再找最近词,完全命中才算对。这让“词向量里有线性关系”从漂亮例子变成了可批量计分的实验。
研究背景与动机
2013 年以前,连续词向量已经不是新概念。Bengio 等人的 neural probabilistic language model 已经说明,可以用一个投影层把 one-hot 词索引映射到稠密向量,再通过神经网络预测语言模型概率。Collobert、Weston、Mnih、Huang 等工作也展示了词向量在 NLP 任务中的价值。问题在于,这些模型通常把词向量学习绑定在更复杂的语言模型上:要预测下一个词,要计算隐藏层,要处理巨大词表输出层。词向量是有价值的,但获取它们的成本很高。
论文的出发点并不是重新定义“语义是什么”,而是回答一个非常工程化的问题:如果目标只是学到可迁移、可比较、可做向量代数的词表示,是否真的需要完整的神经语言模型?作者观察到,在传统 NNLM 和 RNNLM 中,真正拖慢训练的主要部分并不是从 one-hot 到向量的查表,而是投影层之后的 dense hidden layer 和输出层计算。只要删掉或弱化这些部分,模型就能把同样的算力投到更大的语料、更高维的向量和更大的词表上。
这正是标题里 Efficient 的含义。论文强调目标是在 very large data sets
上估计词表示,而不是在小语料上训练一个更复杂的网络。这个姿态后来影响极大:Word2Vec 让词向量从研究者手里的模型组件,变成了工业界和开源社区都能训练、下载、复用的基础资产。
当时主流 NLP 系统仍大量使用离散词特征、N-gram、词表索引和人工模板。离散表示的优点是简单、鲁棒、可扩展;缺点是没有天然的相似性结构。系统知道 Paris 和 Berlin 是两个不同 token,却不知道它们和 city、country、capital 之间的关系。连续向量的承诺是:相似词可以在空间中靠近,关系也可以用方向表达。
论文最打动人的地方,是它没有只用“相似词列表”证明词向量好,而是构造了一个更严苛的 semantic-syntactic analogy 测试集。模型不仅要知道 France 和 Italy 相似,还要在 Paris - France + Italy 这种向量运算后找到 Rome。这让词向量质量从“看起来相似”变成了可以批量计算准确率的任务。
预备知识
为了读懂这篇论文,需要先区分三件事:词向量、语言模型、训练目标。词向量是每个词对应的稠密实数向量;语言模型是给定上下文预测词序列概率的模型;训练目标则决定模型如何从语料中更新向量。传统 NNLM 同时做三件事,而本文的策略是:保留“用上下文预测词”或“用词预测上下文”的自监督信号,但尽量简化网络结构。
论文把不同架构的训练复杂度统一写成:
$$ O = E \times T \times Q $$
这里 $E$ 是训练 epoch 数,$T$ 是训练集中 token 数,$Q$ 是每个训练样本需要访问或更新的参数量。人话解释:总训练成本等于“扫几遍数据”乘以“数据有多大”再乘以“每个样本有多贵”。Word2Vec 的核心策略就是压低 $Q$,然后允许 $T$ 和向量维度 $D$ 变大。
另一个关键概念是 hierarchical softmax。普通 softmax 要在大小为 $V$ 的词表上归一化,词表若有一百万词,每次预测都很贵。hierarchical softmax 把词表组织成二叉树,预测一个词等价于沿根到叶子的路径做若干次二分类。若使用 Huffman tree,高频词路径更短,平均计算量会进一步下降。
flowchart TB
A["One-hot word ids"] --> B["Shared embedding table"]
B --> C1["CBOW
context to center word"]
B --> C2["Skip-gram
center word to context"]
C1 --> D["Hierarchical softmax
Huffman path"]
C2 --> D
D --> E["Dense word vectors
semantic and syntactic regularities"]
方法详解
1. 从 NNLM/RNNLM 的复杂度里找到真正瓶颈
论文第 2 节先分析已有神经语言模型。Feedforward NNLM 的输入是 $N$ 个历史词,每个词查到 $D$ 维向量后拼接成 $N \times D$ 的投影层,再连接到 $H$ 维非线性隐藏层,最后预测词表输出。其每个样本复杂度被写为:
$$ Q = N \times D + N \times D \times H + H \times V $$
这个公式对应论文 Eq. (2)。$N \times D$ 是查表和拼接的代价,$N \times D \times H$ 是投影层到隐藏层的矩阵乘,$H \times V$ 是输出层。即使输出层可用 hierarchical softmax 降低,$N \times D \times H$ 仍然很重。作者的判断是:如果词向量学习的主要需求是关系结构,而不是完整语言模型概率,那么这层 hidden layer 可能不是必要成本。
RNNLM 则用隐藏状态记忆历史,不需要固定上下文长度 $N$,但每个时间步要更新 recurrent hidden state。论文给出复杂度:
$$ Q = H \times H + H \times V $$
这是 Eq. (3)。$H \times H$ 是 recurrent matrix 的核心开销,$H \times V$ 是输出层。RNNLM 的优势是理论上可以建模更长依赖,但在本文关注的词向量训练中,它训练慢,且在 semantic analogy 上并不占优。
这一步分析奠定了全文的技术路线:本文不试图让模型表达能力更复杂,而是用更低复杂度换取更大规模训练。这个判断在当时并不保守,因为神经网络研究天然偏向增加非线性和容量。作者反其道而行之,认为对于词向量,保留足够的线性结构可能更重要。
2. CBOW:用上下文的平均向量预测中心词
Continuous Bag-of-Words,简称 CBOW,是论文第 3.1 节提出的第一个新架构。它与 feedforward NNLM 相似,仍使用上下文词作为输入,但删除了非线性隐藏层,并共享所有上下文位置的投影矩阵。上下文词向量被投影到同一个空间后取平均,用这个平均表示预测中心词。
论文 Figure 1 把 CBOW 和 Skip-gram 并排画出:CBOW 的箭头从周围词指向当前词,Skip-gram 的箭头从当前词指向周围词。这个 Figure 1 是理解全文的核心图,因为它说明两种模型不是复杂网络,而是方向相反的局部预测任务。
CBOW 的训练复杂度为:
$$ Q = N \times D + D \times \log_2(V) $$
这是论文 Eq. (4)。$N \times D$ 来自读取并平均 $N$ 个上下文向量;$D \times \log_2(V)$ 来自 hierarchical softmax 路径上的二分类。人话解释:CBOW 每看一个中心词,只需要把窗口里的词向量求平均,再沿树路径更新若干个输出节点,不需要 dense hidden layer。
CBOW 的重要设计是“bag-of-words”:它忽略上下文词序。四个左词和四个右词都会贡献到同一个投影向量,模型不区分某个词在左边还是右边。这牺牲了部分句法顺序信息,却带来两个收益。第一,平均操作非常快;第二,它让模型更关注局部词共现的稳定统计,而不是具体位置模板。
源码中的 CBOW 分支几乎就是论文文字的直接实现。下面片段来自本地克隆的 /tmp/paper_code_word2vec/word2vec.c:435-463,它先收集窗口内词向量,求平均得到 neu1,再沿 Huffman 路径进行 hierarchical softmax 更新。
if (cbow) { //train the cbow architecture
cw = 0;
for (a = b; a < window * 2 + 1 - b; a++) if (a != window) {
c = sentence_position - window + a;
if (c < 0) continue;
if (c >= sentence_length) continue;
last_word = sen[c];
if (last_word == -1) continue;
for (c = 0; c < layer1_size; c++) neu1[c] += syn0[c + last_word * layer1_size];
cw++;
}
if (cw) {
for (c = 0; c < layer1_size; c++) neu1[c] /= cw;
if (hs) for (d = 0; d < vocab[word].codelen; d++) {
f = 0;
l2 = vocab[word].point[d] * layer1_size;
for (c = 0; c < layer1_size; c++) f += neu1[c] * syn1[c + l2];
g = (1 - vocab[word].code[d] - f) * alpha;
for (c = 0; c < layer1_size; c++) syn1[c + l2] += g * neu1[c];
}
}
}
这里 syn0 是输入词向量矩阵,neu1 是上下文平均后的 projection layer,syn1 是 hierarchical softmax 的输出节点向量。论文说 CBOW 的 projection layer 是共享的,代码里体现为所有上下文位置都访问同一个 syn0 表,而不是每个位置一套权重。
3. Skip-gram:用中心词预测周围词
Skip-gram 是论文第 3.2 节提出的第二个架构。它把 CBOW 的预测方向反过来:输入当前中心词,预测窗口内若干上下文词。用形式化写法,可以把目标理解为最大化:
$$ \frac{1}{T}\sum_{t=1}^{T}\sum_{-C \leq j \leq C,\,j \neq 0}\log p(w_{t+j}\mid w_t) $$
这条公式不是论文编号公式,而是对论文第 3.2 节文字的标准化表达。$w_t$ 是中心词,$w_{t+j}$ 是它前后窗口里的上下文词,$C$ 是最大窗口半径。人话解释:模型每看到一个词,就把它当线索,训练它去猜附近可能出现的词。
论文给出的 Skip-gram 复杂度是:
$$ Q = C \times (D + D \times \log_2(V)) $$
这是 Eq. (5)。相比 CBOW,Skip-gram 每个中心词要预测多个上下文位置,因此多了 $C$ 这个因子。它训练更慢,但对稀有词和语义关系往往更强。原因很直观:CBOW 把多个上下文词平均成一个信号,适合高频、句法类规律;Skip-gram 让一个中心词分别面对多个上下文标签,给每个词更多独立训练机会。
论文还说,距离中心词越远的上下文通常相关性越弱,因此训练样本会对远距离词做更少采样。源码中对应的是 b = next_random % window 以及循环边界 a = b 到 window * 2 + 1 - b:随机缩短窗口,使近邻更常出现,远邻更少出现。
} else { //train skip-gram
for (a = b; a < window * 2 + 1 - b; a++) if (a != window) {
c = sentence_position - window + a;
if (c < 0) continue;
if (c >= sentence_length) continue;
last_word = sen[c];
if (last_word == -1) continue;
l1 = last_word * layer1_size;
for (c = 0; c < layer1_size; c++) neu1e[c] = 0;
if (hs) for (d = 0; d < vocab[word].codelen; d++) {
f = 0;
l2 = vocab[word].point[d] * layer1_size;
for (c = 0; c < layer1_size; c++) f += syn0[c + l1] * syn1[c + l2];
g = (1 - vocab[word].code[d] - f) * alpha;
for (c = 0; c < layer1_size; c++) neu1e[c] += g * syn1[c + l2];
for (c = 0; c < layer1_size; c++) syn1[c + l2] += g * syn0[c + l1];
}
for (c = 0; c < layer1_size; c++) syn0[c + l1] += neu1e[c];
}
}
这段代码里需要特别注意一个命名差异:在论文 Figure 1 中,Skip-gram 是“中心词预测周围词”;而在代码循环中,变量 word 是当前句子位置上的词,last_word 是窗口中被遍历到的上下文词。代码使用 l1 = last_word * layer1_size,结合对 vocab[word] 路径的更新,体现的是对每个中心/上下文二元对做训练。不同版本 word2vec 源码和后续负采样解释中,输入/输出矩阵的命名容易让读者困惑,抓住“一个词预测另一个词”这个局部二分类路径即可。
4. Hierarchical softmax:把一百万类分类改成一条树路径
论文第 2.1 节专门解释为什么使用 Huffman tree。普通 softmax 的输出层需要对所有 $V$ 个词计算分数,词表越大越贵。hierarchical softmax 把每个词编码为从根到叶的一串 0/1 决策,因此预测一个词只需更新路径上的节点。若树平衡,路径长度约为 $\log_2(V)$;若用 Huffman tree,高频词路径更短,实际平均路径还会低于平衡树。
对某个目标词 $w$,hierarchical softmax 可以写作:
$$ p(w \mid h)=\prod_{i=1}^{L(w)}\sigma\left(s_i \, u_i^\top h\right) $$
这里 $h$ 是 CBOW 的上下文平均向量或 Skip-gram 的输入词向量,$u_i$ 是路径第 $i$ 个内部节点的向量,$s_i \in {-1,1}$ 表示向左或向右的二分类标签,$L(w)$ 是词 $w$ 的路径长度。人话解释:原来要在百万词中直接挑一个,现在只需沿着树做若干次“往左还是往右”的判断。
源码中的 Huffman tree 构建位置是 /tmp/paper_code_word2vec/word2vec.c:205-266。实现先按词频排序,再反复合并两个最小 count 节点,最后给每个词写入 code 和 point。注释里保留了一个小拼写错误:高频词会得到 short uniqe binary codes
。这正是论文中“频率越高路径越短”的工程落点。
void CreateBinaryTree() {
long long a, b, i, min1i, min2i, pos1, pos2, point[MAX_CODE_LENGTH];
char code[MAX_CODE_LENGTH];
long long *count = (long long *)calloc(vocab_size * 2 + 1, sizeof(long long));
long long *binary = (long long *)calloc(vocab_size * 2 + 1, sizeof(long long));
long long *parent_node = (long long *)calloc(vocab_size * 2 + 1, sizeof(long long));
for (a = 0; a < vocab_size; a++) count[a] = vocab[a].cn;
for (a = vocab_size; a < vocab_size * 2; a++) count[a] = 1e15;
pos1 = vocab_size - 1;
pos2 = vocab_size;
for (a = 0; a < vocab_size - 1; a++) {
/* merge two smallest nodes */
}
for (a = 0; a < vocab_size; a++) {
vocab[a].codelen = i;
vocab[a].point[0] = vocab_size - 2;
}
}
代码还包含 negative sampling,但要与本文区分清楚。论文 1301.3781 的主要实验使用 hierarchical softmax;negative sampling 是后续 NIPS 2013 论文《Distributed Representations of Words and Phrases and their Compositionality》的重要扩展。GitHub 镜像里的 negative 默认值为 5,说明公开代码已经吸收了后续版本的训练技巧。因此本文在解释 1301.3781 时,以 hierarchical softmax 为主;讨论代码时标注 negative sampling 是后续扩展。
5. 类比评测:从相似词展示到可计分任务
论文第 4 节最重要的方法论贡献,是把词向量评测做成了 analogy 问题。传统展示方式是列出 France 的最近邻,比如 Italy、Germany 等。这种展示很直观,但很难系统比较模型。本文构造 Semantic-Syntactic Word Relationship test set,包含 5 类语义关系和 9 类句法关系,共 8869 个语义问题和 10675 个句法问题。
类比求解的公式是:
$$
X = vector(\text{biggest''}) - vector(\text{big''}) + vector(\text{``small''})
$$
然后在向量空间中找与 $X$ 余弦相似度最高、且不等于题目输入词的词。人话解释:如果 biggest - big 表示“最高级变化”这个方向,把这个方向加到 small 上,就应该靠近 smallest。论文要最大化的是这种 linear regularities among words
,而不只是最近邻相似度。
这个评测在源码里对应 /tmp/paper_code_word2vec/compute-accuracy.c:112-120。它读入四元组 $a:b :: c:d$,计算 $b-a+c$,再枚举词表找点积最高的词。
for (a = 0; a < size; a++)
vec[a] = (M[a + b2 * size] - M[a + b1 * size]) + M[a + b3 * size];
TQS++;
for (c = 0; c < words; c++) {
if (c == b1) continue;
if (c == b2) continue;
if (c == b3) continue;
dist = 0;
for (a = 0; a < size; a++) dist += vec[a] * M[a + c * size];
}
这里有一个容易被忽略的严格性:答案必须精确匹配目标词,近义词也算错。论文在 Table 1 附近说明,当前模型没有显式形态学输入,因此达到 100% 准确率几乎不可能。这个评测很苛刻,但正因为苛刻,才让不同模型的差异更清楚。
实验分析
论文的实验不是一个单点结果,而是逐层回答三个问题:向量维度和训练数据如何共同影响准确率?CBOW/Skip-gram 与 NNLM/RNNLM 相比如何?如果上大规模数据和并行训练,简单模型能否超过更复杂模型?
1. 维度与数据规模:二者必须一起增加
Table 2 使用 CBOW,在受限 30k 词表上比较不同维度和训练 token 数。结果清楚显示:只加数据或只加维度都会遇到边际收益下降,而同时增加二者才最有效。
| CBOW 维度 / 训练词数 | 24M | 49M | 98M | 196M | 391M | 783M |
|---|---|---|---|---|---|---|
| 50 | 13.4 | 15.7 | 18.6 | 19.1 | 22.5 | 23.2 |
| 100 | 19.4 | 23.1 | 27.8 | 28.7 | 33.4 | 32.2 |
| 300 | 23.2 | 29.2 | 35.3 | 38.6 | 43.7 | 45.9 |
| 600 | 24.0 | 30.1 | 36.5 | 40.8 | 46.6 | 50.4 |
这张表的解读不能停在“600 维更好”。更重要的是,50 维从 391M 到 783M 只涨 0.7 个点,而 600 维同样增加数据涨 3.8 个点。高维模型有能力吸收更多数据,低维模型很快容量饱和。反过来,在 24M 词上,300 维到 600 维只涨 0.8 个点,说明没有足够数据时加维度也不划算。
这其实是后来大模型 scaling 讨论的早期形态:模型容量和数据规模需要匹配。本文没有建立现代 scaling law 的完整数学框架,但已经用实验展示了“数据与维度互相制约”的事实。
2. 架构对比:Skip-gram 的语义优势非常突出
Table 3 在同样 320M 训练词、82K 词表、640 维向量的条件下比较 RNNLM、NNLM、CBOW、Skip-gram。这里的控制变量非常关键,因为它把架构差异从数据规模差异中分离出来。
| Architecture | Semantic Accuracy | Syntactic Accuracy | MSR Syntactic Test | 主要结论 |
|---|---|---|---|---|
| RNNLM | 9 | 36 | 35 | 句法可用,语义弱 |
| NNLM | 23 | 53 | 47 | 优于 RNNLM,但训练复杂 |
| CBOW | 24 | 64 | 61 | 句法最佳,语义接近 NNLM |
| Skip-gram | 55 | 59 | 56 | 语义准确率大幅领先 |
这张表是论文最有力的结果之一。Skip-gram 的 semantic accuracy 从 NNLM 的 23 提升到 55,几乎翻倍;CBOW 的 syntactic accuracy 达到 64,超过 NNLM 的 53。也就是说,删掉非线性隐藏层并没有让表示质量崩掉,反而在关键评测上更好。
为什么 Skip-gram 对语义更强?一个合理解释是,语义关系通常依赖更稀疏、更广泛的共现证据。Skip-gram 让每个中心词分别预测多个上下文词,稀有词也能从多个二元训练事件中受益。CBOW 把上下文平均,训练信号更平滑,因此在形态变化、词性变化等句法规律上表现更稳定。
3. 与公开词向量对比:效率转化为质量
Table 4 把本文模型与当时公开词向量比较。最值得注意的是,Skip-gram 300 维只用 783M 训练词,就达到 53.3 total accuracy,高于 6B 训练词的 Our NNLM 100 维总分 50.8。这里的关键不是 Skip-gram 数据更少却更强,而是它把计算用在更直接的词向量目标上。
| 模型 | 维度 | 训练词数 | Semantic | Syntactic | Total |
|---|---|---|---|---|---|
| Collobert-Weston NNLM | 50 | 660M | 9.3 | 12.3 | 11.0 |
| Mikolov RNNLM | 640 | 320M | 8.6 | 36.5 | 24.6 |
| Our NNLM | 100 | 6B | 34.2 | 64.5 | 50.8 |
| CBOW | 300 | 783M | 15.5 | 53.1 | 36.1 |
| Skip-gram | 300 | 783M | 50.0 | 55.9 | 53.3 |
从这张表看,CBOW 不是所有场景都胜出。它速度快,句法尚可,但 semantic accuracy 只有 15.5,低于 Our NNLM 的 34.2。Skip-gram 才是语义关系上的明显突破。这也是后来实践中“训练快用 CBOW,效果尤其语义关系用 Skip-gram”的经验来源。
4. Epoch 与数据:多看新数据胜过重复旧数据
Table 5 比较三轮训练和一轮训练。一个重要结果是:1 epoch Skip-gram 300 维在 1.6B 词上达到 53.8,总分略高于 3 epoch Skip-gram 300 维在 783M 词上的 53.3,训练时间却从 3 天降到 2 天。CBOW 也有类似现象:1 epoch、1.6B 词的总分 36.1,与 3 epoch、783M 词相同,但时间从 1 天降到 0.6 天。
| 设置 | 训练词数 | Semantic | Syntactic | Total | 训练时间 |
|---|---|---|---|---|---|
| 3 epoch CBOW 300 | 783M | 15.5 | 53.1 | 36.1 | 1 天 |
| 1 epoch CBOW 300 | 1.6B | 16.1 | 52.6 | 36.1 | 0.6 天 |
| 3 epoch Skip-gram 300 | 783M | 50.0 | 55.9 | 53.3 | 3 天 |
| 1 epoch Skip-gram 300 | 1.6B | 52.2 | 55.1 | 53.8 | 2 天 |
| 1 epoch Skip-gram 600 | 783M | 56.7 | 54.5 | 55.5 | 2.5 天 |
这说明 Word2Vec 的有效训练信号高度依赖语料覆盖。重复扫同一批 token 可以继续优化,但在大规模语料条件下,模型更需要看到更多不同上下文。这个结论也解释了为什么本文不断强调效率:只有每个样本足够便宜,才可能把训练重点从“重复拟合小数据”转向“吸收更多真实文本”。
5. 分布式训练:简单模型在大规模下释放优势
Table 6 使用 DistBelief 分布式框架,在 Google News 6B 语料上比较 NNLM、CBOW 和 Skip-gram。结果显示,1000 维 CBOW 用 2 天 × 140 CPU cores 达到 63.7 total accuracy;1000 维 Skip-gram 用 2.5 天 × 125 CPU cores 达到 65.6;而 NNLM 100 维用 14 天 × 180 CPU cores 只有 50.8。
| 模型 | 维度 | 训练词数 | Semantic | Syntactic | Total | 训练成本 |
|---|---|---|---|---|---|---|
| NNLM | 100 | 6B | 34.2 | 64.5 | 50.8 | 14 天 × 180 cores |
| CBOW | 1000 | 6B | 57.3 | 68.9 | 63.7 | 2 天 × 140 cores |
| Skip-gram | 1000 | 6B | 66.1 | 65.1 | 65.6 | 2.5 天 × 125 cores |
这张表把论文的标题兑现了:效率不是牺牲质量,而是质量的前提。NNLM 结构更复杂,但训练成本太高,无法同等规模地增加维度和数据。CBOW/Skip-gram 结构更简单,却能在固定时间内使用更高维向量、更大语料、更大词表,最终准确率更高。
6. Sentence Completion 与互补性
论文还在 Microsoft Research Sentence Completion Challenge 上测试 Skip-gram。单独 Skip-gram 得到 48.0,低于 average LSA similarity 的 49,也低于 log-bilinear model 的 54.8 和 RNNLMs 的 55.4。但 Skip-gram 与 RNNLMs 加权组合后达到 58.9,超过当时已报告结果。
| Architecture | Accuracy | 解读 |
|---|---|---|
| 4-gram | 39 | 局部统计不足以解决语义填空 |
| Average LSA similarity | 49 | 全局主题相似性有帮助 |
| Log-bilinear model | 54.8 | 语言模型目标更适合句子填空 |
| RNNLMs | 55.4 | 序列建模能力最强 |
| Skip-gram | 48.0 | 单独不是句子概率模型 |
| Skip-gram + RNNLMs | 58.9 | 词向量关系与序列概率互补 |
这个结果很重要,因为它防止读者误解 Word2Vec。Skip-gram 学到的是局部上下文共现下的词表示,不是完整句子概率模型。它不一定单独解决所有语言理解任务,但能作为特征与 RNNLM 互补。Word2Vec 后来的成功,也很大程度来自这种“可作为通用表示接入其他系统”的性质。
代码对应与复现边界
官方 README 明确说,该工具实现了 Continuous Bag-of-Words 和 Skip-gram,并允许用户指定向量维度、窗口大小、训练算法、频繁词下采样阈值、线程数和输出格式。这与论文第 3 节的模型描述、第 4 节的大规模训练关注点是一致的。
训练入口在 word2vec.c:640-715。命令行参数 -size 对应向量维度 $D$,-window 对应上下文窗口 $C$ 或 $N$,-cbow 选择 CBOW/Skip-gram,-hs 控制 hierarchical softmax,-negative 控制后续扩展的 negative sampling,-threads 对应多线程训练。
源码里的学习率衰减也能对应论文实验设置。论文第 4.2 节说使用起始学习率 0.025 并线性下降到接近 0;代码在 word2vec.c:397-398 中用训练进度线性更新 alpha,并设定最小值为起始值的 0.0001。公开代码里 CBOW 默认学习率被设为 0.05,这是后续工具版本的工程默认值,和论文中某些实验设置不完全一致。
需要明确的复现边界有三点。第一,论文报告的 DistBelief 分布式实现没有在该仓库公开;GitHub 代码是单机多线程 C/C++ 工具,训练速度和配置与论文初版报告不同。第二,Google News 6B 和某些 LDC 语料不是随代码发布的开放数据,读者不能仅凭仓库完全复现实验表。第三,仓库包含 negative sampling、phrase detection、subsampling 等后续工作组件,不能把这些全部归功于 1301.3781 的原始方法。
尽管如此,核心思想可复现程度很高。CBOW 的上下文平均、Skip-gram 的窗口预测、Huffman hierarchical softmax、向量类比评测,都能在公开代码中找到直接实现。对于理解 Word2Vec,这已经足够关键:论文给出模型和实验论证,代码给出高效工程化路径。
讨论:这篇论文真正改变了什么
第一,它改变了词向量的生产方式。此前词向量常被视为神经语言模型训练过程中的中间产物;本文把它变成独立目标。只要有大语料,就可以用一个浅层模型快速训练词向量,再把这些向量用于机器翻译、信息检索、问答、情感分析等任务。
第二,它让“语义关系可以线性表达”成为可实验验证的主张。King - Man + Woman = Queen 这类例子后来被过度传播,有时甚至被浪漫化。但论文真正做的是系统构造 14 类关系测试,并用精确匹配准确率比较模型。它不是只讲一个漂亮例子,而是把漂亮例子变成了评测协议。
第三,它展示了一种很现代的工程哲学:更简单的模型,如果复杂度结构正确,反而能通过规模获得更强能力。本文没有追求更深网络,而是删除 hidden layer、共享 projection、使用 Huffman tree,把每个训练样本变便宜。这个思路与后来的许多大规模学习经验相通:模型结构并不总是越复杂越好,瓶颈位置决定了扩展能力。
第四,它把词向量的可用性和社区传播速度大幅提高。论文 v3 的 Follow-Up Work 说明,作者后来发布了单机多线程代码,并公开超过 140 万个 named entity 向量,训练语料超过 100B words。论文甚至展望 CBOW 和 Skip-gram 可以训练到 one trillion words
。这种工程开放性,是 Word2Vec 成为基础工具的重要原因。
局限分析
作者自述的第一类局限来自评测任务。论文在 Table 1 附近明确指出,答案必须完全匹配,近义词会被算错;同时模型没有输入词内部形态结构,因此达到 100% 准确率几乎不可能。这个限制尤其影响句法类问题,例如比较级、最高级、复数、过去式等关系。如果模型不知道字符或子词结构,只靠词级共现学习 morphology,天然会浪费大量数据。
作者自述的第二类局限来自类比准确率本身。Table 8 后的讨论承认,这些关系例子的准确率不错,但 there is clearly a lot of room for further improvements
。使用一个关系样例构造方向向量很脆弱;论文说如果用十个样例平均关系向量,最佳模型在 semantic-syntactic test 上能绝对提升约 10 个百分点。这说明单样本类比并不是关系表示的上限。
我的独立判断是,本文最大的方法局限是词向量静态性。每个词只有一个向量,bank 在 river bank 和 central bank 中共享表示,多义词只能被压成一个折中点。对 2013 年的任务来说,这已经很强;但从后来的 ELMo、BERT、GPT 系列看,语境化表示才更适合处理多义和长程依赖。
第二个独立判断是,CBOW/Skip-gram 的词序建模能力有限。CBOW 显式忽略词序;Skip-gram 虽然使用窗口距离采样,但仍是局部共现目标,不是句法树或完整句子概率模型。这解释了为什么 Skip-gram 在 Sentence Completion 单独只有 48.0,却能与 RNNLM 互补:它擅长词关系,不擅长完整句子选择。
第三个局限是评测污染和语料依赖。Google News、LDC corpora、公开 analogy 集之间可能存在领域重合,且所有结果高度依赖英语新闻语料。论文构造的 14 类关系非常有影响力,但并不能覆盖语言理解的全部现象,也不能直接推广到低资源语言、形态复杂语言或专业领域术语。
结论
《Efficient Estimation of Word Representations in Vector Space》不是第一篇提出词向量的论文,却是让词向量真正规模化、工具化、评测化的一篇论文。它的核心创新不在复杂模型,而在问题重构:既然目标是高质量词表示,就删掉不必要的语言模型复杂度,把计算预算交给更大的语料、更高维的向量、更大的词表和更直接的预测任务。
CBOW 和 Skip-gram 的历史意义,也不只是“发明了 Word2Vec”。更准确地说,它们把分布式表示从神经网络语言模型的附属品,推进为后续 NLP 系统的默认输入层。它们证明了一个朴素但强大的原则:当表示空间被正确训练,语义和句法关系会以可计算的几何结构浮现出来。