文本如何变成数字表示
开篇问题
神经网络只能处理数字。可是我们输入给大语言模型的是文字、代码和符号。
那么问题来了:const user = "Ada" 这样的文本,如何变成模型可以计算的东西?模型又如何知道“猫”和“狗”比“猫”和“数据库连接池”更接近?
这就要进入 embedding,也就是“把离散符号变成连续数字表示”的第一层机制。
本章你会理解什么
读完本章,你应该能够理解:
- 文本进入模型前为什么必须先变成 token ID。
- embedding table 的作用是什么。
- 向量表示为什么能承载语义、语法和位置相关信息。
- 语言模型内部的 embedding 和应用里的向量检索 embedding 有什么区别。
- 为什么 embedding 不是可逆压缩,也不是人工设计的标签。
核心概念
语言模型不能直接计算字符串。它通常会经历三步:
- 文本被 tokenizer 切成 token。
- token 被映射成词表里的数字 ID。
- 每个 token ID 被查表得到一个向量。
这个向量就是模型内部使用的数字表示。它不是一个普通编号,而是一组可以参与神经网络计算的浮点数。
可以粗略写成:
真实模型里的向量维度可能有几百、几千甚至更多。每一维通常没有人类可直接命名的含义,但整个向量会在训练中变成有用的表示。
机制拆解
1. 从文本到 token ID
第一步是 tokenizer。它负责把文本切成模型词表中的 token。
例如:
可能被切成类似这样的片段:
这只是示意,真实切分取决于具体 tokenizer。重点是:模型最终看到的不是原始字符串,而是一串 token ID。
这些 ID 本身没有语义。ID 为 3182 并不比 ID 为 642 “更大”或“更重要”。它们只是词表中的索引。
2. 从 token ID 到向量
如果只把 token ID 直接喂给神经网络,会有一个问题:数字 ID 的大小会制造假的关系。
例如 token 100 和 token 101 不一定语义接近,token 100 和 token 9000 也不一定语义遥远。ID 只是编号,不是意义。
所以模型会使用 embedding table。每个 token ID 对应一个可训练向量。输入 token ID 时,本质上是在表中取出那一行向量。
训练开始时,这些向量可能接近随机。随着模型反复学习下一个 token 预测,向量会被不断调整。那些在相似上下文中出现、承担相似功能的 token,往往会形成更接近的表示。
3. 向量承载的是关系,不是标签
embedding 不是给每个词贴上“名词”“动词”“技术术语”这样的人工标签。它更像一种从训练任务中自动形成的坐标。
如果两个 token 经常出现在相似上下文中,它们的向量可能在某些方向上更接近。比如“北京”和“上海”都可能出现在城市、天气、人口、交通等语境中;“React”和“Vue”都可能出现在组件、状态、前端框架等语境中。
这种接近不是人手写进去的,而是训练目标逼出来的:为了更好预测下一个 token,模型需要把有相似作用的符号放到可计算的相近表示里。
4. 位置也必须变成数字
同一组 token,顺序不同,意义可能完全不同。
如果模型只看到 token 向量而不知道顺序,就无法区分这两句话。
因此 Transformer 类模型还需要位置信息。常见做法是加入 positional embedding 或使用其他位置编码机制,让模型知道每个 token 在序列中的位置以及 token 之间的相对关系。
简化理解就是:
有了 token 表示和位置表示,模型才能进一步计算“谁和谁有关”“当前 token 应该关注前文哪里”。
5. embedding 会进入后续层反复加工
embedding 只是第一步。它把离散 token 转成连续向量,让后续神经网络可以计算。
进入 Transformer 后,每一层都会根据上下文更新这些向量。也就是说,一个 token 的表示不是固定不变的。
例如“Java”在不同上下文中可能表示编程语言,也可能表示咖啡或地名。初始 embedding 只提供基础表示,后续层会结合上下文,让当前位置的向量逐渐带上更具体的含义。
这就是为什么我们不能把 embedding 理解成一本静态词典。它是模型计算的起点,而不是最终理解结果。
开发者常见误解
误解一:embedding 是把文本压缩成可以还原的编码
embedding 不是 zip,也不是 Base64。它通常不能还原成原文。
模型内部会保留 token ID 序列作为输入结构,embedding 只是把这些 ID 转成可计算向量。向量的作用是参与神经网络计算,而不是作为可逆存储格式。
误解二:向量每一维都有明确人类含义
不要试图解释“第 17 维代表语气,第 33 维代表技术性”。真实模型中的维度通常是分布式表示:意义分散在许多维度和它们的组合里。
开发者更应该关注整体关系,而不是单独维度。
误解三:token ID 越接近,语义越接近
token ID 是词表编号,不代表语义距离。真正参与语义关系的是 embedding 向量,而不是 ID 数值。
这和数据库自增 ID 类似:user_id = 1001 和 user_id = 1002 不表示两个用户更相似。
误解四:所有 embedding 都是一回事
语言模型内部的 token embedding,和应用中常说的“把一段文档转成向量”的 embedding,不是同一个层面的概念。
本章讨论的是模型内部的输入表示:每个 token 如何变成向量,以便 Transformer 继续计算。
应用里的文本向量通常是把一句话、一段文本或一个文档片段变成一个整体向量,用于相似度比较等任务。两者都叫 embedding,因为都把离散内容映射到连续向量空间,但用途、粒度和训练方式并不完全相同。
小实验/思考实验
想象你要用二维坐标表示一些技术词:
你可能会把 React 和 Vue 放得近一些,把 PostgreSQL 和 MySQL 放得近一些,把 Docker 和 Kubernetes 放得近一些。
这不是因为你给它们写了分类标签,而是因为你知道它们在工程语境中经常承担相似角色。
现在把二维扩展到几千维,把人工摆放改成模型训练,把几个词扩展到几十万 token。这就是 embedding 的直觉版本:在一个高维空间里,为 token 学习一套有助于预测和计算的位置。
再思考一个问题:如果一个词在不同语境中有多个含义,单个初始 embedding 是否足够?为什么还需要后续 Transformer 层结合上下文继续更新表示?
本章小结
文本进入语言模型前,需要先被切成 token,再映射成 token ID,最后通过 embedding table 转成向量。token ID 只是索引,embedding 向量才是模型可以计算的表示。
embedding 的意义不是可逆压缩,也不是人工标签,而是在训练中形成的连续表示。它让模型能够把离散符号放入可计算空间,并在后续 Transformer 层中结合上下文不断更新。
理解文本如何变成数字,是理解 attention 和 Transformer 的前置基础:只有当 token 被表示成向量,模型才有可能计算 token 之间的关系。
自测题
- 为什么语言模型不能直接把 token ID 当作有意义的数值使用?
- embedding table 在模型输入阶段起什么作用?
- 为什么说 embedding 向量的单个维度通常不适合直接解释?
- token embedding 和应用中的文档 embedding 有什么区别?
- 为什么模型除了 token 表示,还需要位置相关表示?