乐于分享
好东西不私藏

词的上下文关系:Word2Vec

词的上下文关系:Word2Vec

假设你从未见过”番茄”这个词。但你在网上读到:现在猜猜:”番茄”是什么?

你可能已经猜到了。它是一种食物,跟鸡蛋一起炒,酸甜口。

你是怎么猜到的?不是因为有人告诉你”番茄”的定义,而是因为你看到了这个词的”朋友圈”。它总是跟”炒”、”鸡蛋”、”新鲜”、”菜”这些词一起出现。

“You shall know a word by the company it keeps.”(观其友,知其人。)J.R. Firth, 1957

这就是词嵌入的核心理念。


回顾

上一篇的 BoW / TF-IDF 已经能把文本变成数字,但它把每个词当成彼此独立的维度。本篇要补上「词和词之间的相似性」:如果两个词经常出现在相似上下文里,它们的向量也应该更接近。

阅读定位

词嵌入的本质是把离散 token 映射到连续空间。这个空间不是人工写出来的词典,而是从上下文统计中学出来的:相似上下文中的词会被推到相近位置。它表达的是语料中的统计关系,不等同于人类完整语义。

这一篇先讲静态词向量。同一个词无论出现在什么句子里,向量都不变,这个限制会在第 9 篇讲 BERT 时重新打开。

一、从「独热」到「词嵌入」

还记得上一篇里我们怎么表示词的吗?词袋模型

但在词袋模型中,每个词是用”独热向量”表示的:

”猫” → [0, 0, 1, 0, 0, 0, ...]”狗” → [0, 1, 0, 0, 0, 0, ...]

这两个向量的点积是0。也就是说,在这种表示里,”猫”和”狗”不会天然比”猫”和”概率论”更接近。

但人类知道,”猫”和”狗”都是宠物、都是哺乳动物、都经常出现在”宠物”、”动物”、”可爱”附近。

能不能让机器也学会这种”词与词的相似性”?

这就是词嵌入(Word Embedding)要解决的问题。把每个词映射到一个低维稠密向量(比如 100 维或 300 维),在这个向量空间中,语义相近的词距离也近。

# 用预训练词向量看看效果(这里用 GloVe 演示,需要网络下载模型)import gensim.downloader as apimodel = api.load("glove-wiki-gigaword-50")print(model.similarity("cat""dog"))    # 0.92  ← 相似度很高print(model.similarity("cat""phone"))  # 0.23  ← 不相似print(model.similarity("cat""car"))    # 0.18

明明是三个字母 ‘c-a-t’ 和 ‘d-o-g’,机器却能”感知”到它们语义相近。这是怎么做到的?


二、核心直觉:猜词游戏

想象你在玩一个猜词游戏:

你看到目标词的周围有这些词:吃饭、菜单、服务员、账单、小费。你猜这个词是”餐厅”。

你是怎么猜到的?因为”吃饭”、”菜单”、”服务员”这些词经常跟”餐厅”一起出现。换句话说,一个词的”朋友”透露了它的身份

Word2Vec 的核心想法就是把这个直觉变成算法。

第一步:搭一个猜词网络

Word2Vec 搭了一个极简的神经网络来做这个猜词游戏。这个网络有个容易忽略的地方:输出层负责训练时的预测,最终需要的是中间那层权重。

具体来说:

  • 输入:当前词的独热向量(一个 10000 维的向量,只有当前词的位置是 1,其余是 0)
  • 隐藏层:一个很小的密集向量(比如 100 维),也就是词嵌入
  • 输出:预测周围词的概率分布(又回到 10000 维,每个位置对应一个词的概率)

训练时发生了什么?

训练的目标很简单:输入”猫”,让网络预测”喜欢”(如果”喜欢”是”猫”的相邻词)。网络一开始猜得乱七八糟,但每猜错一次就调整隐藏层的数值,让下次更准一些。

反复调整的过程中,隐藏层的 100 个数会逐渐编码词的上下文规律。因为「猫」和「狗」周围常出现相似的词,例如「喜欢」「宠物」「可爱」,它们的向量也可能逐渐靠近。

用更直白的话说:为了降低预测误差,网络会把相似上下文中的词映射到相近的向量位置。这里的「理解」更接近统计表示,不等同于人类意义上的理解。

训练完成后,扔掉输出层,只保留隐藏层的权重矩阵。这时候每个词都对应了一行 100 维的向量,这就是词嵌入。

两种猜法

这个猜词游戏有两种玩法,对应 Word2Vec 的两个变体:

  • CBOW 用周围词猜中间的词。例如看到「我 __ 猫」,让模型猜出「喜欢」。它训练较快,对高频词通常表现较好。
  • Skip-Gram 反过来,用中间词预测周围词。例如输入「喜欢」,预测附近可能出现「我」和「猫」。它通常更有利于学习低频词。

2013 年,Google 的 Mikolov 团队发表了 Word2Vec。它可以用上面这个极简神经网络来理解:输入是词,中间层是词向量,输出是预测的词。训练完成后,扔掉输出层,只保留中间层的权重,那就是词嵌入矩阵。


三、直觉验证:做个小实验

在一个小语料上训练 Word2Vec:

from gensim.models import Word2Vec# 我们的"宇宙":只有 5 个句子sentences = [    "我 喜欢 自然 语言 处理",    "深度 学习 在 NLP 中 有 广泛 应用",    "词 嵌入 是 NLP 的 重要 技术",    "Transformer 改变 了 NLP 的 格局",    "BERT 和 GPT 是 预训练 语言 模型",]corpus = [s.split() for s in sentences]model = Word2Vec(    sentences=corpus,    vector_size=50,    window=2,    min_count=1,    sg=1,)# 查看 NLP 的相近词# 注意:这个语料太小,结果只适合观察 API 机制,不适合判断真实语义质量。print(model.wv.most_similar("NLP", topn=3))

这么小的语料很难学出稳定语义。这个例子只说明 Word2Vec 如何训练和查询;可靠的词向量通常需要更大的语料、更长训练和更严格的评估。


四、词向量算术

随着语料增大,词向量空间中有时能看到有趣的规律:

“国王” – “男人” + “女人” ≈ “女王”注意:这只是一种演示词向量方向性的方式,不代表词向量能稳定做逻辑推理。实际效果依赖语料规模、训练配置和评估方式。

# 需要在大语料上训练的模型才能看到这个效果result = model.wv.most_similar(    positive=['king''woman'],    negative=['man'])print(result[0])# → 'queen'  ← 大语料上常见的类比现象

这说明词向量空间里有时会出现可观察的关系方向:

向量空间中的方向 代表的含义─────────────────────────────────────→ (男人 → 女人) 性别变化方向→ (国王 → 女王) 也是性别变化方向→ (走 → 走了) 时态变化方向→ (跳 → 跳了) 也是时态变化方向→ (巴黎 → 法国) 首都-国家关系→ (北京 → 中国) 也是首都-国家关系

一些同类关系会在向量空间中表现出相似方向,但这不是对所有概念关系都稳定成立的规则。词向量算术适合帮助理解分布式表示,不应被当成严格逻辑运算。


五、两种主流方法

Word2Vec 不是唯一的词嵌入方法。还有两个重要代表:

GloVe:全局视角

Word2Vec 只关注局部窗口,GloVe 则统计整个语料库中词与词共同出现的次数(共现矩阵),然后分解这个矩阵得到词向量。

glove = api.load(”glove-wiki-gigaword-100”)glove.most_similar(”apple”)# → ['apple computer''mac''ipad''ipod', ...]

FastText:给 Word2Vec 加上”偏旁部首”

FastText 在 Word2Vec 的基础上加了子词信息。每个词被看作一组字符 n-gram 的集合。

”自然” → [”<自然”, ”自然>”, ”自”, ”然”, ”自然”, ...]

这意味着:

  • 即使某个词从未在训练数据中出现,也能通过子词组合猜出它的向量
  • 对德语、俄语等形态变化丰富的语言尤其有用;处理中文时,效果取决于采用字符、字节还是其他子词设计
  • 对拼写错误更鲁棒
    from gensim.models import FastTextmodel = FastText(sentences=corpus, vector_size=50)unknown = model.wv["番茄"]  # 即使训练时没见过也能输出
    Word2Vec(2013)主要使用局部上下文窗口,标准模型无法直接表示未登录词。
  • GloVe(2014)使用全局词共现统计,标准模型同样无法直接处理未登录词。
  • FastText(2016)同时利用局部上下文和字符 n-gram,因此可以用子词组合生词的向量。

六、词嵌入的边界

局限 1:一词多义

“苹果很好吃” vs “苹果发布了新手机”

在 Word2Vec 里,两个”苹果”用同一个向量。它不会变。

“苹果”究竟是水果还是公司?Word2Vec 没法区分,因为它生成的是静态向量。不管上下文是什么,同一个词永远对应同一个向量。

等到后面讲 BERT 的时候你会看到,它用了一个巧妙的方法来解决这个问题:让每个词的向量根据上下文动态变化。”苹果”出现在”很好吃”附近时是一种向量,出现在”发布了新手机”附近时是另一种向量。不过这是后话了。

局限 2:偏见

”医生 - 男人 + 女人 ≈ 护士” ← 性别偏见

词嵌入会从训练数据中学到社会偏见。这是一个需要认真对待的问题。我们在第 9 篇讲 BERT 时会看到,预训练数据的选择对模型行为有直接影响。偏见不只是嵌入层的问题,而是整个数据驱动的 NLP 都需要处理的问题。

局限 3:领域适应

用 Wikipedia 训练的向量拿去分析医疗文本,效果会下降。专业术语的用法和通用语料中不一样。


七、词有了位置,句子还没有

词嵌入是 NLP 深度学习的重要基础。它让模型能用连续向量表示词语之间的统计关系。

Word2Vec 是静态词向量:同一个词在不同上下文中仍然只有一个向量。语料中的社会偏见和领域偏差也会进入向量空间,小语料上的 nearest neighbors 则常常不稳定。

但词嵌入仍然主要在词级别工作。如果我们要处理整个句子呢?

“我喜欢猫” 和 “猫喜欢我”,词都一样,意思完全不同。要理解序列,我们需要序列模型。下一篇,我们来讲 RNN,让模型有”记忆”。