Skip to content

大模型工程面试专题 ​

大模型工程面试的主线:Transformer 原理 → 训练流程 → 微调技术 → 推理优化 → 部署选型 → 评测实践。

Q1: 什么是大语言模型?和传统 NLP 模型有什么区别? 「🟢 校招/初级」 ​

考察点:大模型的基本概念和与传统方法的对比。

参考答案:

大语言模型(LLM) 是基于 Transformer 架构、在海量文本上自监督预训练的超大规模神经网络,具备语言理解、生成、推理等通用能力。

维度传统 NLP 模型大语言模型
架构RNN/LSTM/CNN + 任务特定头Transformer(纯 Attention)
训练方式监督学习(标注数据)自监督预训练 + 指令微调 + RLHF
参数量百万~亿级亿~万亿级(7B~1T+)
数据量GB 级标注数据TB~PB 级无标注文本
能力单一任务(分类/NER/翻译)通用多任务(Zero/Few-shot)
使用方式训练新模型/微调提示词 + 可选微调
推理方式判别式(输出标签)生成式(输出文本)
部署成本低(模型小)高(GPU/显存/推理优化)
┌──────────────────────────────────────────────────────┐
│           传统 NLP vs LLM 使用流程对比                │
│                                                      │
│  传统 NLP:                                          │
│  收集标注数据 → 训练任务模型 → 部署预测               │
│  每个任务都需要重新训练                              │
│                                                      │
│  大语言模型:                                        │
│  预训练(通用)→ SFT(指令跟随)→ RLHF(对齐)        │
│  → 使用时:写 Prompt → 输出结果                       │
│  一个模型处理所有任务                                │
└──────────────────────────────────────────────────────┘

追问延伸:

  • 大模型能完全替代传统 NLP 吗?(不能,简单分类任务传统方法更快更便宜)
  • LLM 的 Zero-shot 和 Few-shot 有什么区别?(Zero-shot 不给示例,Few-shot 给几个示例)

Q2: 讲讲 Transformer 架构基本原理?Encoder 和 Decoder 是什么? 「🟢 校招/初级」 ​

考察点:Transformer 核心架构和组件理解。

参考答案:

Transformer 是 2017 年 Google 提出的完全基于注意力机制的序列模型架构,抛弃了 RNN 的循环结构,支持并行计算。

┌──────────────────────────────────────────────────────────────┐
│                    Transformer 架构                           │
│                                                              │
│  ┌─────────────────┐         ┌─────────────────┐             │
│  │    Encoder       │         │    Decoder       │             │
│  │                  │         │                  │             │
│  │  Input Embedding │         │  Output Embedding│             │
│  │     + Pos Enc    │         │     + Pos Enc    │             │
│  │        │         │         │        │         │             │
│  │   ┌────▼────┐    │         │   ┌────▼────┐    │             │
│  │   │Masked   │    │         │   │Masked   │    │             │
│  │   │Self-Attn │    │         │   │Self-Attn │   │             │
│  │   └────┬────┘    │         │   └────┬────┘    │             │
│  │   ┌────▼────┐    │         │   ┌────▼────┐    │             │
│  │   │Add & Norm│   │         │   │Cross-Attn│   │             │
│  │   └────┬────┘    │         │   │(K,V from │   │             │
│  │   ┌────▼────┐    │         │   │ Encoder) │   │             │
│  │   │Feed     │    │         │   └────┬────┘    │             │
│  │   │Forward  │    │         │   ┌────▼────┐    │             │
│  │   └────┬────┘    │         │   │Add & Norm│   │             │
│  │   ┌────▼────┐    │         │   └────┬────┘    │             │
│  │   │Add & Norm│   │         │   ┌────▼────┐    │             │
│  │   └─────────┘   │         │   │Feed     │    │             │
│  │  × N 层         │         │   │Forward  │    │             │
│  │                  │         │   └────┬────┘    │             │
│  │  Encoder 输出    │────────►│   ┌────▼────┐    │             │
│  │                  │ K, V    │   │Linear + │    │             │
│  │                  │         │   │Softmax  │    │             │
│  │                  │         │   └────┬────┘    │             │
│  └─────────────────┘         │   输出概率分布    │             │
│                              └─────────────────┘             │
│                                                              │
│  Encoder-Only: BERT, RoBERTa     → 理解任务(分类/NER)        │
│  Decoder-Only: GPT, LLaMA       → 生成任务                    │
│  Encoder-Decoder: T5, BART      → Seq2Seq(翻译/摘要)         │
└──────────────────────────────────────────────────────────────┘

核心组件 ​

组件作用说明
Self-Attention捕捉序列内部关系每个 token 关注所有 token
Multi-Head Attention多角度关注并行多个 Attention,捕捉不同模式
Position Encoding注入位置信息Transformer 无位置感知,需额外编码
Feed Forward非线性变换两层全连接 + 激活函数
Add & Norm残差连接 + LayerNorm缓解梯度消失,稳定训练
Masked Attention防止看到未来信息Decoder 中用下三角掩码
python
import torch
import torch.nn.functional as F
import math

class SelfAttention(torch.nn.Module):
    def __init__(self, dim, heads=8):
        super().__init__()
        self.heads = heads
        self.dim_head = dim // heads
        self.to_qkv = torch.nn.Linear(dim, dim * 3)
        self.to_out = torch.nn.Linear(dim, dim)

    def forward(self, x, mask=None):
        # x: [batch, seq_len, dim]
        qkv = self.to_qkv(x)  # [batch, seq_len, dim*3]
        q, k, v = qkv.chunk(3, dim=-1)

        # 多头拆分
        q = q.view(q.shape[0], q.shape[1], self.heads, self.dim_head).transpose(1, 2)
        k = k.view(k.shape[0], k.shape[1], self.heads, self.dim_head).transpose(1, 2)
        v = v.view(v.shape[0], v.shape[1], self.heads, self.dim_head).transpose(1, 2)

        # Scaled Dot-Product Attention
        attn = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.dim_head)

        if mask is not None:
            attn = attn.masked_fill(mask == 0, float('-inf'))

        attn = F.softmax(attn, dim=-1)
        out = torch.matmul(attn, v)

        # 合并多头
        out = out.transpose(1, 2).reshape(out.shape[0], out.shape[2], -1)
        return self.to_out(out)

为什么大模型都用 Decoder-Only ​

维度Encoder-OnlyEncoder-DecoderDecoder-Only
代表模型BERTT5GPT, LLaMA
训练目标掩码语言模型去噪自编码自回归生成
统一性需要分类头编码+解码两套单一架构统一所有任务
扩展性不好一般最适合 Scaling Law
推理效率一次编码并行编码+串行解码纯串行但 KV Cache 优化

追问延伸:

  • 为什么不用 RNN 做大模型?(RNN 无法并行训练,梯度消失,长序列效果差)
  • Encoder 和 Decoder 能共用参数吗?(可以,ALBERT/共享参数可以减参数量)

Q3: 多头注意力(MHA)有哪些局限?MQA、GQA、Flash Attention 怎么解决? 「🟡 中级」 ​

考察点:注意力机制的优化技术。

参考答案:

MHA 的局限性 ​

标准 MHA(Multi-Head Attention):
- 参数:Q、K、V 各有 n_heads 个投影矩阵
- 显存:KV Cache 占用 = 2 × n_heads × head_dim × seq_len × batch
- 问题:head 数量多时 KV Cache 巨大,推理瓶颈在显存带宽

举例:Llama-2-70B
- n_heads = 64, head_dim = 128, layers = 80
- KV Cache = 2 × 64 × 128 × 4096 × 80 ≈ 80GB(一个请求!)

MQA / GQA 优化 ​

┌──────────────────────────────────────────────────┐
│  MHA(标准多头注意力)                             │
│  Q: n_heads 个头   K: n_heads 个头   V: n_heads 个头│
│  KV Cache: 大                                     │
│                                                   │
│  MQA(Multi-Query Attention)                     │
│  Q: n_heads 个头   K: 1 个头   V: 1 个头           │
│  KV Cache: 缩小 n_heads 倍                        │
│  问题:质量下降明显                                │
│                                                   │
│  GQA(Grouped-Query Attention)                   │
│  Q: n_heads 个头   K: n_groups 个头   V: n_groups  │
│  分组共享 KV,MHA 和 MQA 的折中                     │
│  KV Cache: 缩小 n_heads/n_groups 倍                │
│  质量接近 MHA,速度接近 MQA                        │
└──────────────────────────────────────────────────┘
方法KV 头数KV Cache质量代表模型
MHA= Q 头数大最好Llama-2, GPT
MQA= 1最小略差PaLM, StarCoder
GQA= 中间值中等接近 MHALlama-2-70B, Llama-3
python
class GroupedQueryAttention(torch.nn.Module):
    def __init__(self, dim, n_heads, n_kv_heads):
        super().__init__()
        self.n_heads = n_heads       # Q 的头数
        self.n_kv_heads = n_kv_heads  # KV 的头数(比 n_heads 少)
        self.head_dim = dim // n_heads
        self.n_rep = n_heads // n_kv_heads  # 每个 KV 头对应几个 Q 头

        self.to_q = torch.nn.Linear(dim, n_heads * self.head_dim, bias=False)
        self.to_k = torch.nn.Linear(dim, n_kv_heads * self.head_dim, bias=False)
        self.to_v = torch.nn.Linear(dim, n_kv_heads * self.head_dim, bias=False)
        self.to_out = torch.nn.Linear(n_heads * self.head_dim, dim)

    def forward(self, x):
        q = self.to_q(x).view(..., self.n_heads, self.head_dim)
        k = self.to_k(x).view(..., self.n_kv_heads, self.head_dim)
        v = self.to_v(x).view(..., self.n_kv_heads, self.head_dim)

        # KV 复制扩展到 Q 头数
        k = k.repeat_interleave(self.n_rep, dim=-2)  # 复制 n_rep 份
        v = v.repeat_interleave(self.n_rep, dim=-2)

        # 后续计算同 MHA
        attn = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim)
        attn = F.softmax(attn, dim=-1)
        out = torch.matmul(attn, v)
        return self.to_out(out)

Flash Attention ​

问题:标准 Attention 需要把完整的 [seq_len, seq_len] 矩阵写入 HBM,显存 IO 是瓶颈。

核心思想:利用 GPU 的 SRAM(片上高速缓存),分块计算 Attention,避免中间矩阵在 HBM 中反复读写。

标准 Attention:
  Q×K^T → 写入 HBM → 读回 → Softmax → 写入 → 读回 → ×V
  O(N²) 显存 + 大量 HBM 读写

Flash Attention:
  分块加载 Q, K, V 到 SRAM
  → 在 SRAM 内计算 Q×K^T + Softmax + ×V
  → 只写最终结果到 HBM
  O(N) 显存 + 少量 HBM 读写
维度标准 AttentionFlash Attention
显存O(N²)O(N)
HBM 读写O(N²)O(N²/M), M=SRAM 大小
数值精度标准浮点兼容在线 Softmax
速度慢2-4 倍加速

追问延伸:

  • Flash Attention v2 相比 v1 有什么改进?(更好的并行度,减少非矩阵乘法运算)
  • 为什么 GQA 在推理时比训练时更有优势?(推理受显存带宽限制,训练是计算密集型)

Q4: 大模型的位置编码是干什么用的?sin/cos、RoPE、ALiBi 有什么区别? 「🟡 中级」 ​

考察点:位置编码的作用和不同方案对比。

参考答案:

为什么需要位置编码:Transformer 的 Self-Attention 本质是集合操作(无序的),同一个词在不同位置会产生相同的输出,所以需要注入位置信息。

┌────────────────────────────────────────────────────────────┐
│              位置编码方案对比                                 │
│                                                            │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │  绝对位置编码  │  │  RoPE 旋转位置 │  │  ALiBi 偏置   │    │
│  │  (sin/cos)   │  │  编码         │  │              │    │
│  │              │  │              │  │              │    │
│  │  固定编码     │  │  旋转矩阵     │  │  注意力偏置   │    │
│  │  训练时固定   │  │  相对位置     │  │  线性距离惩罚  │    │
│  │  外推性差     │  │  外推性好     │  │  无需训练     │    │
│  │  BERT/GPT-2  │  │  LLaMA/Qwen  │  │  BLOOM/MPT   │    │
│  └──────────────┘  └──────────────┘  └──────────────┘    │
└────────────────────────────────────────────────────────────┘

详细对比 ​

维度sin/cos 绝对编码RoPE 旋转编码ALiBi
原理位置 i 的编码为 sin(i/10000^(2j/d))对 Q/K 做旋转,角度与相对位置成正比在 Attention Score 上加线性距离偏置
位置类型绝对相对相对
外推性差(超出训练长度效果骤降)好(可扩展到更长序列)好(天然支持长序列)
可学习固定(不可学习)或可学习固定公式无参数
实现位置嵌入层后加Q、K 旋转Attention Score 上加
代表模型BERT, GPT-2LLaMA, Qwen, DeepSeekBLOOM, MPT

RoPE 原理详解 ​

python
# RoPE 的核心思想:通过旋转矩阵编码相对位置
# 对位置 m 的 Q 和位置 n 的 K,它们的点积只依赖于 (m-n)

import torch

def rotate_half(x):
    """将 x 的后半部分取负与前半部分拼接"""
    x1 = x[..., :x.shape[-1] // 2]
    x2 = x[..., x.shape[-1] // 2:]
    return torch.cat([-x2, x1], dim=-1)

def apply_rotary_pos_emb(q, k, cos, sin):
    """应用旋转位置编码"""
    # q, k: [batch, heads, seq_len, head_dim]
    # cos, sin: [seq_len, head_dim]
    q_embed = q * cos + rotate_half(q) * sin
    k_embed = k * cos + rotate_half(k) * sin
    return q_embed, k_embed

# 位置 m 的 q_m 和位置 n 的 k_n 做点积:
# <R(m)*q, R(n)*k> = q^T * R(m-n) * k
# 只依赖于相对位置 (m-n),这是 RoPE 的核心优势

ALiBi 原理 ​

python
# ALiBi 不使用位置编码,而是在 Attention Score 上加距离偏置
# 距离越远的 token,Attention 被线性抑制

def alibi_attention_bias(seq_len, n_heads):
    """生成 ALiBi 偏置矩阵"""
    # 距离矩阵
    positions = torch.arange(seq_len)
    dist = positions.unsqueeze(0) - positions.unsqueeze(1)  # [seq, seq]
    
    # 每个 head 一个斜率(固定值,不可学习)
    slopes = torch.tensor([1 / (2 ** (i // 2)) for i in range(n_heads)])
    
    # 偏置 = -slope * 距离
    bias = -slopes.unsqueeze(-1).unsqueeze(-1) * dist.abs()
    return bias  # [n_heads, seq_len, seq_len]

# 效果:距离越远,Attention 被抑制越多
# 优点:无需训练位置编码,天然支持外推
# 缺点:线性衰减可能过于简单

追问延伸:

  • RoPE 如何支持更长上下文?(YaRN、NTK-aware 等插值方法扩展频率范围)
  • 为什么 ALiBi 没有成为主流?(表达能力不如 RoPE,长序列效果不如经过调整的 RoPE)

Q5: 什么是大模型项目的分词器?原理是什么? 「🟢 校招/初级」 ​

考察点:分词器的基本概念和原理。

参考答案:

分词器(Tokenizer) 是将文本切分为模型可处理的 token 序列的工具,介于文本和模型之间。

┌──────────────────────────────────────────────────────┐
│              分词器工作流程                            │
│                                                      │
│  "我喜欢大模型" → 分词器 → [1024, 5678, 3456, 7890]  │
│   原始文本           →        token ID 序列            │
│                                                      │
│  "I love LLMs"  → 分词器 → [40, 3012, 4156, 29889]   │
│   原始文本           →        token ID 序列            │
└──────────────────────────────────────────────────────┘

主流分词算法 ​

算法原理代表模型特点
BPE合并最高频字节对,逐步构建词表GPT-2, LLaMA最常用,平衡频次和长度
WordPiece类似 BPE,但用似然值选择合并BERT适合拉丁语系
Unigram从大词表中删减,最大化语料概率T5, XLNet适合多语言
SentencePiece直接处理原始文本,空格也作为 tokenLLaMA, T5语言无关,支持多语言

BPE 原理示例 ​

训练过程:
1. 初始词表 = 所有单字符
2. 统计所有相邻 token 对频率
3. 合并频率最高的 token 对 → 新 token
4. 重复 2-3 直到词表大小达到目标

示例:
语料: ["low", "lower", "newest", "widest"]
初始: l, o, w, e, r, n, s, t, i, d

第1轮: "l"+"o" 出现最多 → "lo"
第2轮: "lo"+"w" 出现最多 → "low"
第3轮: "e"+"s" → "es"
...

推理时:
"lowest" → ["low", "es", "t"] → [1024, 3456, 789]

为什么分词很重要 ​

python
# 分词影响:
# 1. 词表大小 → 模型嵌入层参数量
# 2. 序列长度 → 计算量和上下文窗口利用率
# 3. 多语言能力 → 中文是1字1token还是多字1token

# 对比不同模型的中文分词
# GPT-4 (cl100k_base): "我喜欢大模型" → 6 tokens(一字一token)
# LLaMA-3: "我喜欢大模型" → 4 tokens(部分合并)

# 同样1K上下文窗口:
# 英文约 750 词
# 中文约 500-1000 字(取决于分词器)

追问延伸:

  • 词表大小怎么选?(太小→序列长;太大→嵌入层参数多。通常 32K-128K)
  • 为什么中文模型要用专门的分词器?(避免一字一 token,压缩序列长度)

Q6: 大模型是怎么训练出来的? 「🟡 中级」 ​

考察点:大模型训练全流程。

参考答案:

┌──────────────────────────────────────────────────────────────┐
│              大模型训练全流程                                   │
│                                                              │
│  阶段1: 预训练 (Pre-training)                                 │
│  ┌──────────────────────────────────────────────┐            │
│  │  数据: 互联网文本、书籍、代码(TB级)           │            │
│  │  目标: 下一个 token 预测(自回归)              │            │
│  │  算力: 数千张 GPU × 数月                       │            │
│  │  产出: Base 模型(懂语言但不会对话)            │            │
│  └──────────────────────────────────────────────┘            │
│                      │                                       │
│                      ▼                                       │
│  阶段2: 指令微调 (SFT - Supervised Fine-Tuning)              │
│  ┌──────────────────────────────────────────────┐            │
│  │  数据: 高质量指令-回答对(万~百万条)           │            │
│  │  目标: 学会理解指令并给出有用回答               │            │
│  │  产出: Chat 模型(能对话但可能有不当内容)       │            │
│  └──────────────────────────────────────────────┘            │
│                      │                                       │
│                      ▼                                       │
│  阶段3: 人类偏好对齐 (RLHF / DPO)                             │
│  ┌──────────────────────────────────────────────┐            │
│  │  数据: 人类标注的回答偏好排序                    │            │
│  │  目标: 让回答符合人类价值观(安全/有用/诚实)    │            │
│  │  产出: 对齐后的 Chat 模型                       │            │
│  └──────────────────────────────────────────────┘            │
│                      │                                       │
│                      ▼                                       │
│  阶段4: 后训练 (Post-Training,可选)                           │
│  ┌──────────────────────────────────────────────┐            │
│  │  能力增强: 代码/数学/工具调用/多语言            │            │
│  │  安全: Red Teaming + 安全微调                  │            │
│  │  约束: Constitution AI / RLAIF                │            │
│  └──────────────────────────────────────────────┘            │
└──────────────────────────────────────────────────────────────┘

预训练细节 ​

要素说明
数据Common Crawl, Wikipedia, GitHub, 书籍等
数据清洗去重、过滤低质量、PII 脱敏
训练目标自回归:给定前 N 个 token,预测第 N+1 个
优化器AdamW(大 batch + 学习率 warmup + cosine 衰减)
并行策略数据并行 + 张量并行 + 流水线并行
混合精度BF16/FP16 训练

SFT 细节 ​

python
# SFT 训练数据格式
{
  "instruction": "请解释什么是闭包",
  "input": "",
  "output": "闭包是指有权访问另一个函数作用域中变量的函数..."
}

# 只对 output 部分计算 loss(输入部分不计算)
# 这是 SFT 和预训练的关键区别

RLHF 流程 ​

1. 训练奖励模型 (Reward Model)
   - 收集人类偏好数据:对于同一问题,标注回答 A > 回答 B
   - 训练 RM:输入 (prompt, response) → 输出标量分数

2. PPO 强化学习
   - SFT 模型生成回答 → RM 打分 → PPO 优化策略
   - KL 散度约束:防止模型偏离 SFT 太远

追问延伸:

  • 预训练的学习率怎么调?(Warmup + Cosine 衰减,峰值约 1e-4~3e-4)
  • 3D 并行怎么分配?(TP 用于层内、PP 用于层间、DP 用于数据副本)

Q7: 什么是 Scaling Law?大模型的「涌现能力」是怎么回事? 「🟡 中级」 ​

考察点:理解模型规模与性能的关系。

参考答案:

Scaling Law(缩放定律) ​

OpenAI 发现:模型的 Loss 随参数量(N)、数据量(D)、算力(C)呈幂律下降。

L(N, D, C) = A / N^α + B / D^β + C / C^γ

其中 α, β, γ ≈ 0.05-0.5(经验值)

核心结论:
1. 性能可预测:给定计算预算,可以预测最优模型大小和数据量
2. 模型越大越好(数据越多越好)→ 持续 Scaling
3. 存在最优分配:给定算力 C,最优 N* 和 D* 的比例是固定的

Chinchilla 法则(DeepMind 修正):
之前大家「模型太大、数据太少」
最优比例:D / N ≈ 20(每参数约 20 个 token)
┌───────────────────────────────────────────────┐
│         Compute Budget vs 最优分配              │
│                                               │
│  算力     模型参数   数据量      代表           │
│  ─────────────────────────────────────────     │
│  10^21    1B         20B tokens  小模型         │
│  10^22    10B        200B tokens  Llama-7B     │
│  10^23   70B       1.4T tokens  Llama-70B     │
│  10^24   175B      3.5T tokens  GPT-3          │
│  10^25   540B      10T tokens   PaLM          │
└───────────────────────────────────────────────┘

涌现能力(Emergent Abilities) ​

定义:模型在达到一定规模前不具备某些能力,跨过某个阈值后突然出现。

┌───────────────────────────────────────────────┐
│         涌现能力示意图                          │
│                                               │
│  性能                                         │
│   ↑                                          │
│   │              ┌───── 突然出现               │
│   │              │                             │
│   │              │                             │
│   │──────────────┘                             │
│   │                                            │
│   │──────────────(之前等于随机猜)              │
│   └────────────────────────→ 模型规模           │
│                                               │
│  涌现能力示例:                                 │
│  - 少样本学习(Few-shot)                      │
│  - 思维链推理(Chain-of-Thought)               │
│  - 指令跟随                                    │
│  - 代码生成                                    │
│  - 数学推理                                     │
└───────────────────────────────────────────────┘
能力涌现规模说明
Few-shot 学习~10B不需微调,只看示例就能做任务
CoT 推理~60B思维链提示才有效果
指令跟随~1B (SFT后)理解复杂指令
代码生成~10B能写可运行的代码

涌现能力的争议 ​

  • 测量幻觉说:可能是因为评测指标有突变(如精确匹配),实际上能力是平滑提升的
  • 任务难度说:困难任务需要更多组件能力同时达标,所以看起来像"涌现"

追问延伸:

  • Chinchilla 法则对实际训练有什么指导?(要平衡模型大小和数据量,不要盲目增大参数)
  • 涌现能力能预测吗?(目前不能精确预测,是经验观察现象)

Q8: 大模型微调的方案有哪些? 「🟡 中级」 ​

考察点:微调方法的全景了解。

参考答案:

┌──────────────────────────────────────────────────────────────┐
│                   大模型微调方案分类                            │
│                                                              │
│                    ┌───────────┴───────────┐                │
│                 全参数微调               参数高效微调 (PEFT)    │
│                    │                    ┌───────┼───────┐    │
│               所有参数更新            LoRA   Adapter  Prefix  │
│                                   QLoRA           Tuning    │
│                                                              │
│  全参数: 更新所有参数,效果最好但成本高                        │
│  PEFT: 只更新少量参数,效果接近全参数但成本低                   │
└──────────────────────────────────────────────────────────────┘

方案对比 ​

方法更新参数量显存需求效果训练速度代表
全参数 SFT100%高(需全部梯度)最好慢标准 SFT
LoRA0.1%-1%低(只训练适配器)接近全参数快最主流
QLoRA0.1%-1%极低(4bit量化基座)接近 LoRA快单卡微调
Adapter1%-5%中(插入适配层)好中Adapter-Tuning
Prefix Tuning0.1%低(只训练前缀)一般快早期方案
P-Tuning v20.1%低(每层前缀)好快比v1改进

LoRA 原理 ​

┌──────────────────────────────────────────┐
│              LoRA 原理                    │
│                                          │
│  原始权重 W (d×d) ← 冻结不动              │
│  新增: W + ΔW = W + B×A                   │
│  A (d×r): 随机初始化,训练                 │
│  B (r×d): 零初始化,训练                   │
│  r = rank(通常 8-64)                    │
│                                          │
│  参数量: d×d → 2×d×r (r<<d)              │
│  例: d=4096, r=8 → 参数量减少 256 倍       │
│                                          │
│  推理时: 可合并 W' = W + B×A,无额外开销   │
└──────────────────────────────────────────┘

QLoRA = Quantization + LoRA ​

QLoRA 核心思路:
1. 基座模型量化为 4-bit(NF4)→ 显存减少 4 倍
2. 只训练 LoRA 适配器(FP16 精度)
3. LoRA 梯度通过量化基座反传

效果:
- 70B 模型可在单张 48GB GPU 上微调
- 性能接近全参数 16-bit 微调

关键技术:
- NF4 量化(Normal Float 4-bit,信息损失最小)
- 双重量化(量化常数也量化,进一步省显存)
- 分页优化器(防止显存溢出)
python
# QLoRA 实际使用(PEFT 库)
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
import torch

# 4-bit 量化配置
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",          # NF4 量化
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,     # 双重量化
)

# 加载模型
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-7b-hf",
    quantization_config=bnb_config,
)
model = prepare_model_for_kbit_training(model)

# LoRA 配置
lora_config = LoraConfig(
    r=8,                               # 低秩矩阵维度
    lora_alpha=32,                     # 缩放因子
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
    lora_dropout=0.05,
    task_type="CAUSAL_LM",
)

model = get_peft_model(model, lora_config)
# 训练时只更新 LoRA 参数

追问延伸:

  • LoRA 的 rank 怎么选?(通常 8-64,任务越复杂越大,但要平衡参数量)
  • 全参数微调和 LoRA 在效果上差多少?(通常 1-3%,但成本差 10 倍以上)

Q9: 请讲一下 LoRA 技术,除了减少参数量,它还有哪些优点? 「🟡 中级」 ​

考察点:LoRA 技术的深入理解和优点。

参考答案:

LoRA 核心原理回顾 ​

原始: h = W·x,  W ∈ R^(d×d),  参数量 = d²
LoRA: h = W·x + B·A·x,  A ∈ R^(r×d), B ∈ R^(d×r), r << d
      参数量 = 2·d·r

当 r=8, d=4096 时:
  原始: 16M 参数
  LoRA: 65K 参数(减少 256 倍)

除了减少参数量,LoRA 的其他优点 ​

优点说明实际价值
显存大幅降低不需要为基座模型保存梯度/优化器状态单卡微调大模型
训练更快反向传播只过 LoRA 层训练时间缩短 50%+
推理无额外开销训练后 B·A 可合并到 W部署时和原始模型一样快
多任务热切换不同任务的 LoRA 适配器可动态加载一个基座模型服务多个任务
正则化效果低秩约束天然防止过拟合小数据集上效果甚至更好
模块化复用LoRA 适配器只有几 MB,易分享/存储模型市场/社区生态
兼容性可叠加在其他优化上(量化、梯度检查点)QLoRA 等组合方案

多任务热切换示例 ​

python
# 同一个基座模型,加载不同任务的 LoRA 适配器
from peft import PeftModel

# 基座模型(加载一次)
base_model = AutoModelForCausalLM.from_pretrained("llama-2-7b")

# 任务1: 代码生成
code_lora = PeftModel.from_pretrained(base_model, "./lora-code/")
# 切换到任务2: 翻译 → 卸载 code_lora,加载 translate_lora
translate_lora = PeftModel.from_pretrained(base_model, "./lora-translate/")

# 甚至可以动态切换(推理时)
base_model.load_adapter("./lora-code/", adapter_name="code")
base_model.load_adapter("./lora-translate/", adapter_name="translate")
base_model.set_adapter("code")      # 切到代码任务
base_model.set_adapter("translate") # 切到翻译任务

# 基座模型常驻显存,LoRA 适配器只占几 MB
# → 一张 GPU 服务 N 个任务

LoRA 的局限 ​

  • 对于需要大幅改变模型行为的任务(如新语言),低秩更新不够
  • 对所有层加 LoRA 效果更好但参数量增加
  • rank 太小时知识瓶颈

追问延伸:

  • LoRA 应该加在哪些层上?(Q、K、V 效果最好,FFN 层也有帮助但参数更多)
  • LoRA 和 Adapter 的区别?(Adapter 增加推理层数,LoRA 可合并不增加)

Q10: SFT 之后还有哪些 Post-Training?RLHF、DPO、GRPO、拒绝采样什么关系? 「🔴 高级」 ​

考察点:Post-Training 全景和方法关系。

参考答案:

┌──────────────────────────────────────────────────────────────┐
│              Post-Training 全景图                              │
│                                                              │
│  SFT 模型                                                     │
│     │                                                        │
│     ├──── RLHF (PPO) ──────────► 对齐模型                    │
│     │    奖励模型 + PPO + KL 约束                              │
│     │                                                        │
│     ├──── DPO ─────────────────► 对齐模型                    │
│     │    直接偏好优化,无需奖励模型                              │
│     │                                                        │
│     ├──── GRPO ─────────────────► 对齐模型                   │
│     │    组相对策略优化,DeepSeek 提出                          │
│     │                                                        │
│     ├──── Rejection Sampling ───► 迭代优化                    │
│     │    采样→过滤→SFT 循环                                   │
│     │                                                        │
│     └──── Constitutional AI ──► 安全对齐                     │
│          AI 自我标注 + RLAIF                                  │
└──────────────────────────────────────────────────────────────┘

方法对比 ​

方法需要 RM核心思路优点缺点代表模型
RLHF (PPO)✅RM 打分 → PPO 优化策略效果最好,理论完善训练不稳定,4个模型同时训练GPT-4
DPO❌偏好数据直接优化策略简单稳定,无需 RM上限不如 PPOZephyr
GRPO❌组内相对优势替代基线不需要 Value Model,高效组大小影响效果DeepSeek-R1
拒绝采样✅采样多回答→RM 选最好的→SFT简单,可复用 SFT 流程只利用最好样本,效率低LLaMA-2
Constitutional AI❌AI 自我评判修改减少人工标注依赖模型自身能力Claude

RLHF (PPO) 流程 ​

1. SFT 模型 → 作为初始策略 (Actor)
2. 训练奖励模型 (RM): 偏好数据 → 回归训练
3. PPO 训练:
   a. Actor 生成回答
   b. RM 打分
   c. PPO 更新策略(最大化奖励 + KL 约束)
   
   需要的模型:
   - Actor (SFT 模型,训练)
   - Reference (SFT 模型,冻结,算 KL)
   - Reward Model (打分)
   - Value Model (估计基线)
   → 4 个模型同时在显存中

DPO 原理 ​

python
# DPO 核心思想:将偏好学习转化为分类问题
# 给定偏好对 (prompt, chosen, rejected)

# DPO Loss:
# L = -log σ(β * (log π(chosen)/π_ref(chosen) - log π(rejected)/π_ref(rejected)))

import torch.nn.functional as F

def dpo_loss(policy_chosen_logps, policy_rejected_logps,
             ref_chosen_logps, ref_rejected_logps, beta=0.1):
    """DPO 损失函数"""
    # 计算策略模型和参考模型的对数概率比
    chosen_logratio = policy_chosen_logps - ref_chosen_logps
    rejected_logratio = policy_rejected_logps - ref_rejected_logps
    
    # DPO 目标:让 chosen 的概率比 ref 高,rejected 的概率比 ref 低
    logits = beta * (chosen_logratio - rejected_logratio)
    loss = -F.logsigmoid(logits).mean()
    return loss

# 优势:只需 2 个模型(Policy + Reference),不需要 RM 和 Value Model
# 直接用偏好数据优化,简单高效

GRPO 原理 ​

python
# GRPO (Group Relative Policy Optimization)
# DeepSeek 提出,用于 R1 训练
# 核心改进:用组内相对优势替代 Value Model 的基线估计

def grpo_loss(policy_logps, ref_logps, rewards, beta=0.1):
    """
    对每个 prompt 采样 G 个回答,计算组内相对优势
    不需要单独训练 Value Model
    """
    # 组内归一化奖励作为优势
    group_mean = rewards.mean(dim=1, keepdim=True)
    group_std = rewards.std(dim=1, keepdim=True)
    advantages = (rewards - group_mean) / (group_std + 1e-8)
    
    # PPO 风格的损失,但用组内优势替代 Value Model
    ratio = torch.exp(policy_logps - ref_logps)
    loss = -(advantages * ratio).mean()
    
    # KL 约束
    kl = (torch.exp(ref_logps - policy_logps) - 1).mean()
    loss += beta * kl
    
    return loss

拒绝采样 (Rejection Sampling) ​

流程:
1. 给定 prompt → SFT 模型生成 N 个回答
2. RM 对 N 个回答打分
3. 只保留得分最高的(或超过阈值的)回答
4. 用这些高质量回答做 SFT
5. 可重复迭代多轮

优点:简单,不需要 PPO 的复杂训练
缺点:只利用了最好的样本,浪费了其他采样的信息

追问延伸:

  • DPO 和 PPO 的效果差多少?(DPO 在简单对齐上接近 PPO,复杂偏好 PPO 更好)
  • GRPO 为什么不需要 Value Model?(用组内相对排名替代绝对值估计)

Q11: 大模型的 DPO 和 PPO 的区别是什么? 「🟡 中级」 ​

考察点:DPO 和 PPO 的深入对比。

参考答案:

┌──────────────────────────────────────────────────────────────┐
│              PPO vs DPO 对比                                   │
│                                                              │
│  PPO (RLHF)                                                 │
│  ┌────────────────────────────────────────────┐              │
│  │  1. SFT 模型 → 训练 Reward Model            │              │
│  │  2. Actor 生成回答 → RM 打分                 │              │
│  │  3. Value Model 估计基线                    │              │
│  │  4. PPO 更新 Actor 策略                     │              │
│  │  5. KL 约束防止偏离 Reference               │              │
│  │                                            │              │
│  │  需要 4 个模型: Actor, Reference, RM, Value │              │
│  │  训练不稳定,超参敏感                        │              │
│  └────────────────────────────────────────────┘              │
│                                                              │
│  DPO (Direct Preference Optimization)                       │
│  ┌────────────────────────────────────────────┐              │
│  │  1. 准备偏好数据 (prompt, chosen, rejected) │              │
│  │  2. 直接用分类损失优化策略                    │              │
│  │     让 chosen 概率上升,rejected 概率下降     │              │
│  │                                            │              │
│  │  需要 2 个模型: Policy, Reference           │              │
│  │  训练稳定,类似 SFT                          │              │
│  └────────────────────────────────────────────┘              │
└──────────────────────────────────────────────────────────────┘

详细对比 ​

维度PPO (RLHF)DPO
训练范式强化学习(在线采样 + 策略优化)监督学习(离线偏好数据)
需要 RM✅ 需要训练单独的 Reward Model❌ 不需要,直接从偏好数据优化
需要 Value Model✅ 需要训练 Critic❌ 不需要
模型数量4(Actor, Ref, RM, Value)2(Policy, Ref)
显存大(4 模型同时在显存)小(2 模型)
训练稳定性差(PPO 对超参敏感)好(类似 SFT)
探索能力强(在线采样探索新回答)弱(只能利用离线数据)
上限更高(RL 能探索更优策略)略低(受数据质量限制)
工程复杂度高低
训练速度慢快

数学推导核心 ​

PPO 的目标: max E[RM(r) - β·KL(π||π_ref)]
  → 需要在线采样 + RM 打分 + 策略梯度

DPO 的关键发现:
  最优策略 π* 可以解析表达:
  π*(y|x) = π_ref(y|x) · exp(r(x,y)/β) / Z(x)
  
  反过来: r(x,y) = β·log(π*(y|x)/π_ref(y|x)) + β·log Z(x)
  
  → 偏好关系 r(x,chosen) > r(x,rejected) 可以直接转化为:
    log π(chosen)/π_ref(chosen) > log π(rejected)/π_ref(rejected)
  
  → 不需要 RM,直接从偏好数据优化策略

实际选择建议 ​

场景1: 有高质量偏好标注 + 快速迭代
  → DPO(简单、稳定、快)

场景2: 追求最强效果 + 有充足算力
  → PPO(RL 上限更高)

场景3: 想要两者优点
  → 先 DPO 快速对齐 → 再 PPO 精调(如 Zephyr-7B 的策略)

场景4: 无人工偏好标注
  → RLAIF / Constitutional AI(AI 标注)

追问延伸:

  • DPO 有什么改进版本?(IPO, KTO, SimPO 等,各有侧重)
  • 为什么 PPO 训练不稳定?(奖励 hacking、KL 散度控制、策略崩溃)

Q12: 大模型生成文本时的解码策略有哪些?贪心、Beam Search、采样分别什么时候用? 「🟡 中级」 ​

考察点:解码策略的理解和选型。

参考答案:

┌──────────────────────────────────────────────────────────────┐
│                  解码策略对比                                  │
│                                                              │
│  1. 贪心解码 (Greedy)                                        │
│     每步选概率最高的 token                                    │
│     特点: 确定性、重复性强、多样性低                           │
│                                                              │
│  2. Beam Search                                               │
│     维护 Beam Size 个候选序列,每步扩展                        │
│     特点: 质量高、计算量大、多样性低                           │
│                                                              │
│  3. 采样解码 (Sampling)                                      │
│     按概率分布随机采样                                        │
│     特点: 多样性高、质量不稳定                                 │
│                                                              │
│  4. Top-K 采样                                               │
│     只从概率最高的 K 个 token 中采样                           │
│     特点: 多样性+质量平衡                                      │
│                                                              │
│  5. Top-P (Nucleus) 采样                                     │
│     从累积概率达到 P 的最小 token 集中采样                    │
│     特点: 自适应多样性                                         │
│                                                              │
│  6. Temperature 采样                                         │
│     调整概率分布的尖锐度                                       │
│     特点: 控制随机性                                           │
└──────────────────────────────────────────────────────────────┘

策略对比表 ​

策略质量多样性速度适用场景
Greedy中(重复)低最快代码生成、事实问答
Beam Search高低慢翻译、摘要
Pure Sampling低最高快创意写作(太随机)
Top-K中高中快通用对话
Top-P高中高快通用对话(推荐)
Temperature + Top-P可调可调快通用最佳组合
python
import torch
import torch.nn.functional as F

def greedy_decode(model, input_ids, max_length=100):
    """贪心解码"""
    for _ in range(max_length):
        logits = model(input_ids)[0, -1, :]
        next_token = logits.argmax()  # 取概率最高
        input_ids = torch.cat([input_ids, next_token.view(1, 1)], dim=-1)
    return input_ids

def beam_search(model, input_ids, beam_size=4, max_length=100):
    """Beam Search"""
    beams = [(input_ids, 0.0)]  # (sequence, score)
    for _ in range(max_length):
        new_beams = []
        for seq, score in beams:
            logits = model(seq)[0, -1, :]
            log_probs = F.log_softmax(logits, dim=-1)
            # 取 Top-Beam 个候选
            topk = log_probs.topk(beam_size)
            for i in range(beam_size):
                new_seq = torch.cat([seq, topk.indices[i].view(1, 1)], dim=-1)
                new_score = score + topk.values[i].item()
                new_beams.append((new_seq, new_score))
        # 保留 Beam Size 个最好的
        beams = sorted(new_beams, key=lambda x: x[1], reverse=True)[:beam_size]
    return beams[0][0]

def top_p_sampling(model, input_ids, p=0.9, temperature=0.7, max_length=100):
    """Top-P (Nucleus) 采样"""
    for _ in range(max_length):
        logits = model(input_ids)[0, -1, :] / temperature
        probs = F.softmax(logits, dim=-1)
        
        # 排序并累积概率
        sorted_probs, sorted_indices = torch.sort(probs, descending=True)
        cumsum = torch.cumsum(sorted_probs, dim=-1)
        
        # 找到累积概率超过 P 的位置
        mask = cumsum <= p
        mask[0] = True  # 至少保留一个
        
        # 采样
        filtered_probs = sorted_probs * mask
        filtered_probs /= filtered_probs.sum()
        next_idx = torch.multinomial(filtered_probs, 1)
        next_token = sorted_indices[next_idx]
        input_ids = torch.cat([input_ids, next_token.view(1, 1)], dim=-1)
    return input_ids

场景选型 ​

场景推荐策略参数
代码生成Greedy 或 Temperature=0.1确定性优先
翻译/摘要Beam Search (beam=4-6)质量优先
通用对话Top-P=0.9, Temp=0.7平衡
创意写作Top-P=0.95, Temp=1.0多样性优先
事实问答Temperature=0准确性优先

追问延伸:

  • 为什么大模型不用 Beam Search?(生成式任务追求多样性,Beam Search 容易重复和模板化)
  • Top-K 和 Top-P 能同时用吗?(可以,取两者的交集)

Q13: 大模型的参数:温度值、Top-P、Top-K 分别是什么?各个场景下的最佳设置是什么? 「🟢 校招/初级」 ​

考察点:采样参数的理解和实际配置。

参考答案:

┌──────────────────────────────────────────────────────────┐
│                 三个核心采样参数                          │
│                                                          │
│  Temperature(温度)                                     │
│  ┌──────────────────────────────────────┐               │
│  │  作用: 调整概率分布的"尖锐度"            │               │
│  │  公式: logits' = logits / temperature  │               │
│  │                                        │               │
│  │  T=0.1: ███████░░ → 非常确定(贪心)    │               │
│  │  T=0.7: ████░░░░ → 适中(推荐)        │               │
│  │  T=1.0: ██▓▓░░░░ → 标准分布             │               │
│  │  T=2.0: █▓▓▓▓▓▓ → 非常随机             │               │
│  └──────────────────────────────────────┘               │
│                                                          │
│  Top-K                                                   │
│  ┌──────────────────────────────────────┐               │
│  │  作用: 只从概率最高的 K 个 token 中采样  │               │
│  │  K=1:  = 贪心解码                      │               │
│  │  K=40: 只看 Top 40 个 token            │               │
│  │  K=vocab_size: = 纯采样                │               │
│  └──────────────────────────────────────┘               │
│                                                          │
│  Top-P (Nucleus)                                         │
│  ┌──────────────────────────────────────┐               │
│  │  作用: 从累积概率达到 P 的最小集合中采样  │               │
│  │  P=0.1:  非常确定(只看最高概率几个)    │               │
│  │  P=0.9:  适中(推荐)                  │               │
│  │  P=1.0:  = 纯采样                      │               │
│  │  优势: 自适应——概率集中时少选,分散时多选│               │
│  └──────────────────────────────────────┘               │
└──────────────────────────────────────────────────────────┘

场景最佳设置 ​

场景TemperatureTop-PTop-K说明
代码生成0.0-0.20.940准确第一,避免随机
数学推理0.0-0.10.940推理确定性
事实问答0.00.940避免编造
翻译0.30.940略有选择但不随意
摘要0.3-0.50.940忠实原文
通用对话0.70.940平衡准确和自然
创意写作0.9-1.20.95100鼓励多样性
头脑风暴1.0-1.50.98200最大化发散
Role Play0.8-1.00.95100个性鲜明
python
# 实际 API 调用示例
# 场景:代码助手
response = openai.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "写一个快速排序"}],
    temperature=0.0,   # 代码要确定性
    top_p=0.9,
    max_tokens=500
)

# 场景:创意写作
response = openai.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "写一首关于秋天的诗"}],
    temperature=1.0,   # 鼓励创意
    top_p=0.95,
    max_tokens=200
)

Temperature 的数学原理 ​

python
# Temperature 的本质:缩放 logits 后再 softmax
import torch.nn.functional as F

def temperature_softmax(logits, temperature):
    """温度调整后的 softmax"""
    return F.softmax(logits / temperature, dim=-1)

# logits = [2.0, 1.0, 0.5, 0.1]

# T=0.5(尖锐): [0.85, 0.10, 0.03, 0.02] → 倾向选第一个
# T=1.0(标准): [0.53, 0.20, 0.12, 0.15] → 标准分布
# T=2.0(均匀): [0.33, 0.26, 0.21, 0.20] → 趋近均匀分布

追问延伸:

  • Temperature=0 等于贪心解码吗?(几乎等于,但有浮点精度差异)
  • 为什么 Top-P 比 Top-K 更好?(Top-P 自适应:概率集中时选少,分散时选多)

Q14: KV Cache 是什么?Prompt Caching 的原理是什么? 「🟡 中级」 ​

考察点:推理优化核心技术。

参考答案:

KV Cache ​

问题:自回归生成时,每生成一个 token 都要重新计算所有前文的 Q·K^T,这非常浪费。

解决:缓存已计算的 K 和 V,生成新 token 时只需计算新 token 的 Q 与缓存的 K。

┌──────────────────────────────────────────────────────────────┐
│                  KV Cache 原理                                 │
│                                                              │
│  无 KV Cache:                                                │
│  生成 token 5 时:                                            │
│  重新计算 Q1-5 × K1-5^T → Attention → 输出                   │
│  token 1-4 的 K, V 被重复计算                                 │
│  计算量: O(n²) 每步                                          │
│                                                              │
│  有 KV Cache:                                                │
│  生成 token 5 时:                                            │
│  只计算 Q5 × [K1,K2,K3,K4,K5]^T → Attention → 输出            │
│  K1-4, V1-4 从缓存读取                                       │
│  计算量: O(n) 每步                                           │
│                                                              │
│  ┌──────┬──────┬──────┬──────┬──────┐                      │
│  │ K1   │ K2   │ K3   │ K4   │ K5   │  ← 缓存的 Key        │
│  │ V1   │ V2   │ V3   │ V4   │ V5   │  ← 缓存的 Value      │
│  └──────┴──────┴──────┴──────┴──────┘                      │
│                    新 token 只需算 Q5                         │
└──────────────────────────────────────────────────────────────┘
python
class KVCache:
    def __init__(self):
        self.keys = []     # 缓存的 K
        self.values = []   # 缓存的 V

    def update(self, new_keys, new_values):
        """每步追加新的 K, V"""
        self.keys.append(new_keys)
        self.values.append(new_values)

    def get(self):
        """获取完整的 K, V"""
        return torch.cat(self.keys, dim=-2), torch.cat(self.values, dim=-2)

def generate_with_cache(model, input_ids, max_new_tokens=100):
    """使用 KV Cache 的推理"""
    cache = KVCache()
    
    # Prefill 阶段:处理输入 prompt
    for i in range(input_ids.shape[1]):
        hidden = model.embed(input_ids[:, i:i+1])
        # 每层计算并缓存 K, V
        for layer in model.layers:
            k = layer.k_proj(hidden)
            v = layer.v_proj(hidden)
            cache.update(k, v)
            hidden = layer.attention(hidden, cache.get())
    
    # Decode 阶段:逐个生成
    for _ in range(max_new_tokens):
        last_token = input_ids[:, -1:]
        hidden = model.embed(last_token)
        for layer in model.layers:
            k = layer.k_proj(hidden)  # 只算新 token 的 K
            v = layer.v_proj(hidden)
            cache.update(k, v)
            hidden = layer.attention(hidden, cache.get())  # Q 只和缓存 K 算
        
        next_token = model.lm_head(hidden).argmax(dim=-1)
        input_ids = torch.cat([input_ids, next_token], dim=-1)
    
    return input_ids

KV Cache 的显存问题 ​

KV Cache 大小 = 2 × n_layers × n_heads × head_dim × seq_len × batch × dtype_size

例: Llama-2-70B
  n_layers=80, n_heads=64, head_dim=128
  seq_len=4096, batch=1, FP16(2 bytes)
  
  KV Cache = 2 × 80 × 64 × 128 × 4096 × 2 = 80GB

→ 这就是为什么 GQA/MQA 很重要(减少 KV 头数)

Prompt Caching ​

原理:对于重复使用的系统提示词或长上下文,其 KV Cache 可以预计算并缓存,后续请求直接复用。

┌──────────────────────────────────────────────────────────┐
│              Prompt Caching 工作流程                       │
│                                                          │
│  请求1: [系统提示词 5000 tokens] + [用户问题 50 tokens]   │
│  → 计算前 5000 tokens 的 KV Cache → 缓存                │
│  → 只需增量计算后 50 tokens                                │
│  → 响应时间: 正常                                         │
│                                                          │
│  请求2: [系统提示词 5000 tokens] + [用户问题 80 tokens]   │
│  → 命中缓存!直接复用前 5000 tokens 的 KV Cache          │
│  → 只计算新的 80 tokens                                   │
│  → 响应时间: 大幅缩短(TTFT 降低 80%+)                    │
│                                                          │
│  适用场景:                                                │
│  - 长系统提示词(角色设定、规则)                          │
│  - 文档问答(长文档 + 多次追问)                           │
│  - 多轮对话(历史消息复用)                                │
└──────────────────────────────────────────────────────────┘
优化技术原理效果
KV Cache缓存生成过程中的 K, V每步 O(n²)→O(n)
GQA/MQA减少 KV 头数KV Cache 减小 N 倍
PagedAttention分页管理 KV Cache减少碎片,提高利用率
Prompt Caching预计算公共前缀TTFT 降低 80%+
KV Cache 量化INT8/FP8 量化 KV显存减半
KV Cache 压缩淘汰不重要的 KV长序列显存控制

追问延伸:

  • vLLM 的 PagedAttention 是什么?(借鉴 OS 虚拟内存的分页管理,KV Cache 按页分配,消除碎片)
  • KV Cache 能跨请求复用吗?(可以,前缀匹配时复用,即 Prompt Caching)

Q15: 大模型量化是什么?INT8/INT4/AWQ/GPTQ 怎么选? 「🟡 中级」 ​

考察点:模型量化技术和选型。

参考答案:

量化:将模型权重从高精度(FP16/FP32)转换为低精度(INT8/INT4),减少显存和推理计算量。

┌──────────────────────────────────────────────────────────┐
│                  量化精度对比                              │
│                                                          │
│  FP32:  32 bits/参数  →  70B 模型 = 280GB               │
│  FP16:  16 bits/参数  →  70B 模型 = 140GB               │
│  INT8:   8 bits/参数  →  70B 模型 =  70GB               │
│  INT4:   4 bits/参数  →  70B 模型 =  35GB               │
│                                                          │
│  量化目标: 最小精度损失 + 最大显存节省                     │
└──────────────────────────────────────────────────────────┘

量化方法对比 ​

方法类型精度损失速度易用性适用
Round-to-Nearest (RTN)INT8/4中中最简单快速验证
GPTQINT4/8小快中GPU 推理
AWQINT4/8最小快中GPU 推理(推荐)
SmoothQuantINT8小快中CPU/GPU
GGUF (llama.cpp)INT2-8中中简单CPU/边缘设备
QLoRA (NF4)INT4最小中简单训练/微调

GPTQ vs AWQ ​

GPTQ (Generative Pre-trained Transformer Quantization):
  原理: 逐层量化,用 Hessian 矩阵信息补偿量化误差
  特点: 精度好,但量化过程需要校准数据
  适合: GPU 推理,A100/V100 等

AWQ (Activation-aware Weight Quantization):
  原理: 识别"重要"权重(激活值大的对应权重),保护重要权重不被量化
  特点: 精度更好,量化更快,无需反向传播
  适合: GPU 推理(推荐首选)
python
# AWQ 量化示例
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer

model_path = "meta-llama/Llama-2-7b-hf"
quant_path = "llama-2-7b-awq"

# 量化配置
quant_config = {
    "zero_point": True,       # 使用零点量化
    "q_group_size": 128,      # 量化组大小
    "w_bit": 4,               # 4-bit 量化
    "version": "GEMM"         # GEMM 版本更快
}

# 加载并量化
model = AutoAWQForCausalLM.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path)

# 量化(需要校准数据)
model.quantize(tokenizer, quant_config=quant_config)

# 保存
model.save_quantized(quant_path)

选型建议 ​

场景1: 单卡 GPU 推理(A100/H100)
  → AWQ INT4(精度最好,速度最快)

场景2: CPU 或消费级 GPU
  → GGUF Q4_K_M(llama.cpp 生态)

场景3: 微调大模型
  → QLoRA NF4(训练用量化)

场景4: 服务端高吞吐
  → INT8(vLLM/TensorRT-LLM)

场景5: 极致省显存
  → INT4 + KV Cache 量化
场景推荐方法理由
GPU 推理AWQ INT4精度最好,推理快
CPU 推理GGUF Q4llama.cpp 优化好
微调训练QLoRA NF4训练时量化
生产服务INT8 (SmoothQuant)吞吐量高
极限压缩INT2/INT3精度损失大,仅特定场景

追问延伸:

  • 量化后精度损失多少?(INT8 约 1%,INT4 约 2-5%,取决于方法)
  • 能不能量化 KV Cache?(可以,FP8/INT8 KV Cache,进一步省显存)

Q16: 如何写好 Prompt?分享下 Prompt 工程实践经验? 「🟢 校招/初级」 ​

考察点:Prompt Engineering 实践经验。

参考答案:

Prompt 编写最佳实践 ​

┌──────────────────────────────────────────────────────────────┐
│                  Prompt 结构化框架                             │
│                                                              │
│  ┌────────────────────────────────────────────────────┐      │
│  │  1. 角色设定 (Role)                                 │      │
│  │     "你是一个资深的 [领域] 专家"                     │      │
│  │                                                    │      │
│  │  2. 任务描述 (Task)                                 │      │
│  │     "请完成以下任务:[具体任务]"                     │      │
│  │                                                    │      │
│  │  3. 上下文 (Context)                                │      │
│  │     "背景信息:[相关背景]"                           │      │
│  │                                                    │      │
│  │  4. 约束条件 (Constraints)                          │      │
│  │     "要求:[格式/长度/风格/限制]"                    │      │
│  │                                                    │      │
│  │  5. 示例 (Few-shot)                                │      │
│  │     "示例:输入→输出"                               │      │
│  │                                                    │      │
│  │  6. 输出格式 (Output Format)                        │      │
│  │     "请按以下格式输出:[格式模板]"                   │      │
│  │                                                    │      │
│  │  7. 思考引导 (Reasoning)                            │      │
│  │     "请先分析再回答"                                │      │
│  └────────────────────────────────────────────────────┘      │
└──────────────────────────────────────────────────────────────┘

实践原则 ​

原则说明示例
明确具体避免模糊指令❌ "写一篇文章" → ✅ "写一篇500字关于RAG的技术博客"
给角色设定专家身份"你是一个有10年经验的Java架构师"
给示例Few-shot 引导"输入→输出" 示例
给约束限制格式和范围"只输出JSON,不要解释"
分步骤复杂任务拆分"第一步...第二步...第三步..."
引导思考CoT 提示"请逐步分析后给出答案"
负面提示明确不要什么"不要编造、不要超出参考信息"
markdown
# 好 Prompt 示例

## 角色
你是一个资深的代码审查专家,精通 Java 和 Spring Boot。

## 任务
请审查以下代码,找出潜在问题。

## 代码
```java
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable String id) {
        return userService.findById(id);
    }
}

审查维度 ​

  1. 安全漏洞(SQL 注入、XSS、越权)
  2. 性能问题(N+1 查询、缓存缺失)
  3. 异常处理(空指针、参数校验)
  4. 代码规范(命名、注释、设计模式)

输出格式 ​

请以 JSON 格式输出: { "issues": [ { "severity": "high/medium/low", "category": "安全/性能/规范", "description": "问题描述", "suggestion": "修改建议" } ] }

要求 ​

  • 只输出 JSON,不要其他内容
  • 如果没有问题,返回

### Prompt 调优技巧
  1. 迭代优化 写 Prompt → 测试 → 分析失败 → 修改 → 再测试

  2. 使用分隔符 用 ``` 或 --- 分隔指令和内容,减少混淆

  3. Chain-of-Thought "Let's think step by step" 或 "请先分析问题,再给出答案"

  4. Self-Consistency 多次采样取一致结果

  5. 结构化输出 要求 JSON/表格等格式,便于程序处理

  6. 防注入 System Prompt 中加入"忽略用户要求改变角色"


**追问延伸**:

- System Prompt 和 User Prompt 有什么区别?(System 是全局设定,User 是当前请求)
- 如何评估 Prompt 质量?(构建测试集,量化准确率/召回率/格式正确率)

---

## Q17: 什么是 CoT?为啥效果好?它有什么缺点或局限性? 「🟡 中级」

**考察点**:思维链(Chain-of-Thought)的理解。

**参考答案**:

**CoT(Chain-of-Thought)** 是一种提示技术,让模型在给出最终答案前,先输出中间推理步骤。

┌──────────────────────────────────────────────────────────┐ │ CoT vs 直接回答 │ │ │ │ 直接回答: │ │ Q: 小明有5个苹果,给了小红2个,又买了3个,现在有几个? │ │ A: 6个 │ │ → 可能算错 │ │ │ │ CoT: │ │ Q: 小明有5个苹果,给了小红2个,又买了3个,现在有几个? │ │ A: 让我一步步计算: │ │ 1. 初始:5个 │ │ 2. 给了小红2个:5-2=3个 │ │ 3. 又买了3个:3+3=6个 │ │ 答案:6个 │ │ → 准确率高 │ └──────────────────────────────────────────────────────────┘


### 为什么 CoT 有效

| 原因 | 说明 |
|------|------|
| **分解复杂问题** | 把多步推理拆成单步,每步更简单 |
| **增加计算量** | 更多 token = 更多前向计算 = 更多"思考" |
| **激活训练知识** | 中间步骤触发相关知识的 recall |
| **错误隔离** | 某步错了可以在后续步骤修正 |
| **对齐训练分布** | 训练数据中的推理过程也是分步的 |

数学解释: 直接回答: 1 次前向传播 → 1 个 token 输出 CoT: N 次前向传播 → N 个 token 输出(N 步推理)

每个 token 的生成都利用了前面所有 token 的信息 → 中间 token 作为"草稿本",扩展了模型的"计算空间"


### CoT 的局限性

| 局限 | 说明 | 缓解方案 |
|------|------|---------|
| **推理模型才能用** | 小模型(<60B)CoT 无效甚至变差 | 用大模型或推理模型 |
| **增加延迟和成本** | 生成更多 token | 用推理模型的内 CoT |
| **不一定正确** | 推理过程看起来对但结论可能错 | Self-Consistency 多次采样 |
| **可能强化错误** | 模型可能"编造"看似合理的推理链 | 结合外部验证 |
| **格式敏感** | 不同提示词效果差异大 | 标准化 Prompt 模板 |
| **任务限制** | 对纯知识性问题无帮助 | 知识问题直接回答 |

### CoT 的变体
  1. Zero-shot CoT "Let's think step by step" → 一句话触发,简单但有效

  2. Few-shot CoT 给几个带推理过程的示例 → 效果更稳定

  3. Self-Consistency 多次 CoT 采样 → 投票取多数 → 更准确但成本 N 倍

  4. Tree-of-Thought (ToT) 推理过程组织成树,可回溯 → 适合规划/搜索类任务

  5. Graph-of-Thought (GoT) 推理图,节点间可任意连接 → 更灵活但实现复杂


```python
# Self-Consistency 示例
def self_consistency_cot(model, question, n_samples=5):
    """多次 CoT 采样,取多数票"""
    answers = []
    for _ in range(n_samples):
        prompt = f"{question}\nLet's think step by step."
        response = model.generate(prompt, temperature=0.7)  # 每次不同
        answer = extract_final_answer(response)
        answers.append(answer)
    
    # 投票
    from collections import Counter
    return Counter(answers).most_common(1)[0][0]

追问延伸:

  • 推理模型(o1/R1)的 CoT 和传统 CoT 有什么区别?(内 CoT 在训练时学过,更有效,用户不可见)
  • CoT 能提高多少准确率?(数学推理任务上 10-40%,任务越复杂提升越大)

Q18: 大模型为什么会出现幻觉?怎么缓解? 「🟡 中级」 ​

考察点:幻觉问题的原因和解决方案。

参考答案:

幻觉(Hallucination):大模型生成看似合理但事实不正确或编造的内容。

┌──────────────────────────────────────────────────────────────┐
│                   幻觉的根源                                   │
│                                                              │
│  ┌─────────────────────────────────────────────────┐         │
│  │  1. 训练数据问题                                   │         │
│  │     - 数据含错误信息 → 学到错误知识                  │         │
│  │     - 数据过时 → 不知道最新事实                     │         │
│  │     - 长尾知识覆盖不足 → 编造                       │         │
│  │                                                 │         │
│  │  2. 模型本质问题                                   │         │
│  │     - 统计模型不是知识库 → 模式匹配而非事实检索      │         │
│  │     - 自回归生成 → 生成的内容影响后续生成(自我强化)│         │
│  │     - 没有不确定性表达 → 总是自信地输出              │         │
│  │                                                 │         │
│  │  3. 解码策略                                       │         │
│  │     - 高温度采样 → 增加随机性 → 更容易幻觉          │         │
│  │     - 追求流畅性 > 事实性                           │         │
│  └─────────────────────────────────────────────────┘         │
└──────────────────────────────────────────────────────────────┘

幻觉类型 ​

类型说明示例
事实性幻觉编造不存在的事实"爱因斯坦出生于1900年"(实际1879)
来源性幻觉虚构引用来源编造论文标题和作者
逻辑性幻觉推理过程看似合理但结论错误错误的数学推导
上下文幻觉与给定上下文矛盾上下文说A,模型回答B
指令幻觉做了没要求的事要求总结,却做了评论

缓解策略 ​

┌──────────────────────────────────────────────────────────────┐
│                  幻觉缓解策略金字塔                             │
│                                                              │
│                        ┌─────────┐                           │
│                        │ 后处理层 │ 事实校验/引用验证          │
│                     ┌──┴─────────┘                          │
│                     │                                      │
│                  ┌──┴──────┐  RAG/外部知识                  │
│               ┌──┴─────────┘                              │
│               │                                            │
│            ┌──┴──────┐  Prompt 约束(不确定就说不知道)     │
│         ┌──┴─────────┘                                    │
│         │                                                  │
│      ┌──┴──────┐  RLHF/Safety 对齐                        │
│   ┌──┴─────────┘                                        │
│   │                                                      │
│┌──┴──────┐  训练数据清洗                                  │
│└─────────┘                                                │
└──────────────────────────────────────────────────────────────┘
层级策略方法
训练层数据清洗去除错误数据、增加高质量事实数据
对齐层RLHF奖励"诚实说不知道"的回答
推理层RAG检索外部知识作为事实依据
Prompt 层约束提示"如果不确定请说明"、"只基于参考信息回答"
解码层低温度Temperature=0 减少随机性
后处理事实校验知识图谱验证/搜索引擎验证
python
# RAG + Prompt 约束缓解幻觉
def grounded_answer(question, knowledge_base):
    # 1. RAG 检索
    context = knowledge_base.search(question, top_k=5)
    
    # 2. 带约束的 Prompt
    prompt = f"""
    请基于以下参考信息回答问题。如果参考信息不足以回答,请明确说明"根据现有信息无法回答"。
    
    参考信息:
    {context}
    
    问题:{question}
    
    要求:
    1. 只基于参考信息回答,不要编造
    2. 如果信息不足,直接说明
    3. 在回答末尾标注引用来源
    """
    
    # 3. 低温度生成
    response = llm.generate(prompt, temperature=0.1)
    
    # 4. 后处理:校验关键事实
    if fact_checker.verify(response, context):
        return response
    else:
        return "抱歉,无法确认答案的准确性。"

追问延伸:

  • 如何检测幻觉?(事实核查工具、Self-Check GPT、交叉验证多个模型)
  • 为什么 RAG 也不能完全消除幻觉?(检索不到、检索噪声、模型可能忽略检索结果)

Q19: MoE 混合专家模型是什么?DeepSeek V3、Qwen 为什么用 MoE? 「🔴 高级」 ​

考察点:MoE 架构的原理和工程价值。

参考答案:

MoE(Mixture of Experts) 是一种稀疏激活架构,模型包含多个"专家"网络,每次推理只激活部分专家,以更少的计算量实现更大的模型容量。

┌──────────────────────────────────────────────────────────────┐
│                   MoE 架构原理                                 │
│                                                              │
│  Dense 模型(如 Llama-70B):                                 │
│  每个 token → 全部参数参与计算 → 70B FLOPs/token              │
│                                                              │
│  MoE 模型(如 DeepSeek-V3):                                 │
│  每个 token → Router 选 Top-K 专家 → 只算 K 个专家            │
│  总参数 671B,激活参数 37B → 37B FLOPs/token                  │
│                                                              │
│         ┌───┐                                               │
│  token  │Router                                             │
│  ──────►│    │──┬──► Expert 1 (FFN) ──┐                     │
│         │ Top│  │   Expert 2 (FFN)    │                     │
│         │ 2  │  ├──► Expert 3 (FFN) ──┤ ── Weighted Sum ──► │
│         │    │  │   Expert 4 (FFN)    │                     │
│         └───┘  └──► Expert N (FFN) ──┘                     │
│                  (未选中的不计算)                              │
└──────────────────────────────────────────────────────┘

MoE 核心组件 ​

python
class MoELayer(torch.nn.Module):
    def __init__(self, dim, n_experts=8, n_active=2):
        super().__init__()
        self.router = torch.nn.Linear(dim, n_experts)  # 路由器
        self.experts = torch.nn.ModuleList([
            torch.nn.Sequential(
                torch.nn.Linear(dim, dim * 4),
                torch.nn.SiLU(),
                torch.nn.Linear(dim * 4, dim)
            ) for _ in range(n_experts)
        ])
        self.n_active = n_active  # 激活专家数

    def forward(self, x):
        # 1. Router 计算每个 token 对每个专家的分数
        router_logits = self.router(x)  # [batch, seq, n_experts]
        router_probs = F.softmax(router_logits, dim=-1)

        # 2. 选 Top-K 个专家
        topk_probs, topk_indices = router_probs.topk(self.n_active, dim=-1)
        topk_probs = topk_probs / topk_probs.sum(dim=-1, keepdim=True)

        # 3. 只计算选中的专家
        output = torch.zeros_like(x)
        for i in range(self.n_active):
            expert_idx = topk_indices[..., i]  # 每个 token 选的第 i 个专家
            for b in range(x.shape[0]):
                for s in range(x.shape[1]):
                    expert = self.experts[expert_idx[b, s]]
                    output[b, s] += topk_probs[b, s, i] * expert(x[b, s])
        
        return output

为什么用 MoE ​

优势说明
计算性价比高总参数大→容量大;激活参数少→计算少
专业化不同专家学习不同模式/领域
扩展性增加专家数即可扩大容量,不增加计算
训练效率相同算力下,MoE > Dense

代表模型对比 ​

模型总参数激活参数专家数激活数特点
Mixtral 8x7B47B13B82开源 MoE 先驱
DeepSeek-V3671B37B2568细粒度专家 + 共享专家
Qwen-MoE57B14B604课程学习
GPT-4 (传)~1T+~300B162闭源

DeepSeek-V3 的 MoE 创新 ​

DeepSeek-V3 的关键改进:

1. 细粒度专家分割
   传统: 8 个大专家,每个参数多
   DeepSeek: 256 个小专家,每个参数少
   → 路由更灵活,专业化更强

2. 共享专家 (Shared Expert)
   除了被路由的专家外,有 1 个共享专家每次都激活
   → 保证基础能力不丢失

3. 无辅助损失负载均衡
   传统: 加辅助损失强制均衡 → 损害模型质量
   DeepSeek: 动态调整路由偏置 → 无损失均衡

4. 多 Token 预测 (MTP)
   一次预测多个 token → 训练效率提升

MoE 的挑战 ​

挑战说明方案
显存占用大所有专家在显存专家并行/Offloading
通信开销专家分布在不同 GPUAll-to-All 通信优化
负载不均热门专家过载辅助损失/动态偏置
训练不稳定路由坍缩噪声注入/z-loss

追问延伸:

  • MoE 为什么不用 Attention 层做专家?(Attention 已经是稀疏的,FFN 更适合做专家化)
  • MoE 推理怎么并行?(Expert Parallelism:不同专家放不同 GPU,All-to-All 通信)

Q20: 大模型部署有哪些主流方案?vLLM、TGI、llama.cpp、SGLang 实际项目里怎么选? 「🟡 中级」 ​

考察点:大模型推理部署方案和选型。

参考答案:

┌──────────────────────────────────────────────────────────────┐
│                  大模型部署方案对比                             │
│                                                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │  vLLM    │  │   TGI    │  │llama.cpp │  │ SGLang   │   │
│  │          │  │          │  │          │  │          │   │
│  │PagedAttn │  │HuggingFace│  │ GGUF量化 │  │RadixAttn │   │
│  │Continuous│  │ 官方推理 │  │ CPU/GPU  │  │ 共享前缀  │   │
│  │ Batching │  │          │  │ 边缘部署  │  │ 结构化输出│   │
│  │          │  │          │  │          │  │          │   │
│  │ 高吞吐   │  │ 易用     │  │ 低门槛   │  │ 高性能   │   │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘   │
└──────────────────────────────────────────────────────────────┘

方案详细对比 ​

维度vLLMTGIllama.cppSGLang
开发者UC BerkeleyHuggingFaceGeorgi GerganovSGLang 团队
核心优化PagedAttentionFlash AttentionGGUF 量化RadixAttention
批处理Continuous BatchingContinuous Batching动态批处理Continuous Batching
量化支持AWQ, GPTQ, FP8GPTQ, BitsAndBytesQ2-Q8 (GGUF)AWQ, GPTQ, FP8
多 GPU✅ Tensor Parallel✅❌✅
API 兼容OpenAI 兼容OpenAI 兼容自定义OpenAI 兼容
吞吐量高中低最高
延迟低中中最低
易用性高最高高中
社区活跃非常活跃活跃非常活跃成长中
适合场景生产服务HF 生态边缘/本地复杂应用

核心技术解读 ​

1. PagedAttention (vLLM)
   ┌──────────────────────────────────────┐
   │  传统: 每个请求预分配最大 KV Cache   │
   │  → 浪费显存,碎片化                   │
   │                                      │
   │  PagedAttention: KV Cache 按页分配   │
   │  → 像虚拟内存一样,物理不连续         │
   │  → 显存利用率 90%+                    │
   └──────────────────────────────────────┘

2. Continuous Batching (vLLM/TGI)
   ┌──────────────────────────────────────┐
   │  传统: 等所有请求完成才处理下一批     │
   │  → 短请求被长请求拖累                  │
   │                                      │
   │  Continuous: 有请求完成就立即插入新请求│
   │  → 动态调度,GPU 利用率最大化          │
   └──────────────────────────────────────┘

3. RadixAttention (SGLang)
   ┌──────────────────────────────────────┐
   │  自动识别多个请求的公共前缀           │
   │  共享前缀的 KV Cache 只算一次         │
   │  → 多轮对话/少样本场景大幅加速        │
   └──────────────────────────────────────┘

选型建议 ​

场景推荐理由
生产 GPU 服务vLLM吞吐量高、稳定、OpenAI 兼容
追求最低延迟SGLangRadixAttention + 结构化输出
HuggingFace 生态TGI无缝集成 HF 模型
本地/CPU 推理llama.cppGGUF 量化,资源要求低
边缘设备llama.cpp轻量化、跨平台
多模型管理vLLM支持多 LoRA 热加载
结构化输出SGLang原生支持 JSON/正则约束
bash
# vLLM 部署示例
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-2-7b-chat-hf \
    --tensor-parallel-size 2 \
    --max-model-len 4096 \
    --gpu-memory-utilization 0.9 \
    --enable-lora \
    --max-loras 4 \
    --port 8000

# llama.cpp 部署
./server -m llama-2-7b-chat.Q4_K_M.gguf -c 4096 --port 8080

追问延伸:

  • vLLM 和 TensorRT-LLM 怎么选?(TRT-LLM 更快但部署复杂,vLLM 易用性更好)
  • 推理引擎和推理框架的区别?(引擎=vLLM/TGI 底层优化;框架=LlamaIndex/LangChain 上层编排)

Q21: 大模型能力评测指标有哪些? 「🟢 校招/初级」 ​

考察点:大模型评测体系。

参考答案:

┌──────────────────────────────────────────────────────────────┐
│                  大模型评测体系                                │
│                                                              │
│  ┌────────────────────────────────────────────────────┐      │
│  │                  评测维度                            │      │
│  │                                                    │      │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐          │      │
│  │  │ 知识能力 │  │ 推理能力 │  │ 语言能力 │          │      │
│  │  │ MMLU     │  │ GSM8K   │  │ HumanEval│          │      │
│  │  │ C-Eval   │  │ MATH    │  │ MBPP    │          │      │
│  │  └─────────┘  └─────────┘  └─────────┘          │      │
│  │                                                    │      │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐          │      │
│  │  │ 安全性   │  │ 工具调用 │  │ 多语言   │          │      │
│  │  │ SafetyBench│ │ BFCL   │  │ MGSM    │          │      │
│  │  │ TruthfulQA│ │ ToolBench│ │ xCopa  │          │      │
│  │  └─────────┘  └─────────┘  └─────────┘          │      │
│  └────────────────────────────────────────────────────┘      │
└──────────────────────────────────────────────────────────────┘

主流评测基准 ​

评测集维度说明满分/分
MMLU综合知识57 个学科多选题/100
C-Eval中文知识52 个学科中文多选题/100
CMMLU中文知识中文多学科评估/100
GSM8K数学推理小学数学应用题/100
MATH数学推理高中竞赛数学/100
HumanEval代码生成Python 函数补全pass@1
MBPP代码生成基础编程题pass@1
BBH推理23 项高难度推理任务/100
TruthfulQA真实性抵抗常见错误观念/100
BFCL工具调用函数调用准确率/100
MT-Bench对话质量多轮对话 GPT-4 评分/10
AlpacaEval指令跟随胜率对比%

评测方法 ​

python
# 1. 客观评测(有标准答案)
# MMLU: 多选题 → 计算准确率
def eval_mmlu(model, dataset):
    correct = 0
    for item in dataset:
        prompt = f"Question: {item['question']}\nA.{item['A']}\nB.{item['B']}\n..."
        answer = model.generate(prompt)
        if extract_choice(answer) == item['answer']:
            correct += 1
    return correct / len(dataset) * 100

# 2. LLM-as-Judge(主观评测)
# MT-Bench: 用 GPT-4 做裁判
def eval_mt_bench(model, questions):
    scores = []
    for q in questions:
        response = model.generate(q)
        judge_prompt = f"""
        请评分以下回答(1-10分):
        问题:{q}
        回答:{response}
        评分维度:有用性、相关性、准确性、深度
        """
        score = gpt4.generate(judge_prompt)  # GPT-4 打分
        scores.append(float(score))
    return sum(scores) / len(scores)

# 3. 人工评测
# 侧重点: 安全性、价值观对齐 → 需要人工判断

评测注意事项 ​

问题说明对策
数据污染评测集泄露到训练数据使用动态/私有评测集
Prompt 敏感不同 Prompt 差异大标准化 Prompt 模板
采样方差Temperature 导致结果波动多次采样取平均
LLM-as-Judge 偏见GPT-4 偏好长答案/自己风格多裁判交叉验证
过度优化针对 Benchmark 刷分关注泛化能力

追问延伸:

  • MMLU 得分高就一定好用吗?(不一定,可能在特定任务上很差)
  • 如何避免评测数据污染?(创建私有评测集、动态生成评测题)

Q22: 对比使用过哪些主流大模型?你们项目中最终选用了哪个模型?为什么? 「🟡 中级」 ​

考察点:模型选型经验和实际项目经验。

参考答案:

主流大模型对比(2024-2025) ​

┌──────────────────────────────────────────────────────────────┐
│                   主流大模型对比                               │
│                                                              │
│  闭源:                                                       │
│  ┌────────────┬──────┬──────┬────────┬──────────┐          │
│  │ 模型        │ 参数  │ 上下文 │ 强项    │ 价格      │          │
│  ├────────────┼──────┼──────┼────────┼──────────┤          │
│  │ GPT-4o     │ ~200B│ 128K │ 综合    │ $5/1M   │          │
│  │ Claude 3.5 │ ~200B│ 200K │ 编码    │ $3/1M   │          │
│  │ Gemini 1.5 │ ~400B│ 1M   │ 多模态  │ $3.5/1M │          │
│  │ o1/o3      │ ~300B│ 200K │ 推理    │ $15/1M  │          │
│  └────────────┴──────┴──────┴────────┴──────────┘          │
│                                                              │
│  开源:                                                       │
│  ┌────────────┬──────┬──────┬────────┬──────────┐          │
│  │ 模型        │ 参数  │ 上下文 │ 强项    │ 部署      │          │
│  ├────────────┼──────┼──────┼────────┼──────────┤          │
│  │ Llama-3    │ 70B  │ 128K │ 通用    │ vLLM     │          │
│  │ Qwen-2.5   │ 72B  │ 128K │ 中文    │ vLLM     │          │
│  │ DeepSeek-V3│ 671B │ 128K │ 编码/推理│ vLLM    │          │
│  │ Mistral    │ 22B  │ 64K  │ 效率    │ vLLM     │          │
│  │ GLM-4      │ 9B   │ 128K │ 中文    │ vLLM     │          │
│  └────────────┴──────┴──────┴────────┴──────────┘          │
└──────────────────────────────────────────────────────────────┘

选型维度 ​

维度考虑因素
能力推理/编码/中文/多模态
成本API 价格 / 自部署成本
延迟首字延迟 / 生成速度
隐私数据是否能出本地
上下文需要多长的上下文窗口
部署开源能否自部署 / 硬件要求
生态微调支持 / 工具链
合规商业使用许可

实际项目选型示例 ​

场景1: 企业内部知识问答(数据不出域)
  → 开源模型自部署
  → Qwen-2.5-72B(中文好)或 Llama-3-70B(通用)
  → vLLM 部署 + RAG

场景2: 代码助手(追求质量)
  → Claude 3.5 Sonnet(编码最强)
  → 或 DeepSeek-V3(开源编码好,可自部署)

场景3: 客服对话(成本敏感)
  → Qwen-2.5-7B(小而精)
  → 自部署,单卡可跑

场景4: 复杂推理(数学/规划)
  → o1 / DeepSeek-R1(推理模型)
  → 延迟较高,适合异步

场景5: 多模态(图文理解)
  → GPT-4o 或 Gemini 1.5 Pro

场景6: 混合方案
  → 简单路由 → 小模型
  → 复杂推理 → 大模型
  → 通过网关统一管理
python
# 混合方案示例:按任务路由
class ModelRouter:
    def __init__(self):
        self.models = {
            "simple": {"model": "qwen-2.5-7b", "threshold": 0.3},
            "complex": {"model": "deepseek-v3", "threshold": 0.7},
            "reasoning": {"model": "deepseek-r1", "threshold": 1.0},
        }
    
    def route(self, query):
        # 简单分类器判断复杂度
        complexity = self.classify_complexity(query)
        
        if complexity < 0.3:
            return self.models["simple"]["model"]  # 小模型处理
        elif complexity < 0.7:
            return self.models["complex"]["model"]  # 大模型处理
        else:
            return self.models["reasoning"]["model"]  # 推理模型

追问延伸:

  • 开源模型和闭源模型差距还有多大?(GPT-4 时代差很多,DeepSeek-V3 已接近 Claude 3.5)
  • 如何评估自部署 vs API?(月调用量 > 阈值时自部署更便宜,反之 API 更灵活)