查看全课大纲(第 18 / 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.跨媒体信息检索与未来展望

One-Hot 表达与词表示的局限

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

在自然语言处理中,如何将离散的符号(如单词)转化为计算机能够理解和计算的数学形式,是构建所有模型的第一步。本节我们将从最直观的 One-Hot 表示入手,探讨其原理、优势与核心局限,并引出构建更优词表示(词嵌入)的通用思想。理解本节内容,是后续学习 Word2Vec 等现代词向量方法的重要基础。

💡 核心导读

  • One-Hot 表示:将词汇表中的每个词映射为一个唯一的、长度为词汇表大小的二进制向量。
  • 核心局限:向量维度高且稀疏,无法表达词与词之间的任何语义关系(正交性)。
  • 嵌入矩阵:一种将高维 One-Hot 向量映射到低维稠密向量的参数矩阵,是词嵌入学习的核心。
  • 通用嵌入学习框架:通过定义评分函数和损失函数,联合学习嵌入矩阵和词间关系参数,使学到的向量能反映词的语义。

从离散符号到向量表示

自然语言文本本质上是离散符号(如单词、字符)的序列。要让机器学习模型处理文本,我们必须先将这些符号转化为数值向量。最朴素、最直接的方法就是 One-Hot 表示。

假设我们有一个包含 ∣V∣|V| 个词的词汇表 VV。对于词汇表中的第 ii 个词,其 One-Hot 向量是一个 ∣V∣|V| 维的向量,其中只有第 ii 个位置为 1,其余位置均为 0。

例如,词汇表 VV = {“猫”, “狗”, “兔子”},则它们的 One-Hot 表示分别为:

  • “猫”: [1,0,0][1, 0, 0]
  • “狗”: [0,1,0][0, 1, 0]
  • “兔子”: [0,0,1][0, 0, 1]
# 示例:One-Hot 编码
vocabulary = ["猫", "狗", "兔子"]
word_to_index = {word: i for i, word in enumerate(vocabulary)}

def one_hot_encode(word):
    vec = [0] * len(vocabulary)
    vec[word_to_index[word]] = 1
    return vec

print("词汇表:", vocabulary)
print("\"猫\"的 One-Hot 向量:", one_hot_encode("猫"))
print("\"狗\"的 One-Hot 向量:", one_hot_encode("狗"))
print("\"兔子\"的 One-Hot 向量:", one_hot_encode("兔子"))
词汇表: ['猫', '狗', '兔子']
"猫"的 One-Hot 向量: [1, 0, 0]
"狗"的 One-Hot 向量: [0, 1, 0]
"兔子"的 One-Hot 向量: [0, 0, 1]

One-Hot 表示简单明了,能唯一标识每个词。在传统的信息检索(IR) 和文档分类任务中,文档和查询常被表示为词向量的叠加(如词袋模型)。然而,这种表示方法存在几个根本性的问题:

  1. 维度灾难:向量维度等于词汇表大小 ∣V∣|V|,对于大规模语料(∣V∣|V| 可达数十万甚至百万),向量极其高维。
  2. 稀疏性:每个向量中仅有一个非零元素,计算和存储效率低下。
  3. 语义缺失(正交性):任意两个不同的 One-Hot 向量是正交的(内积为0)。这意味着从数学上看,“猫”和“狗”的相似度与“猫”和“飞机”的相似度没有任何区别,完全无法反映词之间的语义关联。

迈向语义表示:分布假说与计数方法

为了获得能表达语义相似度的词表示,我们引入分布假说(Distributional Hypothesis):一个词的含义可以由其上下文(即经常与其共同出现的词)来定义。正如语言学家 J.R. Firth (1957) 所言:“You shall know a word by the company it keeps.”

基于此假说,一种直观的方法是计数法(Count-Based Methods):

  1. 定义一个上下文词基础词汇表 CC。
  2. 设定一个窗口大小 ww(例如,左右各2个词)。
  3. 在语料库中,统计每个目标词上下文中出现的基础词汇表 CC 中词的次数。
  4. 用这些计数构成目标词的向量表示。

示例: 假设上下文基础词汇表 CC = {“咬”, “可爱”, “毛茸茸”, “大声”, “喵”, “呼噜”, “跑”, “小”}。 给定句子片段:

  • “… and the cute kitten purred and then …”
  • “… the cute furry cat purred and miaowed …”
  • “… that the small kitten miaowed and she …”
  • “… the loud furry dog ran and bit …”

我们可以统计出:

  • “kitten”(小猫)的上下文词:{“可爱”, “呼噜”, “小”, “喵”}
  • “cat”(猫)的上下文词:{“可爱”, “毛茸茸”, “喵”, “呼噜”}
  • “dog”(狗)的上下文词:{“大声”, “毛茸茸”, “跑”, “咬”}

将其转化为计数向量(顺序按 CC):

  • “小猫”: [0,1,0,0,1,1,0,1][0, 1, 0, 0, 1, 1, 0, 1]
  • “猫”: [0,1,1,0,1,1,0,0][0, 1, 1, 0, 1, 1, 0, 0]
  • “狗”: [1,0,1,1,0,0,1,0][1, 0, 1, 1, 0, 0, 1, 0]
# 示例:基于窗口的上下文词计数(一种简单的计数方法)
context_vocab = ["咬", "可爱", "毛茸茸", "大声", "喵", "呼噜", "跑", "小"]
context_to_idx = {c: i for i, c in enumerate(context_vocab)}

# 模拟的上下文计数向量 (|C|维)
# 行:目标词;列:上下文词;值:共现次数
cooccurrence_counts = {
    "小猫": [0, 1, 0, 0, 1, 1, 0, 1],  # 上下文: 可爱, 喵, 呼噜, 小
    "猫":   [0, 1, 1, 0, 1, 1, 0, 0],  # 上下文: 可爱, 毛茸茸, 喵, 呼噜
    "狗":   [1, 0, 1, 1, 0, 0, 1, 0],  # 上下文: 咬, 毛茸茸, 大声, 跑
}

print("上下文基础词汇表:", context_vocab)
print("\n目标词的上下文计数向量:")
for word, vec in cooccurrence_counts.items():
    print(f"{word}: {vec}")
上下文基础词汇表: ['咬', '可爱', '毛茸茸', '大声', '喵', '呼噜', '跑', '小']

目标词的上下文计数向量:
小猫: [0, 1, 0, 0, 1, 1, 0, 1]
猫: [0, 1, 1, 0, 1, 1, 0, 0]
狗: [1, 0, 1, 1, 0, 0, 1, 0]

现在,我们可以计算词之间的相似度,例如使用余弦相似度: sim(v,w)=v⋅w∣∣v∣∣⋅∣∣w∣∣\text{sim}(\mathbf{v}, \mathbf{w}) = \frac{\mathbf{v} \cdot \mathbf{w}}{||\mathbf{v}|| \cdot ||\mathbf{w}||} 计算可得,“小猫”和“猫”的相似度会高于“猫”和“狗”的相似度,这更符合我们的语义直觉。

然而,简单的计数向量仍有问题:高频但无信息量的上下文词(如“的”、“是”)会主导向量表示。因此需要引入TF-IDF、点互信息(PMI) 等方法进行加权归一化,以突出有区分度的上下文。

嵌入矩阵:连接 One-Hot 与稠密表示

学习到的计数向量(维度为 ∣C∣|C|)可以按行排列,形成一个矩阵 E∈R∣V∣×∣C∣E \in \mathbb{R}^{|V| \times |C|},其中每一行对应一个目标词的向量。这个矩阵 EE 被称为嵌入矩阵(Embedding Matrix)。

这里有一个关键的联系:我们可以通过 One-Hot 向量 与 嵌入矩阵 的乘法,来查找某个词的稠密向量表示。

设词汇表 VV 的大小为 ∣V∣|V|,我们希望得到维度为 dd 的词向量(dd 通常远小于 ∣V∣|V| 和 ∣C∣|C|)。我们定义一个可学习的嵌入矩阵 E∈Rd×∣V∣E \in \mathbb{R}^{d \times |V|}。注意,这里 EE 的每一列对应一个词的向量(与计数矩阵按行排列的约定不同,这只是一种设计选择,转置即可等价)。

对于一个词 ww,其 One-Hot 向量为 ow∈R∣V∣\mathbf{o}_w \in \mathbb{R}^{|V|}(一个 ∣V∣|V| 维向量,只有对应词索引的位置为1)。那么,该词的稠密向量表示 vw\mathbf{v}_w 可以通过下式获得: vw=E⋅ow\mathbf{v}_w = E \cdot \mathbf{o}_w

由于 ow\mathbf{o}_w 仅在索引 ii 处为1,这个矩阵乘法的结果就是 EE 的第 ii 列。这相当于用 One-Hot 向量作为“索引”,从嵌入矩阵中“取出”对应的词向量。

import numpy as np

# 模拟一个小的嵌入矩阵 E (d=3, |V|=3)
# 列0对应"猫",列1对应"狗",列2对应"兔子"
np.random.seed(42) # 固定随机种子保证输出确定性
d = 3
vocabulary = ["猫", "狗", "兔子"]
E = np.random.randn(d, len(vocabulary)).round(4)  # 随机初始化,保留4位小数
print("嵌入矩阵 E (每一列是一个词的向量):")
print(E)
print("\n通过矩阵乘法获取词向量:")
# "狗"的 One-Hot 向量是 [0,1,0]
one_hot_dog = np.array([0, 1, 0])
v_dog = E.dot(one_hot_dog)
print(f"\"狗\"的 One-Hot 向量: {one_hot_dog}")
print(f"\"狗\"的稠密向量 v_dog = E · one_hot_dog: {v_dog}")
print(f"直接取 E 的第1列: {E[:, 1]}")
print("两者相等吗?", np.allclose(v_dog, E[:, 1]))
嵌入矩阵 E (每一列是一个词的向量):
[[ 0.4967 -0.1383  0.6477]
 [ 1.523  -0.2342 -0.2341]
 [ 1.5792  0.7674 -0.4695]]

通过矩阵乘法获取词向量:
"狗"的 One-Hot 向量: [0 1 0]
"狗"的稠密向量 v_dog = E · one_hot_dog: [-0.1383 -0.2342  0.7674]
直接取 E 的第1列: [-0.1383 -0.2342  0.7674]
两者相等吗? True

因此,词嵌入(Word Embedding) 的核心任务,就是学习到一个好的嵌入矩阵 EE,使得其列向量(即词向量)能够捕捉丰富的语义和语法信息。

神经嵌入模型的通用框架

如何学习这个嵌入矩阵 EE 呢?Collobert 等人(2011)的工作提出了一个通用的神经嵌入模型框架,其思想影响深远,直接启发了后来的 Word2Vec。该框架不依赖于具体的神经网络结构,其通用流程如下:

  1. 收集实例:从语料中收集目标词 tt 的每个出现实例 tit_i。
  2. 收集上下文:对于每个实例 tit_i,收集其上下文词 c(ti)c(t_i)(例如,使用固定大小的窗口)。
  3. 定义评分函数:定义一个可学习的评分函数 score(ti,c(ti);θ,E)\text{score}(t_i, c(t_i); \theta, E)。该函数衡量在给定参数下,目标词 tit_i 与其上下文 c(ti)c(t_i) 共现的“合理性”或“分数”。参数包括:
    • EE:嵌入矩阵,决定每个词用什么向量表示。
    • θ\theta:描述词与词之间关系(或上下文预测关系)的其他模型参数。
  4. 定义损失函数:基于评分函数,定义一个损失函数 L(θ,E)\mathcal{L}(\theta, E)。例如,我们可以希望真实观察到的 (ti,c(ti))(t_i, c(t_i)) 对的得分尽可能高,而随机构造的“负样本”对的得分尽可能低。
  5. 估计参数:通过优化算法(如随机梯度下降)最小化损失函数,同时更新嵌入矩阵 EE 和关系参数 θ\theta。
  6. 产出嵌入:训练完成后,矩阵 EE 就是我们所需的词嵌入矩阵。

以 Collobert & Weston 2011 年的模型为例:

  • 他们将句子中每个词通过嵌入矩阵 EE 转换为向量。
  • 在词向量序列上应用一个浅层卷积,以捕捉局部上下文特征。
  • 卷积的输出再通过一个多层感知机(MLP) 映射为一个标量分数。
  • 整个网络(卷积核、MLP权重)构成了参数 θ\theta,与 EE 一同训练。

这个框架的要点在于:EE 负责词的表示,θ\theta 负责词间关系的建模,两者通过一个可微分的评分函数和损失函数联合优化,最终目标是使学到的词向量(即 EE)能够很好地服务于我们定义的上下文预测任务。

这为下一节即将深入讲解的 Word2Vec(包括 CBOW 和 Skip-gram 模型)奠定了理论基础。Word2Vec 可以看作是这个通用框架下,评分函数和网络结构极其简化的一个特例,它去掉了复杂的卷积和深层MLP,仅用简单的线性层和 softmax(或负采样)就取得了惊人的效果。

📝 动手练一练

  1. 概念辨析:One-Hot 表示和基于计数方法的分布式向量表示,各自最主要的优点和缺点是什么?为什么说后者是迈向“语义”表示的关键一步?
👉 点击查看参考答案

One-Hot 表示:

  • 优点:简单、唯一,能明确区分词汇表中的每一个词。
  • 缺点:
    1. 维度高且稀疏:维度等于词汇表大小,存储和计算效率低。
    2. 语义缺失(正交性):任意两个不同词的向量内积为0,无法表达任何语义相似度。从模型角度看,“猫”和“狗”的关系与“猫”和“飞机”没有区别。

基于计数的分布式向量表示:

  • 优点:
    1. 维度可控:向量维度等于上下文特征数(∣C∣|C|),可远小于词汇表大小。
    2. 蕴含语义:基于分布假说,通过共享的上下文特征,语义相近的词(如“猫”和“狗”)会拥有相似的向量,从而可以通过余弦相似度等度量计算语义关联。
  • 缺点:
    1. 仍然可能高维:当上下文词汇表 CC 很大时,维度依然不低。
    2. 共现矩阵稀疏:对于大规模语料,词-上下文共现矩阵非常稀疏。
    3. 权重问题:需要 TF-IDF、PMI 等方法处理高频但无信息的上下文词。
    4. 静态表示:一旦从语料中统计完毕,向量就固定了,无法随着新任务动态调整。

关键一步:基于计数的方法首次将词的表示与其上下文分布联系起来,这直接体现了“一个词的含义由其上下文决定”的分布假说,从而让向量表示包含了语义信息,是词表示从“标识符”走向“含义载体”的根本转变。

  1. 手算理解:假设嵌入矩阵 E∈R2×4E \in \mathbb{R}^{2 \times 4} 如下,词汇表 VV = [“A”, “B”, “C”, “D”]。 E=[0.10.20.30.40.50.60.70.8]E = \begin{bmatrix} 0.1 & 0.2 & 0.3 & 0.4 \\ 0.5 & 0.6 & 0.7 & 0.8 \end{bmatrix} 请计算词“C”(其 One-Hot 向量为 [0,0,1,0][0,0,1,0])通过 v=E⋅ov = E \cdot o 得到的稠密向量 vv。并验证它是否等于 EE 的第3列(索引从0开始)。
👉 点击查看参考答案

词“C”的 One-Hot 向量为 oC=[0,0,1,0]To_C = [0, 0, 1, 0]^T。

计算 vC=E⋅oCv_C = E \cdot o_C: vC=[0.10.20.30.40.50.60.70.8]⋅[0010]=[0.30.7]v_C = \begin{bmatrix} 0.1 & 0.2 & 0.3 & 0.4 \\ 0.5 & 0.6 & 0.7 & 0.8 \end{bmatrix} \cdot \begin{bmatrix} 0 \\ 0 \\ 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 0.3 \\ 0.7 \end{bmatrix}

EE 的第3列(索引为2,对应第三个词“C”)是 [0.30.7]\begin{bmatrix} 0.3 \\ 0.7 \end{bmatrix}。

两者相等,均为 [0.3,0.7]T[0.3, 0.7]^T。这验证了“通过 One-Hot 向量与嵌入矩阵相乘来获取词向量”的操作,本质上是从矩阵中选取对应列。

本章小结

本节我们深入探讨了词表示的起点与核心挑战。One-Hot 编码作为最基本的表示方法,因其高维稀疏和语义缺失的固有局限,无法满足现代 NLP 的需求。为了获得蕴含语义的稠密向量,我们引入了基于分布假说的计数方法,并从中抽象出嵌入矩阵的概念——一个将高维 One-Hot 向量映射到低维语义空间的可学习参数矩阵。最后,我们介绍了神经嵌入模型的通用框架,该框架通过联合优化嵌入矩阵(定义词表示)和关系参数(定义上下文预测),为学习高质量词向量提供了蓝图。这为我们接下来学习里程碑式的 Word2Vec 模型做好了充分的理论准备。

📋 行动清单

  • 理解 One-Hot 表示的原理,并能手动为小词汇表构造 One-Hot 向量。
  • 明确说出 One-Hot 表示的至少两个核心局限(高维稀疏、语义正交)。
  • 掌握“嵌入矩阵”的概念,理解其如何通过矩阵乘法连接 One-Hot 向量与稠密词向量。
  • 了解神经嵌入模型通用框架(收集实例、评分、优化)的核心思想,知道参数 EE 和 θ\theta 分别负责什么。

—— 小象教研组