AI 技术演进与核心算法实战 | 第三篇:向量的语义世界:Embedding 原理、Word2Vec 到 Transformer 的向量空间演化



万物皆可 Embedding。如果说 Token 是 AI 世界的原子,那么 Embedding 就是连接这些原子的化学键。
在上一篇中,我们通过 BPE 算法将文本切分成了模型可以接受的基本单位——Token,并给每个 Token 分配了一个唯一的数字 ID。
但这里有一个致命的问题:数字本身是不携带语义的。
假设在词汇表中,"苹果" 的 ID 是 100,"香蕉" 的 ID 是 101,"汽车" 的 ID 是 999。如果模型仅仅看着这些离散的数字,它会认为 "苹果" 和 "香蕉" 之间的关系,就像 100 和 101 的关系;而 "苹果" 和 "汽车" 的关系就像 100 和 999 的关系。
这显然荒谬到了极点!在人类的常识中,苹果和香蕉都是水果,它们在语义上应该非常“接近”;而汽车和苹果八竿子打不着,它们应该离得很远。
如何让模型不仅认识这些词,还能理解词与词之间的关系与距离?
这就是本篇要探讨的核心:Embedding(词向量)技术。它是连接符号世界与连续高维空间的桥梁,也是 Transformer 乃至整个大模型能够理解语言含义的基础。
1. 独热编码 (One-Hot):高维空间的“孤岛”
在 Embedding 出现之前,早期 NLP 最朴素的做法是独热编码(One-Hot Encoding)。
假设我们的词汇表里只有 4 个词:["苹果", "香蕉", "汽车", "猫"]。
独热编码会为每个词分配一个长度为 4 的向量,除了它自己对应的位置是 1,其余全为 0:
苹果=[1, 0, 0, 0]香蕉=[0, 1, 0, 0]汽车=[0, 0, 1, 0]猫=[0, 0, 0, 1]
致命缺陷:
- 维度灾难:如果大模型词汇表有 10 万个 Token,那么每个词都是一个 10 万维的向量,且其中 99999 个位置都是
0,这种极其稀疏的矩阵会让计算资源直接崩溃。 - 毫无语义关联:在数学上,任意两个不同独热向量的内积(点乘)永远为
0。这意味着,在向量空间中,任意两个词之间的夹角都是 90 度(正交),它们互不相干。 苹果和香蕉的距离,与苹果和汽车的距离完全一样。这被称为“语义鸿沟”。
2. Word2Vec:开启词向量革命
2013 年,Google 团队的 Tomas Mikolov 提出了一项彻底改变 NLP 历史的算法:Word2Vec。
它的核心理念非常具有哲学意味,源自语言学家 Firth (1957) 的名言:
“You shall know a word by the company it keeps.” (通过一个词周围的邻居,就能知道它的含义。)
Word2Vec 放弃了人工标注特征,转而让神经网络去阅读海量文本,通过“猜词”游戏,强行逼迫模型学习到词汇的隐含特征(Dense Vector,稠密向量)。
两种经典的“猜词”玩法
Word2Vec 提出了两种架构来学习 Embedding:
- CBOW (Continuous Bag of Words):根据上下文,猜中间的词。 (例如:“我 昨天 吃了 一个 [苹果] 味道 很 甜”,通过周围的词猜中间的 “苹果”)
- Skip-gram:根据中间的词,猜周围的上下文。 (例如:已知中间词是 “苹果”,让模型去预测它周围可能会出现 “吃”、“甜”、“水果” 等词)
经过这种海量文本的自我博弈(自监督学习),原本毫无关联的词,被映射成了一个个几百维的浮点数向量(Dense Vector)。
奇迹发生了:在这些多维向量的坐标系中,语义相近的词,它们在空间里的距离也越近。 更有趣的是,它们不仅有位置,还具有了“线性加减”的代数性质!
最著名的例子就是:
这说明模型不仅记住了词,还提取出了“性别”、“阶级”这些人类难以用规则去写清楚的抽象语义维度!
3. 词汇的坐标系:从代码看 Embedding 的本质
为了让你不再觉得这是一个黑盒,我们用 NumPy 手写一个极简版的 Word2Vec (基于 Skip-gram)。
如果你想直接获取完整可运行的源码,可以在本文配套文章目录的
src/word2vec_demo.py中找到。
import numpy as np
class SimpleWord2Vec:
"""
一个极简的基于 NumPy 的 Skip-gram 模型实现。
主要用于演示 Word2Vec 中词向量是如何通过梯度下降学习到的。
"""
def __init__(self, vocab_size: int, embedding_dim: int, learning_rate: float = 0.01):
self.vocab_size = vocab_size
self.embedding_dim = embedding_dim
self.learning_rate = learning_rate
# W1 是输入词矩阵 (V x D),也就是我们最终要提取的 Embedding 矩阵!
self.W1 = np.random.randn(vocab_size, embedding_dim) * 0.01
# W2 是输出词矩阵 (D x V),用于预测上下文
self.W2 = np.random.randn(embedding_dim, vocab_size) * 0.01
def forward(self, target_word_index: int):
# 1. 查表(相当于用独热编码乘以 W1 矩阵,直接提取出该词对应的向量)
h = self.W1[target_word_index]
# 2. 计算输出层的 logits
u = np.dot(h, self.W2)
# 3. Softmax 转化为概率分布
y_pred = np.exp(u) / np.sum(np.exp(u))
return h, u, y_pred
def backward(self, target_word_index: int, context_word_index: int, h, y_pred):
# 计算预测误差 (目标上下文词的理想概率是 1)
e = y_pred.copy()
e[context_word_index] -= 1.0
# 反向传播计算梯度
dW2 = np.outer(h, e)
dh = np.dot(self.W2, e)
# 梯度下降,更新权重(这就是模型“学习”语义的过程)
self.W2 -= self.learning_rate * dW2
self.W1[target_word_index] -= self.learning_rate * dh
运行结果解析
如果你运行了 src/word2vec_demo.py 中完整的训练和测试代码,你会看到类似如下的输出:
开始训练极简 Word2Vec 模型...
Epoch 0, Loss: 61.5246
Epoch 200, Loss: 33.5273
Epoch 400, Loss: 33.5079
Epoch 600, Loss: 33.5146
Epoch 800, Loss: 33.5202
训练完成,测试余弦相似度:
king 与 queen 的相似度: 1.0000
king 与 man 的相似度: 0.2902
queen 与 man 的相似度: 0.2879
(king - man + woman) 与 queen 的相似度: 0.9814
- 损失函数 (Loss) 下降:随着 Epoch 的增加,Loss 逐渐降低,说明模型正在不断调整 W1 和 W2 矩阵,使得预测上下文越来越准确。
- 语义相似度:在训练好的坐标系中,
king和queen靠得非常近;而它们与man的距离相对较远。 - 向量代数运算:最令人惊叹的是,当我们把
king的向量减去man的向量,再加上woman的向量后,得到的新向量与queen的相似度高达 0.9814!这完美印证了 Word2Vec 捕捉到了词汇间隐含的抽象关联。
你看,所谓的 Embedding 矩阵(在 PyTorch 中就是 nn.Embedding),本质上就是一个庞大的查找表(Lookup Table)。它的行数是词汇表的大小,列数是向量的维度(如 GPT-3 中的 12288 维)。模型输入一个 Token ID,就直接去表里把对应那一行的数组“抽”出来,这个数组就是该 Token 的“灵魂坐标”。
你可以尝试修改我给的语料库,看看模型的学习效果如何。
4. 从 Word2Vec 到 Transformer:语境的演化
Word2Vec 已经很强了,但它有一个天生的硬伤:静态词向量(Static Embedding)。
在 Word2Vec 的查找表里,一个词永远只有一个固定的向量。但人类语言充满了多义词。
比如单词 "bank":
- 在
"I went to the bank to deposit money."中,它是“银行”。 - 在
"I sat on the river bank."中,它是“河岸”。
如果 "bank" 的向量是固定的,那它怎么可能同时表示“银行”和“河岸”呢?模型最后只能取一个这两种含义的平均值,导致在任何场景下都不够准确。
这就是 Transformer(以及 ELMo、BERT)必须解决的问题:动态/上下文相关的词向量(Contextualized Embedding)。
Self-Attention 如何改变向量空间
在现代的大模型中,当一段文本被转换为初始的 Embedding 之后,这仅仅是个开始。这些向量随后会被送入 Transformer 的注意力机制(Self-Attention) 网络层中。
在这个过程中,每一个 Token 都会环顾四周(Context),并动态地吸收周围 Token 的语义特征,从而修改自己的向量。
在经过多层 Transformer 的洗礼后,"bank" 这个 Token 的向量,在遇到 "money" 时,会向“金融”的维度偏移;而在遇到 "river" 时,会向“水体”的维度偏移。
最终大模型输出的,是一个融入了整个句子上下文语境的超级动态向量。这正是 ChatGPT 等模型能够拥有极强语义理解能力的根基。
结语
本篇我们从零探索了词向量(Embedding)的原理,看到了数字是如何被赋予语义的。
从独热编码到 Word2Vec 的静态向量,再到 Transformer 的动态上下文向量,每一次演进,都是人类教导机器“像人一样理解世界”的巨大跨越。
但我们在本篇留了一个巨大的悬念:Transformer 到底是如何通过 Self-Attention 机制,让词与词之间互相“吸收”特征的?
这就是我们下一篇要手撕的核心内容:《注意力机制解密:Self-Attention 的矩阵运算图解与 Q,K,V 的物理意义》。 我们将彻底拆解大模型的心脏,用最直观的图解和矩阵运算,带你真正看懂 Q,K,V 到底在算什么。敬请期待!
📚 参考文献与延伸阅读
- Efficient Estimation of Word Representations in Vector Space (Mikolov et al., 2013) - Word2Vec 的开山之作,提出了 CBOW 和 Skip-gram 模型。
- Distributed Representations of Words and Phrases and their Compositionality (Mikolov et al., 2013) - 进一步优化了 Word2Vec,提出了负采样(Negative Sampling)等技术。
- Attention Is All You Need (Vaswani et al., 2017) - Transformer 架构的奠基论文,彻底改变了 NLP 领域的向量表征方式。
- The Illustrated Word2vec (Jay Alammar) - 极度推荐的可视化博客,用生动的图解详细解释了 Word2Vec 的工作原理。