← 返回全部思考

AI 基础课 02:Embedding,模型如何把意义放进空间

从 one-hot、向量相似度到训练出来的语义坐标,建立理解检索、注意力和大模型输入层的共同地基。

LISTEN TO THIS LESSON开车听本课07:36

支持锁屏播放与前进/后退 15 秒;首次播放需要手动点一下。

上一课里,我们已经知道:语言模型接收的不是“词”,而是 token;它也不是一次写完整段话,而是根据已有 token 预测下一个 token。

但这里还缺了一座桥。计算机最终只会处理数字,那么 token 怎样变成模型能计算的数字?为什么模型会觉得“猫”和“狗”相近,却觉得“猫”和“数据库连接池”很远?这就是 Embedding(嵌入) 要解决的问题。

本课的目标不是背一个定义,而是建立一个空间直觉:模型把离散符号放进连续空间,让距离和方向开始承载意义。

1. 最朴素的办法为什么不够

假设词表只有四个 token:猫、狗、汽车、香蕉。最简单的编码叫 one-hot:

  • 猫:[1, 0, 0, 0]
  • 狗:[0, 1, 0, 0]
  • 汽车:[0, 0, 1, 0]
  • 香蕉:[0, 0, 0, 1]

它的好处是身份明确,不会把两个 token 搞混。但它有两个根本问题。

第一,维度太大。一个现代大模型的词表可能有十几万个 token,one-hot 向量就要有十几万维,而且绝大多数位置都是零。

第二,也是更重要的:它没有表达任何关系。“猫”和“狗”的距离,与“猫”和“汽车”的距离完全一样。编号只是在点名,不是在描述意义。

2. Embedding 是一张可学习的坐标表

Embedding 层可以先理解为一张矩阵:

E ∈ R^(词表大小 × 向量维度)

矩阵的每一行对应一个 token,每一行都是长度为 d 的稠密向量。输入 token id 时,模型做的第一件事不是复杂计算,而是“查表”:

x = E[token_id]

如果词表有 100,000 个 token,向量维度是 4,096,那么这张表就有大约 4.1 亿个可训练参数。它不是人为写出来的词典,而是在训练过程中和模型其他参数一起被梯度下降不断调整。

训练开始时,“猫”的坐标可能是随机的。模型看过大量文本后,为了更准确地预测下一个 token,会逐渐把在相似上下文中出现的 token 调整到具有相似结构的位置。于是“猫”“狗”可能靠近,“巴黎—法国”和“东京—日本”可能呈现类似方向。

这里要非常谨慎:向量的单个维度通常没有稳定的人类含义。不是第 17 维代表“动物”、第 83 维代表“城市”。意义往往分布在许多维度的组合和相对关系里。

3. 怎样判断两个向量是否相似

最常见的方法是余弦相似度:

cos(a, b) = (a · b) / (||a|| ||b||)

它衡量的是两个向量方向是否一致,而不是绝对长度是否接近。

  • 接近 1:方向很像
  • 接近 0:大致无关
  • 接近 -1:方向相反

为什么常看方向?因为向量长度可能混入频率、置信度等其他因素,而方向更适合比较“结构是否相似”。这也是语义搜索的基础:先把用户问题和文档都变成向量,再找余弦相似度最高的文档。

但“距离近”不等于事实正确。Embedding 学到的是训练数据中的统计关系,也会继承数据里的偏见;它还会受到模型、语言、上下文和训练目标影响。不同模型产生的向量通常不能直接混用。

4. 一个 token 只有一个固定向量吗

输入层查表时,同一个 token 的初始 embedding 的确相同。但 Transformer 会让它经过多层 Attention 和 MLP,不断融合上下文。

比如“苹果发布了新芯片”和“我吃了一个苹果”里,初始的“苹果”可能来自同一行查表结果;经过上下文计算后,它们的隐藏状态会走向不同位置。前者吸收“发布、芯片”的信息,后者吸收“吃”的信息。

因此要区分两件事:

  • Token embedding:刚进入模型时的初始表示。
  • Contextual representation:经过 Transformer 后、已经包含上下文的动态表示。

现代 Embedding 模型用来做检索时,通常不是简单取输入层那一行,而是对上下文化后的多个 token 表示做 pooling,得到整段文本的向量。

5. 它与 Attention、RAG 和模型容量有什么关系

Embedding 是 Attention 的入口。上一课里的 Q、K、V 并不是从 token id 直接长出来的,而是从 token 的向量表示经过线性变换得到:

q = Wq x, k = Wk x, v = Wv x

没有向量表示,就无法做点积,也无法判断当前 token 应该关注上下文里的谁。

Embedding 也是 RAG 的入口。一个典型检索增强流程是:

  1. 把文档切成片段。
  2. 用同一个 Embedding 模型把片段编码为向量,存进向量数据库。
  3. 把用户问题编码为向量。
  4. 做近邻搜索,找出方向最相似的片段。
  5. 把片段交给大模型生成答案。

这里最常见的工程坑并不是向量数据库选错了,而是切块不合理、查询与文档使用了不匹配的模型、升级 Embedding 模型后没有重建索引,或者只看相似度却没有做重排和权限过滤。

6. 一个可以心算的例子

假设我们人为设计二维坐标:

  • 猫:[0.9, 0.8]
  • 狗:[0.8, 0.9]
  • 汽车:[-0.8, 0.2]

不需要精确计算也能看出:猫和狗大致指向右上方,方向相似;汽车指向左上方,与它们相差很大。真实 Embedding 不是二维,而可能是 768、1,536 或更多维,但基本思想相同。

二维图只是帮助理解。高维空间里还会出现“维度灾难”和距离集中等反直觉现象,所以实际系统需要用专门的近似最近邻索引,在召回速度、内存和准确率之间权衡。我们讲模型 Infra 时会再回到这里。

7. 本课留下的三个判断

第一,Embedding 不是给词分配一个神秘编号,而是学习一套可计算的相对位置。

第二,模型里的“意义”首先表现为关系:谁和谁靠近、哪些方向相似、在什么上下文中发生变化。

第三,Embedding 同时连接了模型算法与工程系统:向前连接 Attention,向外连接搜索、推荐、聚类和 RAG。

动手练习

不用任何 AI 库,先感受余弦相似度:

import math

def cosine(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    norm_a = math.sqrt(sum(x * x for x in a))
    norm_b = math.sqrt(sum(y * y for y in b))
    return dot / (norm_a * norm_b)

cat = [0.9, 0.8]
dog = [0.8, 0.9]
car = [-0.8, 0.2]

print(cosine(cat, dog))
print(cosine(cat, car))

然后想一个产品问题:如果用户搜索“退款多久到账”,文档里写的是“原路退回通常需要三个工作日”,关键词并不完全一致。为什么 Embedding 检索可能找到它?又为什么它仍然可能召回一段语义相近、但适用国家或用户等级不对的规则?

下一课,我们把视野拉高,完整拆开 Transformer:Attention 负责跨 token 取信息,MLP 负责逐位置加工,残差连接与归一化负责让深层网络能够稳定训练。