查看全课大纲(第 21 / 69 节)
  1. 1.NLP 的研究领域、应用与学习路径
  2. 2.自然语言的基本术语
  3. 3.信息论基础:信息熵与交叉熵
  4. 4.语言的进化与语义变迁
  5. 5.主观概率、客观概率与条件概率
  6. 6.贝叶斯原理与推理
  7. 7.随机变量:二项式、期望方差与联合概率
  8. 8.常见概率分布
  9. 9.辛普森悖论与贝叶斯解题实例
  10. 10.语言模型:语言概率与词袋模型
  11. 11.n-gram 语言模型(二元 / 三元 / N-Gram)
  12. 12.语言模型评价:交叉熵、困惑度与插值
  13. 13.朴素贝叶斯文本分类:垃圾邮件
  14. 14.拼写检查
  15. 15.中文分词:构词法、最大匹配、最优路径与 Jieba
  16. 16.词表示问题与语义相似度
  17. 17.TF-IDF 权重处理
  18. 18.One-Hot 表达与词表示的局限
  19. 19.Word2Vec 核心思想
  20. 20.Word2Vec 训练过程
  21. 21.Skip-gram 模型详解
  22. 22.词性标注任务与标注方法(规则 + N-Gram)
  23. 23.EM 算法与混合模型
  24. 24.高斯混合模型 GMM
  25. 25.从混合模型到隐马尔可夫模型
  26. 26.隐马尔可夫模型(HMM)推导与维特比解码
  27. 27.线性链条件随机场 CRF
  28. 28.贝叶斯网络与图模型
  29. 29.隐马尔可夫链与 HMM 推导
  30. 30.PLSA 主题模型
  31. 31.LSI 与 SVD 向量表达
  32. 32.LDA 与狄利克雷分布
  33. 33.主题模型实验复现(困惑度 / 分类 / 相似度 / 延展)
  34. 34.上下文无关文法(CFG)与句法分析
  35. 35.概率上下文无关文法 PCFG
  36. 36.统计机器翻译:词对齐与翻译模型
  37. 37.统计机器翻译中的对齐模型与参数估计
  38. 38.从统计机器翻译到神经机器翻译
  39. 39.神经元与反向传播速览(指向深度学习课)
  40. 40.CNN 原理及其在 NLP 中的应用
  41. 41.循环神经网络的基本原理与 Elman / Jordan 网络
  42. 42.LSTM:长短期记忆网络的核心思想与分步实现
  43. 43.Encoder-Decoder 框架
  44. 44.Seq2Seq 与早期注意力机制
  45. 45.注意力机制的背景与动机
  46. 46.注意力模型实现原理与匹配函数
  47. 47.记忆网络:组成与计算
  48. 48.注意力机制的应用与历史延展
  49. 49.Transformer 结构:Encoder-Decoder 栈与 Self-Attention
  50. 50.Multi-Head Attention、FFNN 与位置编码
  51. 51.层归一化、损失函数与 Universal Transformer 延展
  52. 52.预训练范式:从 ELMo 到 BERT
  53. 53.BERT 与掩码语言模型
  54. 54.BERT 下游任务微调与 GLUE 口径
  55. 55.GPT 与自回归语言建模
  56. 56.GPT-1 到 GPT-4 的演进
  57. 57.指令微调与 RLHF
  58. 58.分词器:BPE、WordPiece 与中文词表
  59. 59.上下文窗口与长文本处理
  60. 60.能力边界、成本与适用场景
  61. 61.文本分类实战:从朴素贝叶斯到逻辑回归
  62. 62.命名实体识别实战
  63. 63.文本生成与摘要实战
  64. 64.语义检索与向量库实战
  65. 65.RAG 知识库问答
  66. 66.NLP 评估指标:BLEU / ROUGE / BERTScore 与人工评估
  67. 67.幻觉与事实性问题
  68. 68.文本与知识结合(知识图谱压缩)
  69. 69.跨媒体信息检索与未来展望

Skip-gram 模型详解

小象实战讲义 · 自然语言处理

在上一节我们学习了 CBOW 模型,它通过上下文词来预测中心词。本节我们将介绍 Word2Vec 的另一种核心模型——Skip-gram。它的思路与 CBOW 恰好相反,通过中心词来预测其周围的上下文词。这种“由内而外”的建模方式,在捕捉词语的细粒度语义和低频词表示上往往表现更佳。理解 Skip-gram 是掌握现代词向量技术的关键一步。

💡 核心导读

  • 模型反转:Skip-gram 将 CBOW 的预测方向反转,用中心词预测其上下文窗口内的多个词。
  • 目标函数:核心是最大化给定中心词时,其所有上下文词出现的联合概率,通常通过最小化交叉熵损失来实现。
  • 网络结构:一个浅层神经网络,输入是中心词的独热向量,通过嵌入矩阵得到词向量,再通过另一个矩阵映射为对每个词的“得分”,最后经 softmax 转为概率分布。
  • 计算效率:全量 Softmax 在大词表上开销很高;负采样、层次 Softmax 等近似方法把每次训练只需处理少量样本,是 Word2Vec 能在大规模语料上训练的关键。
  • 语义关系:训练得到的词向量能捕捉丰富的语义和语法关系,如“国王-男人+女人≈女王”。

Skip-gram 模型的基本思想

CBOW 模型的核心是“由外而内”:给定一个上下文窗口(例如“我爱___安门”),模型学习预测中间缺失的中心词(例如“天”)。

Skip-gram 模型则反其道而行之,采用“由内而外”的思路:给定一个中心词(例如“北京”),模型学习预测其周围特定窗口大小内的所有上下文词(例如“我”、“爱”、“天安门”)。

这种反转带来了一个关键区别:一个输入对应多个输出。在 CBOW 中,多个上下文词向量平均后得到一个向量,去预测一个中心词。在 Skip-gram 中,一个中心词向量需要分别去预测窗口内的每一个上下文词。

我们可以用一个更直观的例子来理解:

  • CBOW:看到“我”、“爱”、“安门”,猜中间是“天”。
  • Skip-gram:看到“北京”,猜它周围可能出现“我”、“爱”、“天安门”。

这种设计使得 Skip-gram 尤其擅长处理低频词,因为每个词作为中心词时,都能直接贡献梯度来更新其自身的词向量。

Skip-gram 的网络结构与前向传播

Skip-gram 的网络结构同样非常简洁,可以看作一个单隐藏层的神经网络。

1. 输入层

输入是中心词 wcw_c 的 独热编码(One-Hot)向量 x∈R∣V∣\mathbf{x} \in \mathbb{R}^{|V|},其中 ∣V∣|V| 是词汇表大小。

2. 隐藏层(词向量层)

隐藏层没有激活函数,仅是一个线性投影。我们有一个 嵌入矩阵(Embedding Matrix) V∈R∣V∣×d\mathbf{V} \in \mathbb{R}^{|V| \times d},其中 dd 是词向量的维度。

中心词的词向量 vc\mathbf{v}_c 通过简单的矩阵乘法得到: vc=V⊤x\mathbf{v}_c = \mathbf{V}^\top \mathbf{x} 由于 x\mathbf{x} 是独热向量,这个操作实质上就是从矩阵 V\mathbf{V} 中 查找(lookup) 出对应于 wcw_c 的那一行向量。因此,矩阵 V\mathbf{V} 的每一行就对应一个词的词向量。

3. 输出层

输出层旨在为词汇表中的每一个词计算一个“得分”,表示其作为上下文词出现的可能性。我们引入另一个矩阵 U∈Rd×∣V∣\mathbf{U} \in \mathbb{R}^{d \times |V|}。

对于中心词向量 vc\mathbf{v}_c,我们计算其对于词汇表中所有词的得分向量 z\mathbf{z}: z=U⊤vc\mathbf{z} = \mathbf{U}^\top \mathbf{v}_c 这里,z∈R∣V∣\mathbf{z} \in \mathbb{R}^{|V|},其第 jj 个分量 zjz_j 表示词 jj 作为上下文词的得分。

4. 概率化(Softmax)

为了得到概率分布,我们对得分向量 z\mathbf{z} 应用 softmax 函数: y^j=p(wj∣wc)=exp⁡(zj)∑k=1∣V∣exp⁡(zk)=exp⁡(uj⊤vc)∑k=1∣V∣exp⁡(uk⊤vc)\hat{y}_j = p(w_j | w_c) = \frac{\exp(z_j)}{\sum_{k=1}^{|V|} \exp(z_k)} = \frac{\exp(\mathbf{u}_j^\top \mathbf{v}_c)}{\sum_{k=1}^{|V|} \exp(\mathbf{u}_k^\top \mathbf{v}_c)} 其中 uj\mathbf{u}_j 是矩阵 U\mathbf{U} 的第 jj 列向量。y^\hat{\mathbf{y}} 是一个概率分布,表示给定中心词 wcw_c 后,词汇表中每个词作为其上下文词出现的预测概率。

import numpy as np

# 假设一个极简的词汇表 V = {"北京":0, "我":1, "爱":2, "天安门":3}
vocab = ["北京", "我", "爱", "天安门"]
V_size = len(vocab)
d = 3  # 词向量维度

# 初始化参数矩阵 V (|V| x d) 和 U (d x |V|)
np.random.seed(42)
V = np.random.randn(V_size, d)  # 嵌入矩阵
U = np.random.randn(d, V_size)  # 输出矩阵

# 前向传播示例:中心词是"北京"(索引0)
center_word_idx = 0

# 1. 输入:中心词的独热向量
x = np.zeros(V_size)
x[center_word_idx] = 1

# 2. 隐藏层:查找词向量 v_c
v_c = V.T @ x  # 等价于 V[center_word_idx, :]
print("中心词'北京'的词向量 v_c:", v_c)

# 3. 输出层:计算所有词的得分
z = U.T @ v_c
print("\n所有词的得分 z:", z)

# 4. Softmax:得到预测概率分布 y_hat
exp_z = np.exp(z)
y_hat = exp_z / np.sum(exp_z)
print("\n预测概率分布 y_hat:", y_hat)
中心词'北京'的词向量 v_c: [ 0.49671415 -0.1382643   0.64768854]

所有词的得分 z: [ 1.20950838 -1.14003529 -0.68750654 -1.00681806]

预测概率分布 y_hat: [0.738314   0.07044453 0.11075874 0.08048274]

注:末项概率为 0.0805(四位小数),并非接近 0;以上四个概率均由该组随机参数下的 Softmax 得到。

Skip-gram 的目标函数与损失

对于一个中心词 wcw_c,其上下文窗口(大小为 2m2m)内的词为 wc−m,...,wc−1,wc+1,...,wc+mw_{c-m}, ..., w_{c-1}, w_{c+1}, ..., w_{c+m}。Skip-gram 的目标是最大化给定中心词时,所有这些上下文词出现的 联合概率。

我们做一个强独立性假设:给定中心词,所有上下文词的出现是相互独立的。因此,联合概率可以分解为每个上下文词条件概率的乘积: p(wc−m,...,wc−1,wc+1,...,wc+m∣wc)=∏j=0,j≠m2mp(wc−m+j∣wc)p(w_{c-m}, ..., w_{c-1}, w_{c+1}, ..., w_{c+m} | w_c) = \prod_{j=0, j \neq m}^{2m} p(w_{c-m+j} | w_c) 这里 jj 从 0 遍历到 2m2m,但跳过 j=mj=m(即中心词自身的位置);左侧联合概率同样只包含上下文位置,不含中心词 wcw_c 自身。

我们的目标是最大化这个联合概率,等价于最小化其负对数似然,从而得到损失函数 JJ: J=−log⁡p(wc−m,...,wc−1,wc+1,...,wc+m∣wc)=−∑j=0,j≠m2mlog⁡p(wc−m+j∣wc)J = -\log p(w_{c-m}, ..., w_{c-1}, w_{c+1}, ..., w_{c+m} | w_c) = -\sum_{j=0, j \neq m}^{2m} \log p(w_{c-m+j} | w_c)

将 softmax 概率公式代入,得到 Skip-gram 的完整损失函数: J=−∑j=0,j≠m2m[uc−m+j⊤vc−log⁡∑k=1∣V∣exp⁡(uk⊤vc)]J = -\sum_{j=0, j \neq m}^{2m} \left[ \mathbf{u}_{c-m+j}^\top \mathbf{v}_c - \log \sum_{k=1}^{|V|} \exp(\mathbf{u}_k^\top \mathbf{v}_c) \right]

其中 uc−m+j\mathbf{u}_{c-m+j} 是真实上下文词 wc−m+jw_{c-m+j} 在输出矩阵 U\mathbf{U} 中对应的列向量。

损失函数的直观理解:对于中心词 wcw_c,我们希望模型预测的概率分布 y^\hat{\mathbf{y}} 在那些真实的上下文词(如“我”、“爱”、“天安门”)位置上有很高的概率。这等价于要求 y^\hat{\mathbf{y}} 与这些上下文词的 多个独热标签向量 之间的“距离”之和最小。通常,我们使用交叉熵来衡量这个距离。

以中心词"北京"和上下文窗口 ["我", "爱", "天安门"] 为例:

1. 模型输出 y_hat 是一个 |V| 维概率向量。
2. 对于每个上下文词(如"我"),其真实标签是一个独热向量,只有"我"的索引处为1。
3. 损失 J 是"我"、"爱"、"天安门"这三个独热向量分别与 y_hat 的交叉熵之和。
4. 训练时,通过反向传播调整矩阵 V 和 U 的参数,使得 J 最小化。

全量 Softmax 的瓶颈与负采样

全量 Softmax 的开销

上面每个上下文词都要在整个词表 ∣V∣|V| 上计算 exp⁡\exp、求和并归一化;当 ∣V∣|V| 达到数万甚至数十万时,这一步是训练的主要瓶颈。负采样和层次 Softmax 正是为绕开全表归一化而提出的两类近似方法。

负采样(Negative Sampling)

负采样把“在 ∣V∣|V| 个词中选对上下文词”的多分类,拆成若干二分类:

  • 正样本:真实上下文词 wow_o。
  • 负样本:从噪声分布 Pn(w)∝freq(w)3/4P_n(w) \propto \text{freq}(w)^{3/4} 中抽取 KK 个(常用 K=5∼20K=5\sim20)。对频次开 3/43/4 次幂,是为了抑制超高频词被反复采到,同时保留稀有词一定的入选概率。
  • 用 sigmoid 函数 σ(x)=1/(1+e−x)\sigma(x) = 1/(1+e^{-x}) 判别:希望 σ(uwo⊤vc)\sigma(\mathbf{u}_{w_o}^\top \mathbf{v}_c) 接近 1,而对负样本希望 σ(uwk⊤vc)\sigma(\mathbf{u}_{w_k}^\top \mathbf{v}_c) 接近 0。
  • 负采样的训练目标(最大化对数似然)为: LNS=log⁡σ(uwo⊤vc)+∑k=1Klog⁡σ(−uwk⊤vc)\mathcal{L}_{\text{NS}} = \log \sigma(\mathbf{u}_{w_o}^\top \mathbf{v}_c) + \sum_{k=1}^{K} \log \sigma(-\mathbf{u}_{w_k}^\top \mathbf{v}_c) 训练时梯度推高正样本得分、压低负样本得分,且只更新涉及到的中心词向量与样本输出向量,不必遍历整个词表。

与层次 Softmax 的区别

层次 Softmax 用二叉树把输出归一化的复杂度降到 O(log⁡∣V∣)O(\log|V|),按树路径分配概率;负采样则不建树,只对采样到的少量负例做二分类。两者都避免全表 Softmax,但原理不同;在 Word2Vec 中它们是并列可选的高效训练方法。

import numpy as np

# 负采样演示(微型数据,概念示意)
v_c = np.array([0.5, 0.2])                          # 中心词向量
u_pos = np.array([0.4, -0.1])                       # 正样本(真实上下文词)输出向量
u_neg = [np.array([-0.3, 0.6]), np.array([0.1, -0.5])]  # K=2 个负样本输出向量

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

pos_score = float(np.dot(v_c, u_pos))
pos_prob = sigmoid(pos_score)
neg_scores = [float(np.dot(v_c, u)) for u in u_neg]
neg_probs = [sigmoid(s) for s in neg_scores]

# 负采样目标:最大化“正样本为1、负样本为0”的对数似然
objective = np.log(pos_prob) + sum(np.log(1 - p) for p in neg_probs)

print(f"正样本得分: {pos_score:.4f}, σ(正样本) = {pos_prob:.4f}")
for i, (s, p) in enumerate(zip(neg_scores, neg_probs), start=1):
    print(f"负样本{i}得分: {s:.4f}, σ(负样本{i}) = {p:.4f}")
print(f"负采样目标值(对数似然): {float(objective):.4f}")
正样本得分: 0.1800, σ(正样本) = 0.5449
负样本1得分: -0.0300, σ(负样本1) = 0.4925
负样本2得分: -0.0500, σ(负样本2) = 0.4875
负采样目标值(对数似然): -1.9539

与 CBOW 模型的对比

让我们通过一个表格来清晰对比 CBOW 和 Skip-gram 的核心差异:

特性CBOW (Continuous Bag-of-Words)Skip-gram
预测方向上下文 → 中心词中心词 → 上下文
输入数量多个(窗口内所有上下文词)一个(中心词)
输出数量一个(中心词)多个(窗口内所有上下文词)
隐藏层计算上下文词向量求平均直接使用中心词向量
训练速度通常更快通常稍慢(因输出多)
对低频词效果相对一般通常更好
直观类比完形填空词语联想

核心计算差异的再阐释:

  • 在 CBOW 中,我们需要先将多个上下文词的词向量 求平均,得到一个综合向量,然后用这个向量去预测一个目标(中心词)。
  • 在 Skip-gram 中,我们直接用中心词的词向量,去分别预测 多个目标(每个上下文词)。虽然公式中有一个求和,但那是损失函数的求和,在正向传播时,中心词向量是直接用于每个上下文词的得分计算的。

这种差异也影响了参数更新:在 Skip-gram 中,一个中心词会与其窗口内的每一个上下文词组成训练样本,因此低频词作为中心词时,也能获得多次参数更新的机会。

Word2Vec 的成效与语义关系

Word2Vec(包括 CBOW 和 Skip-gram)训练出的词向量之所以强大,是因为它完美地实现了 分布式假设(Distributional Hypothesis):“一个词的含义由其上下文决定”。模型通过迫使词向量能够预测上下文,从而将语义相似的词映射到向量空间中相近的位置。

更神奇的是,词向量空间还编码了复杂的 类比关系,可以通过向量运算来体现: vking−vman+vwoman≈vqueen\mathbf{v}_{king} - \mathbf{v}_{man} + \mathbf{v}_{woman} \approx \mathbf{v}_{queen} vparis−vfrance+vitaly≈vrome\mathbf{v}_{paris} - \mathbf{v}_{france} + \mathbf{v}_{italy} \approx \mathbf{v}_{rome}

# 概念演示:向量类比(此处为模拟数据,展示概念)
# 假设我们已经有了训练好的词向量
print("类比关系示例(概念性):")
print("向量('king') - 向量('man') + 向量('woman') ≈ 向量('queen')")
print("向量('Paris') - 向量('France') + 向量('Italy') ≈ 向量('Rome')")
print("\n这些关系并非预设,而是模型从'伦敦-英国'、'巴黎-法国'等大量共现模式中自动学习到的。")
类比关系示例(概念性):
向量('king') - 向量('man') + 向量('woman') ≈ 向量('queen')
向量('Paris') - 向量('France') + 向量('Italy') ≈ 向量('Rome')

这些关系并非预设,而是模型从'伦敦-英国'、'巴黎-法国'等大量共现模式中自动学习到的。

需要强调的是,词向量所捕获的关系是 多样且隐含的。它可能是“首都-国家”关系,也可能是“性别”关系(男人-女人)、“动词时态”关系(swim-swam)或“整体-部分”关系。模型并不知道这些关系的具体标签,它只是学习到:若向量差 A−B\mathbf{A}-\mathbf{B} 与 C−D\mathbf{C}-\mathbf{D} 方向相近(即 A−B≈C−D\mathbf{A}-\mathbf{B} \approx \mathbf{C}-\mathbf{D}),则 A−B+D≈C\mathbf{A}-\mathbf{B}+\mathbf{D} \approx \mathbf{C}。以经典例子核对方向:vking−vman≈vqueen−vwoman\mathbf{v}_{king}-\mathbf{v}_{man} \approx \mathbf{v}_{queen}-\mathbf{v}_{woman},因此 vking−vman+vwoman≈vqueen\mathbf{v}_{king}-\mathbf{v}_{man}+\mathbf{v}_{woman} \approx \mathbf{v}_{queen}。

词向量的本质:它是一种 基于任务的表示。我们通过设计“预测上下文”这个代理任务(proxy task),并定义相应的损失函数,驱动模型学习将词映射到一个高维空间中。在这个空间里,词向量的几何关系(如距离、方向)反映了词语在自然语言中使用的语义和语法关系。因此,词向量并非唯一的、固有的表示,而是由我们设定的训练目标和数据所共同塑造的。

📝 动手练一练

  1. 概念辨析:假设窗口大小 m=2。对于句子“我爱北京天安门”,分别写出 CBOW 和 Skip-gram 模型在中心词为“北京”时的一个训练样本(即输入和输出分别是什么)?
👉 点击查看参考答案
  • CBOW 样本:

    • 输入(上下文):[“我”, “爱”, “天安门”] (注意:窗口内去除中心词自身)
    • 输出(目标):[“北京”] (中心词)
  • Skip-gram 样本:

    • 输入(中心词):[“北京”]
    • 输出(上下文):[“我”, “爱”, “天安门”] (窗口内所有词)
  1. 手算理解:在一个极简词汇表 {A, B, C} 中,假设中心词 A 的词向量 v_A = [0.5, 0.5],输出矩阵 U 的列向量(对应每个词的上下文向量)为 u_A = [1, 0], u_B = [0, 1], u_C = [1, 1]。请计算给定中心词 A 时,词 B 作为其上下文词的预测概率 p(B | A)。
👉 点击查看参考答案
  1. 计算得分 z = U^T * v_A:
    • z_A = u_A · v_A = 1*0.5 + 0*0.5 = 0.5
    • z_B = u_B · v_A = 0*0.5 + 1*0.5 = 0.5
    • z_C = u_C · v_A = 1*0.5 + 1*0.5 = 1.0
  2. 计算 softmax 概率:
    • exp_z = [exp(0.5), exp(0.5), exp(1.0)] ≈ [1.6487, 1.6487, 2.7183]
    • sum_exp = 1.6487 + 1.6487 + 2.7183 ≈ 6.0157
    • p(B | A) = exp(0.5) / sum_exp ≈ 1.6487 / 6.0157 ≈ 0.2741

本章小结

本节我们深入剖析了 Word2Vec 的 Skip-gram 模型。它通过“中心词预测上下文”的反向思路,构建了一个高效的单层神经网络来学习词向量。其核心在于最大化给定中心词时其上下文词的联合概率,并通过最小化交叉熵损失来训练;面对大词表,再用负采样或层次 Softmax 绕开全表归一化。与 CBOW 相比,Skip-gram 在处理低频词和捕捉细粒度语义方面更具优势。最终,通过这种基于分布式假设的预测任务,模型学习到的词向量空间能够蕴含丰富的语义和语法类比关系。

📋 行动清单

  • 在脑海中清晰复述 Skip-gram 模型前向传播的四个步骤:独热输入 → 查找词向量 → 计算所有词得分 → Softmax 得概率。
  • 理解 Skip-gram 损失函数中求和符号的意义:它是对窗口内所有上下文词的交叉熵损失求和。
  • 对比 CBOW 和 Skip-gram 的异同,能够举例说明两者输入输出的区别。

—— 小象教研组