示例图:

1. 分词(Tokenization)

1.1 分词的作用以及过程

      将输入文字分割成模型输入的最小单位,即token。后续每个token会进行词嵌入转换成向量矩阵,然后通过Transformer架构不断预测下一个token,最终生成完整回答。

  • 过程: 输入文本(例如:"Hello world")被分词器(Tokenizer)切分成一个个片段。
  • 例子: "Hello world" → ["Hello", " world"]
  • 映射: 每个 Token 被映射为一个唯一的整数 ID(基于词表 Vocab)。
    • "Hello" → 15496
    • " world" → 3186
  • 输出: 一个整数序列 [15496, 3186]

举个中文分词的例子:

测试句子:“中华人民共和国成立于 1949 年。”

Qwen 分词器极有可能将其切分为 5 到 7 个 Token。以下是一种典型的切分方式:

Token 内容 推测 Token ID 说明
中华人民共和国 (例如 12345) 1 个 Token。这是高频专有名词,直接被收入词表。
成立 (例如 6789) 1 个 Token。高频动词。
(例如 101) 1 个 Token。高频介词。
1949 (例如 2024) 1 个 Token。数字通常整体编码。
(例如 303) 1 个 Token。高频量词。
(例如 505) 1 个 Token。标点符号。
1.2 为什么会分词成这个,背后使用的算法?

核心算法是 BPE(Byte Pair Encoding,字节对编码)

BPE 的核心思想非常简单:“出现频率最高的相邻字符对,优先合并”

分词器的构建过程(注意:是分词器的构建,不是大模型的训练),就是一个不断合并字符的过程。

模拟分词器构建过程:

假设我们的训练数据里有 1 亿篇中文新闻,其中“中华人民共和国”这个词出现了 500 万次。

  • 初始状态(Level 0): 所有文本都被拆成最小的单元(字节或字符)。 序列:['中', '华', '人', '民', '共', '和', '国', ...] 此时,每个字都是一个 Token。

  • 第 1 轮统计与合并: 算法统计所有相邻字符对的频率。

    • ('中', '华') 出现了 500 万次。
    • ('的', ' ') 出现了 800 万次。
    • ... 假设 ('的', ' ') 频率最高,它们先合并成 。 接着,('中', '华') 频率也很高,被合并成 中华。 现在序列变成了:['中华', '人', '民', '共', '和', '国', ...]
  • 第 2 轮统计与合并: 继续统计新的相邻对。

    • ('中华', '人') 现在出现了 500 万次(因为“中华”已经绑定了)。
    • 如果这个频率在当前剩余的对子里是最高的,它们就会合并成 中华人
    • 同理,('民', '共') 可能合并成 民共(如果频率够高)。
  • 第 N 轮合并(直到词表满): 这个过程不断重复。 中华人 +  → 中华人民 中华人民 + 共和国 → 中华人民共和国

注意:终止条件通常是词表的大小

最终结果: 如果在分词器构建结束前,这个合并链条没有被中断,且词表还有空间,那么 中华人民共和国 就会作为一个整体被写入词表(Vocab),分配一个唯一的 ID。

2. 嵌入(Embedding)

2.1 词嵌入

词嵌入将整数序列 [15496, 3186](即Token ID),通过嵌入矩阵(Embedding Table)映射为实数向量矩阵(如 768 维或 4096 维)。

嵌入矩阵(Embedding Table)矩阵里的数字初始是随机的。在训练过程中,通过反向传播不断更新,使得语义相近的词(如“猫”和“狗”),它们的向量在空间中的距离(余弦相似度)更近。训练完成后,矩阵参数固定,推理时直接查表使用。

推理时使用过程如下:

  • 查找: 模型有一个巨大的嵌入矩阵,形状为 (词表大小,向量维度),例如:词表 15 万,隐藏层维度 4096 → 矩阵大小 150,000 × 4,096
  • 操作: 根据输入的整数序列 ID 查表,取出对应的向量。
  • 输出: 形状为 (N, d_model) 的矩阵 X 。
    • N = 分词后Token 数量(整数序列长度)
    • d_model= 嵌入矩阵的向量维度(如 4096)
2.2 位置嵌入(Positional Encoding)

为什么需要位置编码?

Transformer 的核心是 Self-Attention(自注意力机制),自注意力机制是 排列不变(Permutation Invariant) 的。

数学角度:

注意:这里的QK相乘,会得到注意力分数矩阵,这个矩阵里面,每个token只关注自己所在的一行,如果没有位置信息,这个顺序你随意变换是没有任何影响的,有了位置信息你变换位置计算的注意力分数矩阵就会不一样。

直观案例:“猫吃鱼”vs“鱼吃猫”

假设我们有两个句子,词嵌入完全相同(没有位置编码):

  • 句子 1: [猫,吃,鱼]
  • 句子 2: [鱼,吃,猫]
  • 对于“吃”这个 Token,(仅针对这个字,其他字计算注意力机制是没有影响的):

    • 在句子 1 中,“吃”会去计算它和“猫”的相似度,以及它和“鱼”的相似度。
    • 在句子 2 中,“吃”依然会去计算它和“猫”的相似度,以及它和“鱼”的相似度。
    • 结果: “吃”这个词最终生成的向量表示(Output Vector)在两个句子里是完全一样的。
  • 对于“猫”这个 Token:

    • 在句子 1 中,“猫”是主语,它关注“吃”。
    • 在句子 2 中,“猫”是宾语,它依然关注“吃”(猫所在行的向量没变,相似度没变)。
    • 结果: 模型无法区分“猫”是施动者还是受动者。

后果: 模型认为 [猫,吃,鱼] 和 [鱼,吃,猫] 表达的是同一个意思。这显然违背了语言逻辑。

嵌入过程如下:

  • 操作: 给每一个token生成一个位置向量(如第 1 个词是 [1,0,0...] (维度和上面词嵌入的维度一直,1*4096维),第 2 个词是 [0,1,0...] 或更复杂的 RoPE 旋转编码)。
  • 融合(就是相加): 将位置向量加到输入嵌入向量 X 上,Hinput​=Etoken​+Epos​。
  • 结果:  输出相加结果Hinput(保持不变4096 维)。
  • 原理: 虽然向量混合了,但模型具有强大的拟合能力,可以在后续层中将“语义”和“位置”信息解耦(Disentangle)

位置编码方式有三个大方向:绝对位置编码、相对位置编码和旋转位置编码

编码方式 核心思想 实现方式 典型模型 外推能力
绝对位置编码 为每个位置赋予唯一向量,加到词嵌入上 正弦/余弦函数 或 可学习嵌入 Transformer, BERT, GPT 正弦函数可外推,可学习嵌入不可外推
相对位置编码 在注意力计算中引入相对位置偏置 可学习的相对嵌入或函数 Transformer-XL, Shaw et al. 通常可处理任意长序列
旋转位置编码 通过旋转矩阵使内积包含相对位置 对 Query/Key 进行二维旋转 LLaMA, PaLM, GPT-NeoX 良好外推性

3. Transformer架构训练以及底层原理

论文:Attention Is All You Need  arxiv.org/pdf/1706.03762https://arxiv.org/abs/1706.03762arxiv.org/pdf/1706.03762

matmul 是 Matrix Multiplication(矩阵乘法)

标准的 Transformer 分为 Encoder(编码器) 和 Decoder(解码器),但现代大模型(如 GPT, Llama)大多采用 Decoder-only 架构。

3.1 标准的 Transformer架构

1. 输入层

  • Tokenization:将文本切分为 Token(如 BPE, WordPiece)。

  • Embedding:将 Token 映射为稠密向量。

  • Position Embedding:注入位置信息。

2. Encoder (用于理解任务,如 BERT)

  • Masked Self-Attention:双向注意力,每个词可以看到所有词。

  • Feed-Forward Network (FFN):两个线性层中间加激活函数(GELU/ReLU),对每个位置独立处理,增加非线性能力。

  • 堆叠:通常 6-12 层。

3. Decoder (用于生成任务,如 GPT)

  • Masked Self-Attention (Causal Mask)关键区别。使用上三角矩阵掩码,确保位置 t 只能看到 0…t 的词,不能看到未来。这是自回归生成的基础。

  • Cross-Attention(仅 Encoder-Decoder 架构有):Decoder 的 Q 来自上一步输出,K, V 来自 Encoder 输出。用于机器翻译等任务。

  • FFN:同 Encoder。

4. 输出层

  • Linear Projection:将隐藏层向量映射到词表大小 (Vocab Size)。

  • Softmax:计算下一个 Token 的概率分布。

3.2 为什么现代 LLM 首选 Decoder-only 架构?

A. 任务天然契合(自回归生成)

LLM 的核心能力是 Chat(对话) 和 Completion(续写)

  • 这本质上是 P(xt​∣x<t​) 的概率预测问题。
  • Decoder 结构天然带有 Causal Mask(因果掩码),强制模型只能利用过去的信息预测未来,这完美契合“生成”任务。
  • Encoder-Decoder 架构虽然也能做生成,但它的优势在于“理解源序列 + 生成目标序列”(如翻译),对于单纯的对话任务,Encoder 部分显得冗余。

B. 推理效率与 KV Cache

  • Decoder-only:在推理时,每一个新生成的 Token,只需要计算一次 Attention。由于结构统一,KV Cache(键值缓存) 技术非常容易实现,极大地加速了推理。
  • Encoder-Decoder:推理时需要先跑一遍 Encoder 编码 Prompt,再跑 Decoder。虽然也可以缓存,但架构更复杂,显存管理更麻烦。

C. 扩展性(Scaling Laws)

  • 业界经验表明(如 GPT-3, Chinchilla 论文),在同样的参数量和数据量下,Decoder-only 架构在通用语言任务上的扩展性更好
  • 去掉 Encoder 和 Cross-Attention 层,减少了计算图的复杂度,使得在有限的显存下可以堆叠更多的层数或增加更大的隐藏层维度。

D. 训练数据的利用

  • Decoder-only 可以直接在原始文本上进行训练(从左读到右)。
  • Encoder-Decoder 通常需要构造“输入 - 输出”对(例如把一句话的前半段当输入,后半段当输出),数据处理相对麻烦一些(虽然也可以做,但 Decoder-only 更直接)。
3.3 大模型Decoder-only 架构训练和推理过程

答案的核心在于 因果掩码(Causal Mask) 和 并行计算

下面我将基于你的例子 ["I", "love", "AI", "<eos>"],一步步拆解训练时的前向传播(Forward)、**损失计算(Loss)反向传播(Backward)**全过程。


第一步:数据构造(Input & Label)

训练的本质是监督学习。我们需要构造“题目”和“答案”。

  • Input (题目)[I, love, AI, <eos>]
    • 这是模型实际看到的输入。
  • Label (答案)[love, AI, <eos>, <pad>]
    • 这是我们希望模型输出的目标。
    • 注意:Label 是 Input 向右移动一位。
    • <pad>:最后一个位置没有“下一个词”了,所以通常计算 Loss 时会忽略这个位置。

第二步:前向传播(Forward Pass)—— 并行计算

这是最关键的一步。虽然推理是串行的,但训练是并行的。模型一次性处理整个序列。

1. Embedding 层

模型首先将 Input 中的每个 Token 转化为向量,并加上位置编码。 X=[I,love,AI,<eos>​] 此时,X 是一个 4×dmodel​ 的矩阵(假设序列长度 4,隐藏层维度 dmodel​ )。

2. Self-Attention 层(核心魔法)

模型计算 Q, K, V。如前所述,它们都来自 X 。 Q=XWQ​,K=XWK​,V=XWV​

接下来计算注意力分数 QKT 。此时得到一个 4×4 的矩阵。如果没有掩码,每个词都能看到所有词。 为了模拟“只能看过去”,我们应用 Causal Mask(因果掩码)

掩码矩阵示意(1 表示可见,0 表示屏蔽):

查询 \ 键 I (1) love (2) AI (3) <eos> (4)
I (1) 1 0 0 0
love (2) 1 1 0 0
AI (3) 1 1 1 0
<eos> (4) 1 1 1 1
  • 第 1 行 (对应 "I"):只能看到 "I"。
    • 任务:基于 "I" 的信息,预测下一个词。
    • 目标:对应 Label 的第 1 位 "love"
  • 第 2 行 (对应 "love"):能看到 "I" 和 "love"。
    • 任务:基于 "I love" 的上下文,预测下一个词。
    • 目标:对应 Label 的第 2 位 "AI"
  • 第 3 行 (对应 "AI"):能看到 "I", "love", "AI"。
    • 任务:基于 "I love AI" 的上下文,预测下一个词。
    • 目标:对应 Label 的第 3 位 "<eos>"
  • 第 4 行 (对应 "<eos>"):能看到全部。
    • 任务:预测结束后的词。
    • 目标:对应 Label 的第 4 位 "<pad>"(通常忽略 Loss)。

经过 Softmax 和加权 V 后,模型输出了 4 个隐藏状态向量 H=[h1​,h2​,h3​,h4​] 。

  • h1​ 包含了预测 "love" 所需的信息。
  • h2​ 包含了预测 "AI" 所需的信息。
  • h3​ 包含了预测 "<eos>" 所需的信息。

3. 输出层 (Logits)

最后,通过线性层将隐藏状态映射到词表大小(假设词表只有 5 个词方便理解): Logits=H⋅Wout​ 输出形状为 4×VocabSize 。这意味着模型同时给出了 4 个位置的预测概率分布。


第三步:损失计算(Loss Calculation)

现在模型输出了 4 组概率,我们需要检查它猜得对不对。

  • 位置 1 预测:模型认为下一个词是 "love" 的概率是 P1​ 。
    • 真实 Label"love"
    • Loss 1:交叉熵损失 −log(P1​) 。
  • 位置 2 预测:模型认为下一个词是 "AI" 的概率是 P2​ 。
    • 真实 Label"AI"
    • Loss 2:交叉熵损失 −log(P2​) 。
  • 位置 3 预测:模型认为下一个词是 "<eos>" 的概率是 P3​ 。
    • 真实 Label"<eos>"
    • Loss 3:交叉熵损失 −log(P3​) 。
  • 位置 4 预测:忽略(因为 Label 是 <pad>,没有实际意义)。

总 Loss: Total Loss=3Loss1​+Loss2​+Loss3​​

关键点: 通过一次前向传播,我们实际上完成了 3 个训练样本 的学习:

  1. 样本 1: Context="I", Target="love"
  2. 样本 2: Context="I love", Target="AI"
  3. 样本 3: Context="I love AI", Target="<eos>"

这就是为什么 Transformer 训练效率比 RNN 高得多的原因:RNN 必须串行算 3 次,Transformer 并行算 1 次。


第四步:反向传播(Backward Pass)

  1. 计算梯度:根据 Total Loss,计算损失函数对每个参数的梯度(∂W∂Loss​ )。
    • 这包括 WQ​,WK​,WV​ (注意力权重)。
    • 包括 WFFN​ (前馈网络权重)。
    • 包括 Wout​ (输出层权重)。
    • 包括 Embedding 权重。
  2. 更新参数:使用优化器(如 AdamW)更新参数。 Wnew​=Wold​−LearningRate×Gradient
  3. 效果
    • 如果位置 1 没预测对 "love",梯度会传回,调整 WQ​,WK​ 等,让 "I" 的向量表示在未来能更倾向于关联到 "love"。
    • 如果位置 2 没预测对 "AI",梯度会调整模型,让 "I love" 的上下文组合能更倾向于 "AI"。

第五步:为什么训练时“并行”不影响推理时“串行”?

这是最容易混淆的地方。

  • 训练时:我们用 Mask 强行模拟了“只能看过去”的环境。模型参数 W 学到的规律是:“在只能看到前文的情况下,下一个词最可能是什么”。
  • 推理时:我们真的只给模型前文(例如只给 "I"),模型根据训练好的 W ,算出下一个词 "love"。然后把 "I love" 再喂进去,算出 "AI"。
  • 一致性:因为训练时 Mask 的存在,模型从来没有见过未来,所以它在推理时即使真的没有未来数据,也能正常工作。如果训练时不用 Mask,模型就会“作弊”偷看答案,推理时一旦没有答案可看,模型就废了。

总结图解

训练步骤全景图 (基于 "I love AI <eos>")

1. 输入:[I, love, AI, <eos>]
          |      |      |       |
          v      v      v       v
2. 并行计算 (带 Mask):
   - 位置 1 (I)    : 只关注 [I]          --> 输出分布 1 --> 对比 Label "love"
   - 位置 2 (love) : 只关注 [I, love]    --> 输出分布 2 --> 对比 Label "AI"
   - 位置 3 (AI)   : 只关注 [I, love, AI]--> 输出分布 3 --> 对比 Label "<eos>"
   - 位置 4 (<eos>): 只关注 [全部]       --> 输出分布 4 --> 忽略 (Label <pad>)

3. 计算 Loss:
   Loss = (Loss_1 + Loss_2 + Loss_3) / 3

4. 反向传播:
   更新所有权重 (Q, K, V, FFN...),让下次预测更准。

通过这种方式,Decoder-only 模型在海量文本上不断重复这个过程,最终学会了语言的语法、逻辑和世界知识。

    4、生成token以及结束

    大模型(LLM)的生成过程(推理/Inference)与训练过程截然不同。训练是并行的(一次性看全文),而生成是串行的(一个字一个字蹦)。

    以下详细解析 生成流程核心优化(KV Cache)采样策略 以及 停止条件


    4.1 大模型生成流程(Step-by-Step)

    生成过程是一个 自回归(Autoregressive) 的循环。假设用户输入提示词(Prompt):"请介绍一下"

    1. 预处理(Prefill 阶段)

    • Tokenization:将 Prompt "请介绍一下" 切分为 Token 序列,例如 [请,介绍,一,下]

    • 一次性计算:模型将这 4 个 Token 一次性 输入网络。

    • KV Cache 初始化:计算这 4 个 Token 对应的所有层的 Key (K) 和 Value (V) 向量,并缓存到显存中。

    • 输出 Logits:模型输出第 5 个位置(下一个 Token)的概率分布。

    2. 采样(Sampling)

    • 模型输出的是一个概率分布(例如:“了”30%,“人工智能”20%,“中国”10%...)。

    • 根据采样策略(见下文),从中选出一个 Token,比如 "人工智能"

    3. 循环生成(Decoding 阶段)

    • 追加:将选出的 "人工智能" 追加到序列末尾。

    • 新输入:此时序列变为 [请,介绍,一,下,人工智能]

    • 增量计算

      • 关键点:不需要重新计算前 4 个 Token 的 Q/K/V!直接从 KV Cache 中读取。

      • 只计算新 Token "人工智能" 的 Q/K/V。

      • 新 Token 的 Q 与 Cache 中所有旧的 K 做注意力计算。

    • 输出:预测下一个 Token(例如 "。")。

    • 重复:重复“追加 -> 计算 -> 采样”的过程,直到触发停止条件。


    4.2 核心优化:KV Cache(为什么生成这么快?)

    如果没有 KV Cache,每生成一个新字,都要把前面所有字重新算一遍,速度会极慢(O(N2) )。

    步骤

    输入序列

    计算内容

    显存操作

    第 1 轮

    [A, B, C]

    计算 A, B, C 的 K, V

    缓存 K_A, K_B, K_C...

    第 2 轮

    [A, B, C, D]

    只计算 D 的 K, V

    读取缓存,追加 K_D...

    第 3 轮

    [A, B, C, D, E]

    只计算 E 的 K, V

    读取缓存,追加 K_E...

    • 原理:因为前面的 Token 不变,它们的 K 和 V 向量也不变。

    • 收益:将每次生成的计算量从 O(N2) 降低到 O(N) ,显存占用随长度线性增加。


    4.3 采样策略(如何决定下一个词?)

    模型输出的是概率,选哪个词决定了生成的质量。

    1. 贪婪搜索 (Greedy Search)

    • 规则:永远选概率最大的那个词(Top-1)。

    • 缺点:容易重复、枯燥,缺乏创造性。

      • :一直生成“的的的的..."。

    2. 随机采样 (Random Sampling)

    • 规则:按概率分布随机抽取。

    • 缺点:可能抽到低概率的乱码。

    3. 温度采样 (Temperature)

    • 规则:在 Softmax 之前,将 Logits 除以温度系数 T 。

      • T<1 (如 0.1):概率分布更尖锐,模型更保守、确定。

      • T>1 (如 1.5):概率分布更平滑,模型更随机、有创造性。

      • T=1 :原始分布。

    4. Top-K 采样

    • 规则:只在概率最高的 K 个词中采样(例如只在前 50 个词里选)。

    • 作用:过滤掉低概率的垃圾词。

    5. Top-P (Nucleus Sampling) —— 最常用

    • 规则:动态选择词。累加概率,直到达到阈值 P (如 0.9)。

      • 如果前 3 个词概率和就超过 0.9,只在这 3 个里选。

      • 如果前 100 个词概率和才到 0.9,就在这 100 个里选。

    • 作用:比 Top-K 更灵活,兼顾了确定性和多样性。


    4.4 什么时候停止生成?(停止条件)

    生成不会无限进行下去,通常由以下条件触发停止(满足任一即可):

    1. 预测出结束符 (EOS Token) —— 自然停止

    • 原理:模型在训练时学习了 <eos> (End of Sequence) token。当模型认为“话说完了”,它会预测出这个 token。

    • 表现:一旦生成 <eos>,系统立即截断,不再继续。

    • 示例

      • 用户:“今天天气不错”

      • 模型:“是的,适合出门。<eos>" -> 停止

    2. 达到最大长度限制 (Max Tokens / Max Length) —— 强制停止

    • 原理:为了防止模型死循环、节省算力或控制成本,系统会设置一个硬上限。

    • 表现:如果生成了 2048 个 Token 还没出现 <eos>,强制切断。

    • 后果:回答可能没说完,句子截断。

    3. 遇到特定停止字符串 (Stop Sequences) —— 用户定义

    • 原理:用户或系统指定某些字符串作为停止信号。

    • 场景

      • 多轮对话:设置 "User:" 为停止符。模型生成完回答后,一旦检测到它想生成 "User:"(模拟用户说话),立即停止,把控制权交还给用户。

      • 代码生成:设置 "\n\n" 或 "}" 为停止符,防止模型生成无关的注释。

    4. 内容安全拦截 (Safety Filter)

    • 原理:在生成过程中或生成后,安全系统检测到违规内容(色情、暴力、政治敏感)。

    • 表现:立即中断生成,并替换为“我无法回答该问题”等标准回复。

    5. 时间或预算超时 (Timeout / Budget)

    • 原理:API 调用设置的时间限制(如 30 秒)或 Token 预算耗尽。


    4.5 生成过程全景图解
    用户输入:[请,介绍,一,下]
                  |
                  v
    +-----------------------------+
    | 1. Prefill (预填充)          |
    |    - 计算所有层的 KV Cache    |
    |    - 输出第 1 个 Logits       |
    +-----------------------------+
                  |
                  v
    +-----------------------------+
    | 2. 采样 (Sampling)           |
    |    - 应用 Temperature/Top-P  |
    |    - 选出 Token: "Python"     |
    +-----------------------------+
                  |
                  v
    +-----------------------------+
    | 3. 检查停止条件              |
    |    - 是 <eos> 吗?No          |
    |    - 达到 Max Length 吗?No    |
    |    - 命中 Stop String 吗?No   |
    +-----------------------------+
                  |
                  v
    +-----------------------------+
    | 4. 更新 KV Cache             |
    |    - 追加 "Python" 的 K, V    |
    +-----------------------------+
                  |
                  +------> 回到步骤 2 (预测下一个词)
    4.6 总结
    1. 生成是串行的:一个字一个字生成,依赖之前的所有历史(通过 KV Cache 加速)。

    2. 停止主要由 EOS 控制:模型自己决定什么时候说完(预测 <eos>)。

    3. 系统限制是兜底:Max Length 防止无限生成,Stop Strings 用于控制对话轮次。

    4. 采样决定风格:Temperature 和 Top-P 决定了模型是“严谨”还是“发散”。

    理解了这个流程,你就明白了为什么大模型有时候会**“话没说完就停了”(触发了 Max Length),或者“一直在重复”**(采样策略 Temperature 太低或模型陷入循环)。

    Logo

    AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

    更多推荐