Embedding 在大模型中起什么作用?

从文字到数字:Embedding 如何让机器读懂语言
上回聊完 Token 和分词方法,你已经知道 "今天天气真好" 会被切成一个个模型能处理的最小单元。但问题来了——模型是个数学函数,它只认数字。这些 "今天"、"天气"、"真"、"好" 的符号,怎么变成它能算的东西?
这就要说到 Embedding 了。
Embedding 是什么:给每个词发一张数字名片
计算机存储的文字,本质上是一串字符编码。"苹果" 两个字在内存里可能是两个数字:24370 和 33529。但这种编码是随机的——"苹果" 编成 24370,"香蕉" 编成 33529,这两个数字之间没有任何关系。
这就有问题了。"苹果"和"香蕉"都是水果,"汽车"和"飞机"都是交通工具。模型如果只能看到这些孤立的数字,就没法知道它们之间的语义关联。
Embedding 要做的,就是给每个词创建一张有意义的"数字名片"。
这张名片是一个向量——比如 512 个数字排成一排。这 512 个数字不是随便填的,它们是通过学习得到的,每个维度都可能代表着某种语义特征。

关键在于:语义相近的词,它们的数字名片也相近。
"苹果"和"香蕉"的向量距离会很近,因为都是水果。 "苹果"和"汽车"的向量距离会很远,因为一个是水果一个是交通工具。 模型在学习过程中,会自动调整每个词的向量,让语义相近的词在向量空间中"住"得近,语义不同的词"住"得远。
这就好比一个大型派对的座位安排:喜欢吃甜食的人坐一桌,喜欢咸口的人坐另一桌,水果爱好者们又凑在另一个角落。Embedding 就是在高维空间里做这种"自动排座"。
Token Embedding:用查表的方式找向量
现在你理解了 Embedding 的概念。但模型在实际计算时,是怎么获取每个 Token 的向量的?
Token Embedding 的实现其实很朴素——它就是一个"查找表"。
模型在训练之前,会先创建一个巨大的矩阵。假设我们的词表有 50000 个词,每个词的向量是 512 维,那么这个矩阵就是 50000 行、512 列。
Token Embedding 矩阵 (50000 × 512)
词表ID 0 → [0.12, -0.34, 0.56, ..., 0.78] (512维向量)
词表ID 1 → [-0.23, 0.45, -0.67, ..., 0.89] (512维向量)
词表ID 2 → [0.34, -0.56, 0.78, ..., -0.91] (512维向量)
...
词表ID 49999 → [0.45, 0.67, -0.89, ..., 0.12] (512维向量)当模型看到 token ID = 1024 的词时,它不需要任何复杂计算,直接去矩阵的第 1024 行,把那 512 个数字取出来就行了。这就是"查表"。
为什么叫"Embedding 层"?因为这整个矩阵就是模型的一个参数层。训练开始时,这些数字是随机初始化的。随着模型在海量文本上训练,这些数字会不断被调整,最终每个词都获得一张有意义的数字名片。

用查表而不是计算有什么好处?
一个字:快。
矩阵乘法需要大量计算,但查表只是一个随机访问操作。模型每秒处理几十个 token,这种方式效率极高。
Position Embedding:解决词序问题
Token Embedding 搞定了语义,但还有一个大问题没解决。
请看这句话:"猫吃鱼"。
把它拆成 tokens:["猫", "吃", "鱼"]。对应的向量分别是 v1、v2、v3。
如果我把顺序打乱,变成"鱼吃猫",tokens 是 ["鱼", "吃", "猫"],向量分别是 v4、v2、v5。
现在的问题是:模型看到 ["猫", "吃", "鱼"] 和 ["鱼", "吃", "猫"],它能区分吗?
答案是:不能。
因为 Transformer 的自注意力机制是"位置无关"的。它只关心每个 token 是什么,不关心它们在哪个位置出现。"猫" 的向量是 v1,"鱼" 的向量是 v4,模型知道这两个是不同的词,但不知道 v1 出现在第 1 位、v4 出现在第 3 位有什么区别。
这显然是致命的。人类语言里词序至关重要——"猫吃鱼" 和"鱼吃猫" 完全不是一回事。
Position Embedding 就是来解决这个问题的。
它的任务很简单:给每个位置也分配一个向量,用来表示"这是第几个词"。
第 1 个位置的 Position Embedding:p1 = [0.1, -0.2, 0.3, ...](512维)
第 2 个位置的 Position Embedding:p2 = [-0.1, 0.4, -0.5, ...](512维)
第 3 个位置的 Position Embedding:p3 = [0.2, -0.6, 0.7, ...](512维)
这样一来,"猫吃鱼" 的三个 token 加上位置信息后,变成了:
- 猫 + 位置1 → v1 + p1
- 吃 + 位置2 → v2 + p2
- 鱼 + 位置3 → v3 + p3
"鱼吃猫" 的情况:
- 鱼 + 位置1 → v4 + p1
- 吃 + 位置2 → v2 + p2
- 猫 + 位置3 → v1 + p3
现在模型能区分了——虽然"猫"和"鱼"的 token 向量不变,但它们加上的位置向量不同,最终的输入向量就不同。

你可以这样理解:Token Embedding 告诉模型"你是谁",Position Embedding 告诉模型"你站在哪里"。
两者相加:最终输入向量的诞生
现在我们知道有两个 Embedding:
- Token Embedding:负责语义信息
- Position Embedding:负责位置信息
大语言模型的做法是:把它们相加。
最终的输入向量 = Token Embedding + Position Embedding
相加有一个前提:两个向量的维度必须相同。如果 Token Embedding 是 512 维,Position Embedding 也必须是 512 维。
为什么是相加而不是拼接?
相加能实现一种巧妙的"信息融合"。当"猫"出现在第 1 位时,它的最终向量是 v1 + p1;当"猫"出现在第 5 位时,它的最终向量是 v1 + p5。虽然 v1 不变,但加上不同的 p,得到的最终向量也不同。
如果用拼接的话,"猫在第1位"和"猫在第5位"会变成两个完全不同的向量,模型需要学习它们之间的关系,成本更高。
相加的方式更简洁高效——它让语义和位置信息在同一个向量空间里共存,模型可以自由地学习它们之间的组合关系。

扩展话题:BERT 的三种 Embedding 与位置编码方案
讲到这里,基础的 Embedding 机制就说完了。但如果你去看 BERT 的论文,会发现它有三种 Embedding:
- Token Embedding:词本身的信息
- Position Embedding:位置信息
- Segment Embedding:句子信息
为什么 BERT 要多一个 Segment Embedding?
因为 BERT 在预训练时用的是"句子对"任务,比如判断"今天天气真好"和"适合出去玩"是不是同一个语义。Segment Embedding 用来区分这两个句子——第一个句子的所有 token 都加同一个 segment 向量,第二个句子的所有 token 都加另一个 segment 向量。
至于 Position Embedding 的实现方式,也有不同的流派:
可训练的方式:把 Position Embedding 也当作模型参数,随训练一起更新。GPT 系列就是这种方式。
固定函数的方式:用 sin/cos 函数直接计算每个位置的向量,不需要训练。原始 Transformer 采用这种方式。
相对位置编码:不关心 token 的绝对位置,只关心 token 之间的相对距离。RoPE(旋转位置编码)就是这种思路,LLaMA 等模型采用。
这些不同的实现方式,本质上都是在回答同一个问题:如何高效地让模型知道"谁在谁旁边"。
到这里,Embedding 的核心机制就讲完了。Token Embedding 负责"说什么",Position Embedding 负责"谁先说",两者相加后送入模型,模型才能完整地理解一句话。
但你有没有想过:模型拿到这些向量之后,是怎么判断每个词和其他词的关系的?"猫"和"吃"有什么关系?"吃"和"鱼"又是什么关系?
这就要说到 Self-Attention 机制了。下一次我们来聊聊 Q、K、V——这三个字母到底代表什么意思,模型是怎么通过它们计算出词语之间的关联度的。
