词嵌入

给每个词配一个可学习的稠密向量,让意思相近的词在空间里自然靠拢

知识课 · 约 30 分钟 · 更新于 2026-08-14

用稠密向量代替编号

one-hot 又长又稀疏、还表达不了语义。换个思路: 给每个词配一个 不太长、每一维都用上的实数向量 , 用这个向量来代表这个词送进神经网络。这就是 词嵌入(Word Embedding) 。

具体来说,把每个 token 的 ID 映射到一个 固定长度的稠密向量 , 长度记作 嵌入维度( ) , 常取 128、300 这样的量级,比起几万维的 one-hot 短了两三个数量级,而且每一维都携带信息。

如果这套向量学得好,「猫」和「狗」的向量会很接近(毕竟它们常出现在相似的语境里), 而「猫」和「桌子」的向量则相距较远。余弦相似度终于能区分出词义的远近了。

嵌入矩阵:一张可查的表

这些向量存在哪?存在一张大矩阵里。维护一个 的矩阵,叫 嵌入矩阵 :它有 行, 每一行是一个词的词向量。 token 的 ID 就是行号 , 要取某个词的向量,直接去矩阵里取出对应那一行即可。所以嵌入层做的事, 本质就是 查表 。

为什么说「查表 = one-hot 乘矩阵」

查表这件事,还能用熟悉的 one-hot 串起来。 把一个词的 one-hot 向量(只有第 位是 1) 左乘嵌入矩阵 :

结果 恰好就是矩阵的第 行 , 因为其余位都是 0,只有第 位的 1 把那一行「挑」了出来。 所以「查第 行」和「one-hot 乘矩阵」是同一件事, 嵌入层不过是它的 高效实现 :直接按行号取,省去了又长又稀疏的乘法。

关键:这些向量是「学」出来的

嵌入矩阵里的每一个数值,都是模型参数 。 它和网络里的权重一样, 一开始是随机初始化的 ,毫无意义; 然后在训练过程中,随着反向传播 一起被更新 。

词向量不是人工设定的,而是模型在完成某个任务的过程中 自己学出来的 。学得好不好,取决于用什么任务、什么信号去训练它。 这也解释了为什么随机初始化的嵌入一开始毫无规律,它还没被训练过。

动手:使用 nn.Embedding

PyTorch 把这张可学习的查找表封装成了 nn.Embedding 。 下面创建一个小小的嵌入层,先确认这张表确实是可训练的参数, 再把一串 token ID 查成词向量:

看一眼训练好的词向量

训练好的词向量长什么样?最直观的办法是把它画出来,看看「意思相近的词是不是真的聚在一起」。

下图把一批常见词的词向量降维后画在了二维平面上,每个点是一个词。 可以先整体看一眼点的分布,再把鼠标悬浮到任意一个词上,看看离它最近的词是谁。

分布很清楚:「猫 / 狗 / 兔 / 马 / 鱼」这些动物挤成一簇,「跑 / 跳 / 走 / 飞」这些动作聚在另一处, 家具和食物也各自成团。 语义相近的词,向量确实落在了相近的位置 , 这正是「好的词向量」该有的样子。

向量从哪来

现在框架搭好了:每个词对应嵌入矩阵的一行,这些行是可学习的参数。 但一个随机初始化的嵌入矩阵毫无语义, 得有一个明确的训练信号 , 才能把它推成「语义相近就靠近」的样子。

有没有一种专门用来「生产好词向量」的方法?有,其中最经典的就是 word2vec, 它巧妙地利用了「上下文」这个免费的监督信号,这是接下来要展开的内容。