大模型内部架构地图
# 推理大模型——哪里是哪里 欢迎来纠错
> **目的**: 给你一张地图,标清楚模型内部每个部件的位置和作用
> **不碰**: 训练数据
> **只做**: 结构示例,告诉你"那里是那里"
---
## 地图总览
```
你输入一句话: "9.11和9.9哪个大?"
│
▼
┌─ 分词器 (Tokenizer) ─┐
│ "9.11" → [24, 13, 11]│ ← ❶ 这里把文字变成数字
│ "和" → [456] │
│ "9.9" → [24, 13, 9] │
│ "哪个大" → [789, 234]│
└──────────┬───────────┘
│ 一串数字 [24,13,11,456,24,13,9,789,234]
▼
┌─ 嵌入层 (Embedding) ─┐
│ 每个数字 → 一个向量 │ ← ❷ 这里把数字变成"意义"
│ 24 → [0.12, -0.34, ..]│ 每个向量有几千维
│ (查表,不是计算) │
└──────────┬───────────┘
│ 一组向量
▼
┌─────────────────────────────────────┐
│ Transformer 层 × N(重复60-120次) │ ← ❸ 核心计算区
│ │
│ 每一层做两件事: │
│ │
│ ┌─ 自注意力 (Attention) ──────┐ │
│ │ "这个9.11和那个9.9有什么 │ │ ← ❸a 看上下文
│ │ 关系?让我看看周围..." │ │ 每个词看其他所有词
│ │ │ │ 计算"谁跟谁相关"
│ │ Q: 我在找什么? │ │
│ │ K: 我身上有什么标签? │ │
│ │ V: 我实际携带什么信息? │ │
│ └────────────────────────────┘ │
│ │
│ ┌─ 前馈网络 (FFN/MoE) ──────┐ │
│ │ 对每个位置独立"思考" │ │ ← ❸b 处理信息
│ │ │ │ 这里存储着"知识"
│ │ 普通模型: 一个大矩阵运算 │ │
│ │ MoE模型: 选2-8个专家来算 │ │
│ └────────────────────────────┘ │
│ │
└──────────┬──────────────────────────┘
│ 处理完的向量
▼
┌─ 输出头 (LM Head) ──────┐
│ 向量 → 词表概率分布 │ ← ❹ 选下一个词
│ "9" → 概率 0.001 │
│ "9.9" → 概率 0.85 │ 概率最高的就是答案
│ "9.11" → 概率 0.12 │
└──────────┬───────────────┘
│
▼
输出: "9.9"
```
---
## ❶ 分词器——嘴巴
**位置**: 模型外部,最前面
**作用**: 把人类文字切成模型能吃的"碎片"(token)
**类比**: 嚼碎食物
```
示例:
"Hello World" → ["Hello", " World"] → [15496, 2159]
"你好世界" → ["你好", "世界"] → [57668, 99834]
"def func():" → ["def", " func", "(", "):", ] → [755, 2163, 7, 1680]
```
**关键细节**:
- 模型不认识"字",只认识"编号"
- 同一个词在不同位置可能编号不同
- 常见词是一个token,罕见词会被拆成多个
- 词表大小: 通常 32K - 128K 个 token
---
## ❷ 嵌入层——翻译官
**位置**: 第一层,分词器后面
**作用**: 把编号变成向量(一串浮点数)
**类比**: 查字典,给每个词一个"位置坐标"
```
token编号 15496 ("Hello") → 查表 → [0.023, -0.156, 0.089, ..., 0.034]
↑ 这个向量有 4096~16384 维
相似的词,向量距离近:
"cat" → [0.82, 0.14, ...]
"dog" → [0.79, 0.18, ...] ← 距离近!
"car" → [-0.31, 0.67, ...] ← 距离远!
```
**位置编码也在这里**:
```
token向量 + 位置向量 = 最终输入
"I"在第1位: [0.1, 0.2, ...] + [sin(1), cos(1), ...] = [0.94, 0.74, ...]
"I"在第5位: [0.1, 0.2, ...] + [sin(5), cos(5), ...] = [-0.86, 0.48, ...]
↑ 同一个词不同位置,向量不同!
```
- **RoPE** (旋转位置编码): 主流方案,用旋转矩阵编码位置
- 这就是为什么 "I love you" 和 "you love I" 有不同含义
---
## ❸a 自注意力——眼睛
**位置**: 每个 Transformer 层的前半部分
**作用**: 让每个词"看到"所有其他词,计算谁跟谁相关
**类比**: 一个人在人群中环顾四周,评估每个人跟自己的关系
```
输入: "猫 坐在 垫子 上"
Q(猫): "我在找什么?" → [0.3, 0.7, ...]
K(坐在): "我身上的标签" → [0.1, 0.6, ...]
K(垫子): "我身上的标签" → [0.8, 0.2, ...]
注意力分数:
猫→坐在: Q(猫)·K(坐在) = 0.85 ← 高!猫和"坐在"强相关
猫→垫子: Q(猫)·K(垫子) = 0.42 ← 中等
猫→上: Q(猫)·K(上) = 0.11 ← 低
Softmax后:
猫→坐在: 0.55
猫→垫子: 0.35
猫→上: 0.10
最终: 猫的新表示 = 0.55×V(坐在) + 0.35×V(垫子) + 0.10×V(上)
↑ "猫"现在融合了上下文信息
```
### 多头注意力——多只眼睛
```
头1: 关注语法关系 "猫"→"坐在"(谁做了什么动作)
头2: 关注位置关系 "垫子"→"上"(什么在什么上面)
头3: 关注语义关系 "猫"→"垫子"(猫和垫子的关联)
...
头64: 关注其他模式
```
- 每个头看到的"视角"不同
- 最后把所有头的结果拼接起来
### GQA / MLA——省内存的眼睛
```
标准: 64个Q头, 64个K头, 64个V头 → KV缓存很大
GQA: 64个Q头, 8个K头, 8个V头 → KV缓存减少8倍
MLA: 64个Q头, 共享压缩的KV → KV缓存减少90%+
```
---
## ❸b 前馈网络 / MoE——大脑
**位置**: 每个 Transformer 层的后半部分
**作用**: 存储知识,做"思考"
**类比**: 图书馆 + 思考室
### 普通 FFN
```
输入向量 x (4096维)
│
├──→ 上投影矩阵 W_up (4096→11008) ═══╗
│ ╠═ 逐元素相乘 → 中间结果 (11008维)
├──→ 门控矩阵 W_gate (4096→11008) ═══╝
│
└──→ [中间结果] → 下投影矩阵 W_down (11008→4096) → 输出 (4096维)
知识存在哪里?就在 W_up, W_gate, W_down 这三个矩阵里!
每个矩阵是几千万到几亿个浮点数。
```
### MoE (专家混合)
```
输入向量 x
│
▼
┌─ 路由器 (Router) ─┐
│ "这个问题属于哪个 │
│ 领域?" │
│ │
│ 专家1(数学): 0.05 │
│ 专家2(代码): 0.01 │ ← 打分
│ 专家3(语言): 0.82 │ ← 最高!
│ 专家4(逻辑): 0.12 │
│ ... │
│ 专家256: 0.00 │
└────────┬──────────┘
│ 选 top-8
▼
┌──────────────────────┐
│ 激活专家3(语言) ×0.82 │ ← 每个专家就是一个独立的FFN
│ 激活专家4(逻辑) ×0.12 │
│ (其他254个专家休眠) │ ← 不激活 = 不消耗算力
└──────────┬───────────┘
│ 加权求和
▼
输出向量
为什么MoE厉害?
671B总参数,但每次只用37B → 算力节省18倍
相当于有256个专家团队,每次只请最相关的8个来干活
```
---
## ❹ 输出头——嘴巴
**位置**: 最后一层
**作用**: 把向量变回词
**类比**: 从一堆候选词中选一个说出来
```
最后一层的输出向量 (4096维)
│
▼
┌─ LM Head (线性层) ─┐
│ 4096维 → 128000维 │ ← 映射到词表大小
│ 每个位置是一个词的 │
│ "得分" │
└────────┬───────────┘
│
▼
┌─ Softmax ──────────┐
│ 得分 → 概率 │
│ "the": 0.23 │
│ "a": 0.15 │
│ "cat": 0.08 │
│ "9.9": 0.85 │ ← 最高概率
│ ... │
└────────┬───────────┘
│
▼
┌─ 采样策略 ─────────┐
│ temperature=0: 选最高│ ← 贪心,确定性
│ temperature=0.7: 加点│ ← 有创造性
│ 随机在高概率词中选 │
│ top_p=0.9: 只从前90% │ ← 核采样
│ 概率的词中选 │
└────────┬───────────┘
│
▼
输出 token → 解码回文字
```
---
## ❺ 推理模型的额外部件——思考回路
**位置**: 不是新硬件,是新的"使用方式"
**作用**: 让模型在回答前先"想一想"
```
普通模型:
问题 → [一次通过所有层] → 答案
推理模型:
问题 → [通过所有层] → <think> → [再通过所有层] → "让我想想"
→ [再通过所有层] → "首先..."
→ [再通过所有层] → "然后..."
→ [再通过所有层] → "等等,这不对" ← 自我纠错!
→ [再通过所有层] → "换个方法..."
→ [再通过所有层] → </think>
→ [再通过所有层] → "答案是42"
每生成一个token,都要完整跑一遍所有层!
想得越久(token越多)= 消耗越多算力 = 答案越好
```
### 训练出来的"思考能力"(不是编程的,是学出来的)
```
强化学习过程:
第1轮: 模型瞎猜 → 答错 → 奖励=0
第2轮: 模型试着多写几步 → 偶然答对 → 奖励=1 → "记住刚才的方式!"
第3轮: 模型开始模仿成功的模式 → 答对更多 → 奖励更高
...
第N轮: 模型自己发现了"分步思考"→"检查"→"纠错"的模式 → 稳定高分
没有人教它怎么思考。它自己发现思考能拿高分。
```
---
## ❻ KV Cache——记忆缓冲区
**位置**: GPU显存中
**作用**: 缓存已处理token的Key和Value,避免重复计算
**类比**: 做笔记,不用每次都从头想
```
生成第1个token时:
处理所有输入 → 计算K1,V1 → 存入Cache → 输出token1
生成第2个token时:
不需要重新算之前的K,V!
只算新token的Q → 和Cache中的K,V做Attention → 输出token2
把新K,V也存入Cache
生成第1000个token时:
Cache中已有999个token的K,V
只算1个新token的Q → 和999个K,V做Attention
Cache大小 = 层数 × 2(K和V) × 头数 × 头维度 × 序列长度
80层 × 2 × 8(GQA) × 128 × 128000 = 约20GB!
这就是为什么长对话这么耗显存。
```
---
## ❼ 权重文件——模型的"身体"
**位置**: 磁盘上的文件
**作用**: 存储所有参数(矩阵中的数字)
```
以GGUF文件为例 (你的Daredevil 7.95GB):
文件结构:
┌─ GGUF Header ──────────────────┐
│ magic: "GGUF" │
│ version: 3 │
│ tensor_count: 292 │ ← 292个张量
│ metadata_kv_count: 26 │
├─ Metadata ─────────────────────┤
│ general.architecture: "llama" │
│ llama.block_count: 32 │ ← 32层
│ llama.attention.head_count: 32 │ ← 32个注意力头
│ llama.embedding_length: 4096 │ ← 嵌入维度4096
│ tokenizer.ggml.tokens: [...] │ ← 词表
├─ Tensor Data ──────────────────┤
│ token_embd.weight [128256×4096] Q8_0 ← ❷嵌入层
│ blk.0.attn_q.weight [4096×4096] Q8_0 ← ❸a Q矩阵 第0层
│ blk.0.attn_k.weight [1024×4096] Q8_0 ← ❸a K矩阵 (GQA,8头)
│ blk.0.attn_v.weight [1024×4096] Q8_0 ← ❸a V矩阵
│ blk.0.attn_output.weight [4096×4096] Q8_0 ← ❸a 输出投影
│ blk.0.ffn_gate.weight [14336×4096] Q8_0 ← ❸b 门控
│ blk.0.ffn_up.weight [14336×4096] Q8_0 ← ❸b 上投影
│ blk.0.ffn_down.weight [4096×14336] Q8_0 ← ❸b 下投影
│ blk.0.attn_norm.weight [4096] F32 ← LayerNorm
│ blk.0.ffn_norm.weight [4096] F32
│ ... ← 重复32层
│ blk.31.attn_q.weight ... ← 最后一层
│ output_norm.weight [4096] F32 ← 最终归一化
│ output.weight [128256×4096] Q8_0 ← ❹输出头
└────────────────────────────────┘
每个 Q8_0 值 = 8位整数 + 缩放因子
原始 FP16: 0.0234375 → Q8_0: 量化为整数6, 缩放0.00390625
还原: 6 × 0.00390625 ≈ 0.0234 → 几乎无损!
```
---
## ❽ 完整前向传播追踪——一个token的一生
```
你输入: "天空是什么颜色?" → 模型输出"蓝"的完整路径:
1. 分词: "天空是什么颜色?" → [22656, 25001, 73199, ...]
↑ 假设5个token
2. 嵌入: 5个token → 5个4096维向量
+ RoPE位置编码
3. 第0层:
3a. LayerNorm → 归一化
3b. Self-Attention:
- Q = x × W_q (5个4096维 → 5个4096维)
- K = x × W_k (5个4096维 → 5个1024维) ← GQA, 8个KV头
- V = x × W_v (5个4096维 → 5个1024维)
- Scores = Q × Kᵀ / √128 (5×5 矩阵)
- Weights = Softmax(Scores + CausalMask) ← 因果掩码:只能看前面
- Output = Weights × V
- 缓存K,V到KV Cache
3c. 残差连接: output = x + attention_output
3d. LayerNorm → 归一化
3e. FFN:
- gate = x × W_gate → SiLU激活
- up = x × W_up
- mid = gate ⊙ up ← 逐元素相乘
- down = mid × W_down
3f. 残差连接: output = x + ffn_output
4. 第1-31层: 重复步骤3
5. 最终LayerNorm
6. LM Head: 4096维 → 128256维概率分布
7. 采样: 概率最高的token编号 → 28810 → 解码 → "蓝"
总计算量:
一个token需要: ~160亿次浮点运算 (8B模型)
GPT-4级别: ~3500亿次浮点运算
```
---
## ❾ 各部分的参数占比——谁最大?
```
以 Llama 3.1 8B 为例:
参数量 占比 作用
嵌入层(Embedding): 525M (0.5B) 6.4% 词→向量
注意力层(Q,K,V,O): 2,147M (2.1B) 26.2% 看上下文
FFN层(gate,up,down): 4,718M (4.7B) 57.5% 存知识、做计算 ← 最大!
LayerNorm: 0.26M ~0% 归一化
输出头(LM Head): 525M 6.4% 向量→词
RoPE(位置): 0 0% 公式计算,无参数
─────────────────────────────────────────
总计: ~8.2B 100%
结论: FFN/MoE 占了 57.5%,是模型的"大脑"
Attention 占了 26.2%,是模型的"眼睛"
嵌入层+输出头 占了 12.8%,是"嘴巴"
```
---
## ❿ 一张完整的地图
```
┌──────────────────────────────────────────────────────────────┐
│ 推理大模型全貌 │
│ │
│ ┌─────────┐ ┌─────────────────────────────────────────┐ │
│ │ ❶分词器 │ │ GPU 显存 │ │
│ │ (嘴巴) │ │ │ │
│ │ 文字→数字│ │ ┌────── ❻ KV Cache (记忆) ──────────┐ │ │
│ └────┬────┘ │ │ K₁V₁ K₂V₂ K₃V₃ ... K_nV_n │ │ │
│ │ │ │ 随着对话变长,越来越大 │ │ │
│ ▼ │ └────────────────────────────────────┘ │ │
│ ┌─────────┐ │ │ │
│ │ ❷嵌入层 │ │ ┌─────────────────────────────────┐ │ │
│ │ (翻译官) │ │ │ ❸ Transformer × N层 │ │ │
│ │数字→向量│ │ │ ┌──────────────────────────┐ │ │ │
│ └────┬────┘ │ │ │ ❸a 自注意力 (眼睛) 26% │ │ │ │
│ │ │ │ │ Q×Kᵀ→Softmax→×V │ │ │ │
│ ▼ │ │ │ 多头/GQA/MLA │ │ │ │
│ ┌─────────┐ │ │ └──────────────────────────┘ │ │ │
│ │ 送入GPU │ │ │ ┌──────────────────────────┐ │ │ │
│ │ │───▶│ │ │ ❸b FFN/MoE (大脑) 57% │ │ │ │
│ └─────────┘ │ │ │ 知识存储 + 信息处理 │ │ │ │
│ │ │ │ MoE: 256专家选8个 │ │ │ │
│ │ │ └──────────────────────────┘ │ │ │
│ │ │ × 32~120层 │ │ │
│ │ └─────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────┐ │ │
│ ┌─────────┐ │ │ ❹ 输出头 (嘴巴) 6% │ │ │
│ │ 解码输出│◀───│ │ 向量→概率→采样→token │ │ │
│ │ │ │ └─────────────────────────────────┘ │ │
│ └────┬────┘ │ │ │
│ │ └─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ ❺ 推理模型专属: 思考回路 │ │
│ │ │ │
│ │ 输出token │ │
│ │ │ │ │
│ │ ├─ 如果是<think>: 继续生成思考token │ │
│ │ │ ↑ │ │
│ │ │ └── 每个token都完整跑一遍❸ │ │
│ │ │ "想"得越多 = 算力消耗越大 │ │
│ │ │ 可以自我纠错和回溯 │ │
│ │ │ │ │
│ │ └─ 如果是</think>: 开始输出正式答案 │ │
│ └──────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ ❼ 权重文件 (磁盘上) │ │
│ │ GGUF / SafeTensors / PyTorch │ │
│ │ 292个张量,每个是一个大矩阵 │ │
│ │ 加载到GPU显存后才能运算 │ │
│ └──────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
```
---
## 一句话版本
| 编号 | 部件 | 一句话 | 占比 |
|------|------|--------|------|
| ❶ | 分词器 | 把文字切成碎片编号 | 0% (规则,无参数) |
| ❷ | 嵌入层 | 查表把编号变成向量 | 6.4% |
| ❸a | 自注意力 | 每个词看周围所有词 | 26.2% |
| ❸b | FFN/MoE | 存知识、做计算 | **57.5%** |
| ❹ | 输出头 | 选下一个最可能的词 | 6.4% |
| ❺ | 思考回路 | 生成更多token来"想" | 0% (复用❸) |
| ❻ | KV Cache | 缓存历史避免重算 | 动态增长 |
| ❼ | 权重文件 | 所有参数的存储格式 | = 模型大小 |
**记住这一句**: 模型的57%是FFN(存知识),26%是注意力(看关系),剩下的是进出口。推理模型不是新架构,只是学会了多跑几圈❸。
---
*没有黑箱,只有懒得看的人。*
*神经风暴图谱*
更多推荐
所有评论(0)