Word2Vec

Word2Vec 原论文的核心贡献在于提出了两种新的模型架构(CBOWSkip-gram),它们去掉了传统神经网络语言模型(NNLM)中计算量最大的“非线性隐藏层”,从而极大地提高了训练效率,使得在十亿级词量的语料上训练成为可能。

1. 核心训练原理:去除隐藏层

传统的神经网络语言模型(NNLM)结构是:输入层 -> 投影层 -> 隐藏层(非线性) -> 输出层。作者指出,大部分计算复杂度来自于那个非线性隐藏层。为了加速,Word2Vec 的训练模型直接去掉了隐藏层,变成了:
输入层 -> 投影层(共享) -> 输出层(Log-linear分类器)

所有的模型都使用**随机梯度下降(Stochastic Gradient Descent, SGD)反向传播(Backpropagation)**进行训练。


2. 两种具体的训练架构

A. CBOW 模型 (Continuous Bag-of-Words)

它的逻辑是:根据“上下文”来预测“中间词”。

  • 输入 (Input): 当前词周围的上下文单词(包括历史词和未来词)。例如,对于句子 “The cat sits on the mat”,如果预测 “sits”,输入就是 “The”, “cat”, “on”, “the” 。
  • 投影 (Projection): 所有输入上下文单词的向量被**求和(或平均)**投影到同一个位置。
    • 注意: 因为是求和/平均,所以词的顺序丢失了(这就是为什么叫 Bag-of-Words,词袋模型)。
  • 输出 (Output): 预测当前的中间词。
  • 训练目标: 最大化正确分类当前中间词的概率。
B. Skip-gram 模型 (Continuous Skip-gram)

它的逻辑是:根据“中间词”来预测“周围词”。

  • 输入 (Input): 当前的一个词。
  • 输出 (Output): 预测该词前后一定范围内的上下文单词。
    • 作者发现增加预测范围(Range)能提高向量质量,但也会增加计算量。
    • 实验中通常使用 C=10 的距离。
  • 采样策略: 离当前词越远的词,通常相关性越低。因此训练时,作者对距离较远的词进行降权采样(给予较小的权重)。
  • 具体过程: 如果选定范围 C=5C=5C=5,对于每一个训练词,随机选一个数 R∈[1,C]R \in [1, C]R[1,C],然后使用当前词去预测其前后 RRR 个词。这意味着对于一个输入词,网络要进行 R×2R \times 2R×2 次分类任务。

3. 关键的加速技巧:Hierarchical Softmax

在输出层,如果直接计算词汇表中所有词(比如 100 万个词)的概率(Softmax),计算量是巨大的(O(V)O(V)O(V))。为了解决这个问题,论文使用了 Hierarchical Softmax(分层 Softmax)

  • 原理: 将词汇表组织成一棵 Huffman 树(哈夫曼树)
  • 效果:
    • 高频词被分配较短的二进制编码,位于树的较浅层。
    • 计算概率不再需要遍历整个词汇表,只需要沿着树的路径走到叶子节点。
    • [cite_start]复杂度从 VVV 降低到了 log2(V)log_2(V)log2(V)
  • 具体优势: 对于 100 万词的词汇表,这带来了巨大的速度提升。由于去掉了隐藏层,模型的瓶颈主要在输出层的 Softmax 归一化上,因此这个技术至关重要。

4. 训练参数与环境设置

  • 优化算法: 使用带有 Adagrad 自适应学习率的 Mini-batch 异步梯度下降 。
  • 学习率 (Learning Rate): 初始学习率设为 0.025,并在训练过程中线性递减,直到训练结束时接近 0。
  • 训练轮数 (Epochs):
    • 对于小数据集或对比实验,通常训练 3 个 Epoch。
    • 但在大数据集上,作者发现训练 1 个 Epoch(只过一遍数据)就能得到比 3 个 Epoch 更好或相当的结果,因为数据量足够大。
  • 并行化: 基于 Google 的 DistBelief 框架进行分布式训练,使用 50 到 100 个模型副本并行更新参数。

总结

简单来说,Word2Vec 的训练之所以“Efficient”(高效),是因为:

  1. 架构极简: 砍掉了计算昂贵的非线性隐藏层。
  2. 数学优化: 使用 Hierarchical Softmax 将输出层的计算复杂度从线性级降为对数级。
  3. 任务设计: 利用 CBOW(上下文测中心词)或 Skip-gram(中心词测上下文)作为“假想任务”来训练权重,这些权重最终就是我们想要的词向量。

word2vec训练方法

word2vec原论文非常详细地描述了两种模型架构(CBOW 和 Skip-gram)是如何组织数据进行训练的。核心思想都是基于**“滑动窗口”(Sliding Window)**来构建训练样本,但两者的方向相反。

为了方便理解,我们假设有一句话:

“The quick brown fox jumps over the lazy dog”

下面详细拆解这两种模型具体是怎么构造输入(Input)和标签(Label)的。


1. CBOW (Continuous Bag-of-Words)

逻辑: 给我上下文,我来填中间的空。

  • 输入组织 (Input):
    • 论文中设定了一个上下文窗口大小。例如,选取中间词前后各 4 个词(共 8 个词)作为输入。
    • 模型读取这 8 个上下文单词的向量。
    • 关键步骤(Projection): 这些输入向量在投影层被**求和(Sum)**或平均。因为是求和,所以词的顺序被丢弃了(这就叫 Bag-of-Words,词袋)。
  • 标签组织 (Label):
    • 标签就是当前窗口中心的那个词(中间词)。
  • 具体案例:
    如果窗口中心是 “jumps”:
    • 输入: {"The", "quick", "brown", "fox", "over", "the", "lazy", "dog"}
    • 标签: "jumps"
    • 训练: 这是一个多分类问题。输入这 8 个词的向量和 -> 预测输出是 “jumps” 的概率最大化。

2. Skip-gram (Continuous Skip-gram)

逻辑: 给我中间词,我来猜它旁边可能出现什么词。

这是论文重点推荐的架构,虽然训练更慢,但在语义任务上表现更好。

  • 输入组织 (Input):
    • 当前的中间词作为唯一的输入。
  • 标签组织 (Label):
    • 标签是该词前后一定范围内的上下文单词。
    • 这意味着,一个输入词会产生多个训练样本(一对一的映射)。
  • 关键细节:动态窗口机制 (Dynamic Window Size)
    论文中提到了一个非常具体的训练细节,常被忽略:
    • 虽然设定了最大距离 CCC(例如 C=10C=10C=10),但对于每一个训练样本,模型会先随机选一个数字 RRR1<R<C1 < R < C1<R<C) 。
    • 然后使用当前词去预测前后各 RRR 个词。
    • 为什么要随机 RRR 作者认为,离得越远的词,关系越弱。通过这种随机窗口机制,距离较远的词被选中的概率会降低(降权采样),从而让模型更关注紧邻的词。
  • 具体案例:
    如果当前词是 “fox”,假设随机到的 R=2R=2R=2
    • 样本 1: 输入 "fox" -> 标签 "brown" (前一个词)
    • 样本 2: 输入 "fox" -> 标签 "quick" (前两个词)
    • 样本 3: 输入 "fox" -> 标签 "jumps" (后一个词)
    • 样本 4: 输入 "fox" -> 标签 "over" (后两个词)
    • 训练: 这变成了 4 个独立的分类任务(R×2R \times 2R×2 个分类) [cite: 149]。

3. 训练的核心:怎么算“标签”的概率?

如果词表有 100 万个词,通常的 Softmax 需要计算 100 万次才能知道哪个概率最大,这太慢了。论文中使用了 Hierarchical Softmax (分层 Softmax) 来加速 。

  • 构建哈夫曼树 (Huffman Tree):
    • 整个词表被组织成一棵二叉树。
    • 频率高的词(如 “the”)靠近根节点(编码短),频率低的词靠近叶子节点。
  • 标签的本质:
    • 训练时的“标签”不再是一个 One-hot 向量,而是从树根走到该词叶子节点的一条路径
    • 模型不需要计算 100 万个输出,只需要计算这条路径上的每一个分叉口(二分类)往左走还是往右走。
    • 计算量从 VVV 变成了 log2(V)log_2(V)log2(V)。对于 100 万词表,只需要计算约 20 次二分类,而不是 100 万次多分类。

总结流程

  1. 取词: 从语料库中滑动取出一段文字。
  2. 定架构:
    • 如果是 CBOW:把周围词的向量加起来,去预测中间词在哈夫曼树上的路径。
    • 如果是 Skip-gram:拿中间词的向量,逐个去预测周围词在哈夫曼树上的路径。
  3. 反向传播: 如果预测路径错了(比如该往左却预测往右),就通过 SGD 调整输入词的向量(Input Vector)和树节点的参数。
  4. 最终产物: 我们并不关心预测得准不准,我们要的是训练好的输入层向量,这就是 Word2Vec 词向量。

Skip-gram的小tricky

在传统的函数拟合(Regression)思维中,如果一个输入 xxx 既要等于 y1y_1y1 又要等于 y2y_2y2,模型确实会感到困惑(通常会收敛到两者的均值)。

但在 Skip-gram 模型中,这种情况不仅不会让模型“跑飞”,反而是它能够捕捉丰富语义的关键所在

原因主要有以下三点:

1. 目标是“概率分布”而非“单一值”

Skip-gram 并不是在做一个简单的函数映射(如 f(x)=yf(x) = yf(x)=y),它做的是概率密度估计

  • 直观理解: 当输入是“苹果”时,模型并不是被迫在“手机”和“水果”之间二选一。它的任务是学习到:
    • P(手机∣苹果)P(\text{手机} | \text{苹果})P(手机苹果) 的概率很高。
    • P(水果∣苹果)P(\text{水果} | \text{苹果})P(水果苹果) 的概率也很高。
    • P(卡车∣苹果)P(\text{卡车} | \text{苹果})P(卡车苹果) 的概率很低。
  • 数学机制: 输出层使用的是 Softmax(或者近似的 Hierarchical Softmax),它的输出总和为 1。只要“手机”和“水果”经常出现在“苹果”周围,模型就会调整参数,让这两个词的输出概率都变大,同时压低其他不相关词的概率。

2. 梯度的“微调”与“合力”

训练是基于随机梯度下降 (SGD) 进行的。这意味着模型不会因为一次冲突的更新就崩塌,而是通过无数次微小的调整寻找平衡点。

  • 场景模拟:
    1. 遇到样本 (苹果, 手机):模型把“苹果”的向量往“手机”的方向拉一点点
    2. 遇到样本 (苹果, 水果):模型把“苹果”的向量往“水果”的方向再拉一点点
    3. 遇到样本 (苹果, 乔布斯):模型把“苹果”的向量往“乔布斯”的方向又拉一点点
  • 最终结果: 经过数十亿次这样的训练,“苹果”的向量最终会停留在所有这些相关词的**“重心”**位置。
  • 几何意义: 这样训练出来的“苹果”向量,在向量空间中会同时靠近“手机”、“水果”和“乔布斯”。这就是为什么 Skip-gram 能学会单词的多种含义(多义词特性)。

3. 一对多正是 Skip-gram 的优势

论文作者在结果对比中发现,Skip-gram 在语义任务上的表现比 CBOW 更好

  • CBOW 的做法(平均化): CBOW 把上下文的所有词(手机、水果、乔布斯)加起来取平均,合成一个向量作为输入,去预测“苹果” 。这实际上是一种**平滑(Smoothing)**操作,它模糊了上下文的具体细节。
  • Skip-gram 的做法(精细化): 它强迫输入词(苹果)必须能够独立地预测每一个上下文词。这意味着“苹果”的向量必须非常强壮,包含足够的信息量来分别应对不同的上下文。

总结

模型不会“跑飞”,是因为:

  1. 任务性质: 它是分类任务(提升相关词的概率),不是回归任务,允许由一个输入对应多个高概率的输出。
  2. 数据规模: 在海量数据(如 10 亿词)的支撑下,噪声会被抵消,只有稳定的共现关系(Co-occurrence)会被保留在向量中。
  3. 距离权重: 论文还提到,为了防止模型被太远的、不相关的词带偏,他们对距离较远的上下文词进行了降权采样(Sampling less from distant words)。

所以,“一对多”不仅不是 bug,反而是 Skip-gram 能够学出高质量、蕴含丰富语义关系的词向量的核心 Feature。

词表大小的演化

2013 年的 Word2Vec 论文中使用的词表非常大(100 万甚至更多),而现代大语言模型(LLM,如 GPT-4、Llama 3 等)的词表规模通常在 3 万到 12.8 万之间。

造成这种巨大差异的核心原因在于分词方式(Tokenization)的演变

1. Word2Vec 时代的“词”是“整词”(Whole Word)

在这篇论文中,作者将每一个独立的单词视为一个原子单位(Atomic Unit) 。这意味着:

  • 形态变化被视为不同词: 单词的不同形态(如单复数、时态)在词表中是完全独立的条目。例如,“apple”和“apples”是两个词;“go”、“going”、“went”也是三个完全不同的词。为了捕捉这些词之间的关系(如论文中提到的 big - bigger 这种类比关系),模型必须为每一个变体都单独学习一个向量。
  • 无法处理未登录词(OOV): 这种方法必须尽可能收录更多的词。如果词表太小,很多生僻词就会变成 <UNK>(未知词)。因此,为了覆盖 60 亿词的语料库 ,作者不得不使用极大的词表,论文中明确提到他们将词表限制在最频繁的 100 万个词 ,甚至提到在分布式框架下可以支持“基本上无限大小的词表” 。
  • 硬性截断: 即便用了 100 万的词表,依然是基于频率截断的,这意味着极其罕见的词(人名、地名、拼写错误)依然会被丢弃。

2. 现代 LLM 使用的是“子词”(Subword)

现在的模型(如 BERT, GPT, Llama)普遍使用 BPE (Byte-Pair Encoding)WordPiece 等子词分词技术。

  • 化整为零: 现代模型不再把“单词”作为最小单位,而是把词拆开。
    • 例如:“unhappiness” 可能被拆成 un + happi + ness
    • “apples” 可能被拆成 apple + s
  • 词表极其紧凑: 因为大部分单词都可以由常见的“字根”或“词缀”拼凑出来,现代 LLM 只需要 5 万到 10 万左右的 Token(词元)就能覆盖几乎所有语言的表达,甚至包括从未见过的生造词。
  • 不再需要“百万级”词表: 依靠组合能力,现代 LLM 用更少的参数实现了更强的泛化能力。

总结

Word2Vec 论文中之所以出现 100 万这样巨大的词表,是因为当时还没有广泛应用子词分词技术,必须“死记硬背”每一个单词的每一个形态。

正如论文中自己承认的局限性:“目前的模型没有任何关于单词形态(morphology)的输入信息”。这正是现代 LLM 通过子词技术解决的问题。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐