09aba-将离散的 token ID 映射为连续的稠密向量
本文档深入讲解 Token Embedding 的核心原理,涵盖从离散符号到稠密向量的映射机制、Embedding 层与查找表的等价关系、PyTorch 从零实现完整代码及逐行解析、为什么要用稠密向量而非 One-Hot,以及一个完整可运行的示例。帮助读者彻底理解 Embedding 层的运行机制 💡
章节阅读路线图 🗺️
- 核心概念 → 理解 Token、Embedding 和向量化的本质区别
- 为什么需要 Embedding → 从 One-Hot 到稠密向量的演进逻辑
- Embedding 层的本质 → 查找表 vs 矩阵乘法的等价关系
- 总结 → 回顾核心要点
1. 核心概念 💡
本章理解 Token、Embedding 和向量化的本质区别
在深入代码之前,先明确几个关键概念:
1.1 什么是 Token?
Token 是模型处理文本的最小单位。它可以是:
- 单词:如 "hello"、"world"
- 子词:如 "un-"、"believ-"、"-able"(BPE 分词的结果)
- 字符:如 "h"、"e"、"l"、"l"、"o"
- 特殊符号 :如
<PAD>、<UNK>、<CLS>
经过 Tokenizer 处理后,每个 Token 都会被映射为一个唯一的整数 ID:
arduino
"我" → 1024
"喜欢" → 2048
"深度" → 3072
"学习" → 4096
什么是 Tokenizer(分词器)?
Tokenizer 是 NLP 模型的前置组件,负责将原始文本切分为 Token 序列,并转换为整数 ID。它的作用类似于"翻译官"------把人类语言翻译为机器可理解的数字。
例如:
python
文本: "我喜欢深度学习"
↓ (Tokenizer)
Token: ["我", "喜欢", "深度", "学习"]
↓ (查词汇表)
ID: [1024, 2048, 3072, 4096]
参考资料:
1.2 什么是 Embedding?
Embedding(嵌入)是将离散的 Token ID 映射为连续的稠密向量的技术。
例如,假设向量维度为 4:
less
Token ID: 1024 ("我") → [0.2, -0.5, 0.8, 0.1]
Token ID: 2048 ("喜欢") → [0.6, 0.3, -0.2, 0.9]
Token ID: 3072 ("深度") → [0.1, -0.8, 0.5, 0.4]
Token ID: 4096 ("学习") → [0.7, 0.2, 0.3, -0.6]
什么是"稠密向量"?
稠密向量(Dense Vector)是指向量中大多数元素都是非零值,且每个维度都携带语义信息。这与稀疏向量(如 One-Hot 编码)形成鲜明对比。
例如:
- 稠密向量 :
[0.2, -0.5, 0.8, 0.1](所有维度都有值) - 稀疏向量(One-Hot) :
[0, 0, 1, 0, 0, ...](只有一个 1,其余全是 0)
稠密向量的优势在于:
- 语义相似性:语义相近的词,向量在空间中的距离也近
- 信息密度高:每个维度都编码了某种语义特征
- 维度低:通常 128~768 维,远低于 One-Hot 的几万到几十万维
1.3 Embedding vs 向量化
很多文章没有区分这两个概念,但它们有本质区别:
| 维度 | Embedding(嵌入) | 向量化(Vectorization) |
|---|---|---|
| 目的 | 学习低维稠密语义表示 | 将数据转换为数值向量(可能稀疏) |
| 是否需要学习 | 需要(通过神经网络训练) | 不需要(基于规则或统计) |
| 语义表示 | 保留深层语义关系和相似性 | 可能不保留语义,仅是机械化表示 |
| 典型方法 | Word2Vec、BERT、GloVe | One-Hot、TF-IDF、词袋模型 |
| 结果维度 | 低维且稠密(如 512 维) | 高维且稀疏(如 50000 维) |
直观理解:
- 向量化像是"机械翻译"------直接把文字转成数字,不理解含义
- Embedding像是"智能翻译"------不仅转成数字,还理解了语义关系
例如,对于"新年快乐"四个字:
- 向量化:生成 4 个独立的向量,彼此没有关系
- Embedding:生成的向量蕴含语义结构("新"和"年"可能更接近,"快"和"乐"可能更接近)
参考资料:
2. 为什么需要 Embedding 🎯
本章理解从离散符号到稠密向量的演进逻辑
计算机无法直接处理文字,必须转换为数值。但为什么要选择稠密向量?让我们看看发展历程:
2.1 第一阶段:直接用数字(索引化)
最简单的做法是给每个词分配一个唯一编号:
python
词汇表 = {"新": 1, "年": 2, "快": 3, "乐": 4}
"新年快乐" → [1, 2, 3, 4]
问题:单一数字信息量不足
- 无法描述语义关系
- "abeyance"(中止)、"abide"(遵守)、"ability"(能力)在字典中索引临近,但语义相差甚远
- 而 "a" 和 "an" 这两个同质的词却隔得很远
本质问题:单一标量无法表达词的复杂语义。
2.2 第二阶段:One-Hot 编码
为了解决单一数字信息量不足 的问题,我们想到用多个数字(向量)来表示一个词。最直观的方法是 One-Hot 编码:
python
词汇表大小 = 4
"新" → [1, 0, 0, 0]
"年" → [0, 1, 0, 0]
"快" → [0, 0, 1, 0]
"乐" → [0, 0, 0, 1]
One-Hot 编码的规则:
- 向量长度 = 词汇表大小
- 每个词对应一个位置为 1,其余全为 0
- 第 i 个词的向量,第 i 位是 1
问题:维度灾难 + 语义孤立
-
维度灾难:
- 如果词汇表有 50000 个词,每个向量就是 50000 维
- 存储一个句子需要巨大的内存(50000 × 句子长度)
- 计算效率极低(大量乘以 0 的无效运算)
-
语义孤立:
- 任意两个 One-Hot 向量的点积都是 0(正交)
- 无法表达"相似性"(如"猫"和"狗"应该相似,但向量完全不相关)
- 每个词都是独立的"孤岛",没有任何关联
-
稀疏性:
- 99.99% 的元素都是 0,信息密度极低
- 浪费了绝大部分存储空间
2.3 第三阶段:稠密向量(Embedding)
为了解决 One-Hot 的问题,我们引入稠密向量:
python
词汇表大小 = 50000
向量维度 = 512 # 远低于 50000
"新" → [0.2, -0.5, 0.8, ..., 0.3] # 512 维稠密向量
"年" → [0.1, -0.4, 0.7, ..., 0.2]
"快" → [0.6, 0.3, -0.2, ..., 0.9]
"乐" → [0.7, 0.2, 0.3, ..., 0.8]
为什么稠密向量更好?
-
语义相似性:
- 语义相近的词,向量在空间中的距离也近
- 例如:
cos("猫", "狗") = 0.85(相似度高) - 例如:
cos("猫", "汽车") = 0.12(相似度低)
-
信息密度高:
- 每个维度都编码了某种语义特征
- 例如:维度 1 可能表示"动物 vs 非动物"
- 例如:维度 2 可能表示"大小"
- 例如:维度 3 可能表示"情感极性"
-
维度大幅降低:
- 512 维 vs 50000 维,存储和计算效率提升 100 倍
- 避免了维度灾难
-
可学习性:
- Embedding 向量在训练过程中不断优化
- 模型自动学习哪些语义特征对任务最重要
直观类比:
想象你要描述一个人的特征:
- One-Hot:用一个超长的清单,只有"身高"那栏打勾,其他全是空白
- 稠密向量:用一个紧凑的档案,包含身高、体重、年龄、性格等多个维度的具体数值
显然,稠密向量能传递更多信息,且更高效。
参考资料:
- 从One-Hot到Embedding:揭秘投影层在语言模型中的核心作用 -- CSDN ⭐值得阅读
- Word Embeddings in NLP -- GeeksforGeeks
- 探秘Transformer系列之(7):embedding -- 掘金
3. Embedding 层的本质 🔍
本章理解查找表与矩阵乘法的等价关系
3.1 Embedding 层的本质是查找表
Embedding 层内部维护了一个嵌入矩阵(Embedding Matrix) ,形状为 [vocab_size, embedding_dim]:
python
词汇表大小 = 5
向量维度 = 4
嵌入矩阵 = [
[0.1, 0.2, -0.3, 0.4], # Token 0 的向量
[0.5, -0.1, 0.6, 0.2], # Token 1 的向量
[0.3, 0.8, -0.2, 0.1], # Token 2 的向量
[0.9, 0.4, 0.5, -0.3], # Token 3 的向量
[0.2, -0.6, 0.7, 0.8] # Token 4 的向量
]
当输入 Token ID 为 [2, 0, 3] 时,Embedding 层直接查表取出对应行:
python
输入 ID: [2, 0, 3]
查表结果:
Token 2 → [0.3, 0.8, -0.2, 0.1]
Token 0 → [0.1, 0.2, -0.3, 0.4]
Token 3 → [0.9, 0.4, 0.5, -0.3]
输出形状: [3, 4] # 3个token,每个4维
为什么叫"查找表(Lookup Table)"?
因为 Embedding 操作等价于用 Token ID 作为索引,从矩阵中直接取出对应行,就像查字典一样快速。
3.2 查找表 vs 矩阵乘法的等价性
Embedding 操作在数学上等价于 One-Hot 向量 × 嵌入矩阵:
python
# 方法 1: 查找表(高效)
Token ID: 2
嵌入矩阵[2] → [0.3, 0.8, -0.2, 0.1]
# 方法 2: 矩阵乘法(理论等价,但低效)
One-Hot(2) = [0, 0, 1, 0, 0] # 第 2 位为 1
One-Hot(2) × 嵌入矩阵:
[0, 0, 1, 0, 0] × [
[0.1, 0.2, -0.3, 0.4],
[0.5, -0.1, 0.6, 0.2],
[0.3, 0.8, -0.2, 0.1], ← 只有这一行被选中
[0.9, 0.4, 0.5, -0.3],
[0.2, -0.6, 0.7, 0.8]
]
= [0.3, 0.8, -0.2, 0.1] # 结果完全相同!
为什么等价?
One-Hot 向量中只有一个 1,矩阵乘法的结果就是嵌入矩阵中对应行的加权和。由于其他位置都是 0,实际只取出了第 i 行。
但查找表快 3~5 倍:
- 矩阵乘法需要做
vocab_size × embedding_dim次乘法和加法 - 查找表直接取出对应行,时间复杂度 O(1)
- 避免了大量乘以 0 的无效计算
反向传播的差异:
- 矩阵乘法:梯度会传播到整个嵌入矩阵(大部分是 0,无意义)
- 查找表:梯度只更新被查询的那些行(高效且精准)
参考资料:
- 从One-Hot到Embedding:揭秘投影层在语言模型中的核心作用 -- CSDN
- 推荐系统embedding原理及实践 -- 李乾坤 ⭐值得阅读
- 深度学习推荐系统-Embedding -- 知乎
4. 总结 📝
本节我们完成了 Token Embedding 的核心原理和代码实现,核心要点回顾:
| 概念 | 说明 | 关键点 |
|---|---|---|
| Token | 文本处理的最小单位 | 可以是单词、子词、字符 |
| Embedding | 将离散 ID 映射为稠密向量 | 保留语义关系,维度低 |
| 查找表 | Embedding 层的本质 | 用 ID 索引直接取行,比矩阵乘法快 3~5 倍 |
| One-Hot | 早期向量化方法 | 维度灾难、语义孤立、已被淘汰 |
| 稠密向量 | 现代表示方法 | 语义相似性、信息密度高、可学习 |
🔴 关键理解:
- Embedding 是查找表,不是矩阵乘法(虽然数学等价,但实现差异大)
- 稠密向量比 One-Hot 更高效(维度降低 100 倍,语义关系可学习)
padding_idx用于标记填充位置(保持为 0,不参与训练)- Embedding 向量在训练中不断优化(模型自动学习语义特征)
最后更新时间:2026-06-01