第 2 章 词嵌入:把文字变成向量
本章目标:理解为什么神经网络需要将离散的字 ID 转换为连续向量,以及
nn.Embedding层的工作原理。
2.1 独热编码的问题
计算机只认识数字。要把"春"这个字输入神经网络,最直接的方法是独热编码(One-Hot Encoding):词表有多少个字,就用多长的向量,只有对应位置为 1,其余全为 0。
假设词表大小为 5(简化示例):
春 → [1, 0, 0, 0, 0]
眠 → [0, 1, 0, 0, 0]
不 → [0, 0, 1, 0, 0]这有两个严重问题:
- 维度爆炸:本项目词表有 2439 个字,每个字就是一个 2439 维的稀疏向量,参数量极大
- 无法表达语义相似性:"春"和"夏"都是季节,但它们的独热向量之间的余弦相似度为 0,两者在数学上完全无关
2.2 稠密词嵌入
词嵌入(Word Embedding) 的思路是:用一个低维稠密向量来表示每个字,向量中的每个维度都是可学习的实数。
春 → [0.23, -0.81, 0.44, ..., 0.12] # 256 维
眠 → [0.11, 0.56, -0.33, ..., 0.87]
不 → [-0.42, 0.29, 0.71, ..., -0.15]关键性质:训练结束后,语义相近的字在向量空间中会自动靠近。例如"春夏秋冬"四个字的嵌入向量会聚在相近的区域,"山水云月"也会形成另一个簇。
这个性质不是人工设计的,而是模型在最小化预测损失的过程中自发学习到的。
2.3 Embedding 层的本质:一张查找表
nn.Embedding 在实现上非常简单——它就是一个形状为 (vocab_size, embedding_dim) 的矩阵(查找表):
embedding_dim (256)
┌─────────────────┐
字 ID=0 │ 0.23 -0.81 ... │ ← "春"的嵌入向量
字 ID=1 │ 0.11 0.56 ... │ ← "眠"的嵌入向量
字 ID=2 │ -0.42 0.29 ... │ ← "不"的嵌入向量
... │ ... │
字 ID=2438│ 0.77 -0.13 ... │ ← "<UNK>"的嵌入向量
└─────────────────┘前向传播就是查表:给定字 ID,返回对应的行向量。这个操作不涉及矩阵乘法,效率极高。
embedding = nn.Embedding(num_embeddings=2439, embedding_dim=256)
# 输入:字 ID 序列,形状 (N, L) = (批大小, 序列长度)
x = torch.LongTensor([[10, 23, 5, 88]]) # shape: (1, 4)
# 输出:对应的嵌入向量,形状 (N, L, E) = (1, 4, 256)
embedded = embedding(x)2.5 维度详解:N、L、E 各代表什么
这是很多同学最容易混淆的地方。我们用一个具体例子来把三个维度讲清楚。
从一首诗到一个批次
假设我们有两首诗(已去标点),各取前 4 个字:
样本 0:春 眠 不 觉 → ID: [156, 891, 73, 441]
样本 1:山 月 随 人 → ID: [623, 891, 512, 307]把它们打包成一个 batch,得到形状为 (N=2, L=4) 的整数张量:
x = [[156, 891, 73, 441], ← 第 0 首诗的 4 个字 ID
[623, 891, 512, 307]] ← 第 1 首诗的 4 个字 ID
x.shape = (2, 4) 即 (N, L)N(batch size)= 2:一次喂给模型的样本数量,两首诗并排放在第 0 轴。
L(sequence length)= 4:每首诗的序列长度,沿第 1 轴展开每个时间步。
经过 Embedding 后多了一个轴
Embedding 层对 x 中每个整数 ID 独立查表,返回对应的 256 维向量:
x.shape = (N=2, L=4) ← 输入:整数 ID
embedded.shape = (N=2, L=4, E=256) ← 输出:每个 ID 换成了 256 维向量用图来理解这个"升维"过程:
输入 x (2×4 个整数) 输出 embedded (2×4×256 个浮点数)
┌──────────────────┐ ┌──────────────────────────────┐
│ 156 891 73 441│ 查表 │[0.23,-0.81,...] ← 位置(0,0) │
│ 623 891 512 307│ ─────▶ │[0.11, 0.56,...] ← 位置(0,1) │
└──────────────────┘ │ ... │
N=2 L=4 │[0.45,-0.32,...] ← 位置(1,3) │
└──────────────────────────────┘
N=2 L=4 E=256E(embedding size)= 256:每个字的向量表示有多少维。第 2 轴(最后一轴)就是这个向量本身。
三个轴的物理含义
| 轴 | 符号 | 大小 | 含义 |
|---|---|---|---|
| 第 0 轴 | N | batch_size = 32 | 哪一条样本(批次中第几首诗) |
| 第 1 轴 | L | seq_len = 24 | 哪一个时间步(诗的第几个字) |
| 第 2 轴 | E | embedding_size = 256 | 向量的第几个维度(嵌入向量本身) |
记忆口诀:轴 0 选样本,轴 1 选位置,轴 2 看特征。
2.4 嵌入维度怎么选
嵌入维度
| 向量空间太拥挤,无法区分相似字 | 表达能力与计算量平衡 | 参数量暴增,容易过拟合 |
一个经验规则:
实践中常用的值是 128、256、512。本项目选择 256,足以区分 2439 个字的语义,同时参数量(
小结
- 独热编码:维度爆炸 + 无法表达语义相似性
- 词嵌入:低维稠密向量,可学习,语义相近的字向量相近
nn.Embedding= 可学习的查找表,前向传播就是按 ID 取行- 本项目使用 256 维嵌入,参数量约 62 万