Large Language Model(LLM) Basic introduction

Large Language Model(LLM) Basic introduction

本教程通过追踪张量的形状变化,完整解析大语言模型从用户输入到生成输出的全过程。 - Batch Size (B): 32 - Sequence Length (T): 128 - Embedding Dimension (Dmodel): 512 - Number of Heads (H): 8 -...

LLM完整工作流程:从文本到文本

本教程通过追踪张量的形状变化,完整解析大语言模型从用户输入到生成输出的全过程。

基础维度设定

  • Batch Size (B): 32
  • Sequence Length (T): 128
  • Embedding Dimension (D_model): 512
  • Number of Heads (H): 8
  • Dimension per Head (D_head): 64
  • FFN Inner Dimension (D_ffn): 2048
  • Vocabulary Size (V): 10000

第一阶段:输入预处理

1. 分词 (Tokenization)

输入: "你好,世界!"

操作: 使用预训练分词器将字符串切分为tokens

输出: ['你好', ',', '世界', '!']

形状变化: StringList[String] (长度=4)

2. Token ID转换

操作: 将每个token映射到词汇表中的整数ID

输出: [5091, 101, 8224, 102]

形状变化: List[String]List[int] (长度=4)

3. 长度标准化

核心问题: 输入长度可变,但模型需要固定形状的张量

解决方案: - 填充 (Padding): 长度不足时,用特殊token [PAD] (通常ID=0)填充至固定长度 - 截断 (Truncation): 长度超出时,丢弃超出部分(或使用滑动窗口)

示例: [5091, 101, 8224, 102][5091, 101, 8224, 102, 0, 0, ..., 0]

形状变化: List[int] (长度=4) → Tensor (1, 128)

4. 嵌入 (Embedding)

将整数ID转换为稠密的浮点向量,这是从"符号"到"语义"的关键转换。

a. 词嵌入 (Token Embedding) - 权重矩阵: (V, D_model) = (10000, 512) - 操作: 查表,每个ID取出对应的512维向量 - 形状变化: (1, 128)(1, 128, 512)

b. 位置嵌入 (Positional Embedding) - 权重矩阵: (T, D_model) = (128, 512) - 操作: 为位置0~127各生成一个512维向量 - 形状: (1, 128, 512)

c. 融合 - 操作: x = token_emb + position_emb - 最终输出: (1, 128, 512)

此时,数据已准备好进入Transformer的第一个块


第二阶段:Transformer块处理

每个Transformer块由两个子层组成:多头自注意力 (MHA)前馈网络 (FFN)

输入形状: (32, 128, 512)

子层1:多头自注意力 (Multi-Head Attention)

步骤1:Layer Normalization

  • 形状: (32, 128, 512)(32, 128, 512) (形状不变)

步骤2:生成Q, K, V

线性投射 - 权重矩阵: (512, 1536) (一次性生成Q、K、V) - 形状变化: (32, 128, 512)(32, 128, 1536)

拆分为Q, K, V - 形状: 每个都是 (32, 128, 512)

步骤3:拆分多头

操作: 将512维拆分为8个头,每头64维 - view: (32, 128, 512)(32, 128, 8, 64) - transpose: (32, 128, 8, 64)(32, 8, 128, 64)

关键:现在有8个并行的"注意力专家",各自在64维空间观察整个序列

步骤4:缩放点积注意力

计算注意力分数: Q @ K^T - Q形状: (32, 8, 128, 64) - K^T形状: (32, 8, 64, 128) - 输出: (32, 8, 128, 128) — 这是注意力矩阵,(i,j)位置表示token i对token j的关注度

加权求和: softmax(scores) @ V - Softmax后: (32, 8, 128, 128) (形状不变,值转为概率) - V形状: (32, 8, 128, 64) - 输出: (32, 8, 128, 64) — 每个token融合了全序列的上下文信息

步骤5:合并多头

操作: - transpose: (32, 8, 128, 64)(32, 128, 8, 64) - reshape: (32, 128, 8, 64)(32, 128, 512)

最终线性投射 - 权重矩阵: (512, 512) - 输出: (32, 128, 512)

步骤6:残差连接

操作: x = x + attention_output

输出形状: (32, 128, 512)

子层2:前馈网络 (FFN)

步骤1:Layer Normalization

  • 形状: (32, 128, 512)(32, 128, 512)

步骤2:两层MLP

第一层:扩展 - 权重: (512, 2048) - 形状: (32, 128, 512)(32, 128, 2048) - 激活: GELU (形状不变)

第二层:收缩 - 权重: (2048, 512) - 形状: (32, 128, 2048)(32, 128, 512)

FFN让每个token在高维空间中独立"思考",然后提炼结果

步骤3:残差连接

操作: x = x + ffn_output

Transformer块最终输出: (32, 128, 512)


第三阶段:输出生成

经过N个Transformer块后,得到最终隐藏状态。

输入形状: (32, 128, 512) (称为 final_hidden_state)

1. Language Model Head

权重矩阵: (512, 10000) (与输入嵌入矩阵共享并转置)

操作: 线性变换,将语义空间映射回词汇空间

形状变化: (32, 128, 512)(32, 128, 10000)

解读: 对每个位置,得到词汇表中所有词的原始得分 (Logits)

2. Softmax归一化

操作: 对最后一维应用Softmax

形状: (32, 128, 10000)(32, 128, 10000) (值变为概率分布)

3. 解码/采样

聚焦: 只取输入序列最后一个有效位置的概率分布

示例: 输入4个tokens,取第4个位置的 (10000,) 概率向量

采样策略: - 贪心搜索: 选择概率最大的ID - 随机采样: 根据概率分布随机抽取(可用Temperature/Top-k/Top-p控制)

输出: 一个整数ID,如 6112

4. 反分词 (De-tokenization)

操作: 查询词汇表,ID → 文本

示例: 6112"模型"

5. 自回归生成循环

流程: 1. 将新生成的ID追加到输入序列 2. 将扩展后的序列作为新输入 3. 重复整个流程(嵌入 → Transformer → 解码) 4. 直到生成 [EOS] 标记或达到最大长度


完整数据流总结

文本 "你好,世界!"
    ↓ 分词
['你好', ',', '世界', '!']
    ↓ ID化
[5091, 101, 8224, 102]
    ↓ 填充
[5091, 101, 8224, 102, 0, 0, ..., 0]  → (1, 128)
    ↓ 嵌入
(1, 128, 512) ← 进入Transformer
    ↓ 
× N个Transformer块 (每块内部:MHA → FFN,始终保持 (B, T, D_model))
    ↓
(1, 128, 512) ← 最终隐藏状态
    ↓ LM Head
(1, 128, 10000) ← Logits
    ↓ Softmax
(1, 128, 10000) ← 概率分布
    ↓ 采样 (取最后一个位置)
6112 ← 单个ID
    ↓ 反分词
"模型"
    ↓ 追加到输入,循环生成
"你好,世界!模型正在..."

核心要点

  1. 形状一致性: Transformer块的输入输出始终保持 (B, T, D_model),这是残差连接和深度堆叠的基础

  2. 注意力的本质: (T, T) 的注意力矩阵捕获了序列中所有token对之间的关系

  3. 多头并行: 8个头在不同的64维子空间中独立学习不同类型的语义关系

  4. 自回归本质: 每次只生成一个token,然后将其作为下一次输入的一部分,逐步构建完整回复

Thanks for Reading

If this article was helpful to you, feel free to connect with me!