#标准答案速查区

这一节不是继续加题目,而是把最核心、最常被问、最值得背熟的题,整理成可以直接学习和查阅的标准答案。这里的“标准答案”不是唯一答案,而是面试和笔试里最稳、最通用、最不容易失分的回答版本。

#1. 什么是 Transformer?为什么它适合大模型?

#标准答案

Transformer 是一种以 self-attention(自注意力)为核心的序列建模架构。它的基本模块通常包括输入 embedding、位置编码、多头注意力、前馈网络、残差连接和归一化层。

它适合大模型,核心原因有三点:

  1. 并行性强:和 RNN 按时间步串行计算不同,Transformer 可以在训练时同时处理整段序列,更适合大规模 GPU/TPU 并行。
  2. 长距离依赖建模更直接:任意两个 token 都可以通过 attention 直接交互,路径长度短,不像 RNN 那样需要跨很多时间步传递信息。
  3. 易于扩展:无论是模型参数、数据规模还是训练算力,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,先通过线性投影得到 QKV。然后用 QK^T 计算每个 token 对其他 token 的相关性分数,再除以 sqrt(d_k) 做尺度归一化,接着过 softmax 得到注意力权重,最后用这个权重对 V 加权求和,得到输出。

复杂度是 O(n^2),主要不是因为参数矩阵变大,而是因为长度为 n 的序列里,每个 token 都要和其他 token 计算相关性,所以会形成 n × n 的 attention score 矩阵。

要特别区分三件事:

  1. 参数量主要由投影矩阵决定,和序列长度无关。
  2. 计算量会随序列长度平方增长。
  3. 显存/缓存压力在长上下文下也会明显上升,尤其是训练时的 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 数无关。

#3. 为什么主流 LLM 大多采用 Decoder-only

#标准答案

Decoder-only 架构之所以成为通用 LLM 主流,主要因为它和 next-token prediction(下一个 token 预测)目标天然一致。训练时只需要把海量文本拼成自回归序列,目标简单统一,数据构造直接,扩展起来非常自然。

它的优势主要有:

  1. 训练目标统一:预训练、继续预训练、指令微调都能围绕“给定前文预测后文”来组织。
  2. 生成任务兼容性强:对聊天、写作、代码补全、推理、多轮对话都很自然。
  3. 工程上更简单:相比 Encoder-Decoder,结构更统一,推理缓存机制也更成熟。

但它不是没有代价:

  1. 编码输入和生成输出没有天然分工;
  2. 对某些强条件生成任务,不一定总比 Encoder-Decoder 更优;
  3. 推理时是自回归逐 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 分数天然就会带有相对位置信息。

它有效,主要因为:

  1. 更适合自回归 attention:位置关系直接反映在 Q/K 内积里。
  2. 相对位置信息表达自然:对顺序和距离更敏感。
  3. 长度扩展能力通常比纯绝对位置编码更好

它的局限主要是:

  1. 长度超出训练分布后,效果仍可能明显退化;
  2. “支持更长上下文”不等于”真的能有效利用更长上下文”;
  3. 长上下文退化往往还和训练长度、数据分布、注意力稀释、缓存压力、数值稳定性一起有关。

#深度解析

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_m
  • k_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(继续预训练)有什么区别?

#标准答案

两者的核心区别在于目标不同。

  1. Continue pretraining 主要是往模型里继续注入某个领域的统计规律和语言分布,本质上仍然是预训练范式,重点是“让模型更懂这个领域”。
  2. 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

原因:

  1. 先通过 Continue Pretrain 调整知识分布
  2. 再通过 SFT 学习任务格式
  3. 最后通过 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. LoRAQLoRA 的区别是什么?

#标准答案

LoRA 的核心思想是冻结原始大模型参数,只在某些线性层旁边增加低秩增量矩阵进行训练,从而显著降低训练参数量和显存开销。

QLoRALoRA 的基础上更进一步:先把基座模型量化到更低精度,再在量化模型上训练 LoRA adapter,因此显存更省。

可以这样记:

  1. LoRA:少训练参数。
  2. 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,只训练 ABA 通常用高斯初始化,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. RLHFDPO 的区别是什么?

#标准答案

两者都在做偏好对齐,但训练信号形式不一样。

RLHF 经典流程通常是:

  1. 先做 SFT;
  2. 再训练 reward model(奖励模型);
  3. 最后用 PPO 等强化学习方法,让模型去优化奖励。

DPO(Direct Preference Optimization)则把“偏好回答应当比非偏好回答概率更高”直接写成优化目标,不再显式训练 reward model,也不需要完整 PPO 那一套在线强化学习过程。

所以核心区别是:

  1. RLHF 更完整、更灵活,但实现复杂、训练不稳定、成本更高;
  2. 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

所以它能加速,是因为避免了对历史上下文的重复计算。

但它吃显存,是因为:

  1. 每一层都要存历史 token 的 K/V
  2. 序列越长、batch 越大、层数越多、kv_heads 越多,缓存就越大;
  3. 在长上下文和高并发场景下,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 倍。
  • "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 步:

  1. 文档采集与清洗;
  2. 文档切块(chunking);
  3. 向量化或建立混合索引;
  4. 用户 query 预处理;
  5. 召回(retrieval);
  6. 重排(reranking);
  7. 上下文构造并喂给生成模型;
  8. 生成答案并做评测或引用约束。

RAG 的价值不只是“补知识”,而是把更新频繁、无法全部塞进参数里的知识,以检索方式动态提供给模型。

RAG 失败时最常见的问题有四类:

  1. 切块不好,证据被切碎;
  2. 召回不好,该找的没找回来;
  3. 重排不好,噪声排在前面;
  4. 生成阶段没有正确利用证据,出现幻觉或证据冲突。

#深度解析

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 则会根据环境反馈动态决定下一步动作,适合不确定性更高、需要规划和工具选择的任务。

两者的核心区别不在于“有没有调工具”,而在于:

  1. 决策是否动态
  2. 环境反馈是否会改变后续路径
  3. 系统是否需要显式状态管理和错误恢复

一个稳妥回答是:

  • 如果任务路径高度固定,用 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:

  1. 分支数量是否有限?
    • 是(≤10 个明确分支)→ Workflow
    • 否(开放域)→ Agent
  1. 失败路径是否可枚举?
    • 是 → Workflow
    • 否 → Agent
  1. 每步决策是否需要创造性?
    • 否(规则可描述)→ Workflow
    • 是(需推理判断)→ Agent
  1. 是否需要人类可审计?
    • 强需求 → 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 额外难在:

  1. 状态会累积:前一步错了会影响后一步;
  2. 工具有不确定性:调用可能失败、超时、返回异常;
  3. 环境反馈复杂:不是简单对错,而是部分成功、部分失败;
  4. 评测更难:成功标准常常依赖完整任务轨迹,而不是一句答案。

所以 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 示例。
  • "Agent 的错误恢复应该由模型自己决定,还是由系统预设规则决定?"
    • 答:分层处理:1)可预见的错误(如 API 超时、参数错误)由系统规则处理(自动重试、回退);2)不可预见的错误(如工具返回意外格式)由模型决定(LLM 判断是继续、换工具还是终止)。纯规则太僵化,纯模型太不可控,混合最稳妥。
  • "怎么评估一个 Agent 系统是否'足够好'可以上线?"
    • 答:需要三层评估都达标:1)单步准确率 > 90%(工具调用、参数填充);2)端到端任务成功率 > 80%(完整流程);3)安全指标 100%(不调用危险工具、不泄露数据)。此外还需要监控上线后的实际表现:用户满意度、人工接管率、平均完成时间。

#13. 幻觉是什么?应该怎么分层理解?

#标准答案

幻觉通常指模型生成了看似合理、实则不被事实或证据支持的内容。

但真正回答时,最好不要只给一个定义,而是分层:

  1. 参数知识不足:模型本来就没学到。
  2. 检索失败:外部知识没找对。
  3. 上下文利用失败:证据给了,但没用好。
  4. 解码导致的过度自信补全:模型为了连贯性乱补。
  5. 任务本身模糊:用户问题定义不清,模型被迫猜测。

缓解方法也要分层对应:

  • 补知识:继续预训练或 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 计算量线性爆炸的前提下,把模型总容量做得更大。

它重要,是因为它提供了一条“提高模型容量但不完全等比例增加每步计算”的路径。

但它的代价也很明显:

  1. 路由器训练复杂;
  2. 专家负载可能不均衡;
  3. 分布式通信压力更大;
  4. 推理部署和监控都更复杂;
  5. 某些场景下收益不如 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×
下游任务效果 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_i
    • f_i:专家 i 被选的频率
    • P_i:路由器对专家 i 的平均概率
    • 目标:让 f_iP_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/VGQA 则是在两者之间,用分组共享的方式平衡质量与成本。

它们重要的原因是:

  1. 可以显著减小 KV cache 占用;
  2. 可以降低推理带宽压力;
  3. 尤其适合长上下文和高并发推理服务。

标准说法可以是: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 原始 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 = 32
  • d = 4096
  • V = 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,光写公式不够,标准答案至少要包含两点:

  1. 公式softmax(x_i) = exp(x_i) / sum_j exp(x_j)
  2. 数值稳定:实际实现时通常先减去最大值,即 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 前向通常包括:

  1. 输入 Q/K/V
  2. 计算分数 QK^T / sqrt(d_k)
  3. 加 mask(如果是 causal attention 或 padding mask);
  4. 对分数做 softmax;
  5. 用权重加权 V 得到输出。

如果面试官追问,要主动补两点:

  1. causal mask 是为了防止看见未来 token;
  2. 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-ktop-p 时,最标准的解释是什么?

#标准答案

  • top-k:只保留概率最高的前 k 个 token,再重新归一化采样。
  • top-p:按概率从高到低排序,保留累计概率刚超过阈值 p 的最小集合,再归一化采样。

它们的区别是:

  1. top-k 保留的候选数量固定;
  2. 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. 如果被问“如何回答一道大模型面试题”,最标准的通用框架是什么?

#标准答案

最稳的结构是五步:

  1. 先定义问题:它是什么,解决什么。
  2. 再讲机制:它怎么工作。
  3. 再讲优点:为什么大家会用。
  4. 再讲代价:成本、局限、边界。
  5. 最后讲场景:什么时候该用,什么时候不该用。

这个结构适用于绝大多数大模型问题,包括:

  • RoPE
  • LoRA
  • RAG
  • Agent
  • MoE
  • KV cache
  • DPO

也就是说,真正高分的关键不是背更多名词,而是能稳定地把一个问题讲成“定义 -> 机制 -> 收益 -> 代价 -> 边界”。


#深度解析

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%)。因为面试官最关心你是否真正理解原理。