#标准答案速查区
这一节不是继续加题目,而是把最核心、最常被问、最值得背熟的题,整理成可以直接学习和查阅的标准答案。这里的“标准答案”不是唯一答案,而是面试和笔试里最稳、最通用、最不容易失分的回答版本。
#1. 什么是 Transformer?为什么它适合大模型?
#标准答案
Transformer 是一种以 self-attention(自注意力)为核心的序列建模架构。它的基本模块通常包括输入 embedding、位置编码、多头注意力、前馈网络、残差连接和归一化层。
它适合大模型,核心原因有三点:
- 并行性强:和 RNN 按时间步串行计算不同,Transformer 可以在训练时同时处理整段序列,更适合大规模 GPU/TPU 并行。
- 长距离依赖建模更直接:任意两个 token 都可以通过 attention 直接交互,路径长度短,不像 RNN 那样需要跨很多时间步传递信息。
- 易于扩展:无论是模型参数、数据规模还是训练算力,Transformer 都比较容易按统一方式扩展,因此和 scaling law 更契合。
一句话总结:Transformer 之所以成为大模型主流,不只是因为”效果好”,而是因为它同时兼顾了表达能力、并行训练能力和大规模扩展能力。
#深度解析
1. Transformer 的核心组件拆解
输入文本 → Token Embedding + Positional Encoding
↓
[Encoder/Decoder 层 × N]
↓
├─ Multi-Head Self-Attention
│ ├─ Q/K/V 线性投影
│ ├─ Scaled Dot-Product Attention
│ └─ 多头拼接 + 线性输出
│
├─ Add & Norm(残差 + LayerNorm)
│
└─ Feed-Forward Network
├─ 线性扩张(如 4×hidden_dim)
├─ 激活函数(GELU/ReLU/SwiGLU)
└─ 线性压缩
↓
输出层(LM Head / 分类头)
2. 为什么 Transformer 比 RNN/LSTM 更适合大模型?
| 维度 | RNN/LSTM | Transformer |
|---|---|---|
| 并行性 | 时间步串行,无法并行 | 全序列并行,GPU 利用率高 |
| 长距离依赖 | 需逐步传递,梯度易消失 | 任意位置直接 attention,路径长度 O(1) |
| 计算复杂度 | O(n) 每步,但总步数=n | O(n²) attention,但一步完成 |
| 扩展性 | 深层难以训练 | 配合 Pre-Norm、残差,可堆到 100+ 层 |
| 训练稳定性 | 梯度爆炸/消失问题严重 | 更稳定,但需大量数据 |
3. Transformer 的扩展性为什么好?
Scaling Law 的发现证明:Transformer 的性能随模型规模(N)、数据量(D)、算力(C)可预测地提升。
Loss(N, D) = E + A/N^α + B/D^β
这个公式在 Transformer 上表现特别稳定,因为:
- 统一的 attention 机制,无论多大都能保持相同计算模式
- 没有循环结构带来的梯度问题,深层也能训练
- 数据到模型的映射简单直接,易于扩展
4. 面试官常见深挖追问
- ”Transformer 的 O(n²) attention 不是瓶颈吗?为什么还能扩展?”
- 答:确实是瓶颈,但可以通过工程手段缓解:1)FlashAttention 减少 IO;2)Sparse Attention(Longformer、BigBird)降低计算量;3)线性 attention 近似(如 Performer、Linformer)。而且 n² 的常数因子小(矩阵乘法在 GPU 上高度优化),实际中 2K-8K 序列长度仍能高效处理。
- ”Transformer 和 CNN 相比,各自适合什么?”
- 答:CNN 适合局部模式识别(如图像边缘、文本 n-gram),通过滑动窗口捕获局部特征。Transformer 适合全局依赖建模(如代词指代、远距离语义关系)。图像领域现在也是 Vision Transformer (ViT) 主流,但保留了 CNN 的局部归纳偏置(如 Swin Transformer 的窗口 attention)。
- ”如果序列长度是 100万,Transformer 还能用吗?”
- 答:标准 Transformer 不行(100万² = 10¹² 的 attention 计算量不可承受)。但可以用:1)Mamba/SSM(线性复杂度);2)Ring Attention(分布式处理超长序列);3)分层注意力(先局部后全局)。这是当前研究热点。
#2. Self-Attention 是怎么计算的?复杂度为什么是 O(n^2)?
#标准答案
给定输入隐藏状态 X,先通过线性投影得到 Q、K、V。然后用 QK^T 计算每个 token 对其他 token 的相关性分数,再除以 sqrt(d_k) 做尺度归一化,接着过 softmax 得到注意力权重,最后用这个权重对 V 加权求和,得到输出。
复杂度是 O(n^2),主要不是因为参数矩阵变大,而是因为长度为 n 的序列里,每个 token 都要和其他 token 计算相关性,所以会形成 n × n 的 attention score 矩阵。
要特别区分三件事:
- 参数量主要由投影矩阵决定,和序列长度无关。
- 计算量会随序列长度平方增长。
- 显存/缓存压力在长上下文下也会明显上升,尤其是训练时的 attention map 和推理时的
KV cache。
#深度解析
1. Self-Attention 的完整计算流程(带张量形状)
假设:batch_size=b, seq_len=n, d_model=d, num_heads=h, head_dim=d_k=d/h
Step 1: 线性投影
X: (b, n, d)
W_Q: (d, d) → Q = X @ W_Q: (b, n, d)
W_K: (d, d) → K = X @ W_K: (b, n, d)
W_V: (d, d) → V = X @ W_V: (b, n, d)
Step 2: 分头
Q: (b, n, d) → reshape → (b, h, n, d_k)
K: (b, n, d) → reshape → (b, h, n, d_k)
V: (b, n, d) → reshape → (b, h, n, d_k)
Step 3: 计算 attention score
scores = Q @ K^T: (b, h, n, n)
scores = scores / sqrt(d_k)
scores = scores + mask (causal mask: 下三角为0,上三角为-∞)
weights = softmax(scores): (b, h, n, n)
Step 4: 加权求和
output = weights @ V: (b, h, n, d_k)
Step 5: 拼接 + 线性输出
output: (b, h, n, d_k) → reshape → (b, n, d)
output = output @ W_O: (b, n, d)
2. 为什么是 O(n²)?
瓶颈在 Step 3 的 Q @ K^T:
- Q 形状:
(b, h, n, d_k) - K^T 形状:
(b, h, d_k, n) - 结果:
(b, h, n, n)
计算量:b × h × n × n × d_k = b × n² × d
当 n=4096 时,n²=16M;当 n=32768 时,n²=1B。序列长度翻倍,attention 计算量翻 4 倍。
3. 参数量 vs 计算量 vs 显存的区分
| 指标 | 决定因素 | 是否随 seq_len 增长 |
|---|---|---|
| 参数量 | W_Q, W_K, W_V, W_O 的大小 | 否(固定 4×d²) |
| 计算量 (FLOPs) | QK^T + softmax + weighted sum | 是(∝ n²) |
| 训练显存 | attention map (n×n) + 激活值 | 是(∝ n²) |
| 推理 KV cache | 每层 K/V: (batch, n, d) | 是(∝ n) |
4. 面试官常见深挖追问
- "Self-Attention 的 O(n²) 主要来自哪一步?有没有办法降到 O(n)?"
- 答:主要来自
Q @ K^T的 n×n 矩阵乘法。降到 O(n) 的方法:1)Sparse Attention(只关注局部窗口);2)Linear Attention(用核技巧近似 softmax);3)Mamba/SSM(用状态空间替代 attention)。但这些方法在效果上通常仍有差距,标准 Transformer 仍是主流。
- 答:主要来自
- "为什么 attention 需要 causal mask?没有会怎样?"
- 答:Decoder-only 模型训练时,每个位置只能看到前面的 token(自回归特性)。Causal mask(下三角矩阵)确保位置 i 的 attention 只覆盖位置 1~i。如果没有 mask,模型会"偷看"答案(如位置 5 能看到位置 10),训练-推理不一致,推理时性能暴跌。
- "Multi-Head Attention 中,head 之间会共享参数吗?"
- 答:不会。每个 head 有独立的 W_Q, W_K, W_V 投影矩阵。但所有 head 共享同一个输入 X。输出时,各 head 的结果拼接后再过一个共享的 W_O。所以总参数量是
4 × d_model²(3 个输入投影 + 1 个输出投影),和 head 数无关。
- 答:不会。每个 head 有独立的 W_Q, W_K, W_V 投影矩阵。但所有 head 共享同一个输入 X。输出时,各 head 的结果拼接后再过一个共享的 W_O。所以总参数量是
#3. 为什么主流 LLM 大多采用 Decoder-only?
#标准答案
Decoder-only 架构之所以成为通用 LLM 主流,主要因为它和 next-token prediction(下一个 token 预测)目标天然一致。训练时只需要把海量文本拼成自回归序列,目标简单统一,数据构造直接,扩展起来非常自然。
它的优势主要有:
- 训练目标统一:预训练、继续预训练、指令微调都能围绕“给定前文预测后文”来组织。
- 生成任务兼容性强:对聊天、写作、代码补全、推理、多轮对话都很自然。
- 工程上更简单:相比
Encoder-Decoder,结构更统一,推理缓存机制也更成熟。
但它不是没有代价:
- 编码输入和生成输出没有天然分工;
- 对某些强条件生成任务,不一定总比
Encoder-Decoder更优; - 推理时是自回归逐 token 生成,时延控制更难。
#深度解析
1. 三种架构的范式对比
| 维度 | Encoder-only (BERT) | Decoder-only (GPT) | Encoder-Decoder (T5/BART) |
|---|---|---|---|
| 训练目标 | Masked LM (双向) | Autoregressive LM (单向) | Span corruption + 自回归生成 |
| Attention 掩码 | 双向可见 | Causal Mask (只看前面) | Encoder 双向 + Decoder 单向 |
| 典型任务 | 理解、分类、抽取 | 生成、续写、对话、推理 | 翻译、摘要、条件生成 |
| 代表模型 | BERT, RoBERTa | GPT, LLaMA, Claude | T5, BART, GLM |
| 参数量效率 | 高(理解任务) | 高(通用生成) | 中(需编码+解码两套参数) |
2. 为什么 Decoder-only 成为 LLM 主流?
从第一性原理看:
- 目标一致性:大规模语料预训练的本质是"预测下一个 token",Decoder-only 的 causal attention 天然匹配这个目标
- 数据效率:不需要构造特殊的输入-输出对(如翻译对),任何文本都可以直接作为训练数据
- Scaling 友好:结构简单统一,容易扩展到千亿/万亿参数(Encoder-Decoder 的参数分配需要额外考虑编码器/解码器比例)
- 统一框架:预训练、SFT、RLHF 可以共享同一套模型结构和目标范式
3. 具体数据对比
| 模型 | 架构 | 参数 | 训练数据 | 核心任务 |
|---|---|---|---|---|
| BERT | Encoder | 340M | 维基+图书 | 理解任务 |
| GPT-3 | Decoder | 175B | 网页/图书/代码等 | 通用生成 |
| T5 | Enc-Dec | 11B | C4 数据集 | 翻译/摘要 |
| LLaMA-2 | Decoder | 70B | 2T tokens | 通用对话 |
观察:2020年后发布的通用大模型(GPT-3/4、LLaMA、Claude、Gemini)几乎全部采用 Decoder-only。
4. Decoder-only 的 Attention 效率优势
Decoder-only 使用 Causal Mask,使得:
- 训练时可以用三角形掩码矩阵一次性计算整个序列的 attention
- 推理时可以使用 KV Cache 避免重复计算历史 token
- 而 Encoder-Decoder 的 Encoder 部分需要计算双向 attention,无法利用这种增量优化
5. 什么时候 Encoder-Decoder 仍然更好?
| 场景 | 推荐架构 | 原因 |
|---|---|---|
| 机器翻译 | Encoder-Decoder | 源语言编码和目标语言生成分工明确 |
| 结构化摘要 | Encoder-Decoder | 输入文档需要完整理解后再生成摘要 |
| 代码补全 | Decoder-only | 自回归生成天然匹配从左到右的代码书写 |
| 开放域对话 | Decoder-only | 无需显式编码阶段,流式生成更自然 |
6. 面试官常见深挖追问
- "Decoder-only 做理解任务(如分类)会不会比 BERT 差?"
- 答:在零样本/少样本场景下,Decoder-only 通常更强(因为它见过更多数据、参数更大);但在有充足标注数据的传统理解任务上,同规模的 Encoder 可能仍有优势。不过实践中,用 Decoder-only 做分类很简单(加 prompt 让其输出标签 token),且一个模型可以兼顾理解和生成。
- "GLM 说自己是 Prefix Decoder,和 GPT 的 Causal Decoder 有什么区别?"
- 答:Prefix Decoder(如 GLM)对输入前缀部分使用双向 attention,对生成部分使用单向 attention。好处是输入部分可以充分交互理解,坏处是结构更复杂、预训练目标需要特殊设计(如 GLM 的 blank infilling)。GPT 的 Causal Decoder 对所有 token 都用单向 attention,实现更简单统一。
- "如果做一个翻译系统,你会选 Encoder-Decoder 还是 Decoder-only?"
- 答:取决于场景。如果是高质量双向翻译且数据充足,Encoder-Decoder(如 T5)可能更稳定;但如果是多语言通用模型的一部分,用 Decoder-only(如 GPT-4)也可以做得很好,甚至更好,因为它能把翻译能力和其他能力统一在一个模型里。当前 SOTA 翻译模型很多也是 Decoder-only(如 GPT-4、DeepL 的底层)。
#4. RoPE 为什么有效?它的局限是什么?
#标准答案
RoPE(Rotary Position Embedding,旋转位置编码)的核心思想是:不把位置信息简单加到 token 向量上,而是通过旋转方式把位置信息编码进 Q/K,这样 attention 分数天然就会带有相对位置信息。
它有效,主要因为:
- 更适合自回归 attention:位置关系直接反映在
Q/K内积里。 - 相对位置信息表达自然:对顺序和距离更敏感。
- 长度扩展能力通常比纯绝对位置编码更好。
它的局限主要是:
- 长度超出训练分布后,效果仍可能明显退化;
- “支持更长上下文”不等于”真的能有效利用更长上下文”;
- 长上下文退化往往还和训练长度、数据分布、注意力稀释、缓存压力、数值稳定性一起有关。
#深度解析
1. RoPE 的数学定义
对于位置 m 的向量 x_m,RoPE 将其按维度两两分组,对第 j 组应用旋转:
R(m, θ_j) = [[cos(m·θ_j), -sin(m·θ_j)],
[sin(m·θ_j), cos(m·θ_j)]]
其中 θ_j = base^(-2j/d) = 10000^(-2j/d)
旋转后的 query 和 key:
q_m' = R(m, θ) · q_mk_n' = R(n, θ) · k_n
2. 为什么 RoPE 能表达相对位置?
核心性质:旋转矩阵相乘等于角度相加
R(m, θ)^T · R(n, θ) = R(n-m, θ)
因此 attention 分数中的内积:
q_m'^T · k_n' = (R(m,θ)·q_m)^T · (R(n,θ)·k_n)
= q_m^T · R(n-m, θ) · k_n
结果只依赖于相对距离 n-m,与绝对位置 m, n 无关。
3. 与绝对位置编码的对比
| 特性 | 绝对位置编码 ( sinusoidal / learned ) | RoPE |
|---|---|---|
| 注入方式 | 加到 embedding 上 | 旋转 Q/K |
| 相对位置 | 间接(模型需学习) | 天然满足 |
| 外推性 | 差(超出训练长度效果骤降) | 较好(可通过方法改进) |
| 与 Attention 的关系 | 独立 | 直接融入 Q/K 内积 |
| 实现复杂度 | 简单 | 稍复杂(需逐维度旋转) |
4. 长度外推方法对比
当推理长度超过训练长度时,RoPE 的基频 θ_j 会超出训练分布。解决方法:
| 方法 | 原理 | 效果 | 代价 |
|---|---|---|---|
| Position Interpolation (PI) | 将位置 m 缩放为 m·L_train/L_target | 稳定 | 需微调,短距离区分度下降 |
| NTK-aware | 缩小 base(如 10000→1),高频分量可外推 | 无需训练 | 长距离可能不稳定 |
| YaRN | 同时缩放频率和注意力温度 | SOTA 外推效果 | 需超参调优 |
| Dynamic NTK | 推理时动态调整 base | 自适应长度 | 实现稍复杂 |
典型数据:LLaMA-2 训练长度 4K,用 PI 可扩展到 16K;用 YaRN 可扩展到 128K。
5. RoPE 的实际局限(量化分析)
| 局限 | 具体表现 | 缓解方法 |
|---|---|---|
| 长度外推退化 | 训练 4K → 推理 32K,ppl 可能上升 30-50% | PI/NTK/YaRN 外推 |
| 远距离衰减 | 距离 > 2K 后 attention 分数趋近于 0 | 使用 Alibi/动态基频 |
| 局部精细度 | 旋转角度的离散性限制相邻位置区分 | 增大 d_head |
| 计算开销 | 逐维度旋转增加约 5-10% 计算量 | 融合 kernel 优化 |
6. 面试官常见深挖追问
- ”RoPE 的 base 为什么是 10000?改大或改小会怎样?”
- 答:base 决定旋转角度的频率范围。base 越大,高频分量越丰富,短距离区分度越好,但长距离外推性越差;base 越小,长距离外推越好,但短距离区分度下降。NTK-aware 方法通过动态调整 base 来平衡这两者。
- ”RoPE 和 Alibi 有什么区别?为什么 LLaMA 选 RoPE 而不选 Alibi?”
- 答:RoPE 通过旋转注入位置信息,Alibi 直接在 attention 分数上加一个与距离成线性负相关的偏置。Alibi 的外推性更好(因为偏置函数与长度无关),但 RoPE 在训练长度内的性能通常更强。LLaMA 选 RoPE 可能是因为在预训练长度(如 4K)内 RoPE 的表现更优,且可以通过后续外推方法扩展长度。
- ”如果模型训练长度是 8K,推理时需要 128K,RoPE 直接外推不微调会怎么样?”
- 答:直接外推效果会很差(ppl 显著上升,注意力分散)。因为 128K 的位置对应的旋转角度完全超出训练分布。至少需要:1)使用 NTK-aware 或 YaRN 调整 base;2)理想情况下在更长数据上继续训练一段时间(如 1-2B tokens)。
#5. SFT 和 continue pretraining(继续预训练)有什么区别?
#标准答案
两者的核心区别在于目标不同。
- Continue pretraining 主要是往模型里继续注入某个领域的统计规律和语言分布,本质上仍然是预训练范式,重点是“让模型更懂这个领域”。
- SFT(Supervised Fine-Tuning,监督微调)主要是让模型学会按某种任务格式或指令风格输出,重点是“让模型更会按要求回答”。
一个更直白的说法是:
- continue pretraining 更像补底层知识和语言分布;
- SFT 更像教它怎么按特定任务说话。
什么时候选哪个?
- 如果问题是”模型不懂行业术语、不懂领域知识”,优先考虑继续预训练;
- 如果问题是”模型懂一些知识,但不会按任务格式输出”,优先考虑 SFT;
- 真实项目里,很多时候会先 continue pretrain,再做 SFT。
#深度解析
1. 目标函数对比
| 维度 | Continue Pretraining | SFT |
|---|---|---|
| 目标函数 | 自回归语言建模:`-log P(x_i \ | x_{<i})` |
| 数据格式 | 纯文本,无结构要求 | (instruction, input, output) 三元组 |
| 数据规模 | 大(通常 1B-100B+ tokens) | 小(通常 10K-1M 条) |
| 学习率 | 小(预训练 LR 的 1/10~1/100) | 稍大(通常是预训练 LR 的 1/10) |
| 训练步数 | 多(数千到数万步) | 少(通常 1-10 个 epoch) |
| 改变的层次 | 底层知识表示、词嵌入分布 | 上层行为模式、输出格式 |
2. 效果影响的不同层面
Continue Pretraining 主要改变:
- 词嵌入空间:领域术语获得更准确的向量表示
- 世界知识:补充预训练语料中缺乏的事实
- 语言分布:适应领域特定的表达习惯
SFT 主要改变:
- 指令遵循能力:学会按特定格式响应
- 对话风格:调整语气、礼貌程度、回答长度
- 安全边界:拒绝有害请求、对齐价值观
3. 决策流程:什么时候选哪个?
问题诊断:
├─ 模型不懂领域术语/概念?
│ └─ 是 → Continue Pretraining(注入领域知识)
│
├─ 模型会回答问题但格式不对?
│ └─ 是 → SFT(教输出格式)
│
├─ 模型有知识但推理能力差?
│ └─ 是 → 需要更复杂的方法(SFT + CoT 数据 / RL)
│
└─ 模型在领域任务上整体表现差?
└─ 是 → Continue Pretrain + SFT 两步走
4. 典型数据规模对比
| 阶段 | 数据量 | 训练时间(7B 模型) | 显存需求 |
|---|---|---|---|
| 预训练 | 1-3T tokens | 数周(数百 GPU) | 高 |
| Continue Pretrain | 1B-100B tokens | 数小时到数天 | 高 |
| SFT | 10K-1M 条 | 分钟到小时 | 中 |
5. 组合策略与顺序
标准流程:Pretrain → Continue Pretrain → SFT → RLHF/DPO
原因:
- 先通过 Continue Pretrain 调整知识分布
- 再通过 SFT 学习任务格式
- 最后通过 RL 对齐偏好
如果顺序颠倒(先 SFT 再 Continue Pretrain):
- SFT 学到的格式可能被 Continue Pretrain 的通用文本目标”冲刷”掉
- 需要重新做 SFT 来恢复格式能力
6. 面试官常见深挖追问
- ”Continue Pretraining 和 SFT 的学习率有什么区别?为什么?”
- 答:Continue Pretraining 的学习率通常更低(如 1e-5 ~ 5e-5),因为预训练权重已经收敛,大幅更新会破坏已学到的通用知识;SFT 的学习率可以稍高(如 5e-5 ~ 1e-4),因为数据量小、目标明确,需要较快收敛。同时 SFT 通常使用 cosine decay 或 constant LR,而 Continue Pretrain 使用 warmup + decay。
- ”如果只有 1000 条领域数据,应该 Continue Pretrain 还是 SFT?”
- 答:1000 条数据太少,直接 Continue Pretrain 容易过拟合或产生灾难性遗忘。更好的策略是:1)如果已有通用模型表现尚可,用这 1000 条做 SFT;2)如果需要补充知识,先用更大规模的领域语料(如抓取网页、图书)做 Continue Pretrain,再用 1000 条做 SFT 调整格式。
- ”SFT 会把预训练学到的知识忘掉吗?”
- 答:会部分遗忘,但通常不严重。SFT 数据量远小于预训练,且学习率较低,主要调整的是输出分布的”头部”(高频模式),对底层知识影响有限。但如果 SFT 数据与预训练分布差异极大(如用代码数据微调文学模型),遗忘会更明显。缓解方法:混合一定比例的原预训练数据到 SFT 中。
#6. LoRA 和 QLoRA 的区别是什么?
#标准答案
LoRA 的核心思想是冻结原始大模型参数,只在某些线性层旁边增加低秩增量矩阵进行训练,从而显著降低训练参数量和显存开销。
QLoRA 在 LoRA 的基础上更进一步:先把基座模型量化到更低精度,再在量化模型上训练 LoRA adapter,因此显存更省。
可以这样记:
LoRA:少训练参数。QLoRA:少训练参数 + 少存底座权重显存。
典型 trade-off 是:
QLoRA更省资源,更适合单机或资源有限场景;- 但量化会引入一定误差,某些任务上效果和稳定性不一定和全精度
LoRA完全一致。
#深度解析
1. LoRA 的数学原理
对于原始权重矩阵 W ∈ R^(d_out × d_in),LoRA 不直接训练 W,而是引入低秩分解:
h = W·x + ΔW·x = W·x + B·A·x
其中:
- B ∈ R^(d_out × r)
- A ∈ R^(r × d_in)
- r << min(d_out, d_in)(通常 r = 8, 16, 32, 64)
训练时冻结 W,只训练 A 和 B。A 通常用高斯初始化,B 初始化为 0(保证训练开始时 ΔW = 0,不破坏原始模型行为)。
2. 训练参数量计算
以 LLaMA-2 7B 为例,每层有 q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj 等线性层:
| 配置 | 原始参数量 | LoRA 参数量 | 训练比例 |
|---|---|---|---|
| Full Fine-tuning | 7B | 7B | 100% |
| LoRA (r=16, 仅 Q/V) | 7B | 33M | 0.47% |
| LoRA (r=64, 所有线层) | 7B | 268M | 3.8% |
| QLoRA (r=64) | ~2B (量化后) | 268M | 基座不训练 |
3. QLoRA 的核心技术:4-bit Normal Float
QLoRA 不是简单的 INT4 量化,而是使用 NF4 (Normal Float 4-bit):
- 原理:假设权重服从正态分布,将 4-bit 的 16 个量化点按正态分布的累积密度函数均匀分配
- 优势:比均匀量化的 INT4 对正态分布数据的信息损失更小
- 双量化:对量化常数(scale)再次量化,进一步节省显存
- 分页优化器:当 GPU 显存不足时,将优化器状态分页到 CPU 内存
4. 显存对比(LLaMA-2 7B, batch_size=1, seq_len=512)
| 方案 | 模型权重 | 梯度 | 优化器状态 | 激活值 | 总显存 | 单卡可行? |
|---|---|---|---|---|---|---|
| Full FT (FP16) | 14 GB | 14 GB | 56 GB | ~2 GB | ~86 GB | 否(需多卡) |
| LoRA (FP16) | 14 GB | ~0.1 GB | ~0.4 GB | ~2 GB | ~16.5 GB | 是(24GB卡) |
| QLoRA (4bit) | ~4 GB | ~0.1 GB | ~0.4 GB | ~2 GB | ~6.5 GB | 是(甚至 8GB卡) |
5. LoRA Rank 的选择原则
| r 值 | 适用场景 | 效果 | 训练成本 |
|---|---|---|---|
| r=8 | 简单任务、快速实验 | 可能不足 | 最低 |
| r=16 | 大多数任务的标准选择 | 通常足够 | 低 |
| r=32 | 复杂任务、数据量大 | 更好 | 中 |
| r=64+ | 与全量微调差距极小 | 接近全量 | 较高 |
经验法则:
- 目标任务与原预训练任务越接近,
r可以越小 - 数据量越大,
r可以适当增大 - 通常先尝试 r=16,效果不好再增加到 32/64
6. 面试官常见深挖追问
- "LoRA 为什么只在 attention 的 q/v 上加,而不是所有层?"
- 答:实践中发现对 q_proj 和 v_proj 加 LoRA 的性价比最高。原因:1)Attention 层是模型中"最通用"的部分,微调收益大;2)FFN 层更多是知识存储,微调容易破坏已有知识;3)只调 q/v 可以在效果和效率之间取得最佳平衡。但某些任务(如代码生成)调所有层可能效果更好。
- "QLoRA 的量化误差会在训练中累积吗?"
- 答:不会显著累积。QLoRA 中基座权重是静态量化的(不更新),训练只更新 LoRA 的 A/B 矩阵。量化误差是固定的系统误差,LoRA 的更新是在量化后的权重空间中寻找最优方向。实际经验表明,QLoRA 的效果通常接近 LoRA(差距 < 1%),在大多数任务上可以忽略。
- "LoRA 的 A/B 矩阵可以合并回原始权重吗?"
- 答:可以。推理时将
W' = W + B·A合并,合并后就是一个普通矩阵,没有额外推理开销。这是 LoRA 的一大优势:训练时省显存,推理时零 overhead。QLoRA 也可以先反量化 W,再合并 B·A。
- 答:可以。推理时将
- "如果任务需要更新模型知识(如补充新领域事实),LoRA 够用吗?"
- 答:通常不够。LoRA 主要调整模型的"行为模式"和"输出格式",对底层知识(词嵌入、事实存储)的改变能力有限。如果任务是知识密集型,建议:1)先 Continue Pretrain 注入知识;2)再用 LoRA/SFT 调整格式。或者直接 Full Fine-tuning。
#7. RLHF 和 DPO 的区别是什么?
#标准答案
两者都在做偏好对齐,但训练信号形式不一样。
RLHF 经典流程通常是:
- 先做 SFT;
- 再训练 reward model(奖励模型);
- 最后用 PPO 等强化学习方法,让模型去优化奖励。
DPO(Direct Preference Optimization)则把“偏好回答应当比非偏好回答概率更高”直接写成优化目标,不再显式训练 reward model,也不需要完整 PPO 那一套在线强化学习过程。
所以核心区别是:
RLHF更完整、更灵活,但实现复杂、训练不稳定、成本更高;DPO更简单、更稳定、工程成本更低,因此在很多团队里更受欢迎。
但 DPO 也不是万能的,它通常更依赖偏好对数据质量,而且不像完整 RL 那样容易直接优化复杂长期目标。
#深度解析
1. RLHF 三阶段详解
阶段1: SFT
数据: (prompt, chosen_response)
目标: 最大化 chosen_response 的似然
输出: SFT 模型 π_SFT
阶段2: Reward Model 训练
数据: (prompt, chosen, rejected) 偏好对
目标: r(chosen) > r(rejected)
损失: -log σ(r_θ(chosen) - r_θ(rejected))
输出: Reward 模型 r_θ
阶段3: PPO 强化学习
目标: max E[r_θ(y|x)] - β·KL(π \| π_SFT)
约束: 策略 π 不要偏离 SFT 模型太远(KL 散度约束)
挑战: 需要在线采样、值函数估计、优势函数计算,训练不稳定
2. DPO 的目标函数推导
DPO 的核心洞察:在 Bradley-Terry 偏好模型下,最优策略有闭式解:
π*(y|x) = π_SFT(y|x) · exp(r(y|x)/β) / Z(x)
其中 Z(x) 是配分函数。DPO 不显式学习 r,而是直接优化策略:
L_DPO = -log σ(β·log(π(y_w|x)/π_SFT(y_w|x)) - β·log(π(y_l|x)/π_SFT(y_l|x)))
y_w = 偏好的回答 (chosen)
y_l = 非偏好的回答 (rejected)
关键:DPO 只需要 (prompt, chosen, rejected) 三元组,不需要显式训练 reward model 和 PPO。
3. RLHF vs DPO 全面对比
| 维度 | RLHF (PPO) | DPO |
|---|---|---|
| 训练流程 | 三阶段(SFT→RM→PPO) | 两阶段(SFT→DPO) |
| 额外模型 | 需要 Reward Model + Value Model | 不需要 |
| 在线采样 | 需要(PPO 每步采样新输出) | 不需要(用离线偏好对) |
| 超参敏感度 | 高(KL 系数、PPO clip、学习率) | 低(主要调 β) |
| 训练稳定性 | 容易崩溃、reward hacking | 相对稳定 |
| 计算成本 | 高(需同时加载 4 个模型) | 低(只需 2 个模型) |
| 数据需求 | 偏好对 + 在线生成样本 | 仅偏好对 |
| 灵活度 | 高(可自定义复杂 reward) | 中(依赖偏好对的表达能力) |
| 效果上限 | 通常更高(迭代优化) | 接近 RLHF,某些任务略低 |
4. 为什么 DPO 更简单却有效?
DPO 的本质是把 RL 问题转化为分类问题:
- 不需要在线采样(省计算)
- 不需要值函数估计(省模型)
- 不需要 clip 和 advantage 计算(省调参)
代价是:
- 只能优化成对偏好,难以优化复杂的多步骤目标
- 对偏好数据质量要求高(标注错误直接影响效果)
- 当 chosen 和 rejected 的差距很小时,训练信号弱
5. 何时选 RLHF,何时选 DPO?
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 快速迭代、资源有限 | DPO | 实现简单、训练快、稳定性高 |
| 需要复杂 reward(如长度控制、格式约束) | RLHF | DPO 难以表达多目标组合 |
| 偏好数据质量高 | DPO | 数据好则 DPO 效果接近 RLHF |
| 需要持续在线学习 | RLHF | PPO 的在线采样机制天然支持 |
| 首次尝试对齐 | DPO | 先验证数据质量,再考虑上 RLHF |
6. 面试官常见深挖追问
- "DPO 中的 β 是什么?调大或调小会怎样?"
- 答:β 是温度系数,控制策略偏离 SFT 模型的程度。β 越大,KL 约束越强,策略越接近 SFT(安全但可能不够对齐);β 越小,策略越自由,可能效果更强但也可能偏离太远导致不稳定。通常 β 在 0.1-0.5 之间,需要通过验证集调优。
- "DPO 的偏好对从哪里来?数据质量怎么保证?"
- 答:偏好对通常来自:1)人工标注(成本高但质量高);2)模型自举(用更强模型生成 chosen,用较弱模型生成 rejected);3)用户反馈(如点赞/点踩)。质量保证方法:1)标注者一致性检查(Cohen's Kappa > 0.7);2)过滤模糊样本(chosen 和 rejected 差距太小);3)使用多轮标注减少随机误差。
- "RLHF 的 reward hacking 是什么?怎么解决?"
- 答:Reward hacking 指模型找到 shortcut 来欺骗 reward model,而不是真正改善输出质量。例如:reward model 偏好长回答,模型就故意写很长但空洞的内容。解决方法:1)KL 散度约束防止策略偏离太远;2)定期用新数据更新 reward model;3)多维度 reward(不只一个分数,而是分解为多个维度);4)引入人类在环路审核异常样本。
- "DPO 之后还能再叠加 RLHF 吗?"
- 答:可以。常见策略是:SFT → DPO(快速获得较好基线)→ RLHF(进一步提升上限)。DPO 训练的模型可以作为 RLHF 的初始化策略,这样 PPO 的起始点更好,训练更稳定。也可以迭代 DPO(Iterative DPO),每轮用当前模型生成新偏好对,持续优化。
#8. KV cache 为什么能加速推理,又为什么吃显存?
#标准答案
在自回归生成里,每生成一个新 token,都要和历史 token 做 attention。如果每一步都把所有历史 token 的 K/V 重算一遍,计算会非常浪费。
KV cache 的做法是:把历史 token 每一层的 K/V 缓存起来,后面生成新 token 时直接复用历史缓存,只计算新 token 的 Q 以及它对应的新 K/V。
所以它能加速,是因为避免了对历史上下文的重复计算。
但它吃显存,是因为:
- 每一层都要存历史 token 的
K/V; - 序列越长、batch 越大、层数越多、
kv_heads越多,缓存就越大; - 在长上下文和高并发场景下,
KV cache很容易成为推理瓶颈。
一句话总结:KV cache 是用显存换时间。
#深度解析
1. 有无 KV Cache 的计算复杂度对比
假设序列长度 L,层数 N,hidden dim d,batch size B:
| 操作 | 无 KV Cache | 有 KV Cache |
|---|---|---|
| 每步计算量 | O(B·L²·d)(每次重算全部 attention) |
O(B·L·d)(只算新 token 的 Q/K/V) |
| 每步显存读写 | 读取全部历史 K/V | 读取缓存的 K/V + 写入新的 K/V |
| 生成 L 个 token 总计算 | O(B·L³·d) |
O(B·L²·d) |
以 LLaMA-2 7B 生成长度 4096 为例:
- 无 KV Cache:每步 attention 计算量从 4096² 开始递减,总计算量巨大
- 有 KV Cache:每步固定只算新 token,总计算量降低约
L/2 ≈ 2000倍
2. KV Cache 显存精确计算公式
KV Cache 显存 = 2 × num_layers × num_kv_heads × head_dim × seq_len × batch_size × sizeof(dtype)
其中:
- 2 = K + V 两个张量
- num_layers: 模型层数(如 LLaMA-2 7B = 32)
- num_kv_heads: KV 头数(MHA=32, GQA=8, MQA=1)
- head_dim: 每个头的维度(如 128)
- seq_len: 序列长度
- batch_size: 并发请求的 batch 大小
- sizeof(dtype): 数据类型大小(FP16=2B, FP8=1B, INT8=1B)
3. 具体数值计算
| 模型 | 配置 | KV Cache / token | 4K 上下文 | 32K 上下文 |
|---|---|---|---|---|
| LLaMA-2 7B | MHA, FP16 | 0.5 MB | 2.0 GB | 16.0 GB |
| LLaMA-2 7B | GQA, FP16 | 0.125 MB | 0.5 GB | 4.0 GB |
| LLaMA-2 70B | GQA, FP16 | 0.8 MB | 3.2 GB | 25.6 GB |
| GPT-4 (估计) | MQA/GQA, FP8 | ~0.5 MB | ~2 GB | ~16 GB |
注:batch_size=1 时的计算。batch_size=8 时显存需求 ×8。
4. KV Cache 成为瓶颈的典型场景
| 场景 | 问题 | 表现 |
|---|---|---|
| 长上下文 (32K+) | KV Cache 超过 GPU 显存 | 无法部署或 batch_size 只能为 1 |
| 高并发 | 多个请求的 KV Cache 累积 | 显存耗尽,触发驱逐或 OOM |
| 多轮对话 | 历史上下文持续增长 | 每轮显存增加,最终必须截断 |
5. KV Cache 优化技术
| 技术 | 原理 | 效果 | 代价 |
|---|---|---|---|
| GQA/MQA | 多 query 头共享 K/V 头 | 显存降 4-8 倍 | 效果轻微下降 |
| KV Cache 量化 | 将 KV 从 FP16 量化到 INT8/FP8 | 显存降 50% | 效果下降 1-3% |
| PagedAttention | 分页存储 KV,避免内存碎片 | 提升 20-40% 吞吐 | 实现复杂 |
| 前缀共享 | 多个请求共享相同前缀的 KV | 显存节省与共享长度成正比 | 需请求有公共前缀 |
| 滑动窗口 | 只缓存最近 W 个 token 的 KV | 显存恒定 | 长距离依赖丢失 |
| Eviction 策略 | 根据重要性淘汰部分 KV | 显存可控 | 可能丢失关键信息 |
6. 面试官常见深挖追问
- "KV Cache 在 prefill 阶段和 decode 阶段的行为有什么不同?"
- 答:Prefill 阶段(处理输入 prompt)时,模型需要计算整个 prompt 的 attention,此时 KV Cache 是"从无到有"构建的过程,计算量较大但只需一次。Decode 阶段(逐 token 生成)时,每次只生成一个新 token,只需计算该 token 的 Q/K/V 并与缓存的历史 K/V 做 attention,计算量小但需要重复 L 次。因此 TTFT(Time To First Token)主要受 prefill 影响,TPOT(Time Per Output Token)主要受 decode 影响。
- "如果 batch_size 从 1 增加到 8,KV Cache 显存怎么变?计算量怎么变?"
- 答:KV Cache 显存线性增长 ×8(每个序列独立缓存)。但计算量不是线性增长:prefill 阶段从
O(L²d)变成O(8·L²d)(线性),decode 阶段从O(Ld)变成O(8·Ld)(也是线性)。不过实际中由于 kernel 效率和内存带宽限制,加速比通常不到 8 倍。
- 答:KV Cache 显存线性增长 ×8(每个序列独立缓存)。但计算量不是线性增长:prefill 阶段从
- "MQA 和 GQA 为什么能减少 KV Cache?效果会差多少?"
- 答:MHA 中每个 query 头有独立的 K/V 头(如 32 个),MQA 让所有 query 头共享 1 个 K/V 头,GQA 让每 group(如 4 个)query 头共享 1 个 K/V 头。显存减少比例 = num_kv_heads / num_q_heads。效果方面:LLaMA-2 论文显示 GQA(8 KV heads)相比 MHA 在大多数任务上差距 < 1%,MQA(1 KV head)差距约 2-3%。因此 GQA 是推理优化的常用折中方案。
#9. 一个标准 RAG 系统的完整链路是什么?
#标准答案
一个标准 RAG(检索增强生成)系统通常可以拆成 8 步:
- 文档采集与清洗;
- 文档切块(chunking);
- 向量化或建立混合索引;
- 用户 query 预处理;
- 召回(retrieval);
- 重排(reranking);
- 上下文构造并喂给生成模型;
- 生成答案并做评测或引用约束。
RAG 的价值不只是“补知识”,而是把更新频繁、无法全部塞进参数里的知识,以检索方式动态提供给模型。
RAG 失败时最常见的问题有四类:
- 切块不好,证据被切碎;
- 召回不好,该找的没找回来;
- 重排不好,噪声排在前面;
- 生成阶段没有正确利用证据,出现幻觉或证据冲突。
#深度解析
1. RAG 链路各阶段详解
| 阶段 | 输入 | 输出 | 关键技术 | 常见失败点 |
|---|---|---|---|---|
| 文档采集 | 原始数据源 | 清洗后的文档 | 爬虫、解析器、去重 | 格式混乱、编码错误 |
| 文档切块 | 长文档 | 文本块列表 | 固定长度、语义切分、层次切分 | 切断语义、丢失上下文 |
| 索引构建 | 文本块 | 向量索引 + 倒排索引 | Embedding 模型、向量数据库 | 模型与任务不匹配 |
| Query 预处理 | 用户原始 query | 优化后的 query | Query 扩展、重写、纠错 | 用户表达与文档用语差异 |
| 召回 | 优化 query | Top-K 候选块 | Dense retrieval、BM25、Hybrid | 召回率不足、语义鸿沟 |
| 重排 | 候选块 | 排序后的块 | Cross-Encoder、ColBERT | 精度不足、延迟高 |
| 上下文构造 | 排序块 + query | Prompt | 块截断、去重、引用标注 | 上下文过长、噪声过多 |
| 生成 | Prompt | 最终答案 | LLM、约束解码、引用生成 | 幻觉、不利用证据 |
2. 各阶段数据流转示例
以"查询公司 2024 年 Q3 营收"为例:
1. 采集: 从官网下载 10 份 PDF 财报
2. 清洗: 提取文本,去除页眉页脚
3. 切块: 按"章节"切分,每块约 512 字
→ 得到 200 个文本块
4. 索引: 用 bge-large-zh 编码,存入 Milvus
5. Query: "2024 Q3 营收多少"
6. 召回: Dense retrieval 召回 top-20(可能含 Q1/Q2 数据)
7. 重排: Cross-Encoder 精排,Q3 相关块排到 top-3
8. 构造 prompt:
"基于以下信息回答问题:[块1][块2][块3]
问题:2024 Q3 营收多少?
请引用来源。"
9. 生成: LLM 输出"Q3 营收为 50 亿元(来源:Q3 财报第 3 节)"
3. 系统架构与关键组件
┌─────────────────────────────────────────────────────────────┐
│ 用户 Query │
└──────────────────────┬──────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ Query Processing: 扩展、纠错、意图识别、路由 │
└──────────────────────┬──────────────────────────────────────┘
▼
┌──────────────┴──────────────┐
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ Dense Index │ │ Sparse Index │
│ (向量检索) │ │ (BM25/关键词) │
└──────┬───────┘ └────────┬─────────┘
│ │
└─────────────┬───────────────┘
▼
┌──────────────────┐
│ Fusion / RRF │
│ 融合排序 │
└────────┬─────────┘
▼
┌──────────────────┐
│ Reranker │
│ (精排模型) │
└────────┬─────────┘
▼
┌──────────────────┐
│ Context Builder │
│ (去重/截断/引用) │
└────────┬─────────┘
▼
┌──────────────────┐
│ LLM Generation │
│ (答案生成+引用) │
└──────────────────┘
4. RAG 各阶段性能指标
| 指标 | 定义 | 目标值 | 测量方法 |
|---|---|---|---|
| 召回率@K | 正确答案在前 K 个候选中的比例 | > 90%@10 | 人工标注或自动评估 |
| MRR | 第一个正确答案排名的倒数 | > 0.7 | 排名位置统计 |
| 重排准确率 | 重排后正确答案在 top-3 的比例 | > 85% | 对比重排前后 |
| 引用准确率 | 生成答案中的引用确实支持该说法 | > 80% | 人工审核 |
| 答案有用性 | 答案是否完整回答用户问题 | > 4.0/5.0 | 人工评分 |
5. 面试官常见深挖追问
- "RAG 链路中最容易出问题的环节是哪个?为什么?"
- 答:通常是召回环节。原因:1)query 和文档的语义空间不一致(用户问"怎么修",文档写"故障排除");2)切块策略不当导致关键信息被切断;3)embedding 模型对领域术语理解不足。召回失败会直接导致后续环节无法挽救("garbage in, garbage out")。
- "如果召回的文档很多但 LLM 上下文窗口有限,怎么选择放入哪些?"
- 答:策略包括:1)按重排分数截断 top-N;2)去重(相似度 > 阈值的只保留一个);3)按信息增益选择(优先保留与 query 不同维度的信息);4)层次压缩(长文档先摘要再送入);5)如果窗口实在不够,考虑用 Map-Reduce 方式分批次处理。
- "RAG 系统怎么评估端到端效果?"
- 答:分三层评估:1)检索层:Recall@K、MRR、NDCG;2)生成层:BLEU/ROUGE(参考存在时)、Faithfulness(事实一致性)、Answer Relevance;3)系统层:延迟(检索+生成)、成本(调用次数)、用户满意度。工具上可用 RAGAS 框架自动评测,但最终仍需人工审核关键 case。
#10. RAG 和 fine-tuning 应该怎么选?
#标准答案
核心判断标准不是“哪个更高级”,而是你要解决的是知识问题、行为问题,还是两者都有。
- 如果问题主要是“知识更新快、文档多、要求可追溯”,优先考虑
RAG。 - 如果问题主要是“输出风格、任务格式、工具调用习惯、业务流程行为”需要改变,优先考虑 fine-tuning。
- 如果既要领域知识,又要任务行为改变,通常会是
RAG + fine-tuning组合。
一句话判断:
RAG更像外挂知识库;- fine-tuning 更像改模型习惯和能力分布。
#深度解析
1. RAG vs Fine-tuning 的本质区别
| 维度 | RAG | Fine-tuning |
|---|---|---|
| 作用对象 | 外部知识库 | 模型参数 |
| 更新频率 | 实时(随时改文档) | 慢(需重新训练) |
| 知识边界 | 受限于检索文档 | 受限于训练数据 + 参数容量 |
| 可追溯性 | 强(可展示来源) | 弱(知识在参数中不可解释) |
| 成本 | 低(改文档即可) | 高(训练 + 部署新模型) |
| 适用知识类型 | 频繁更新、结构化、大量 | 稳定、隐性、模式化 |
| 对行为的影响 | 弱(不改变模型习惯) | 强(改变输出风格/格式) |
2. 决策矩阵:什么时候选什么?
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 产品文档 FAQ,答案常更新 | RAG | 更新文档比重新训练模型快得多 |
| 客服话术风格调整(更礼貌/更简洁) | Fine-tuning | 改变的是输出行为,不是知识 |
| 医学诊断辅助(需严格引用指南) | RAG | 必须可追溯,不能靠参数记忆 |
| 代码生成特定公司编码规范 | Fine-tuning | 编码风格是隐性模式,适合参数学习 |
| 法律条款查询 + 回答格式要求 | RAG + Fine-tuning | 知识用 RAG,格式用 SFT |
| 模型完全不懂某小众领域 | Continue Pretrain + SFT | RAG 也救不了,因为基础理解都没有 |
3. RAG + Fine-tuning 组合策略
实际生产中常见三层架构:
┌─────────────────────────────────────┐
│ Layer 3: 行为对齐 (SFT/DPO) │
│ → 调整输出风格、安全边界、格式 │
├─────────────────────────────────────┤
│ Layer 2: 领域适配 (Continue PT) │
│ → 注入领域术语、补充基础知识 │
├─────────────────────────────────────┤
│ Layer 1: 动态知识 (RAG) │
│ → 实时检索最新文档、事实、数据 │
└─────────────────────────────────────┘
执行顺序:RAG 检索 → 构造 prompt → 送入经过领域适配+行为对齐的模型 → 生成答案
4. 常见误区
| 误区 | 错误认知 | 正确理解 |
|---|---|---|
| "RAG 可以替代 Fine-tuning" | RAG 万能 | RAG 只补知识,不改变行为 |
| "Fine-tuning 后就不需要 RAG" | 模型记住了所有知识 | 参数容量有限,且知识更新慢 |
| "先 Fine-tuning 再 RAG 效果更好" | 顺序无所谓 | 通常是先 RAG 再生成,Fine-tuning 是底座能力 |
| "数据少就做 RAG,数据多就 Fine-tune" | 只看数据量 | 关键看"知识是否稳定"和"是否需要追溯" |
5. 量化成本对比
| 成本项 | RAG | Fine-tuning (7B) |
|---|---|---|
| 初始投入 | 低(向量库+Embedding模型) | 中(训练 infra+数据标注) |
| 单次更新 | 分钟级(改文档重索引) | 小时到天级(重新训练) |
| 推理延迟 | +50-500ms(检索时间) | 无额外延迟 |
| 维护成本 | 文档管理 | 模型版本管理 |
| 人力需求 | 内容运营 | ML工程师 |
6. 面试官常见深挖追问
- "如果 RAG 和 Fine-tuning 都能解决,优先选哪个?"
- 答:默认优先 RAG。原因:1)RAG 迭代更快(改文档 vs 重新训练);2)RAG 更可控(可精确查看来源);3)RAG 风险更低(不会破坏模型已有能力)。只有当 RAG 无法解决(如行为模式改变、隐性知识学习)时才考虑 Fine-tuning。
- "一个系统同时用 RAG 和 Fine-tuning,怎么分工?"
- 答:典型分工:RAG 负责"知道什么"(动态知识检索),Fine-tuning 负责"怎么说"(输出格式、风格、安全边界)。例如:法律助手用 RAG 检索法条(保证准确和可追溯),用 SFT 训练模型按"先结论后依据"的格式回答(保证输出规范)。
- "RAG 检索失败时,Fine-tuning 能兜底吗?"
- 答:有限兜底。Fine-tuning 可以让模型在检索失败时给出更合理的"我不知道"或基于参数知识的推测回答,但这可能引入幻觉。更好的做法是:1)检测检索置信度;2)低置信度时触发降级策略(如转人工、要求澄清);3)而不是依赖模型参数中的不稳定知识。
#11. Agent 和 workflow 的边界是什么?
#标准答案
workflow 更像把步骤提前写死,适合流程稳定、分支有限、可预测性强的任务。Agent 则会根据环境反馈动态决定下一步动作,适合不确定性更高、需要规划和工具选择的任务。
两者的核心区别不在于“有没有调工具”,而在于:
- 决策是否动态;
- 环境反馈是否会改变后续路径;
- 系统是否需要显式状态管理和错误恢复。
一个稳妥回答是:
- 如果任务路径高度固定,用 workflow 更稳、更可控;
- 如果任务必须边观察边决策,才更接近 Agent;
- 很多生产系统并不是纯 Agent,而是”workflow 主干 + Agent 局部决策”。
#深度解析
1. Workflow 与 Agent 的本质差异
| 维度 | Workflow | Agent |
|---|---|---|
| 控制流 | 预定义(if/else、循环、并行) | 动态决策(LLM 决定下一步) |
| 状态管理 | 显式变量传递 | 隐式上下文累积 |
| 错误处理 | 预定义回退路径 | 动态恢复或请求澄清 |
| 可解释性 | 高(流程图即逻辑) | 低(需追踪 LLM 推理过程) |
| 开发成本 | 前期设计成本高 | 后期调试成本高 |
| 灵活性 | 低(需改代码才能调整) | 高(换 prompt 即可改变行为) |
| 可靠性 | 高(确定性执行) | 中(概率性决策) |
2. 生产系统的三种典型模式
| 模式 | 结构 | 适用场景 | 示例 |
|---|---|---|---|
| 纯 Workflow | 完全预定义 | 审批流程、报表生成 | 工单审批:提交→审核→归档 |
| 纯 Agent | LLM 全自主决策 | 开放探索、多工具协调 | 科研助手:搜索→阅读→总结→写作 |
| 混合模式 | Workflow 骨架 + Agent 节点 | 大部分生产系统 | 客服:意图识别(workflow) → 知识检索(agent) → 答案生成(workflow) |
3. 如何判断该用 Workflow 还是 Agent?
决策 checklist:
- 分支数量是否有限?
- 是(≤10 个明确分支)→ Workflow
- 否(开放域)→ Agent
- 失败路径是否可枚举?
- 是 → Workflow
- 否 → Agent
- 每步决策是否需要创造性?
- 否(规则可描述)→ Workflow
- 是(需推理判断)→ Agent
- 是否需要人类可审计?
- 强需求 → Workflow 为主
- 可接受黑盒 → Agent
4. “Workflow 主干 + Agent 局部” 的典型架构
┌──────────────────────────────────────────┐
│ Workflow Engine (确定性编排) │
│ ├─ Step 1: 输入验证 (固定规则) │
│ ├─ Step 2: 意图分类 (固定规则) │
│ ├─ Step 3: Agent 节点 (动态决策) │
│ │ ├─ 检索策略选择 │
│ │ ├─ 工具调用组合 │
│ │ └─ 结果综合判断 │
│ ├─ Step 4: 输出格式化 (固定模板) │
│ └─ Step 5: 质量检查 (固定规则) │
└──────────────────────────────────────────┘
5. 面试官常见深挖追问
- ”一个客服系统,用户可能问任何问题,为什么不用纯 Agent?”
- 答:纯 Agent 虽然灵活,但客服系统有关键路径必须可控(如投诉必须转人工、退款必须校验金额)。用 Workflow 保证关键约束不违反,只在”如何回答用户问题”这一环节用 Agent 生成自然语言,是更稳妥的工程方案。
- ”Agent 比 Workflow 容易出错的环节有哪些?”
- 答:1)循环:Agent 可能陷入无限循环(反复调用同一工具);2)工具误用:选错工具或填错参数;3)状态丢失:忘记之前的中间结论;4)过度规划:对简单任务做过多推理;5)幻觉:把猜测当成事实写入记忆。这些都需要额外的 guardrails(护栏机制)来约束。
- ”如果业务方说'我要 Agent 的灵活性,又要 Workflow 的可靠性',怎么设计?”
- 答:这是混合架构的典型需求。设计思路:1)用 Workflow 定义”不可违反的硬约束”(如安全规则、合规检查);2)在约束内给 Agent 最大的决策自由;3)设置最大步数、超时、人工审批等安全网;4)对 Agent 的关键决策增加”确认步骤”(如”我将执行 X,是否继续?”)。
#12. 为什么 Agent 比普通问答难很多?
#标准答案
因为普通问答更像单次输出问题,而 Agent 是多步决策问题。
Agent 额外难在:
- 状态会累积:前一步错了会影响后一步;
- 工具有不确定性:调用可能失败、超时、返回异常;
- 环境反馈复杂:不是简单对错,而是部分成功、部分失败;
- 评测更难:成功标准常常依赖完整任务轨迹,而不是一句答案。
所以 Agent 的难点不只是模型能力,而是编排、状态、工具协议、错误恢复和评测体系一起组成的系统问题。
#深度解析
1. 单步问答 vs 多步 Agent 的复杂度对比
| 维度 | 单步问答 | Agent |
|---|---|---|
| 计算图 | 单向:输入 → 输出 | 有向图:状态 → 决策 → 行动 → 观察 → ... |
| 错误传播 | 局部(只影响当前回答) | 全局(前步错误会累积放大) |
| 输出空间 | 文本空间 | 文本 + 工具调用 + 状态更新 |
| 终止条件 | 生成结束符 | 任务完成/失败/最大步数/人工干预 |
| 评测标准 | 回答质量(流畅、准确) | 任务完成度(是否达成目标) |
| 外部依赖 | 无 | 工具 API、数据库、文件系统等 |
2. Agent 难的五个层面
| 层面 | 具体问题 | 示例 |
|---|---|---|
| 规划层 | 任务分解是否正确、步骤顺序是否合理 | 让 Agent 写报告,它可能先写结论再收集数据 |
| 执行层 | 工具调用参数是否正确、API 是否稳定 | 调用搜索 API 时参数格式错误导致无结果 |
| 感知层 | 是否正确理解工具返回、是否过滤噪声 | API 返回大量 HTML,Agent 无法提取关键信息 |
| 状态层 | 是否记住已完成事项、是否重复工作 | Agent 忘记了第三步已经查过资料,又查一遍 |
| 评测层 | 如何定义"成功"、如何定位失败原因 | Agent 完成了 90% 的任务,算不算成功? |
3. 错误累积的定量分析
假设单步成功率为 p,共需 n 步:
- 单步问答只需
p的准确率 - Agent 需要所有步骤都成功(简化为
p^n)
| 单步成功率 | 3 步 Agent | 5 步 Agent | 10 步 Agent |
|---|---|---|---|
| 95% | 85.7% | 77.4% | 59.9% |
| 90% | 72.9% | 59.0% | 34.9% |
| 80% | 51.2% | 32.8% | 10.7% |
结论:Agent 的每一步都必须非常可靠,否则整体成功率会指数级下降。
4. 为什么"评测更难"?
单步问答评测:
- 有参考答案 → 用 BLEU/ROUGE/Exact Match
- 无参考答案 → 用 LLM-as-a-Judge
Agent 评测:
- 轨迹正确性:中间步骤是否都合理?(即使最终答案对)
- 效率:是否用了最少步骤完成?
- 鲁棒性:工具失败时是否能恢复?
- 安全性:是否调用了不该调用的工具?
- 一致性:同样输入是否产生类似结果?
常用评测 benchmark:WebArena、SWE-bench、OSWorld、ToolBench
5. 缓解 Agent 难度的工程策略
| 策略 | 方法 | 效果 |
|---|---|---|
| 降低步数 | 更好的任务分解、批量工具调用 | 减少错误累积机会 |
| 增加验证 | 每步结果自检、单元测试 | 早发现早纠正 |
| 人在环路 | 关键步骤人工确认 | 提高可靠性 |
| 工具兜底 | 备用工具、默认回退 | 减少单点失败 |
| 状态显式化 | 结构化状态记录、强制更新 | 防止状态丢失 |
6. 面试官常见深挖追问
- "如果 Agent 每一步成功率只有 80%,怎么提升到可用水平(>95% 整体成功率)?"
- 答:1)减少步数(如从 5 步优化到 3 步,
0.8^5=33%→0.8^3=51%);2)增加验证步骤(每步后自检,把 80% 提升到 90%);3)引入 human-in-the-loop(在关键决策点人工审核);4)使用更强的模型(如从 7B 换到 70B,单步成功率可能从 80% 提升到 90%);5)设计更好的 prompt 和 few-shot 示例。
- 答:1)减少步数(如从 5 步优化到 3 步,
- "Agent 的错误恢复应该由模型自己决定,还是由系统预设规则决定?"
- 答:分层处理:1)可预见的错误(如 API 超时、参数错误)由系统规则处理(自动重试、回退);2)不可预见的错误(如工具返回意外格式)由模型决定(LLM 判断是继续、换工具还是终止)。纯规则太僵化,纯模型太不可控,混合最稳妥。
- "怎么评估一个 Agent 系统是否'足够好'可以上线?"
- 答:需要三层评估都达标:1)单步准确率 > 90%(工具调用、参数填充);2)端到端任务成功率 > 80%(完整流程);3)安全指标 100%(不调用危险工具、不泄露数据)。此外还需要监控上线后的实际表现:用户满意度、人工接管率、平均完成时间。
#13. 幻觉是什么?应该怎么分层理解?
#标准答案
幻觉通常指模型生成了看似合理、实则不被事实或证据支持的内容。
但真正回答时,最好不要只给一个定义,而是分层:
- 参数知识不足:模型本来就没学到。
- 检索失败:外部知识没找对。
- 上下文利用失败:证据给了,但没用好。
- 解码导致的过度自信补全:模型为了连贯性乱补。
- 任务本身模糊:用户问题定义不清,模型被迫猜测。
缓解方法也要分层对应:
- 补知识:继续预训练或 RAG;
- 补约束:引用、结构化输出、工具调用;
- 补评测:建立 factuality(事实一致性)和 faithfulness(忠实于证据)评测;
- 补系统防线:高风险场景加 guardrail 和人工复核。
#深度解析
1. 幻觉的五种类型与根因分析
| 类型 | 根因 | 典型表现 | 检测难度 |
|---|---|---|---|
| 参数知识幻觉 | 训练数据中缺少该知识 | 编造不存在的法条、人物、事件 | 难(需事实核查) |
| 检索失败幻觉 | RAG 没召回正确文档 | 回答偏离用户问题主题 | 中(检查 retrieval top-k) |
| 上下文利用失败 | 证据在 prompt 中但模型忽略 | 用户给了资料,模型仍凭记忆回答 | 中(引用一致性检查) |
| 解码过度自信 | temperature 低 + softmax 峰值 | 模型对错误答案置信度极高 | 难(需外部验证) |
| 任务模糊幻觉 | 用户问题本身有歧义 | 回答看似合理但偏离用户真实意图 | 难(需澄清机制) |
2. 为什么大模型特别容易幻觉?
三个结构性原因:
1. 训练目标:CLM 只要求"预测下一个 token 的概率高",不要求"内容真实"
→ 模型学会的是"什么说法最连贯",不是"什么说法最正确"
2. 参数化知识:知识被压缩成数十亿参数的分布式表示
→ 精确事实(如"2024年GDP")和模糊记忆混在一起,难以区分
3. 生成压力:自回归解码必须每步输出一个 token
→ 即使模型"不确定",也被迫生成最连贯的猜测
3. 幻觉缓解的层次化策略
第一层:知识层(解决"不知道")
├─ RAG:用外部检索补充模型缺失的知识
├─ Continue Pretraining:用领域数据补知识缺口
└─ 知识编辑:直接修改模型参数中的特定事实
第二层:行为层(解决"说不好")
├─ SFT:训练模型"不确定时说不知道"
├─ RLHF/DPO:奖励诚实回答,惩罚编造
└─ 引用约束:强制模型引用来源,不引用则拒答
第三层:系统层(解决"管不住")
├─ Guardrail:规则过滤敏感/错误内容
├─ 事实核查:用外部 API/数据库验证关键事实
└─ 人工审核:高风险场景必须人工确认
4. 幻觉评测指标
| 指标 | 含义 | 如何计算 |
|---|---|---|
| Hallucination Rate | 生成内容中幻觉比例 | 人工标注或自动事实核查 |
| Factuality Score | 事实正确性 | 与可信来源对比 |
| Faithfulness | 是否忠实于输入上下文 | 检查输出是否和 prompt 中的证据矛盾 |
| Answer Relevance | 回答是否和问题相关 | 语义相似度或人工判断 |
5. 面试官常见深挖追问
- "RAG 能完全消除幻觉吗?"
- 答:不能。RAG 解决的是"知识不足"导致的幻觉,但还有两种幻觉 RAG 管不了:1)模型忽略了检索到的资料,仍凭参数记忆回答(上下文利用失败);2)检索到的资料本身有错误(垃圾进垃圾出)。完整的防幻觉需要 RAG + 训练(让模型学会忠实于上下文)+ 系统层校验。
- "怎么检测模型是不是在幻觉?"
- 答:多层检测:1)不确定性估计(看 softmax 熵,低熵+错误回答 = 高置信幻觉);2)self-consistency(多次采样,不一致则怀疑);3)外部验证(用搜索引擎/数据库核查关键事实);4)引用检查(要求模型标注来源,检查来源是否存在)。
- "为什么模型对幻觉内容的置信度往往很高?"
- 答:因为训练目标是最大化似然,模型会学会对"连贯"的文本给出高概率。幻觉内容如果语法通顺、符合上下文逻辑,softmax 输出可能接近 1。这和人类"越不懂越自信"的认知偏差类似。缓解:训练时加入"不确定性表达"数据(如"我不确定,但..."),让模型学会校准置信度。
#14. MoE 为什么重要?它的代价是什么?
#标准答案
MoE(混合专家)的核心价值是:通过“每个 token 只激活少量专家”,在不让单 token 计算量线性爆炸的前提下,把模型总容量做得更大。
它重要,是因为它提供了一条“提高模型容量但不完全等比例增加每步计算”的路径。
但它的代价也很明显:
- 路由器训练复杂;
- 专家负载可能不均衡;
- 分布式通信压力更大;
- 推理部署和监控都更复杂;
- 某些场景下收益不如 dense 模型稳定。
所以 MoE 不是白捡参数,它是用更高系统复杂度换更大容量。
#深度解析
1. MoE 的数学原理
标准 Dense 模型的前向传播:
h = FFN(x) = σ(x·W_1)·W_2
参数量:2 × d_model × d_ff
MoE 替换 FFN 为多个专家网络 + 路由器:
g = Router(x) = Softmax(W_g · x) # 路由分数
TopK_idx = topk(g, k) # 选 top-k 专家
h = Σ_{i∈TopK_idx} g_i · Expert_i(x)
其中 Expert_i(x) = σ(x·W_1^i)·W_2^i
| 组件 | 参数量 | 每 token 激活参数 |
|---|---|---|
| Dense FFN | 2·d·d_ff | 2·d·d_ff (100%) |
| MoE (E 专家, top-k) | E × 2·d·d_ff | k/E × 总参数 (如 k=2,E=8 → 25%) |
2. 容量与效率的定量对比
以 Switch Transformer 为例(对比 T5):
| 指标 | T5-Large (Dense) | Switch-Large (MoE) |
|---|---|---|
| 总参数量 | 0.77B | 1.58B (2×) |
| 激活参数 / token | 0.77B | 0.2B (仅 13%) |
| FLOPs / token | 1× | ~1× |
| 训练速度 | 1× | ~1× |
| 下游任务效果 | baseline | +5~10% |
核心洞察:MoE 用 2× 的总参数,只增加 ~0× 的计算量,换取显著的效果提升。
3. MoE 的代价详解
| 代价类型 | 具体表现 | 量化影响 |
|---|---|---|
| 路由复杂度 | 需要训练路由器,可能出现路由崩溃 | 负载均衡损失占总体损失的 1-5% |
| 负载不均 | 某些专家过载,某些专家闲置 | 闲置专家参数浪费,过载专家成瓶颈 |
| 通信开销 | Expert Parallel 需要跨节点 all-to-all | all-to-all 通信可能占训练时间的 10-30% |
| 显存碎片 | 不同 token 激活不同专家,显存访问不规则 | 相比 dense 模型,显存利用率下降 |
| 推理复杂 | 需要动态加载专家,难以 batch 优化 | 高并发时吞吐量不如 dense 稳定 |
| 微调困难 | 专家特化后,领域迁移能力下降 | 需要特殊微调策略(如只调路由或非专家层) |
4. 负载均衡问题与解决
路由崩溃:所有 token 都选同一专家 → 其他专家不训练 → 容量浪费。
解决方法:
- 辅助损失(Auxiliary Loss):
L_aux = α · Σ_i f_i · P_if_i:专家 i 被选的频率P_i:路由器对专家 i 的平均概率- 目标:让
f_i和P_i都均匀分布
- 容量因子(Capacity Factor):限制每个专家处理的 token 数上限,超出的 token 被丢弃或路由到次优专家
- Expert Choice Routing:让专家选 token(而不是 token 选专家),天然负载均衡
5. 典型 MoE 模型配置
| 模型 | 总参数 | 专家数 | Top-k | 激活参数 | 训练 token |
|---|---|---|---|---|---|
| Switch Transformer | 1.6T | 2048 | 1 | ~0.9B | 1.6T |
| GLaM | 1.2T | 64 | 2 | ~97B | 1.6T |
| Mixtral 8×7B | 47B | 8 | 2 | ~13B | - |
| DeepSeek-V2 | 236B | 64 | 6 | ~21B | 8.1T |
6. 面试官常见深挖追问
- "MoE 的总参数量很大,为什么推理时不会变慢?"
- 答:因为每个 token 只激活少数专家(如 top-2/8 = 25% 的参数)。推理时的计算量 ≈ 激活参数对应的计算量,而不是总参数。但注意:1)all-to-all 通信有开销;2)显存仍需加载全部专家权重(或按需加载);3)batch size 小且请求路由分散时,效率会下降。
- "MoE 的 all-to-all 通信为什么比 all-reduce 更棘手?"
- 答:all-reduce 是规约操作(每个卡都有完整数据),通信模式规律;all-to-all 是Scatter-Gather,每个卡需要把 token 送给不同的目标专家,通信模式不规则且依赖输入数据。这导致:1)难以重叠计算和通信;2)负载不均时某些链路成为瓶颈;3)需要专门优化(如 EP 分组、通信压缩)。
- "为什么 Mixtral 8×7B 的实际效果接近 70B dense 模型,但推理更快?"
- 答:Mixtral 8×7B 总参数 47B,但每个 token 只激活 2 个专家(约 13B 参数)。13B 的激活参数规模意味着:1)计算量接近 13B dense 模型;2)但 47B 的总容量提供了更强的表示能力;3)因此效果接近 70B dense(因为容量大),速度接近 13B dense(因为激活少)。这就是 MoE 的核心优势。
#15. GQA / MQA 为什么会成为高频考点?
#标准答案
因为它们直接连接“模型结构”与“推理成本”。
标准 MHA 里,不同 attention head 都有自己的 K/V;而 MQA 让多个 query head 共享同一组 K/V,GQA 则是在两者之间,用分组共享的方式平衡质量与成本。
它们重要的原因是:
- 可以显著减小
KV cache占用; - 可以降低推理带宽压力;
- 尤其适合长上下文和高并发推理服务。
标准说法可以是:MQA 最省,但质量风险更大;GQA 是更常见的折中。
#深度解析
1. MHA / MQA / GQA 的结构差异(图示)
MHA (Multi-Head Attention):
Q: [H1, H2, H3, H4] K: [H1, H2, H3, H4] V: [H1, H2, H3, H4]
↑ 每个 head 独立的 K/V
GQA (Grouped-Query Attention):
Q: [H1, H2, H3, H4] K: [G1, G1, G2, G2] V: [G1, G1, G2, G2]
↑ 2 个 query head 共享 1 组 K/V
MQA (Multi-Query Attention):
Q: [H1, H2, H3, H4] K: [G1, G1, G1, G1] V: [G1, G1, G1, G1]
↑ 所有 query head 共享 1 组 K/V
2. KV cache 节省的定量对比
假设:d_model=4096, num_heads=32, head_dim=128, seq_len=4096, batch=1
| 方法 | KV heads | KV cache 大小 (FP16) | 相对 MHA | 典型代表 |
|---|---|---|---|---|
| MHA | 32 | 2 × 32 × 4096 × 128 × 2 = 64 MB | 1× | 原始 Transformer |
| GQA (8 groups) | 8 | 2 × 8 × 4096 × 128 × 2 = 16 MB | 0.25× | LLaMA-2-70B, Qwen |
| MQA | 1 | 2 × 1 × 4096 × 128 × 2 = 2 MB | 0.03× | PaLM, Falcon |
注意:这是每层每 batch 的 KV cache。32 层时,MHA 总 KV cache = 2GB,GQA = 512MB,MQA = 64MB。
3. 为什么共享 K/V 不会严重损害效果?
核心洞察:不同 query head 关注的信息类型不同,但它们查询的"知识来源"可以共享。
比喻:
- MHA:4 个研究员,每人有自己的图书馆(K/V)
- GQA:4 个研究员,共享 2 个图书馆
- MQA:4 个研究员,共享 1 个图书馆
虽然图书馆少了,但只要藏书够全,研究员仍能找到需要的资料。
实验证明(LLaMA-2 论文):
- GQA 在大部分任务上接近 MHA 效果
- MQA 在长文本和复杂推理上略有下降
4. 生产环境的选择策略
| 场景 | 推荐 | 原因 |
|---|---|---|
| 长上下文 (>32K) | MQA 或 GQA | KV cache 是瓶颈,必须减小 |
| 通用对话 (<8K) | GQA | 平衡效果和效率 |
| 追求绝对效果 | MHA | 无压缩,表达力最强 |
| 高并发 serving | MQA | 显存最小,batch size 可以更大 |
5. 面试官常见深挖追问
- "GQA 的 group size 怎么选?"
- 答:经验值:num_heads / kv_heads = 4 或 8。如 32 个 head 配 8 个或 4 个 KV head。group size 越大 → KV cache 越小,但效果风险越大。LLaMA-2-70B 用 8 KV heads(32/8=4),是一个经过验证的折中。
- "MQA 为什么比 GQA 省更多显存,但效果下降也更明显?"
- 答:MQA 把所有 query head 的 K/V 压缩到 1 组,信息损失最大。当不同 head 需要查询"不同类型"的信息时(如一个 head 查主语、一个 head 查时间),共享同一组 K/V 会导致信息混淆。GQA 分多组,每组服务一部分 head,信息损失更小。
- "GQA/MQA 训练时需要特殊处理吗?"
- 答:训练时通常从 MHA 模型"改造"而来:1)用 MHA 模型初始化;2)把同一组的 K/V head 参数做平均,作为 GQA/MQA 的初始化;3)继续训练。直接从头训练 GQA/MQA 也可以,但用 MHA 做初始化收敛更快、效果更好。
#16. 怎么快速估算一个 7B 模型的参数量?
#标准答案
以 LLaMA 风格模型为例,可以先记一个近似:
- 单层主干参数量大约是
12d^2 - 总参数量大约是
embedding + L * 12d^2
如果:
L = 32d = 4096V = 32000
那么:
- embedding 约
32000 * 4096 ≈ 131M - 单层约
12 * 4096^2 ≈ 201M 32层约6.43B- 再加 embedding 和少量其他参数,总体就在
6.6B - 6.8B
所以通常会被称作 7B 量级。
面试时重点不是每一位都算准,而是数量级、组成和近似思路正确。
#深度解析
1. 参数量估算的通用公式
总参数量 ≈ V × d + L × (12 × d²) + 2 × d
其中:
- V:词表大小
- d:隐藏维度 (hidden_size)
- L:层数 (num_layers)
- 12d²:每层参数量(Attention 4d² + FFN 8d²)
- 2d:最后的 LayerNorm + bias 等
| 组件 | 参数 | 计算 |
|---|---|---|
| Attention Q/K/V/O 投影 | 4 × d² | W_Q, W_K, W_V, W_O 各 d×d |
| FFN 扩张 + 压缩 | 2 × d × (4d) = 8d² | 通常中间维度 = 4d |
| 单层总计 | 12d² |
每层 12d² 的拆解:
2. 快速记忆:不同规模的近似
| 模型规模 | 典型配置 (L/d/V) | 估算公式 | 结果 |
|---|---|---|---|
| 1B | 24/2048/32000 | 32K×2K + 24×12×4M | ~1.2B |
| 7B | 32/4096/32000 | 32K×4K + 32×12×16M | ~6.8B |
| 13B | 40/5120/32000 | 32K×5K + 40×12×26M | ~13.4B |
| 70B | 80/8192/32000 | 32K×8K + 80×12×67M | ~64.5B |
口诀: embedding 占小头,每层 12d² 占大头。
3. 推理显存的快速估算
推理显存 ≈ 模型权重 + KV cache
模型权重 (FP16): 2 × 参数量 bytes
KV cache: 2 × batch × seq_len × L × kv_heads × head_dim × 2 bytes
示例:7B 模型,batch=1, seq_len=4096, GQA(8 heads)
- 权重: 2 × 7B = 14 GB
- KV cache: 2 × 1 × 4096 × 32 × 8 × 128 × 2 = 512 MB
- 总计: ~14.5 GB
4. 训练显存的快速估算
训练显存 ≈ 模型权重 + 优化器状态 + 梯度 + 激活值
- 模型权重 (FP16): 2 × P
- 优化器状态 (Adam, FP32): 2 × 4 × P = 8P (一阶矩 + 二阶矩)
- 梯度 (FP16): 2 × P
- 激活值: 取决于序列长度和 batch size
示例:7B 模型,Adam, no checkpointing
- 基础: (2 + 8 + 2) × 7B = 84 GB
- 加上激活: 轻松超过 100GB
5. 面试官常见深挖追问
- "70B 模型的 FFN 中间维度为什么是 4d 而不是 2d 或 8d?"
- 答:经验值。原始 Transformer 论文用 4d,后续工作发现这个比例在效果和效率间平衡较好。2d → 表达能力不足;8d → 参数量太大,训练更慢。但这不是绝对的,如 PaLM 用 SwiGLU 时中间维度调整为 8d/3(约 2.67d),因为 SwiGLU 有两个线性层。
- "词表大小对总参数量影响大吗?"
- 答:对 7B 模型不大(131M / 6800M ≈ 2%),但对小模型影响大。如 1B 模型,embedding 占 10%+。如果词表从 32K 扩到 100K(多语言),embedding 参数量从 131M 变成 410M,对 7B 模型也增加了 4%。
- "怎么快速估算训练一个 7B 模型需要多少 GPU 小时?"
- 答:粗略公式:
总 FLOPs ≈ 6 × P × D(P=参数量, D=token 数)。7B 模型训 1T tokens → 6 × 7B × 1T = 4.2×10¹⁹ FLOPs。A100 理论峰值 312 TFLOPs,利用率通常 30-50% → 有效 ~100 TFLOPs。所需时间 = 4.2×10¹⁹ / 100×10¹² = 4.2×10⁵ 秒 ≈ 117 小时 ≈ 5 天(单卡)。8 卡并行 → ~15 小时(理想情况,实际可能 20-30 小时)。
- 答:粗略公式:
#17. 手撕 softmax 时,标准回答应该包含什么?
#标准答案
如果让你写 softmax,光写公式不够,标准答案至少要包含两点:
- 公式:
softmax(x_i) = exp(x_i) / sum_j exp(x_j) - 数值稳定:实际实现时通常先减去最大值,即
exp(x - max(x)),避免指数上溢。
如果再进一层,可以补一句:
- 输出是一个概率分布,各项非负且和为 1;
- 在 attention 和采样里都会被大量使用。
#深度解析
1. Softmax 的数值稳定性
def softmax(x):
# 不稳定版本(可能溢出)
exp_x = np.exp(x)
return exp_x / np.sum(exp_x)
# 问题:x = [1000, 1001, 1002]
# exp(1000) = inf(溢出!)
def softmax_stable(x):
# 稳定版本
x_max = np.max(x)
exp_x = np.exp(x - x_max) # 减去最大值
return exp_x / np.sum(exp_x)
# x = [1000, 1001, 1002]
# x - max = [-2, -1, 0]
# exp = [0.135, 0.368, 1.0]
# 结果 = [0.090, 0.245, 0.665] ✓
2. 为什么减去最大值不影响结果?
softmax(x_i) = exp(x_i) / Σ_j exp(x_j)
= exp(x_i - C) / Σ_j exp(x_j - C) (分子分母同乘 exp(-C))
令 C = max(x),则所有指数项 ≤ 0,不会溢出。
3. 面试官常见深挖追问
- "softmax 的输入很大或很小时,输出有什么特点?"
- 答:输入差距大时 → 输出接近 one-hot(赢家通吃);输入接近时 → 输出接近均匀分布。温度参数 T 控制这一点:T→0 时 sharp,T→∞ 时 uniform。
#18. 手撕 attention 时,标准回答应该怎么说?
#标准答案
一个最小 attention 前向通常包括:
- 输入
Q/K/V; - 计算分数
QK^T / sqrt(d_k); - 加 mask(如果是 causal attention 或 padding mask);
- 对分数做 softmax;
- 用权重加权
V得到输出。
如果面试官追问,要主动补两点:
- causal mask 是为了防止看见未来 token;
- softmax 前最好做数值稳定处理。
#深度解析
1. 最小 attention 的 PyTorch 实现
def scaled_dot_product_attention(Q, K, V, mask=None):
"""
Q, K, V: (batch, seq_len, d_model)
mask: (batch, seq_len, seq_len) 或 None
"""
d_k = Q.size(-1)
# Step 1: Q @ K^T
scores = torch.matmul(Q, K.transpose(-2, -1)) # (batch, seq, seq)
# Step 2: Scale
scores = scores / math.sqrt(d_k)
# Step 3: Mask (causal or padding)
if mask is not None:
scores = scores.masked_fill(mask == 0, float('-inf'))
# Step 4: Softmax (with numerical stability)
attn_weights = F.softmax(scores, dim=-1) # (batch, seq, seq)
# Step 5: Weighted sum of V
output = torch.matmul(attn_weights, V) # (batch, seq, d_model)
return output, attn_weights
2. 数值稳定性的具体做法
# 标准 softmax 的数值问题:
scores = [1000, 1001, 1002]
exp(scores) = [inf, inf, inf] # 溢出!
# 数值稳定版本:
scores = scores - scores.max() # 减去最大值
scores = [-2, -1, 0]
exp(scores) = [0.135, 0.368, 1.0] # 安全
PyTorch 的 F.softmax 内部已经做了这个处理。
3. Causal Mask 的构造
# 对于 seq_len=4 的 causal mask:
mask = [[1, 0, 0, 0], # token 0 只能看自己
[1, 1, 0, 0], # token 1 看 token 0,1
[1, 1, 1, 0], # token 2 看 token 0,1,2
[1, 1, 1, 1]] # token 3 看所有
# PyTorch 构造:
causal_mask = torch.tril(torch.ones(seq_len, seq_len)) # 下三角矩阵
4. 面试官常见深挖追问
- "如果 Q/K/V 的维度不同,怎么办?"
- 答:通常 Q/K 的维度相同(d_k),V 的维度可以不同(d_v)。输出维度 = d_v。在 Multi-Head Attention 中,d_k = d_v = d_model / num_heads。
- "为什么要除以 sqrt(d_k)?"
- 答:当 d_k 很大时,Q·K 的点积值会很大(方差随 d_k 增长),导致 softmax 进入饱和区(梯度极小)。除以 sqrt(d_k) 把方差缩回 1,保持梯度健康。
- "手写 attention 时,最常见的错误是什么?"
- 答:1)忘记 transpose K(应该是 K^T 不是 K);2)mask 放错位置(应该在 softmax 前);3)忘记 scale;4)维度不匹配(Q@K^T 的维度计算错误)。
#19. 手撕 top-k 和 top-p 时,最标准的解释是什么?
#标准答案
top-k:只保留概率最高的前k个 token,再重新归一化采样。top-p:按概率从高到低排序,保留累计概率刚超过阈值p的最小集合,再归一化采样。
它们的区别是:
top-k保留的候选数量固定;top-p保留的候选数量动态变化,更自适应分布陡峭程度。
标准补充是:
temperature控制分布平滑度;top-k / top-p控制候选空间大小;- 三者常一起调。
#深度解析
1. Top-K 的完整实现
def top_k_sampling(logits, k=50, temperature=1.0):
"""
logits: (vocab_size,) 未归一化的分数
"""
# 1. Temperature scaling
logits = logits / temperature
# 2. 计算概率
probs = F.softmax(logits, dim=-1)
# 3. 取 top-k
top_k_probs, top_k_indices = torch.topk(probs, k)
# 4. 重新归一化
top_k_probs = top_k_probs / top_k_probs.sum()
# 5. 采样
next_token = torch.multinomial(top_k_probs, num_samples=1)
return top_k_indices[next_token]
2. Top-P (Nucleus Sampling) 的完整实现
def top_p_sampling(logits, p=0.9, temperature=1.0):
"""
logits: (vocab_size,) 未归一化的分数
"""
# 1. Temperature scaling
logits = logits / temperature
# 2. 计算概率并排序
probs = F.softmax(logits, dim=-1)
sorted_probs, sorted_indices = torch.sort(probs, descending=True)
# 3. 计算累计概率
cumsum_probs = torch.cumsum(sorted_probs, dim=-1)
# 4. 找到累计概率超过 p 的位置
mask = cumsum_probs <= p
# 至少保留第一个(mask[0] = True)
mask[0] = True
# 5. 只保留 mask 内的 token
selected_probs = sorted_probs[mask]
selected_indices = sorted_indices[mask]
# 6. 重新归一化并采样
selected_probs = selected_probs / selected_probs.sum()
next_token_idx = torch.multinomial(selected_probs, num_samples=1)
return selected_indices[next_token_idx]
3. Top-K vs Top-P 的直观对比
示例分布:[0.4, 0.3, 0.2, 0.05, 0.03, 0.02]
Top-K (k=3):
保留: [0.4, 0.3, 0.2]
归一化后: [0.444, 0.333, 0.222]
Top-P (p=0.9):
累计: [0.4, 0.7, 0.9] → 保留前 3 个
归一化后: [0.444, 0.333, 0.222]
Top-P (p=0.95):
累计: [0.4, 0.7, 0.9, 0.95] → 保留前 4 个
归一化后: [0.421, 0.316, 0.211, 0.053]
4. Temperature 的作用
| Temperature | 效果 | 适用场景 |
|---|---|---|
| T < 1 (如 0.7) | 分布更尖锐,高概率 token 更容易被选中 | 需要确定性输出(代码、数学) |
| T = 1 | 原始分布 | 通用 |
| T > 1 (如 1.5) | 分布更平缓,低概率 token 也有机会 | 需要创造性输出(写作、头脑风暴) |
5. 面试官常见深挖追问
- "为什么 top-p 比 top-k 更自适应?"
- 答:top-k 固定保留 k 个候选,无论分布形状如何。如果分布很平坦(如 [0.2, 0.18, 0.16, ...]),k=50 可能包含很多低质量候选;如果分布很尖锐(如 [0.9, 0.02, 0.02, ...]),k=50 会包含大量无意义候选。top-p 根据累计概率动态调整保留数量,更灵活。
- "top-k=1 是什么?"
- 答:贪婪解码(greedy decoding)。每次都选概率最高的 token,无随机性。适合需要确定性的任务(如代码生成、数学推理),但容易陷入重复模式。
- "如果 temperature → 0,会发生什么?"
- 答:softmax 输出变成 one-hot 向量(概率全部集中在最大值位置)。采样退化为贪婪解码。如果多个 token 概率相同,可能随机选其中一个。
#20. 如果被问“如何回答一道大模型面试题”,最标准的通用框架是什么?
#标准答案
最稳的结构是五步:
- 先定义问题:它是什么,解决什么。
- 再讲机制:它怎么工作。
- 再讲优点:为什么大家会用。
- 再讲代价:成本、局限、边界。
- 最后讲场景:什么时候该用,什么时候不该用。
这个结构适用于绝大多数大模型问题,包括:
RoPELoRARAGAgentMoEKV cacheDPO
也就是说,真正高分的关键不是背更多名词,而是能稳定地把一个问题讲成“定义 -> 机制 -> 收益 -> 代价 -> 边界”。
#深度解析
1. 五步法的深层逻辑
| 步骤 | 考察能力 | 面试官想听到什么 |
|---|---|---|
| 定义 | 概念理解 | 你知道它是什么,不是背诵名词 |
| 机制 | 技术深度 | 你能讲清楚内部工作原理 |
| 优点 | 价值判断 | 你知道为什么它重要 |
| 代价 | 批判思维 | 你不盲目追捧,知道局限性 |
| 场景 | 工程经验 | 你知道什么时候用、什么时候不用 |
2. 应用示例:回答 "LoRA 是什么"
定义:
LoRA 是一种参数高效微调方法,通过低秩矩阵分解来减少微调参数量。
机制:
冻结原始权重 W,引入低秩增量 B·A,其中 B∈R^(d×r), A∈R^(r×d), r≪d。
只训练 A 和 B,参数量从 d² 降到 2·r·d。
优点:
1. 训练参数量减少 99%+
2. 推理时可合并回原始权重,零 overhead
3. 多任务场景可切换不同 adapter
代价:
1. 表达能力受限(低秩假设)
2. 某些任务需要调 rank
3. 不能注入全新知识(只改行为)
场景:
适合:任务适配、风格调整、多任务切换
不适合:领域知识注入、需要大幅改变模型能力
3. 面试官常见深挖追问
- "如果你只有 2 分钟回答一个问题,怎么取舍这五步?"
- 答:优先讲定义和机制(占 60% 时间),快速带过优点和代价(30%),场景一句话总结(10%)。因为面试官最关心你是否真正理解原理。