从 One-hot 到 Embedding:词的分布式表示是如何从语料中学习得到的

以下内容由AI辅助生成

在自然语言处理任务中,模型面对的基本对象是离散的符号(词或 token),而大多数机器学习方法只能处理连续数值。因此,一个核心问题是:如何将离散的词表示为数值向量,并使这些向量能够反映词的语义与用法差异

词向量(word embedding)正是对这一问题的系统性回答。本文将从最基础的表示方法出发,逐步引入分布式表示与 embedding 的训练机制,并以 Skip-gram + Negative Sampling 为例,说明词向量是如何通过损失函数和梯度下降从大规模语料中学习得到的。


1. One-hot 表示:离散符号的直接编码

最直接的词表示方法是 one-hot 编码。

设词表大小为 ,则每个词对应一个 维向量,仅在该词对应的位置为 1,其余位置为 0。例如,词表为:

则“北京”的 one-hot 表示为:

one-hot 的优点在于形式简单、语义唯一、不需要训练;但其缺点同样明显:

  • 向量维度随词表线性增长,难以扩展
  • 表示高度稀疏,计算与存储效率低
  • 不包含任何语义信息,不同词之间的相似度恒为 0

因此,one-hot 只能作为索引机制,而无法承担“语义表示”的角色。


2. 分布式表示的核心思想

2.1 分布式表示的基本假设

分布式表示(distributed representation)基于一个经典假设:

一个词的意义可以通过它的上下文分布来刻画。

在这一思想下,每个词不再由一个孤立的维度表示,而是由一个低维、稠密、连续的向量表示;语义相近的词,其向量在空间中也应当相近。

2.2 构建分布式表示的两类方法

构建词的分布式表示,主要有两大类方法:

  1. 基于计数的方法(count-based methods)

    • 典型代表:LSA、基于共现矩阵的 SVD、PMI / PPMI
    • 思路:先统计词与上下文的共现频次,再通过矩阵分解获得低维表示
    • 特点:依赖全局统计,数学结构清晰,但在大规模语料上扩展性较差
  2. 基于神经网络的推理方法(prediction-based methods)

    • 典型代表:Word2Vec(Skip-gram / CBOW)、GloVe(部分融合统计思想)
    • 思路:将“学习词向量”转化为一个预测任务,通过最小化损失函数学习向量参数
    • 特点:可在线训练、扩展性好、在实践中效果优异

本文后续讨论的 embedding 学习机制,属于第二类:基于神经网络的推理方法。


3. Embedding 的形式化定义

在神经网络框架中,embedding 可以形式化为一个可学习的查表映射:

  • 词表大小:
  • 向量维度:
  • embedding 矩阵:

对于任意一个词(或 token),embedding 层的作用可以理解为:
根据词的 id,从矩阵中取出对应的一行,作为该词的向量表示。

embedding 的参数并非人工设定,而是通过下游训练目标、借助梯度下降从数据中学习得到。


4. Skip-gram + Negative Sampling:从语料到训练目标

4.1 训练样本的构造

给定一个 token 序列:

设窗口大小为 ,对每个位置 ,构造训练对:

  • 中心词:
  • 上下文词:,其中

这样即可从原始语料中构造大量 正样本对。


4.2 输入向量表与输出向量表

在 Skip-gram 模型中,会维护两张独立的 embedding 表:

  • 输入向量表:用于中心词
  • 输出向量表:用于上下文 / 候选词

对词表中任意词 id 为 i:

  • 在输入向量表中,取到它作为“中心词”时使用的向量
  • 在输出向量表中,取到它作为“候选上下文词”时使用的向量

这两种向量使用相同的词表索引,但参数彼此独立,承担的统计角色也不同。


4.3 打分函数与概率建模

在 Skip-gram 中,模型会为“中心词 + 候选词”这一对分配一个匹配分数

这个分数可以理解为:
中心词向量与候选词向量在向量空间中的相似程度

随后,模型会把这个分数映射成一个介于 0 和 1 之间的值,用来表示“该候选词是否真的是中心词上下文”的概率。


5. 损失函数与向量学习机制

5.1 Negative Sampling 的训练目标

对于一个真实的中心词–上下文词对 ,Negative Sampling 的训练目标可以分解为两点:

  1. 正样本目标
    让中心词 与真实上下文词 的匹配分数变大。

  2. 负样本目标
    随机采样若干个与 无关的词作为负样本,
    并让中心词 与这些负样本的匹配分数变小。

训练时,模型会把“真实上下文”当作正类,“随机词”当作负类,
并对每一对样本计算二分类的对数损失,然后把这些损失加起来进行优化。


5.2 为什么一次更新会同时影响多种向量

需要强调的是:
输入向量和输出向量并不是分别训练的,而是由同一个训练目标同时驱动。

在一次训练中:

  • 中心词对应的输入向量会被更新
  • 真实上下文词对应的输出向量会被更新
  • 每一个负样本对应的输出向量也都会被更新

从几何角度看,训练过程等价于:

  • 把中心词向量与真实上下文向量在空间中拉近
  • 把中心词向量与随机负样本向量在空间中推远

6. 为什么需要两张向量表

引入输入表与输出表的根本原因在于:同一个词在训练目标中承担了两种不同的功能

  • 作为中心词:用于生成一个中间表示,以预测周围词
  • 作为候选词:用于与中心词表示进行匹配,判断是否为真实上下文

这两种角色在语言统计中并不对称。强制它们共享同一套参数,相当于引入了不必要的约束,会削弱模型对真实共现结构的拟合能力。

训练完成后,实践中通常仅使用输入向量表中的向量作为最终词向量表示,因为它更稳定地反映了词在语义空间中的位置。


7. 一个最小可跑的 PyTorch 示例(核心机制版)

下面给出一个极简的 Skip-gram + Negative Sampling 实现,仅保留核心机制:

  • 输入 / 输出向量的分离
  • 正负样本共同驱动参数更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import torch
import torch.nn as nn
import torch.nn.functional as F

V, D = 3, 2 # 示例:吃 / 苹果 / 北京

class SGNS(nn.Module):
def __init__(self):
super().__init__()
self.in_emb = nn.Embedding(V, D)
self.out_emb = nn.Embedding(V, D)

def forward(self, center, pos, neg):
v = self.in_emb(center)
u_pos = self.out_emb(pos)
u_neg = self.out_emb(neg)

pos_loss = -F.logsigmoid(torch.sum(v * u_pos, dim=1))
neg_loss = -F.logsigmoid(-torch.sum(v * u_neg, dim=1))

return (pos_loss + neg_loss).mean()

在这个示例中可以看到:

  • 一个损失函数同时作用于多种向量
  • 每一步更新只涉及当前样本用到的向量行

8. 总结

  • One-hot 只是离散索引,不包含语义
  • 分布式表示的目标是用低维稠密向量刻画词的使用规律
  • 分布式表示可以通过计数方法或基于神经网络的推理方法构建
  • Skip-gram + Negative Sampling 将词向量学习转化为正负样本的区分问题
  • 同一个训练目标同时驱动输入向量与输出向量的学习
  • 通过大量样本的迭代更新,词的语义结构自然在向量空间中形成