大模型基础扫盲------从文本到 Token、Embedding、QKV 与 Transformer 大模型处理文本的全流程解析(一)
大模型基础扫盲------从文本到 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 |
|
| 查词表编号 |
| 词嵌入 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 内部的核心运算包括:
| 运算 | 例子 |
|---|---|
| 矩阵乘法 |
|
| 向量加法 | 残差连接 |
| 逐元素乘法 | 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 为例,大致流程是:
- 先把语料拆成最小单位
- 统计相邻片段出现频率
- 把最高频的相邻片段合并成新 token
- 重复合并,直到词表达到预设大小
例如语料中经常出现:
鲈 鱼
那么 鲈 和 鱼 可能被合并成:
鲈鱼
并分配一个新 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 |
| 一个特殊符号 |
|
| 一个字节片段 | 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 |
|
| 吃 | 1892 |
|
| 鱼 | 3765 |
|
为了便于理解,下面用 4 维示意:
| token | token id | embedding,4 维示意 |
|---|---|---|
| 猫 | 8821 |
|
| 狗 | 7610 |
|
| 汽车 | 3205 |
|
| 飞机 | 4472 |
|
观察:
猫的 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 的向量。
一开始,这些向量通常是随机数。
此时:
“猫”和“狗”的向量可能离得很远。
“猫”和“汽车”的向量也可能离得很近。
完全没有稳定语义。
训练过程大致是:
- 给模型看一句文本:
今天天气很___
- 模型预测下一个 token。
- 正确答案可能是:
-
好 - 如果模型预测错了,就通过反向传播和梯度下降调整相关参数。
- 这个过程重复海量次数。
每一次“预测错了 → 调整参数”,都会让向量往更合理的方向挪一点点。
训练结束后,向量空间会自然形成一定语义结构。
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 结合上下文得到的表示。更多推荐
所有评论(0)