📅 2026-07-17 | 🏷️ Python · AI Agent 方向 | ⏱️ 建议 5-6h(今日内容最硬核,顶住!) | 🎯 覆盖大模型面试 90% 的八股


📌 今日知识地图

大模型面试就考这七块,今天全部拿下:

LLM 面试知识全景
│
├── 模块一:NLP 基础回顾 & Tokenization
│   └── BPE / WordPiece / SentencePiece 的区别
│
├── 模块二:Transformer 完整拆解(面试最核心!)
│   ├── Self-Attention 数学推导(Q·K^T/√dk · Softmax · V)
│   ├── Multi-Head Attention → MQA → GQA 演进
│   ├── 位置编码:Sinusoidal → RoPE(旋转位置编码)
│   ├── FFN + LayerNorm(Pre-Norm vs Post-Norm)
│   └── 残差连接 & 为什么 Transformer 能堆这么深?
│
├── 模块三:三种架构对比
│   └── Encoder-Only(BERT) vs Decoder-Only(GPT) vs Encoder-Decoder(T5)
│
├── 模块四:训练全流程
│   ├── Pre-training → SFT → RLHF(PPO) → DPO
│   └── DeepSeek GRPO(组相对策略优化)
│
├── 模块五:推理优化
│   ├── KV Cache、Flash Attention
│   └── 量化:GPTQ / AWQ / GGUF
│
├── 模块六:解码策略 & MoE
│   └── Greedy / Beam Search / Top-K / Top-P / Temperature + MoE 架构
│
└── 模块七:评估体系
    └── MMLU / HumanEval / AgentBench / LLM-as-a-Judge

模块一:NLP 基础回顾 & Tokenization

1.1 Tokenization 是什么?为什么重要?

LLM 不能直接理解文本,需要把文本切成小块(token),再映射为数字 ID。同一个意思的不同切分方式,会直接影响模型效果和成本。

1.2 三种主流分词算法

算法 全称 原理 代表模型 特点
BPE Byte Pair Encoding 从字符级开始,统计最高频的相邻符号对并合并,直到达到目标词表大小 GPT 系列 最通用,英文效果好
WordPiece 类似 BPE,但合并时用语言模型似然度而非频率来选择合并哪一对 BERT 对英文更友好,中文需预处理
SentencePiece 直接在**原始文本(含空格)**上训练,把空格也当作普通字符处理。底层可用 BPE 或 Unigram LLaMA, Mistral, T5 语言无关,中文日文直接可用
# 直观感受 tokenization(需要 pip install tiktoken)
import tiktoken

enc = tiktoken.get_encoding("cl100k_base")  # GPT-4 的编码器
text = "Hello, AI Agent 开发!"

tokens = enc.encode(text)
print(f"文本: {text}")
print(f"Token IDs: {tokens}")
print(f"Token 数: {len(tokens)}")
# 中文通常 1-2 个字符 = 1 个 token,英文约 0.75 个词 = 1 个 token

# 解码回去
print(enc.decode(tokens))  # Hello, AI Agent 开发!

面试话术:“BPE 和 WordPiece 的核心区别在于合并策略——BPE 选频率最高的对,WordPiece 选对语言模型似然度提升最大的对。SentencePiece 最大的创新是把空格当作普通字符,这让它天然支持所有语言。LLaMA 用 SentencePiece + BPE 的组合。Tokenization 对中文不友好(中文词表覆盖不全导致某些词被切成多个 token),多语言 Agent 开发时需要注意 token 成本差异。”

1.3 Attention 出现之前:RNN/LSTM 为什么不行?

问题 原因 Transformer 怎么解决的
长距离依赖 RNN 串行传递,信息在长序列中逐渐衰减(梯度消失) Self-Attention 每个 token 直接跟所有 token 交互,O(1) 路径长度
无法并行 RNN 必须按时间步串行计算,t 时刻依赖 t-1 时刻 Transformer 所有位置同时计算,矩阵乘法天然并行
计算效率 LSTM 有 4 个门,参数量大 Multi-Head Attention 虽然也是 O(n²),但 GPU 矩阵乘法极度优化

模块二:Transformer 完整拆解(面试最核心的 30 分钟)

2.1 整体结构:一句话说清楚

Transformer = N 个相同的 Block 堆叠,每个 Block = Multi-Head Self-Attention + Feed-Forward Network,两者都带残差连接和 Layer Normalization。

输入 Token 序列
    │
    ▼
Input Embedding + Positional Encoding   ← 词向量 + 位置信息
    │
    ▼
┌─────────────────────────────┐
│  Multi-Head Self-Attention  │  ← 每个 token 看所有 token
│        + Add & Norm         │  ← 残差连接 + LayerNorm
├─────────────────────────────┤
│  Feed-Forward Network       │  ← 位置独立的全连接
│        + Add & Norm         │
└─────────────────────────────┘
    │
    ▼  × N 层(GPT-3: 96层, LLaMA-70B: 80层)
    │
    ▼
Linear + Softmax → 下一个 token 的概率分布

2.2 Self-Attention 数学推导(面试手写题!)

这是整个 LLM 面试最核心的一道题,面试官直接说"推导一下 Self-Attention"。

第一步:从输入到 Q、K、V

输入矩阵 X ∈ R^(n×d_model)   ← n 个 token,每个 d_model 维

Q = X · W_Q    (Query:  我要找什么?)
K = X · W_K    (Key:    我能提供什么?)
V = X · W_V    (Value:  我实际有什么内容?)

W_Q, W_K, W_V ∈ R^(d_model × d_k)   ← 可学习的权重矩阵

第二步:计算注意力分数

Scores = Q · K^T           ← 每对 token 之间的相似度
Scores ∈ R^(n×n)           ← 第 i 行第 j 列 = token_i 对 token_j 的关注度

第三步:缩放 + Softmax

Attention(Q, K, V) = softmax( Q·K^T / √d_k ) · V
                            ↑            ↑       ↑
                         相似度矩阵   缩放因子  加权求和

为什么要除以 √d_k?
  → d_k 越大,Q·K^T 的点积值越大 → Softmax 梯度趋近于 0(饱和区)
  → 除以 √d_k 把方差控制回 1,让 Softmax 保持在梯度敏感区域
import numpy as np

def self_attention(X, W_Q, W_K, W_V, mask=None):
    """
    手写 Self-Attention(单头)——面试常考!
    X:    (n, d_model) — n 个 token
    W_Q, W_K, W_V: (d_model, d_k)
    """
    # 1. 投影得到 Q, K, V
    Q = X @ W_Q   # (n, d_k)
    K = X @ W_K   # (n, d_k)
    V = X @ W_V   # (n, d_k)

    # 2. 计算注意力分数,缩放
    d_k = Q.shape[-1]
    scores = Q @ K.T / np.sqrt(d_k)   # (n, n)

    # 3. Causal Mask(GPT 的自回归遮罩)
    if mask is not None:
        scores = scores + mask  # mask 中 0 的位置正常,-inf 的位置 Softmax 后为 0

    # 4. Softmax + 加权求和
    attn_weights = np.exp(scores) / np.sum(np.exp(scores), axis=-1, keepdims=True)
    output = attn_weights @ V   # (n, d_k)

    return output, attn_weights

# Causal Mask 示例(每个 token 只能看到自己及之前的 token)
def causal_mask(n):
    mask = np.tril(np.ones((n, n)))  # 下三角全 1
    mask[mask == 0] = -np.inf
    mask[mask == 1] = 0
    return mask

2.3 Multi-Head Attention → MQA → GQA 演进(面试必考对比题!)

为什么需要 Multi-Head?
  → 单头只能学到一种"关注模式"(比如关注动词)
  → Multi-Head 让不同头关注不同维度(语法、语义、位置、共指...)
  → 每个头有自己独立的 W_Q, W_K, W_V
  → 所有头的输出拼接后再投影:Concat(head_1, ..., head_h) · W_O
机制 全称 Q 头数 K/V 头数 原理 代表模型
MHA Multi-Head Attention H H 每个头独立 QKV 原始 Transformer, BERT, GPT-3
MQA Multi-Query Attention H 1 所有 Q 头共享同一组 KV PaLM
GQA Grouped-Query Attention H G(1 < G < H) Q 头分组,每组共享一组 KV LLaMA 2/3, Mistral
直观对比(8 个 Q 头为例):

MHA:  Q₁Q₂Q₃Q₄Q₅Q₆Q₇Q₈  →  每组独立 KV₁~KV₈
MQA:  Q₁Q₂Q₃Q₄Q₅Q₆Q₇Q₈  →  全部共享一组 KV
GQA:  Q₁Q₂ | Q₃Q₄ | Q₅Q₆ | Q₇Q₈  →  4 组,每组共享一组 KV

                    参数量       内存(KV Cache)    效果
MHA  ████████████  最多(最大)   最多(最大)      最好
GQA  ██████        中等         中等            接近 MHA
MQA  ███           最少         最少            略差于 GQA

面试话术:“GQA 是目前大模型的事实标准——它在 MHA 的效果和 MQA 的效率之间找到了平衡点。KV Cache 是推理时的内存瓶颈(尤其在长序列场景),GQA 把 KV 头数从 H 降到 G,KV Cache 等比例减小。LLaMA 2 70B 用 GQA 后,KV Cache 内存直接降到 MHA 的 1/8。”

2.4 位置编码:从 Sinusoidal 到 RoPE

为什么需要位置编码?

Self-Attention 本身是置换等变的——打乱输入顺序,输出也等比例打乱。"我打你"和"你打我"在 Attention 眼里是同一组词。位置编码就是给每个位置注入位置信息。

位置编码 原理 优点 缺点 代表模型
Sinusoidal 用 sin/cos 函数手动计算 无需学习、可外推 表达能力有限 原始 Transformer
Learned 每个位置一个可学习向量 灵活 不支持比训练时更长的序列 BERT, GPT-2
RoPE 通过旋转矩阵编码相对位置 支持长度外推 + 天然编码相对位置 实现复杂 LLaMA, Qwen, DeepSeek

RoPE(旋转位置编码)— 当前主流,面试极高频!

RoPE 的核心思想:不把位置加到 Embedding 上,而是通过旋转变换把位置信息融入 Q 和 K 的内积中,使得两个 token 的注意力分数天然依赖它们的相对位置。

import torch

def apply_rope(x, theta=10000.0):
    """
    对输入 x 应用 RoPE 旋转位置编码
    x: (seq_len, d_model) — 实际实现更复杂,这里展示核心思路
    """
    seq_len, d = x.shape
    # 频率:高频分量旋转快(近处敏感),低频旋转慢(远处也有关联)
    freqs = 1.0 / (theta ** (torch.arange(0, d, 2) / d))  # (d/2,)

    # 每个位置的角度
    positions = torch.arange(seq_len).unsqueeze(1)  # (seq_len, 1)
    angles = positions * freqs.unsqueeze(0)          # (seq_len, d/2)

    # 旋转:将每对相邻维度视为 (cos, sin) × (real, imag)
    x_reshaped = x.float().reshape(seq_len, -1, 2)  # (seq_len, d/2, 2)
    cos, sin = angles.cos(), angles.sin()

    # 复数旋转:(a+bi) * (cos+isin) = (a·cos - b·sin) + i(a·sin + b·cos)
    rotated = torch.stack([
        x_reshaped[..., 0] * cos - x_reshaped[..., 1] * sin,
        x_reshaped[..., 0] * sin + x_reshaped[..., 1] * cos,
    ], dim=-1).flatten(start_dim=1)

    return rotated.type_as(x)

# RoPE 关键性质(面试背下来!)
# ① q_m · k_n = f(x_m, x_n, m - n) — 注意力只依赖相对位置,不依赖绝对位置
# ② 训练时长度 L,推理时能外推到更长的序列(通过调整 theta 或 NTK-aware 缩放)

面试追问:“RoPE 为什么能外推到更长的序列?”

回答:“因为 RoPE 编码的是相对位置关系。训练时模型学到的是一定频率范围内的旋转规律。推理时遇到更长序列,通过 NTK-aware 缩放在不改变高频分辨率的前提下扩展低频覆盖范围(调整 theta/base),模型就能泛化到训练时没见过的长度。”

2.5 FFN 和 LayerNorm

# FFN(Feed-Forward Network)
# 每个 token 位置独立应用同一个 FFN(位置独立 = 可以并行)
# FFN(x) = GELU(x · W₁ + b₁) · W₂ + b₂
# 隐藏层通常是 d_model 的 4 倍(LLaMA 中用 SwiGLU 代替 ReLU/GELU)

# SwiGLU(LLaMA / PaLM 使用)
# SwiGLU(x) = (x·W₁ ⊙ SiLU(x·W_g)) · W₂
# ⊙ 是逐元素乘法,SiLU 也叫 Swish 激活函数
# 效果比 ReLU/GELU 好,但多了 1/3 的参数量
LayerNorm 位置 顺序 代表模型 特点
Post-Norm Attention → Add → Norm 原始 Transformer, ViT 训练不稳定,需要 warm-up
Pre-Norm Norm → Attention → Add GPT-3, LLaMA 训练稳定,堆再深也不炸

面试话术:“Pre-Norm 是现在大模型的主流选择。它把 LayerNorm 放在 Attention 之前(而非之后),让残差路径保持’干净’的恒等映射,梯度可以直接从顶层传到底层。这就是为什么 GPT-3 能堆 96 层不崩——Pre-Norm 的梯度流比 Post-Norm 好得多。”


模块三:三种架构对比

架构 代表模型 Attention 方式 适合任务 为什么
Encoder-Only BERT, RoBERTa 双向(每个 token 看整句) 理解(分类、NER、抽取) 上下文全可见 = 最佳理解
Decoder-Only GPT 系列, LLaMA 单向/Causal(只看左边) 生成(对话、续写) 自回归生成天然匹配
Encoder-Decoder T5, BART Encoder 双向 + Decoder 单向交叉 翻译、摘要 编码+生成分离

为什么 GPT 系列选择 Decoder-Only?

Decoder-Only 架构统一了训练和推理的范式:训练时用 Causal Mask 做 Next Token Prediction,推理时也是逐个生成——训练和推理完全一致,没有 gap。而 Encoder-Decoder 在生成任务上需要"强行适配"。In-Context Learning 和 Few-Shot 的能力也是在 Decoder-Only 上最先涌现的。


模块四:训练全流程

4.1 一条线串起来

Pre-training(预训练)
  ↓  互联网海量文本,Next Token Prediction
  ↓  目标:学会语言的基本规律、知识、推理
  ↓  产出:Base Model(基座模型)
  ↓
SFT(监督微调,Supervised Fine-Tuning)
  ↓  高质量对话数据(人工标注),学习对话格式和指令遵循
  ↓  目标:学会"问答"的格式和风格
  ↓  产出:SFT Model(指令微调模型)
  ↓
RLHF(人类反馈强化学习)
  ↓  让模型学会人类的偏好:有用、无害、诚实
  ↓  三阶段:SFT → Reward Model → PPO
  ↓  产出:Chat Model(对齐后的对话模型)
  ↓
DPO / ORPO / GRPO(更高效的替代方案)
  ↓  不用单独训练 Reward Model,直接用偏好数据优化

4.2 RLHF 三阶段详解(面试必考!)

阶段 1 — SFT(Supervised Fine-Tuning)
  │  人工写"问题-答案"对,用对话格式微调基座模型
  │  目的:让模型学会"问答"这个形式
  │
  ▼
阶段 2 — Reward Model(奖励模型训练)
  │  同一个 prompt,收集多个不同的回复
  │  人工对回复进行排序(A > B > C > D)
  │  用排序数据训练 Reward Model,预测哪个回复更好
  │  损失函数(Bradley-Terry 模型):
  │    loss = -log(σ(r(x, y_winner) - r(x, y_loser)))
  │                     ↑
  │            让胜出的回复得分更高
  │
  ▼
阶段 3 — PPO(近端策略优化)
  │  用 Reward Model 打分,更新 SFT 模型的参数
  │  PPO 核心目标:
  │    max E[reward - β·KL(π_new || π_old)]
  │              ↑        ↑
  │          奖励最大化   不要偏离 SFT 太远(防止 Reward Hacking)
  │
  │  KL 惩罚项是关键!没有它模型会学出各种奇葩输出(全是感叹号、重复、阿谀奉承)
# RLHF 三阶段最简模拟(帮助理解流程)

# 阶段 1 — SFT(简化表示)
sft_model = train_sft(base_model, human_demonstrations)

# 阶段 2 — Reward Model
def reward_model_loss(prompt, winner_reply, loser_reply):
    r_winner = reward_model(prompt, winner_reply)  # 胜者得分
    r_loser  = reward_model(prompt, loser_reply)   # 败者得分
    # Bradley-Terry 损失
    return -torch.log(torch.sigmoid(r_winner - r_loser))

reward_model = train_reward_model(comparison_data)

# 阶段 3 — PPO
for prompt in prompts:
    # 当前模型生成回复
    reply = sft_model.generate(prompt)

    # Reward Model 打分
    reward = reward_model(prompt, reply)

    # KL 惩罚:防止偏离 SFT 太远
    kl_penalty = KL(sft_model.logits, reference_model.logits)

    # PPO 最终优化目标
    loss = -(reward - beta * kl_penalty)  # beta 控制 KL 惩罚强度

    sft_model.backward(loss)

4.3 DPO(直接偏好优化)— 干掉 Reward Model

DPO 的核心洞见:不需要单独训练 Reward Model,可以直接从偏好数据中更新策略。

RLHF:  数据 → Reward Model → PPO → 更新策略(三步)
DPO:   数据 → 直接更新策略(一步!)

DPO 的数学思想:
  将 Bradley-Terry 模型中的 reward 用最优策略表示出来,
  代入 PPO 的解析解,推导出一个只依赖 π(策略)和 π_ref(参考策略)的损失函数。

DPO 损失:
  loss = -log σ( β·[log(π(y_win|x)/π_ref(y_win|x)) - log(π(y_lose|x)/π_ref(y_lose|x))] )
                   ↑
         让胜出回复的概率相对提升,败者概率相对下降
方法 Reward Model 训练复杂度 稳定性 数据需求 代表模型
RLHF / PPO 需要单独训练 高(三步) 不稳定(需要调 β、clip 等) 偏好对 InstructGPT, ChatGPT
DPO 不需要 中(一步) 较好 偏好对 Mistral, Zephyr
GRPO 不需要 分组对比 DeepSeek R1

4.4 DeepSeek GRPO(组相对策略优化)

GRPO 的核心创新:扔掉 Reward Model,也扔掉参考策略!

传统 RLHF: 每个回复由 Reward Model 给出绝对分数
GRPO:      对同一个 prompt 生成 G 个回复,组内互相对比

过程:
1. 对每个 prompt,用旧策略 π_old 生成 G 个回复
2. 用预定义的规则(正确性、格式、语言一致性等)给这 G 个回复打分
3. 计算组内均值和标准差,归一化 → "相对优势"(Advantage)
4. 用相对优势更新策略

Advantage_i = (r_i - mean(r_1..r_G)) / std(r_1..r_G)

为什么 GRPO 是突破?
  → 不需要 Reward Model(省掉最贵、最不稳定的一步)
  → 组内对比天然消除了 prompt 难度差异的偏差
  → 规则打分可解释、可调试(vs Reward Model 的黑盒)
  → DeepSeek R1 论文中证明纯 RL 也能涌现出 Chain-of-Thought 推理能力

模块五:推理优化

5.1 KV Cache — 为什么生成 token 不用重算 Attention?

生成 "我 爱 你" 三个 token 的过程:

生成 "我": 输入 ["<BOS>"] → 计算 K₁, V₁ → 输出 "我"
生成 "爱": 输入 ["<BOS>", "我"]
    ↓ 如果不做 KV Cache,每次都要重算前面所有 token 的 K 和 V
    ↓ 用 KV Cache:K₁, V₁ 已经算过,存起来直接用,只算新 token "我" 的 K₂, V₂

KV Cache 作用:
  → 把 Self-Attention 从 O(n²·d) 降到 O(n·d),n 是序列长度
  → 代价:存储所有历史 K, V,显存占用 = 2 × n_layers × n × d_k

显存计算示例(LLaMA 2 7B,batch=1,seq_len=2048):
  模型权重:7B × 2 bytes (FP16) = 14 GB
  KV Cache:2 × 32(layers) × 2048 × 4096 × 2 bytes ≈ 1 GB
  总计 ≈ 15 GB  →  24GB 显卡刚好

5.2 Flash Attention — 不存完整 Attention Matrix

标准 Attention 的问题:
  QK^T 产生 (n×n) 的中间矩阵 → 写入 HBM(显存)→ 读回 → Softmax → 写回 → 读回

Flash Attention 的解决方案:
  → 分块(Tiling):把 Q,K,V 切成小块,每块在 SRAM(速度比 HBM 快 10x)里完成
  → 重计算(Recomputation):反向传播时不存完整 attention matrix,现算 Softmax
  → 结果:显存从 O(n²) 降到 O(n),速度提升 2-4x,数学上完全等价

支持 Flash Attention 的库:flash-attn (Dao-AILab)、xformers (Meta)、PyTorch 2.0+ 内置

5.3 量化:让大模型跑在消费级显卡上

方法 精度 原理 代表工具 适用场景
GPTQ INT4/INT8 逐层量化,基于 Hessian 矩阵补偿误差 AutoGPTQ GPU 推理
AWQ INT4 只量化 99% 的权重,保留 1% 的关键通道为 FP16 AutoAWQ GPU 推理(比 GPTQ 更快)
GGUF INT4/INT5/INT8 CPU 友好的量化格式,支持 offload 到 GPU llama.cpp 本地 CPU/混合推理
BitsAndBytes INT4/INT8/NF4 QLoRA 的底座,NF4 是非均匀量化 transformers 微调 + 推理

面试话术:“GPTQ 和 AWQ 的区别:GPTQ 一次性量化所有层并做误差补偿,AWQ 发现只有 1% 的关键权重通道对精度影响巨大,所以只量化 99% 的权重,保留关键通道为 FP16。AWQ 通常比 GPTQ 更快且效果更好。GGUF 是 llama.cpp 生态的格式,核心优势是 CPU 推理和大模型在消费级硬件上的运行。”


模块六:解码策略 & MoE

6.1 解码策略对比

策略 做法 优点 缺点 适用场景
Greedy 每次选概率最高的 token 快,确定性强 容易重复、缺少创意 翻译、代码生成
Beam Search 保留 Top-K 条路径,最后选总分最高的 全局更优 多样性差,计算量大 翻译
Top-K 每次只从概率最高的 K 个候选里采样 过滤低概率噪音 K 值难界定 通用生成
Top-P (Nucleus) 按概率从高到低累加,直到总和 ≥ p,只从这个集合里采样 动态调整候选数 对话、创意写作首选
Temperature Softmax 前除以 τ:τ 越大越随机,τ 越小越确定 简单,控制"创造性" 仅改变分布,不限制候选集 结合 Top-K/Top-P 使用
def top_p_sampling(logits, p=0.9, temperature=1.0):
    """Top-P (Nucleus) Sampling"""
    logits = logits / temperature
    probs = torch.softmax(logits, dim=-1)

    # 按概率降序排列
    sorted_probs, sorted_indices = torch.sort(probs, descending=True)
    cumsum = torch.cumsum(sorted_probs, dim=-1)

    # 找到累积概率超过 p 的 cutoff
    cutoff_idx = (cumsum > p).nonzero()[0].item() + 1

    # 截断低概率 token,重新归一化
    sorted_probs[cutoff_idx:] = 0
    sorted_probs /= sorted_probs.sum()

    # 采样
    sampled_idx = torch.multinomial(sorted_probs, 1).item()
    return sorted_indices[sampled_idx].item()

6.2 MoE(混合专家模型)

传统 Dense 模型:
  → 每个 token 激活所有参数
  → 模型越大 → 计算量线性增长

MoE 模型:
  → 将 FFN 层替换为多个"专家"(Expert),每个专家是一个独立的 FFN
  → 每个 token 只激活 Top-K 个专家(通常 K=2)
  → 通过 Router(门控网络)决定每个 token 发给哪几个专家

结构:
  x ─→ Router ─→ [Expert 1]──────────┐
       (softmax)  [Expert 2] ── 未激活  ├─→ 加权和 → 输出
                  [Expert 3]──────────┘
                  [Expert 4] ── 未激活

  Router 输出:每个 token 对各专家的权重,选 Top-K 个
  最终输出 = Σ Router_weight_i × Expert_i(x)
MoE 关键 说明
稀疏激活 只激活 Top-K 专家 → 参数量巨大但计算量可控
负载均衡 需要辅助损失(Load Balancing Loss),防止所有 token 都选同一专家
DeepSeek MoE 用了更细粒度的专家 + 共享专家(Shared Expert),在 671B 总参数下只激活 37B
代表模型 Mixtral 8×7B、DeepSeek V3/R1、GPT-4(未证实但广泛猜测)

面试话术:“MoE 的核心是’稀疏激活’——用巨大的总参数量换取每个 token 有限的计算量。Mixtral 8×7B 总参数 46.7B,但每个 token 只激活 12.9B。DeepSeek V3 把这一思路推到极致:671B 参数只激活 37B。MoE 的工程挑战是 Expert Parallelism(不同专家分布在不同的 GPU 上)和负载均衡(避免热门专家成为瓶颈)。”


模块七:评估体系

7.1 主要 Benchmark

Benchmark 评测什么 题型 意义
MMLU 57 个学科的知识水平 四选一 “这个模型有多博学?”
HumanEval Python 代码生成(164 题) 写代码 → 跑测试 “这个模型能过 LeetCode 吗?”
GSM8K 小学数学应用题 解题步骤 “这个模型会做数学吗?”
BBH 23 个 BIG-Bench 难任务 多样 “这个模型的推理极限在哪?”
AgentBench Agent 任务执行 多步交互 “这个模型能当 Agent 的大脑吗?”
SWE-bench 真实 GitHub Issue 修复 理解代码库 + 写 patch “这个模型能做软件工程吗?”
AlpacaEval 对话质量(人类偏好) LLM-as-Judge 对比 GPT-4 “这个模型聊得好不好?”

7.2 LLM-as-a-Judge

传统的 BLEU/ROUGE 为什么不适用?
  → BLEU 看 n-gram 重叠,"我爱你" vs "我恨你" 得分一样高
  → 对语义完全不敏感

LLM-as-a-Judge:
  → 用一个更强的模型(通常是 GPT-4)来评判回复质量
  → 评估维度:有用性(Helpfulness)、无害性(Harmlessness)、诚实性(Honesty)
  → 问题:位置偏见(Position Bias,更偏好排前面的回复)、长度偏见(Verbosity Bias)
  → 缓解:双向比较(交换位置再评一次,看结果是否一致)

面试题精选(10 道)

Q1. 推导 Self-Attention,为什么除以 √d_k?(字节/阿里/百度 高频)

标准回答

Self-Attention = softmax(Q·K^T / √d_k) · V

  • Q, K, V 由输入 X 分别经过 W_Q, W_K, W_V 投影得到
  • Q·K^T 得到注意力分数矩阵,第 i 行第 j 列 = token_i 对 token_j 的原始关注度

为什么除以 √d_k? 假设 Q 和 K 的每个元素独立同分布(均值为 0,方差为 1),则 Q·K 的每个元素 = d_k 个独立项的乘积和,方差 = d_k。d_k 大时,点积值进入 Softmax 饱和区,梯度趋近于 0。除以 √d_k 将方差缩放回 1,让 Softmax 保持在梯度敏感区域。本质上是一个数值稳定性技巧。

Q2. MHA、MQA、GQA 有什么区别?现在主流用哪个?(腾讯/字节)

标准回答

三者都是 Multi-Head Attention 的变体,区别在于 KV 头的共享方式:

  • MHA(标准多头注意力):每个 Q 头对应独立的 KV 头 — 效果最好,但 KV Cache 占用最大
  • MQA(多查询注意力):所有 Q 头共享一组 KV — 最省显存,但效果略差
  • GQA(分组查询注意力):Q 头分组,每组共享一组 KV — 当前事实标准,在效果和效率间取得最优平衡

LLaMA 2/3、Mistral、DeepSeek 都采用 GQA。GQA 把 KV Cache 缩小到原来的 G/H(如 G=4, H=32 时降为 1/8),这对长序列推理和 batch 推理至关重要。

Q3. RLHF 的三个阶段?KL 惩罚项为什么重要?(字节/阿里)

标准回答

RLHF 三阶段:① SFT(用人工示范对话做监督微调,学会对话格式);② Reward Model 训练(同一 prompt 多个回复,人工排序,用 Bradley-Terry 损失训练打分模型);③ PPO(用 Reward Model 打分 + KL 惩罚约束,优化 SFT 模型的参数)。

KL 惩罚项 = β·KL(π_new || π_old),防止新策略偏离 SFT 太远。没有 KL 惩罚 → Reward Hacking:模型学会用"奇技淫巧"(全是感叹号、过度殷勤、重复)骗过 Reward Model 拿高分,但输出质量反而下降。KL 惩罚是 RLHF 有效的关键约束。

Q4. DPO 和 PPO 有什么区别?(阿里/腾讯)

标准回答

  • PPO:需要单独训练 Reward Model,然后用 RL(PPO 算法)更新策略,三步流程,超参数敏感(β、clip 范围、学习率等)
  • DPO:直接将偏好数据转化为策略损失函数,无需 Reward Model,一步到位。数学上通过 Bradley-Terry 模型 + PPO 解析解推导出只依赖 π 和 π_ref 的损失

DPO 更简单、更稳定,但纯 DPO 在需要探索的复杂任务上可能不如 RL(因为有 off-policy 探索)。当前趋势是 GRPO(DeepSeek)——连参考策略都不需要,组内对比打分,纯 RL 也能涌现推理能力。

Q5. RoPE 位置编码的原理?为什么比 Sinusoidal 好?(字节/DeepSeek)

标准回答

RoPE 通过旋转变换将位置信息编码到 Q 和 K 中,使得 Q_m·K_n 只依赖 (m-n) 相对位置。不同维度旋转频率不同(高频捕获近处关系,低频捕获远处关系)。

相比 Sinusoidal:① RoPE 天然编码相对位置(Sinusoidal 是绝对位置);② RoPE 支持长度外推(训练 2K,推理可到 32K+,通过 NTK-aware 或调整 base frequency);③ RoPE 在长距离依赖任务上表现显著更好。

Q6. KV Cache 是什么?为什么推理时需要它?(腾讯/字节)

标准回答

KV Cache 是推理时缓存历史 token 的 Key 和 Value 矩阵,避免在生成每个新 token 时重新计算。没有 KV Cache,每次生成新 token 要重算整个序列的 Attention → O(n²·d)。有 KV Cache,只需计算新 token 的 Q/K/V,旧 token 的 K/V 直接复用 → 降为 O(n·d)。

代价是显存占用。长序列场景中 KV Cache 可能超过模型权重本身。GQA(减少 KV 头数)和 Flash Attention(减少中间矩阵存储)是缓解 KV Cache 压力的两个主要方向。

Q7. Flash Attention 解决了什么问题?(阿里/字节)

标准回答

标准 Attention 的瓶颈不在计算量,而在显存带宽——QK^T 产生的 n×n 矩阵需要从 HBM 读写多次。Flash Attention 用两个技术突破:① 分块(Tiling):把 Q/K/V 切成小矩阵块,每块在 SRAM 中完成计算,避免频繁的 HBM 读写;② 重计算(Recomputation):反向传播时现场重算 Softmax,不存储完整注意力矩阵。

效果:显存从 O(n²) 降到 O(n),速度提升 2-4 倍,数学上完全等价,不是近似。

Q8. MoE 的核心思想?DeepSeek 的 MoE 有什么特别之处?(字节/DeepSeek)

标准回答

MoE 的核心是稀疏激活:用 Router(门控网络)为每个 token 选择 Top-K 个 Expert(独立 FFN),只激活被选中的专家。参数量巨大但每个 token 的计算可控。

DeepSeek MoE 的创新:① 更细粒度的专家(小 Expert 更灵活组合);② 引入共享专家(Shared Expert),所有 token 都会经过,捕获通用知识;③ 辅助负载均衡损失(Auxiliary Loss-Free),通过动态偏置调整路由而非直接加损失惩罚。DeepSeek V3 总参数 671B,每个 token 只激活 37B。

Q9. BPE 和 WordPiece 的核心区别?(百度/字节)

标准回答

都是子词分词算法,从字符级开始迭代合并。核心区别在合并策略:BPE 选择出现频率最高的相邻符号对合并;WordPiece 选择对语言模型似然度提升最大的对合并(用概率而非频率做决策)。另外 BPE 使用 BPE-Dropout 等正则化手段减少过拟合,SentencePiece 在此基础上进一步将空格也作为普通字符处理,实现了语言无关的分词。

Q10. 如何评估一个 LLM 的好坏?(综合)

标准回答

多维度评估:① 知识(MMLU — 57 学科四选一);② 代码能力(HumanEval — 164 道 Python 题,pass@k 指标);③ 推理(GSM8K 数学、BBH 逻辑推理);④ 对话质量(AlpacaEval / MT-Bench — LLM-as-Judge 对比评测);⑤ Agent 能力(AgentBench / SWE-bench — 多步工具调用和软件工程任务)。

Agent 方向面试重点提 AgentBench 和 SWE-bench,这是衡量"模型能不能当 Agent 大脑"的关键指标。LLM-as-a-Judge 存在位置偏见和长度偏见,通过双向评测和长度控制缓解。


今日代码实战(3 道)

# ═══════════════════════════════════════════════
# 练习 1:手写单头 Self-Attention + Causal Mask
# ═══════════════════════════════════════════════
import torch
import torch.nn.functional as F

def scaled_dot_product_attention(Q, K, V, causal=True):
    """
    Q, K, V: (batch, n_heads, seq_len, d_k)
    面试手写版 — 建议把这段背下来
    """
    d_k = Q.size(-1)
    scores = torch.matmul(Q, K.transpose(-2, -1)) / (d_k ** 0.5)  # (B, H, L, L)

    if causal:
        seq_len = scores.size(-1)
        mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1).bool()
        scores = scores.masked_fill(mask.to(scores.device), float('-inf'))

    attn_weights = F.softmax(scores, dim=-1)
    output = torch.matmul(attn_weights, V)
    return output, attn_weights

# ═══════════════════════════════════════════════
# 练习 2:Top-P (Nucleus) Sampling 实现
# ═══════════════════════════════════════════════
def top_p_sampling(logits: torch.Tensor, p: float = 0.9, temperature: float = 1.0):
    """
    从 logits 中按 Top-P 策略采样一个 token
    logits: (vocab_size,) — 最后一个 token 的输出 logits
    """
    logits = logits / temperature
    probs = F.softmax(logits, dim=-1)

    sorted_probs, sorted_indices = torch.sort(probs, descending=True)
    cumsum = torch.cumsum(sorted_probs, dim=-1)

    # 找到累积概率超过 p 的位置
    cutoff_mask = cumsum > p
    cutoff_mask[0] = False  # 至少保留一个 token

    # 把超出 p 的 token 概率置零
    sorted_probs[cutoff_mask] = 0.0
    sorted_probs /= sorted_probs.sum()

    sampled = torch.multinomial(sorted_probs, 1).item()
    return sorted_indices[sampled].item()

# ═══════════════════════════════════════════════
# 练习 3:RLHF 损失函数简化实现
# ═══════════════════════════════════════════════
def reward_model_loss(reward_model, prompts, winners, losers):
    """
    Bradley-Terry 偏好损失
    让胜出回复的 reward 大于失败回复
    """
    r_winner = reward_model(prompts, winners)
    r_loser = reward_model(prompts, losers)
    loss = -torch.log(torch.sigmoid(r_winner - r_loser)).mean()
    return loss

def dpo_loss(pi_model, ref_model, prompts, winners, losers, beta=0.1):
    """
    DPO 损失 — 直接从偏好对更新策略
    """
    # 当前策略下胜者和败者的 log 概率
    log_pi_win = pi_model.log_prob(prompts, winners)
    log_pi_lose = pi_model.log_prob(prompts, losers)

    # 参考策略下胜者和败者的 log 概率
    log_ref_win = ref_model.log_prob(prompts, winners)
    log_ref_lose = ref_model.log_prob(prompts, losers)

    # DPO 损失的核心
    log_ratio_win = log_pi_win - log_ref_win  # 胜者概率相对提升
    log_ratio_lose = log_pi_lose - log_ref_lose  # 败者概率相对下降

    loss = -torch.log(torch.sigmoid(beta * (log_ratio_win - log_ratio_lose))).mean()
    return loss

📊 今日知识图谱

LLM 大模型基础速通 DAY 3
│
├── Tokenization: BPE(频率) vs WordPiece(似然度) vs SentencePiece(语言无关)
│
├── Transformer 核心
│   ├── Self-Attention = softmax(QK^T/√dk)V(手写推导)
│   ├── MHA → MQA → GQA(减少 KV Cache,LLaMA 用 GQA)
│   ├── 位置编码:Sinusoidal → RoPE(相对位置 + 长度外推)
│   ├── SwiGLU 激活(LLaMA 用)替代 ReLU/GELU
│   └── Pre-Norm:Norm → Attn → Add(训练更稳定)
│
├── 训练流程
│   ├── Pre-training(Next Token) → SFT(对话格式) → RLHF(对齐偏好)
│   ├── PPO:需要 Reward Model + KL 惩罚防 Reward Hacking
│   ├── DPO:一步到位,不需要 Reward Model
│   └── GRPO(DeepSeek):组内对比,纯 RL 涌现推理能力
│
├── 推理优化
│   ├── KV Cache:缓存历史 KV,O(n²) → O(n)
│   ├── Flash Attention:分块 + 重计算,O(n²)显存 → O(n)
│   └── 量化:GPTQ/AWQ(GPU) vs GGUF(CPU) vs BitsAndBytes(QLoRA)
│
├── 解码 & MoE
│   ├── Top-P + Temperature(对话生成首选)
│   ├── MoE = 稀疏激活,参数量↑ 计算量→
│   └── DeepSeek MoE:细粒度专家 + 共享专家 + 无辅助损失
│
└── 评估
    ├── MMLU(知识) / HumanEval(代码) / GSM8K(推理)
    ├── AgentBench / SWE-bench(Agent能力)
    └── LLM-as-a-Judge(偏见:位置偏见+长度偏见)

🔜 明日预告

Day 4 — Prompt Engineering + Function Calling + Agent 核心

  • Prompt Engineering:Zero-shot → Few-shot → CoT → ToT → Self-Consistency → ReAct
  • Function Calling:OpenAI 协议 + JSON Schema 工具定义 + 并行调用 + 错误处理
  • Agent 四要素深度拆解:Planning + Memory + Tools + Action
  • ReAct 框架完整手写实现
  • 8 道 Agent 核心面试真题

💡 速通心法:今天是整个系列最硬核的一天。Self-Attention 的推导、MHA/MQA/GQA 对比、RLHF 三阶段、RoPE 原理、DPO vs PPO——这五个是 LLM 面试的"必问五件套",有任何一个说不清楚,面试基本就挂了。建议今天花最多时间在 Transformer 的模块二和训练的模块四上,睡前对着知识图谱完整复述一遍 Transformer 的 forward pass。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐