词向量(Word Embedding)就是给每个词分配一串数字(比如 300 个连续小数),让这串数字成为该词在 AI 脑中的「坐标」。语义相近的词坐标也相近,于是「猫」和「狗」靠在一起,「猫」和「汽车」相隔很远。AI 看不懂字,但能对数字做加减和距离计算——词向量就是把「文字」翻译成「AI 能算的数」的那层关键翻译。
生活类比:每个词都有自己的 GPS 坐标
想象你在用输入法,刚打了「我想吃」,输入法立刻推荐「苹果、香蕉、米饭」。它怎么知道这几个词离「吃」近?因为每个词在它内部都有一组「GPS 坐标」,意义相近的词扎堆成簇。这张词义地图不是人手标的,而是模型自己从海量文本里「读」出来的——吃过几次「苹果」挨着「吃」,它就把这两个词的坐标悄悄拉近了一点。
为什么不用 one-hot?三个致命问题
最早让 AI 表示词的办法叫 one-hot(独热编码):词表里有几万个词,就给每个词一个几万维的向量,只有自己那一维是 1,其余全是 0。
- 维度爆炸:词表一大,向量就几万维,内存和算力都吃不消。
- 稀疏浪费:每个向量只有一个 1,其余几万个 0,存了满屏废话。
- 彼此正交、无法度量相似度:任意两个 one-hot 向量的点积都是 0,在数学上完全「垂直」。于是「猫」和「狗」的距离等于「猫」和「汽车」的距离,AI 看不出谁更近谁更远。
最后这一点最要命——没有相似度,模型就无法把「猫」学到的经验迁移到「狗」身上,每个词都得从头学。
稠密词向量:低维、连续、能算距离
词向量换了个思路:不用几万维,只用几十到几百维(常见 50 / 100 / 300);每一维都是连续小数(不是非 0 即 1),由模型从语料里学出来。更关键的是,这些数字不是随便填的,而是被训练成「语义相近的词,向量空间距离也近」。
| 对比项 | one-hot | 稠密词向量 |
|---|---|---|
| 维度 | 几万维(约等于词表大小) | 几十到几百维 |
| 取值 | 只有 0 和 1 | 连续实数 |
| 是否稀疏 | 极度稀疏(只有一个 1) | 稠密(每一维都有值) |
| 相似度计算 | 全部正交,相似度恒为 0 | 越相似越接近 1 |
图 1:左侧 one-hot 几万维里只有一个 1、两两垂直算不出远近;右侧稠密向量维度低、每维有值,箭头方向接近就能算出相似度。
距离怎么算:余弦相似度
判断两个词像不像,最常用的是余弦相似度——看两个向量的夹角:夹角越小越像,完全同向得 1,垂直得 0,反向得 -1。
符号逐项解读:
/ —— 是两个向量的夹角, 是夹角的余弦值即相似度得分,范围 。 —— 两个词的词向量(比如「猫」和「狗」各一个 300 维向量)。 —— 点积,对应位置相乘再相加,衡量两个向量「同向程度」。 —— 各自的长度(模),用来归一化,只比方向、不比长短。
通俗理解:把每个词当成一个箭头,两个箭头越指向同一方向就越相似;箭头长短不影响判断。
图 2:余弦相似度只看两个向量的夹角——同向得 1(极度相似)、垂直得 0(无关)、反向得 -1(意思相反),与箭头长短无关。
经典例子:king − man + woman ≈ queen
词向量最神奇的演示,是它能做「语义加减法」。把「国王」减去「男性」加上「女性」,得到的向量最接近的词,正是「女王」:
小算例(只用 2 维示意,真实是 300 维):
- 假设
、 、 。 - 代入:
。 - 对比
,方向几乎一致——AI 就这样「算出」了「国王的女性对应词是女王」。
这说明词向量不只是把词随便摆进空间,它还把「性别」「时态」「复数」这类语义关系编码成了空间里的方向。
图 3:把语义关系看成方向——「性别」是横轴方向,「王室」是纵轴方向;从 man 指向 king 的箭头(王室方向)平移到 woman 起点上,箭头正好落到 queen。
三大经典模型
把语料变成词向量,历史上主要有三套方法:
- Word2Vec(Mikolov, 2013):用「猜上下文」(skip-gram)或「猜中心词」(CBOW)的方式训练,开创了神经网络词向量的范式。
- GloVe(Pennington 等, 2014):基于「全局共现矩阵」——统计全语料里两两词一起出现的次数,再做矩阵分解,抓住了语料的整体统计规律。
- fastText(Facebook, 2016):在 Word2Vec 基础上引入「子词」(subword,如把「苹果」拆成更小的片段),连没见过的生词也能拼出向量。
它们训练方式不同,但产物一样:一张「词 → 向量」的对照表。
「ID + 查表」本质就是 Embedding
跳出 NLP,再看「嵌入」这个更通用的概念。不管什么模型,只要做这件事——给每个对象分配一个 ID,再维护一张「ID → 稠密向量」的表,需要时按 ID 查出来——就是在做 Embedding。
在 PyTorch 里,这张表就是 nn.Embedding:
import torch
import torch.nn as nn
# 一张表:词表大小 10000,每个词 300 维向量
embed = nn.Embedding(num_embeddings=10000, embedding_dim=300)
# 输入两个词的 ID(如「我」「爱」),查表得到它们的 300 维向量
vectors = embed(torch.tensor([5, 12]))这张表初始是随机的,但会在训练中不断更新——最终学到的向量就承载了语义。
图 4:词先转成 ID,再到嵌入矩阵里查对应那一行,取出来的稠密向量就是这个词的 Embedding——所有「ID + 查表」的嵌入机制都是这个套路。
完整代码
import torch
import torch.nn as nn
# === 1. 定义嵌入查表 ===
# 词表大小 5,每个词用 4 维向量表示(演示用,真实场景几百维)
vocab_size = 5
embed_dim = 4
embed_layer = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embed_dim)
# === 2. 准备假数据:一句话「我 爱 编 程」已经分词成 ID ===
# ID 映射示意:0=我, 1=爱, 2=编, 3=程
sentence_ids = torch.tensor([0, 1, 2, 3])
# === 3. 前向:查表,得到每个词的 4 维向量 ===
word_vectors = embed_layer(sentence_ids)
print(word_vectors.shape) # torch.Size([4, 4]):4 个词,每个 4 维
print(word_vectors) # 看到每个词被替换成了一串连续实数
# === 4. 训练一步:让「我」和「爱」的向量更靠近 ===
# 简单起见,假装目标是让「我」的向量靠近「爱」的向量
target = word_vectors[1].detach() # 把「爱」的向量当目标
loss = ((word_vectors[0] - target) ** 2).mean() # 「我」与「爱」的均方差
print("loss =", loss.item())
optimizer = torch.optim.SGD(embed_layer.parameters(), lr=0.1)
loss.backward() # 反向传播,算出每个词向量的梯度
optimizer.step() # 更新参数,向量在语义空间里挪了一小步跑完这段,embed_layer 这张表就被「挪动」了一点——每一次更新,都在让相关词的向量更靠近它们该去的位置。
小结
词向量就是「把文字翻译成 AI 能算的数字坐标」:用几十到几百维的连续实数,取代了 one-hot 的几万维稀疏、无相似度。语义相近的词在向量空间里扎堆,还能做加减法(king − man + woman ≈ queen);而「ID + 嵌入查表」这套机制,正是深度学习里所有 Embedding 的共同本质。
参考资料
- Efficient Estimation of Word Representations in Vector Space - Mikolov et al., 2013 https://arxiv.org/abs/1301.3781
- GloVe: Global Vectors for Word Representation - Pennington, Socher & Manning, 2014 https://nlp.stanford.edu/projects/glove/
- Word Embedding (word2vec) - 《动手学深度学习》 https://d2l.ai/chapter_natural-language-processing-pretraining/word2vec.html