大模型基础扫盲------从文本到 Token、Embedding、QKV 与 Transformer(不定期优化修改)大模型处理文本的全流程解析(二)-CSDN博客

大模型基础扫盲------从文本到 Token、Embedding、QKV 与 Transformer(不定期优化修改)大模型处理文本的全流程解析(三)-CSDN博客

大模型基础扫盲------从文本到 Token、Embedding、QKV 与 Transformer(不定期优化修改)大模型处理文本的全流程解析(四)-CSDN博客

一句话概括全文:
文本 → 分词器 → token → token id → embedding → 位置编码 → 多层 Transformer(注意力 + FFN)→ 输出概率 → 生成下一个 token

阅读提示:
文中出现的 token id、向量维度、注意力分数、概率值等,很多是为了教学方便给出的示例数字,不一定对应某个真实模型。



1. 总体流程概览

大模型处理文本的完整流程可以概括为:

【第一阶段:原材料准备 (输入与初始化)】
原始文本 (比如: "苹果很好吃")
  ↓
Tokenizer:切成小块 (苹果 / 很 / 好吃)
  ↓
Token ID:查字典给编号 (101 / 102 / 103)
  ↓
Embedding:查表拿“初始工牌” (101变成初始向量X,此时它还不懂上下文)
  ↓
位置编码:发“座位号” (告诉模型谁是第1个词,谁是第2个词)
  ↓
【第二阶段:核心加工车间 (Transformer Block × N 层)】
  │
  │  开始循环 N 次 (比如循环 32 次,每次都在深化理解)
  │
  ├─ 2.1 注意力机制 (Attention) —— 【核心车间:开早会交换信息】
  │    ├─ 变身:每个词向量 X 分别生成自己的 Q(提问)、K(标签)、V(内容)
  │    ├─ 对暗号:当前词的 Q,去和所有词的 K 算相关性 (点积)
  │    ├─ 算比重:Softmax 把相关性变成百分比权重 (比如“好吃”占 80%,“很”占 20%)
  │    └─ 吸收融合:用权重去提取别人的 V,加到自己身上。
  │          结果:“苹果”吸收了“好吃”的信息,真正变成了【水果】向量!
  │
  ├─ 2.2 前馈神经网络 (FFN) —— 【个人工位:深度独立思考】
  │    └─ 拿到融合后的新向量,经过两层全连接网络,做非线性的深度加工。
  │
  └─ 2.3 残差与归一化 (Add & Norm) —— 【质检与防丢失】
       └─ 把加工后的结果和最初的自己加起来(残差),防止信息丢失;再做标准化(Norm),防止数值爆炸。
  │
  │  循环结束,进入下一层 Block...
  │
【第三阶段:成品输出 (预测)】
  ↓
最后一层的 Hidden State:经过 N 层打磨后,每个词都变成了“终极懂王”向量。
  ↓
LM Head (语言模型头):把“终极懂王”向量映射回词表的概率 (比如预测下一个词是“!”)
  ↓
输出结果:生成下一个 Token!

更具体地:

阶段

输入

输出

作用

原始文本

我吃鱼

字符串

人类可读

分词 tokenizer

我吃鱼

我 / 吃 / 鱼

切成模型可处理单位

token id

我 / 吃 / 鱼

2513 / 1892 / 3765

查词表编号

词嵌入 embedding

token id

向量序列

把离散编号变成连续向量

位置编码

向量序列

带位置信息的表示

告诉模型 token 的顺序

Transformer × N 层

向量序列

新向量序列

融合上下文信息

输出层 LM Head

最后一个位置向量

词表概率分布

预测下一个 token


按训练阶段理解

训练前

准备:

tokenizer
词表
训练数据
模型结构
参数初始化

训练中

通过 预测下一个 token 学习:

embedding
Q/K/V 投影矩阵
输出投影矩阵 W_O
FFN 参数
归一化参数
LM Head 参数

训练后,也就是推理阶段

使用训练好的参数生成文本,并用工程手段优化速度:

KV Cache
PagedAttention
Continuous Batching
量化
FlashAttention
算子融合

关于“位置编码”的说明

流程表里的“位置编码”需要做一点解释:

  • 对原始 Transformer / BERT 来说,位置编码通常是直接加到 embedding 上:
X = Embedding(token) + Position(position)
  • 但对现代模型常用的 RoPE 来说,位置信息不是在 embedding 阶段简单加进去的,而是在注意力计算时对 Q、K 施加旋转:
q_i = rotate(q_i, position=i)
k_j = rotate(k_j, position=j)

所以:

“embedding → 位置编码”这个流程图,对 RoPE 模型来说是一种教学简化。
RoPE 的位置信息实际上是在注意力计算阶段注入的。


2. 文本:模型看到的原始输入

文本就是普通字符串。

例如:

我今天很开心
The cat sat on the mat.
print("hello world")

文本是人类习惯的表达方式,但模型不能直接“读懂”文字。

模型内部只能处理数字,尤其是向量。


2.1 为什么模型只能处理数字

可以从三层理解。


第一层:硬件只能处理数字

CPU / GPU 本质上是电路。电路最终处理的是高电平和低电平,也就是:

1 和 0

所有运算:

加法
乘法
比较
存储
矩阵乘法

最终都是对数字的操作。

文字“猫”在硬件层面没有意义。
电路不认识“猫”,只认识数字。


第二层:神经网络的核心运算是数学运算

Transformer 内部的核心运算包括:

运算

例子

矩阵乘法

Q K^T

向量加法

残差连接 x + F(x)

逐元素乘法

FFN 门控结构

非线性函数

softmax、ReLU、GELU、SiLU

归一化

LayerNorm、RMSNorm

这些运算的输入和输出必须是数字。

你不能对字符串做矩阵乘法:

"猫" × [[0.1, 0.2], [0.3, 0.4]]

这在数学上没有意义。

但你可以对向量做运算:

[0.8, 0.6, 0.1, 0.2] · [0.7, 0.5, 0.2, 0.3]

这有明确结果。


第三层:文字是离散符号,没有天然的数学结构

“猫”和“狗”是两个不同符号,但它们之间没有天然的数学关系:

"猫" 和 "狗" 的距离是多少?
"猫" + "狗" 等于什么?
"猫" × 0.5 又是什么?

这些都没有自然定义。

而向量天然支持:

距离
加减
点积
余弦相似度
线性组合
投影

所以必须把文本转换成向量。

一句话:

embedding 之前的所有步骤,都是在把“人类符号”往“机器数字”推进。


2.2 从文本到数字的转换路线

原始字符串
  ↓
字节 / 字符 / 子词片段
  ↓
token
  ↓
token id
  ↓
embedding 向量
  ↓
Transformer 可计算的张量

例如:

"我今天很开心"
      ↓ 分词器切分
["我", "今天", "很", "开心"]
      ↓ 查词表
[2513, 4021, 892, 5567]
      ↓ 查 embedding 表
[向量A, 向量B, 向量C, 向量D]
      ↓ 进入 Transformer

3. 分词器:把文本切成模型能处理的单位

分词器,英文叫 tokenizer。

它有两个核心职责:

1. 构建词表:离线做一次
2. 切分文本:在线每次训练/推理都做

3.1 构建词表:离线做一次

构建词表时,tokenizer 从大量语料中学习:

哪些片段应该作为一个 token

然后给每个 token 分配固定编号。

以 BPE 为例,大致流程是:

  1. 先把语料拆成最小单位
  2. 统计相邻片段出现频率
  3. 把最高频的相邻片段合并成新 token
  4. 重复合并,直到词表达到预设大小

例如语料中经常出现:

鲈 鱼

那么 可能被合并成:

鲈鱼

并分配一个新 id。

示例词表:

token

token id

256

257

258

259

鲈鱼

5000

人工

5001

智能

5002

人工智能

5003

注意:这些 id 只是示例。


3.2 切分文本:在线每次推理/训练都做

词表固定之后,每次拿着词表去切新文本。

情况 A:词表里有 鲈鱼

输入:

我喜欢吃鲈鱼

可能切成:

我 / 喜欢 / 吃 / 鲈鱼

对应 token id:

[256, 5010, 257, 5000]

情况 B:词表里没有 鲈鱼

输入:

我喜欢吃鲈鱼

可能切成:

我 / 喜欢 / 吃 / 鲈 / 鱼

对应 token id:

[256, 5010, 257, 259, 258]

3.3 字节级处理:现代分词器的主流方向

当前主流大模型的分词器普遍采用字节级 byte-level 或字节回退 byte-fallback 策略。

核心思想是:

先把文本转成 UTF-8 字节,
再在字节层面进行 BPE / BBPE / Unigram 等处理。

常见情况:

模型系列

分词器特点

GPT 系列

典型 Byte-level BPE

LLaMA 系列

SentencePiece / BPE / byte-fallback 相关机制

Qwen 系列

BBPE / 大词表 tokenizer

BERT 系列

WordPiece 常见

T5 / ALBERT

SentencePiece + Unigram 常见

字节级处理的好处是:

通常不会因未知字符而失败。

无论用户输入:

生僻汉字
emoji
代码符号
特殊标点
乱码

tokenizer 通常都能用字节序列表示出来。

但需要注意:

不同 tokenizer 实现细节有差异。
有些模型仍会保留 <unk> 特殊 token,尽管实际很少触发。
具体以模型自带的 tokenizer 配置为准。


3.4 词表构建 ≠ 模型训练

对象

什么时候做

怎么做

产出

词表构建

模型训练之前

统计语料、合并高频片段

token → id 映射表

模型训练

词表固定之后

梯度下降、反向传播

embedding、QKV、FFN 等参数

简单说:

词表决定:有哪些 token、编号是多少。
模型训练决定:每个 token 的 embedding 向量长什么样。

3.5 常见分词算法

算法

核心思路

代表模型

BPE

迭代合并最高频相邻片段

GPT 系列、LLaMA 常见

WordPiece

选择能最大化语料似然的合并

BERT

Unigram

从大候选集逐步裁剪低概率子词

T5、ALBERT

SentencePiece

工具框架,可直接处理原始文本

常配合 BPE / Unigram


3.6 Tokenizer 的工程要点

 MLOps 视角:

tokenizer 不只是“把文字切开”,它会直接影响很多工程问题。

关注点

说明

prompt token 数量

决定计费、上下文窗口占用

最大上下文长度

超出后可能截断或报错

stop token 是否生效

影响生成停止逻辑

chat template

不同模型消息格式不同

中文 / emoji / 代码切分

不同 tokenizer 差异很大

流式输出 decode

一个 token 可能只是半个字符,需要缓冲后再 decode

例如,同一个中文短语,在不同 tokenizer 下可能消耗不同数量的 token。
某些英文为主的 tokenizer 可能把中文切成多个字节级 token;多语言优化较好的 tokenizer 可能更省 token。

工程上不要凭感觉估算 token 数:

上线前一定要用目标模型的 tokenizer 实测。

4. Token:模型处理文本的基本单位

token 是分词器切出来的单位。

例如:

我 / 吃 / 鱼

这里有三个 token:

我
吃
鱼

但 token 不一定是一个字,也不一定是一个词。

它可以是:

类型

例子

一个汉字

我、吃、鱼

一个词语

喜欢、人工、智能

一个子词

un、happy

一个标点

,。!?

一个数字片段

123、45

一个特殊符号

[BOS][EOS][PAD]

一个字节片段

UTF-8 byte token

所以更准确地说:

token 是分词器切出来的、模型处理文本的基本单位。


5. Token ID:一个没有语义的编号

分词器的输出通常已经是一串 token id:

"我 / 今天 / 很 / 开心"
→ [2513, 4021, 892, 5567]

模型输出时,也通过同一张词表把 id 还原成文本:

3765 → 查词表 → "鱼"

5.1 Token ID 只是词表里的行号

token id 和学号、工号、门牌号是同一类东西。

例如:

学号 2024001 不代表比 2024002 聪明。
工号 A103 不代表比 A205 更重要。
token id 8821 不代表“猫”比 id 7610 的“狗”更高级。

5.2 编号大小、差值、顺序都没有语义

假设:

猫 → 8821
狗 → 7610
汽车 → 3205

那么:

8821 > 7610 不代表猫比狗“大”。
8821 - 7610 = 1211 不代表猫和狗的语义距离。
把猫的 id 改成 1、狗的 id 改成 99999,只要相关映射同步更新,模型能力理论上不变。

工程前提:

修改 token id 时,必须同步更新:
- tokenizer 词表
- embedding table
- LM Head 输出层
- special token 映射
- chat template
- stop token 配置
- 所有下游 decode 逻辑

否则可能出现:

生成 id 解码错误
stop token 失效
特殊 token 被当成普通文本
chat 模板错位

5.3 那语义在哪里?

答案:

在 embedding 里。

6. Embedding:把编号变成语义向量

模型不能直接用一个整数编号理解语义。

所以需要把 token id 转换成一个向量。

这个过程叫 embedding,中文常叫词嵌入。

例如:

token

token id

embedding

2513

[0.12, -0.44, 0.81, ..., 0.05]

1892

[-0.21, 0.33, 0.12, ..., 0.77]

3765

[0.55, 0.09, -0.62, ..., 0.18]

为了便于理解,下面用 4 维示意:

token

token id

embedding,4 维示意

8821

[0.8, 0.6, 0.1, 0.2]

7610

[0.7, 0.5, 0.2, 0.3]

汽车

3205

[-0.5, 0.1, 0.9, -0.3]

飞机

4472

[-0.4, 0.2, 0.8, -0.2]

观察:

猫的 id 是 8821,狗的 id 是 7610。
编号大小本身没有语义。

但看向量:
猫 [0.8, 0.6, 0.1, 0.2] 和狗 [0.7, 0.5, 0.2, 0.3] 很接近。
汽车和飞机也很接近。
但猫/狗 和 汽车/飞机 差得较远。

这就是 embedding 的核心意义:

把离散的 token 编号映射到连续的向量空间,
让语义相近的 token 在向量空间中距离更近,
让模型可以基于向量计算相似度、距离、方向、线性组合和注意力权重。

也就是说,embedding 不是为了“好看”,
而是为了让模型能够用数学方式表达和处理语义。

实际模型中的 embedding 维度通常很高,例如:

768
1024
2048
4096
5120
8192

6.1 如果没有 embedding,模型只能看到编号

如果没有 embedding,模型看到的是:

[2513, 1892, 3765]

这些整数之间没有明显语义关系。

有了 embedding 后,模型看到的是:

[向量A, 向量B, 向量C]

向量之间可以计算:

相似度
距离
方向
线性组合
注意力权重

这样模型才能学习:

“猫”和“狗”都是动物。
“吃”是一个动作。
“鱼”可以被吃。
“我”可能是动作发出者。

6.2 Embedding 表是怎么来的

模型内部有一个很大的表,叫 embedding table。

假设:

词表大小 = 50000
向量维度 = 768

那么 embedding table 的形状是:

50000 × 768

每一行对应一个 token 的向量。

一开始,这些向量通常是随机数。

此时:

“猫”和“狗”的向量可能离得很远。
“猫”和“汽车”的向量也可能离得很近。

完全没有稳定语义。

训练过程大致是:

  1. 给模型看一句文本:
今天天气很___
  1. 模型预测下一个 token。
  2. 正确答案可能是:
  3. 如果模型预测错了,就通过反向传播和梯度下降调整相关参数。
  4. 这个过程重复海量次数。

每一次“预测错了 → 调整参数”,都会让向量往更合理的方向挪一点点。

训练结束后,向量空间会自然形成一定语义结构。


6.3 关于“国王 - 男人 + 女人 ≈ 女王”

在早期静态词向量模型,例如 word2vec、GloVe 中,有一个经典例子:

国王 - 男人 + 女人 ≈ 女王

这说明向量之间不仅有距离关系,还可能存在方向关系。

但要注意:

这个例子来自早期词向量模型。
现代 LLM 的表示经过多层 Transformer 后是上下文相关的。
这种几何关系不一定在所有层、所有上下文中都稳定成立。

不过一个基本性质仍然重要:

语义相近的 token,其表示往往更接近。

这个“纠错 → 微调”的机制,在机器学习里叫:

反向传播 + 梯度下降

入门阶段可以先记住一句话:

猜错了就调,调足够多次,参数就逐渐变得有意义。

6.4 “苹果”问题:一个 token 只有一个初始 embedding,但语义会变

很多初学者会问:

“苹果”既可以表示水果,也可以表示手机。
那这个区别是在 embedding 阶段定义的吗?

答案是:

在最初的 Embedding Table 阶段,通常不是。
在后面的 Transformer 层中,才会真正区分上下文语义。

举例:

句子 A:
我吃了一个苹果

句子 B:
我买了一部苹果手机

在 Tokenizer 和 Embedding Table 中,两个“苹果”通常是同一个 token,也对应同一个初始 embedding 向量。

也就是说,在输入层,模型看到的“苹果”是一样的。

但是进入 Transformer 后,情况开始变化。

在句子 A 中:

“苹果”会和“吃”“一个”等 token 发生注意力交互。
“吃”这个动作会让“苹果”的表示更偏向食物、水果、可食用对象。

在句子 B 中:

“苹果”会和“买”“一部”“手机”等 token 发生注意力交互。
这些上下文会让“苹果”的表示更偏向电子产品、品牌、设备。

所以:

同一个 token,
在不同上下文里,
经过多层 Transformer 后,
最终得到的 hidden state 可以完全不同。

这就是现代大模型和早期静态词向量最大的区别之一。

早期 Word2Vec / GloVe:

一个词只有一个固定向量。
“苹果”只有一个向量,很难同时准确表达水果和手机。

现代 Transformer LLM:

token 的初始 embedding 只是起点。
经过 Attention 和 FFN 后,
token 的表示会结合上下文动态变化。

可以这样理解:

Embedding Table 给 token 一个“基础身份”。
Transformer 层根据上下文给 token 一个“具体角色”。

MLOps / RAG 视角
如果你要做语义检索,通常不应该直接拿最原始的 token embedding。
更常见的是使用专门的 embedding 模型输出的句向量,或者使用 LLM 最后一层 hidden state 经过池化、指令化后的表示。
因为真正携带完整语义的,往往不是输入层的 token embedding,而是经过上下文融合后的深层表示。

6.5 入门阶段的一句话总结

入门阶段可以先记住一句话:

Embedding 只是 token 的语义起点。
真正区分“苹果是水果还是手机”的,
是后面多层 Transformer 结合上下文得到的表示。

更多推荐