LLM 基础概念面试题
12 道题- 分类
- AI 与大模型
- 子分类
- llm
- 题目数
- 12 道
1 Transformer 架构的核心组成与自注意力(Self-Attention)机制的数学原理
答案:
Transformer 由 Encoder 与 Decoder 两个堆叠结构组成,核心是 Self-Attention 机制,通过 Query-Key-Value 三元组计算序列内部任意位置之间的依赖关系。
整体架构:
flowchart TB
Input["输入 Token"] --> Embed["Input Embedding + Positional Encoding"]
Embed --> Encoder
subgraph Encoder["Encoder Stack(N 层)"]
direction TB
E1["Multi-Head Self-Attention"] --> E2["Add & LayerNorm(残差 + 归一化)"] --> E3["Feed-Forward Network(FFN)"] --> E4["Add & LayerNorm"]
end
Encoder --> Decoder
Embed --> Decoder
subgraph Decoder["Decoder Stack(N 层)"]
direction TB
D1["Masked Multi-Head Self-Attention(防止看到未来 token)"] --> D2["Add & LayerNorm"] --> D3["Cross-Attention(Encoder-Decoder Attention)"] --> D4["Add & LayerNorm"] --> D5["Feed-Forward Network"] --> D6["Add & LayerNorm"]
end
Self-Attention 数学表达:
$$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^{\top}}{\sqrt{d_k}}\right)V $$- Q(Query):当前位置的查询向量
- K(Key):所有位置的键向量,与 Q 计算相似度
- V(Value):所有位置的值向量,根据注意力权重加权聚合
- $\sqrt{d_k}$:缩放因子,防止点积方差过大导致 softmax 进入饱和区
多头注意力(Multi-Head Attention):
# PyTorch 风格伪代码
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super().__init__()
self.num_heads = num_heads
self.d_k = d_model // num_heads
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)
def forward(self, x, mask=None):
B, T, _ = x.shape
# 拆成 num_heads 个头
Q = self.W_q(x).view(B, T, self.num_heads, self.d_k).transpose(1, 2)
K = self.W_k(x).view(B, T, self.num_heads, self.d_k).transpose(1, 2)
V = self.W_v(x).view(B, T, self.num_heads, self.d_k).transpose(1, 2)
# 注意力分数
scores = (Q @ K.transpose(-2, -1)) / math.sqrt(self.d_k)
if mask is not None:
scores = scores.masked_fill(mask == 0, float('-inf'))
attn = F.softmax(scores, dim=-1)
out = attn @ V
return self.W_o(out.transpose(1, 2).contiguous().view(B, T, -1))
与 RNN/CNN 的对比:
| 维度 | RNN/LSTM | CNN | Self-Attention |
|---|---|---|---|
| 路径长度 | O(n) | O(log n) | O(1) |
| 长程依赖 | 弱(梯度消失) | 受限于感受野 | 强(任意两位置直连) |
| 并行性 | 差(时序依赖) | 好 | 好(无时序约束) |
| 计算复杂度 | O(n·d²) | O(k·n·d²) | O(n²·d) |
| 位置信息 | 隐式 | 局部+位置编码 | 必须显式位置编码 |
2 位置编码(Positional Encoding)的作用及主流实现方式
答案:
Self-Attention 本身是置换等变的(permutation equivariant),对输入 token 顺序不敏感,必须显式注入位置信息。
Sinusoidal 位置编码(原始 Transformer):
$$ PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d}}\right) $$$$ PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d}}\right) $$- 频率沿维度指数衰减,使不同维度对应不同波长
- 可天然外推到比训练更长的序列
- 与 Embedding 相加注入
主流实现方式:
| 方案 | 代表模型 | 核心思想 | 优势 | 劣势 |
|---|---|---|---|---|
| Sinusoidal | 原始 Transformer | 固定三角函数 | 无需训练、可外推 | 相对位置关系表达弱 |
| Learned Absolute | BERT、GPT-1/2 | 可学习 Embedding 表 | 简单 | 最大长度受限 |
| Relative Position | T5、Transformer-XL | 编码 token 相对距离 | 长度外推好 | 实现复杂 |
| RoPE | LLaMA、GPT-NeoX、Qwen | 旋转矩阵编码绝对位置、内积表达相对位置 | 长度外推优、广泛验证 | 极长序列仍有衰减 |
| ALiBi | BLOOM | 在注意力分数上加线性偏置 | 简单、外推性强 | 精度略低于 RoPE |
| NTK-aware / YaRN | 社区扩展 RoPE | 动态调整频率基数 | 支持超长上下文 | 需要调参 |
RoPE 原理示意:
# RoPE 简化实现(按对维度旋转)
def rotate_half(x):
x1, x2 = x[..., :x.shape[-1]//2], x[..., x.shape[-1]//2:]
return torch.cat((-x2, x1), dim=-1)
def apply_rope(x, cos, sin):
return x * cos + rotate_half(x) * sin
RoPE 将位置信息编码为复数域的旋转,Q 与 K 的内积自动携带相对位置差 $|i-j|$。
3 GPT、BERT、Llama 三大模型族的架构差异与适用场景
答案:
三者的根本差异在于 注意力掩码 与 预训练任务,决定了它们擅长理解(NLU)还是生成(NLG)。
架构对比:
| 特性 | GPT(Generative Pre-Training) | BERT(Bidirectional Encoder) | LLaMA(Meta) |
|---|---|---|---|
| 架构 | Decoder-only | Encoder-only | Decoder-only |
| 注意力 | Masked Self-Attention(单向) | 双向 Self-Attention | Masked Self-Attention + GQA |
| 位置编码 | Learned(GPT-1/2)/RoPE | Learned | RoPE |
| 激活函数 | GELU | GELU | SwiGLU |
| 归一化 | Pre-LayerNorm | Post-LayerNorm | RMSNorm(Pre-LN) |
| 预训练任务 | Next Token Prediction(自回归 LM) | MLM + NSP | Next Token Prediction |
| 典型规模 | 175B(GPT-3) | 340M(BERT-Large) | 7B/13B/65B/70B |
| 优势 | 文本生成、Few-shot | 文本理解、分类、抽取 | 开源可商用、推理性能优 |
| 劣势 | 双向理解弱 | 无法直接生成 | 中文能力需额外微调 |
Masked Attention 的区别:
# GPT / LLaMA:因果掩码(Causal Mask),位置 i 只能看到 <=i 的 token
mask = torch.triu(torch.ones(T, T), diagonal=1).bool() # 上三角为 True
attn_scores = attn_scores.masked_fill(mask, float('-inf'))
# BERT:双向注意力,无 mask,所有位置互相可见
attn_scores = attn_scores # 完整 n×n 注意力矩阵
适用场景:
- GPT / LLaMA:对话、写作、代码生成、Chain-of-Thought 推理
- BERT:文本分类、命名实体识别、问答(抽取式)、搜索召回
- LLaMA 系列:开源生态首选,是 vLLM、Ollama、llama.cpp 等推理引擎的事实标准底座
4 Tokenization 原理:BPE、WordPiece、SentencePiece 的差异
答案:
Tokenization 将原始文本切分为模型可识别的最小单元(token),直接影响词表大小、OOV 处理、多语言能力与推理效率。
主流子词算法:
| 算法 | 代表模型 | 训练方式 | 核心指标 | 中文处理 |
|---|---|---|---|---|
| BPE(Byte Pair Encoding) | GPT-2/3、LLaMA | 频次最高的字符对合并 | 合并次数 | 依赖预分词 |
| WordPiece | BERT | 似然增益最大的字符对合并 | 语言模型似然 | 依赖预分词 |
| Unigram LM | XLNet、ALBERT | 反向:从大词表裁剪概率低的 token | 整体似然 | 需 SentencePiece |
| SentencePiece | LLaMA、T5、Qwen | BPE 或 Unigram 统一框架 | 端到端无预分词 | 原生支持 CJK |
BPE 训练流程:
# BPE 简化训练示意
from collections import Counter
def train_bpe(corpus, num_merges):
# 1. 初始化:将每个词拆为字符序列 + 词尾标记 </w>
vocab = Counter()
for word in corpus:
vocab[' '.join(list(word)) + ' </w>'] += 1
# 2. 迭代 num_merges 次
for _ in range(num_merges):
# 统计所有相邻字符对频次
pairs = Counter()
for word, freq in vocab.items():
symbols = word.split()
for i in range(len(symbols) - 1):
pairs[(symbols[i], symbols[i+1])] += freq
# 合并频次最高的字符对
best = pairs.most_common(1)[0][0]
new_vocab = {}
for word in vocab:
new_word = word.replace(' '.join(best), ''.join(best))
new_vocab[new_word] = vocab[word]
vocab = new_vocab
return vocab
关键差异点:
- BPE:基于频次贪心合并,实现简单,GPT 系标配
- WordPiece:基于语言模型似然(合并后似然提升最大),BERT 系标配
- SentencePiece:直接在原始文本上训练,将空格视为特殊 token(
▁),避免语言相关预分词,对中日韩文友好
LLaMA Tokenizer 特征:
- 基于 SentencePiece + BPE
- 词表大小 32K(LLaMA 1)→ 128K(LLaMA 3,支持多语言)
- 数字按位切分,提升数字推理与算术能力
- 特殊 token:
<s>(BOS)、</s>(EOS)、<unk>、<pad>
5 预训练(Pre-training)与微调(Fine-tuning)的核心机制
答案:
预训练在大规模无标注文本上学习通用语言知识,微调在下游任务标注数据上做有监督适配,两者构成现代 LLM 的两阶段范式。
两阶段对比:
| 维度 | 预训练 | 微调 |
|---|---|---|
| 数据量 | TB 级无标注语料 | KB-MB 级任务数据 |
| 算力 | 数千 GPU·月 | 单卡到数十卡·天 |
| 目标函数 | Next Token Prediction(自回归 LM) | 任务 Loss(分类 CE / 指令 SFT) |
| 学习率 | 1e-4 ~ 3e-4(peak) | 1e-5 ~ 5e-5 |
| 训练步数 | 百万 ~ 千万 step | 数百 ~ 数千 step |
| 输出 | 基座模型(Base) | 任务模型 / 指令模型 |
全参数微调的痛点:
- 7B 模型全参数微调需 ~60GB 显存(FP32 梯度 + 优化器状态)
- 灾难性遗忘(Catastrophic Forgetting):下游任务过拟合导致通用能力下降
- 多任务部署成本高:每个任务一份全量权重
LoRA(Low-Rank Adaptation):
核心思想:冻结原始权重 $W_0 \in \mathbb{R}^{d \times k}$,仅训练低秩分解增量 $\Delta W = BA$,其中 $B \in \mathbb{R}^{d \times r}$,$A \in \mathbb{R}^{r \times k}$,秩 $r \ll \min(d, k)$。
# PEFT 库 LoRA 配置示例
from peft import LoraConfig, get_peft_model
config = LoraConfig(
r=8, # 秩,越小越省显存
lora_alpha=16, # 缩放系数,通常 = 2*r
target_modules=["q_proj", "v_proj"], # 注入位置
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(base_model, config)
# 可训练参数量从 7B 降至 ~4M
QLoRA(Quantized LoRA):
- 将基座模型量化到 4-bit(NF4),冻结存储
- 反向传播时反量化到 FP16/BF16 计算梯度
- 配合 Double Quantization 与 Paged Optimizer 进一步省显存
- 单张 24GB 消费级显卡即可微调 65B 模型
| 方法 | 显存占用(7B) | 训练速度 | 性能损失 |
|---|---|---|---|
| Full Fine-tuning | ~60GB | 1x | 0 |
| LoRA(FP16) | ~16GB | ~0.9x | <1% |
| QLoRA(4-bit) | ~6GB | ~0.7x | <2% |
6 RLHF(Reinforcement Learning from Human Feedback)三阶段流程与算法演进
答案:
RLHF 通过人类偏好数据训练奖励模型,再用强化学习将 LLM 对齐到人类意图。后续演进出 DPO、PPO 等多种变体。
经典三阶段流程:
flowchart TD
S1["Stage 1: 监督微调(SFT)
高质量指令-回答数据
Cross-Entropy Loss 训练 SFT 模型"]
S2["Stage 2: 奖励模型训练(Reward Model)
同一 prompt 的多个回答
人类排序标注 → Pairwise Loss"]
S3["Stage 3: 强化学习对齐(RL: PPO)
SFT 模型作为策略 π_θ
RM 评分作为奖励 r
目标: max E[r − β·KL(π_θ ‖ π_ref)]"]
S1 --> S2 --> S3
PPO 目标函数:
$$ \mathcal{L}_{PPO} = \mathbb{E}_{x \sim \mathcal{D}, y \sim \pi_\theta(\cdot|x)} \left[ r_\phi(x, y) - \beta \cdot \text{KL}(\pi_\theta(y|x) \| \pi_{ref}(y|x)) \right] $$- $r_\phi$:奖励模型打分
- $\beta$:KL 惩罚系数,防止策略漂移过远
- $\pi_{ref}$:参考策略(通常是 SFT 模型)
RLHF 算法演进:
| 算法 | 核心思想 | 优势 | 劣势 |
|---|---|---|---|
| PPO | Actor-Critic + KL 约束 | 稳定、可控 | 需训练 4 个模型、显存大 |
| DPO | 将 RL 转化为 SFT,直接拟合偏好 | 简单、无需 RM 与 Critic | 依赖参考模型分布质量 |
| IPO | DPO 改进,解决偏好过拟合 | 对噪声更鲁棒 | 收敛略慢 |
| KTO | 利用 Kahneman-Tversky 前景理论 | 不需要成对数据 | 调参敏感 |
| GRPO | 组内相对优势估计,无 Critic | 显存减半,DeepSeek 验证 | 训练稳定性需经验 |
| RLOO | REINFORCE Leave-One-Out | 实现简单 | 方差大 |
DPO 损失函数:
$$ \mathcal{L}_{DPO} = -\mathbb{E}_{(x, y_w, y_l)} \left[ \log \sigma \left( \beta \log \frac{\pi_\theta(y_w|x)}{\pi_{ref}(y_w|x)} - \beta \log \frac{\pi_\theta(y_l|x)}{\pi_{ref}(y_l|x)} \right) \right] $$其中 $y_w$ 为偏好回答(winner),$y_l$ 为非偏好回答(loser)。
7 提示工程(Prompt Engineering):Few-shot、Chain-of-Thought、ReAct
答案:
提示工程通过设计输入文本的结构与示例,引导 LLM 产生期望输出,是不更新权重即可提升任务表现的核心手段。
主要范式:
| 范式 | 关键思想 | 适用场景 | 局限性 |
|---|---|---|---|
| Zero-shot | 直接提问,依赖模型先验 | 简单分类、翻译 | 复杂推理弱 |
| Few-shot | 在 prompt 中提供 3-10 个示例 | 格式控制、风格迁移 | 占用上下文窗口 |
| Chain-of-Thought(CoT) | 引导模型输出中间推理步骤 | 算术、逻辑、规划 | 推理时延与成本增加 |
| Self-Consistency | 多次采样 CoT,投票选最终答案 | 高准确率场景 | 成本 N 倍 |
| ReAct | Thought + Action + Observation 循环 | 工具调用、Agent | 错误传播 |
| Tree-of-Thoughts | 多路径搜索 + 评估剪枝 | 复杂规划 | 实现复杂 |
CoT 提示示例:
# Zero-shot CoT
Q: 一个水池有两根管子,A 管 4 小时注满,B 管 6 小时注满,两管同时开多久注满?
A: 让我们一步步思考。
# Few-shot CoT
Q: 鸡兔同笼共 35 头 94 足,几只鸡几只兔?
A: 设有鸡 x 只,兔 y 只。x + y = 35,2x + 4y = 94。解得 y = 12, x = 23。答案是 23 只鸡 12 只兔。
Q: 一个水池有两根管子,A 管 4 小时注满,B 管 6 小时注满,两管同时开多久注满?
A:
ReAct 框架:
# ReAct 伪代码
def react_agent(question, tools, max_steps=5):
scratchpad = ""
for step in range(max_steps):
thought = llm(f"{scratchpad}\nQuestion: {question}\nThought:")
if "Final Answer:" in thought:
return extract_answer(thought)
action = llm(f"{scratchpad}\nThought: {thought}\nAction:")
observation = tools[action.tool].run(action.input)
scratchpad += f"\nThought: {thought}\nAction: {action}\nObservation: {observation}"
return "未找到答案"
最佳实践:
- 使用结构化分隔符:
### Instruction ###/### Context ###/### Question ### - 指定输出格式:JSON Schema、Markdown 表格
- 系统提示(System Prompt)放置全局规则,用户提示(User Prompt)放置具体任务
- 复杂任务拆解:让模型"先列出步骤,再逐步执行"
8 LLM 幻觉(Hallucination)的成因与缓解策略
答案:
幻觉指模型生成貌似合理但与事实不符或无中生有的内容,是 LLM 部署的核心风险之一。
幻觉分类:
| 类型 | 描述 | 示例 |
|---|---|---|
| 事实性幻觉 | 与可验证事实矛盾 | 错误引用论文、虚构历史事件 |
| 忠实性幻觉 | 输出与输入上下文矛盾 | 总结时篡改原文档内容 |
| 内在幻觉 | 同一 prompt 多次生成结果自相矛盾 | 数学推理中途更改变量 |
| 外在幻觉 | 输出无法从输入或世界知识中验证 | 捏造不存在的 API 名称 |
根因分析:
- 训练数据噪声:互联网语料本身含错误信息
- Next Token 目标函数:只优化局部似然,不保证全局事实正确
- 知识截止:训练数据时效性限制
- 解码策略:高 temperature、top-p 采样增加随机性
- 长尾知识稀疏:低频实体训练样本不足
- 过度自信:模型未学习"不知道"的能力
缓解策略:
| 层级 | 策略 | 实现方式 |
|---|---|---|
| 数据 | 高质量清洗、事实性过滤 | 数据流水线去重、事实核查 |
| 训练 | RLHF、RLAIF、DPO 对齐 | 人类标注偏好、宪法 AI |
| 解码 | Retrieval-Augmented Generation(RAG) | 检索外部知识注入 Prompt |
| 提示 | Chain-of-Thought、Self-Verification | 引导模型逐步推理并自检 |
| 后处理 | 幻觉检测 + 事实核查 | NLI 模型、引用溯源 |
| 工程 | 降低 temperature、约束解码 | logit processor、JSON Schema |
RAG 缓解幻觉的机制:
- 提供可溯源上下文,模型有据可依
- 实时注入新知识,突破训练截止时间
- 支持引用标注,便于人工核查
9 上下文窗口(Context Window)的技术挑战与长上下文方案
答案:
上下文窗口指模型一次推理能处理的最大 token 数。LLM 的注意力复杂度为 $O(n^2)$,窗口扩大带来显存与计算双重压力。
主流模型上下文长度演进:
| 模型 | 上下文窗口 | 注意力实现 |
|---|---|---|
| GPT-3.5 | 4K | 密集注意力 |
| GPT-4 | 8K / 32K | 密集注意力 |
| GPT-4 Turbo | 128K | 密集注意力 |
| Claude 2 | 100K | 密集注意力 |
| Claude 3 | 200K | 密集注意力 |
| LLaMA 2 | 4K | 密集注意力 |
| LLaMA 3 | 8K → 128K | GQA + RoPE scaling |
| Qwen 2.5 | 128K | 密集注意力 |
| Gemini 1.5 Pro | 1M → 2M | 稀疏/混合注意力 |
长上下文的核心挑战:
- 显存爆炸:$n$ 个 token 的 KV Cache 占用 $O(n \cdot d \cdot L)$,70B 模型 128K 上下文需 ~40GB KV Cache
- 计算复杂度:$O(n^2)$ 自注意力在 128K 时已不可承受
- 位置编码外推:训练时未见过的长度位置表现下降
- “迷失在中间”(Lost in the Middle):长上下文中段信息利用效率低
- 预训练成本:长文本训练数据稀缺
主流长上下文方案:
graph TD
A[长上下文方案] --> B[位置编码外推]
A --> C[注意力优化]
A --> D[KV Cache 压缩]
B --> B1[RoPE Scaling]
B --> B2[ALiBi]
B --> B3[YaRN]
B --> B4[NTK-aware]
C --> C1[Sparse Attention]
C --> C2[Sliding Window]
C --> C3[FlashAttention]
C --> C4[Linear Attention]
D --> D1[PagedAttention]
D --> D2[Multi-Query Attention]
D --> D3[Grouped-Query Attention]
D --> D4[KV Cache Quantization]
| 方案 | 核心机制 | 代表实现 |
|---|---|---|
| RoPE Scaling | 线性 / 动态调整频率基数 | LLaMA-2-Long、Code LLaMA |
| YaRN | NTK-aware + 注意力温度调节 | Mistral、Yi |
| GQA | 多 Q 头共享一组 KV | LLaMA 2/3、Qwen |
| MQA | 所有 Q 头共享 1 组 KV | PaLM、StarCoder |
| FlashAttention | IO-aware 分块计算 | GPT-4、LLaMA |
| Sliding Window | 局部窗口 + 跨层信息传递 | Mistral 7B |
| PagedAttention | 分页管理 KV Cache | vLLM |
10 RAG(Retrieval-Augmented Generation)架构与关键组件
答案:
RAG 通过在生成前检索外部知识库的相关片段,注入 LLM 上下文,解决知识时效性、幻觉、私域知识整合问题。
完整 RAG 流水线:
graph LR
A[用户 Query] --> B[Query 理解]
B --> C[Query 改写/扩展]
C --> D[Embedding 模型]
D --> E[向量检索]
F[BM25/全文检索] --> E
E --> G[Top-K 文档]
G --> H[Rerank 重排]
H --> I[Prompt 拼接]
I --> J[LLM 生成]
J --> K[带引用回答]
L[文档] --> M[Chunk 切分]
M --> N[Embedding]
N --> O[向量数据库]
O --> E
核心组件:
| 组件 | 作用 | 主流实现 |
|---|---|---|
| Chunking | 文档切分,平衡召回与上下文 | LangChain、LlamaIndex |
| Embedding | 文本向量化,捕捉语义 | BGE、M3E、OpenAI text-embedding-3 |
| Vector DB | 高效相似度检索 | Milvus、Qdrant、Weaviate、Chroma |
| Reranker | 精排提升 Top-K 质量 | BGE-Reranker、Cohere Rerank |
| LLM | 最终答案生成 | GPT-4、Claude、LLaMA、Qwen |
Chunking 策略:
# 常见切分方式
strategies = {
"fixed_size": "按固定 token 数切分,块大小 256-512",
"recursive": "按 [\\n\\n, \\n, 。, 空格] 递归切分,LangChain 默认",
"semantic": "按 embedding 相似度聚类切分",
"markdown": "按 Markdown 标题层级切分",
"parent_child": "检索小块,注入大块上下文"
}
RAG 进阶技术:
- Hybrid Search:向量检索 + BM25 关键词检索融合,兼顾语义与字面
- Query Rewriting:用 LLM 改写用户 query,提升召回
- HyDE(Hypothetical Document Embeddings):先用 LLM 生成假设性答案,再检索相似文档
- Self-RAG:模型自主决定是否检索,并自评检索质量
- GraphRAG:构建知识图谱,捕获实体关系
- Agentic RAG:多 Agent 协同检索,支持多轮、工具调用、条件分支
评估指标:
| 指标 | 衡量内容 |
|---|---|
| Context Precision | 检索 Top-K 中相关文档比例 |
| Context Recall | 真实相关文档被检索到的比例 |
| Faithfulness | 回答是否忠于检索上下文 |
| Answer Relevancy | 回答与问题的相关度 |
11 量化(Quantization):INT8、INT4、GPTQ、AWQ 的原理与对比
答案:
量化将模型权重与激活从 FP16/BF16 降精度到 INT8/INT4/FP8,减少显存占用、加速推理,但需控制精度损失。
核心概念:
- 对称量化:$x_q = \text{round}(x / s)$,$s = \max(|x|) / (2^{b-1}-1)$
- 非对称量化:$x_q = \text{round}((x - z) / s)$,多零点偏移
- Per-tensor / Per-channel / Per-group:缩放因子粒度,越细越准
- PTQ(Post-Training Quantization):训练后量化,无需重训
- QAT(Quantization-Aware Training):训练中模拟量化,精度更高
主流方法对比:
| 方法 | 精度 | 是否需要校准集 | 核心思想 | 适用场景 |
|---|---|---|---|---|
| INT8 动态 | W8A8 | 否 | 激活动态计算 scale | 通用部署 |
| INT8 静态 | W8A8 | 是 | 校准集统计激活分布 | 边缘设备 |
| GPTQ | W4A16 | 是(~128 样本) | 二阶 Hessian 信息,逐列量化 | GPU 推理 |
| AWQ | W4A16 | 否 | 保护 1% 显著权重,激活感知 | GPU 推理 |
| SmoothQuant | W8A8 | 是 | 数学等价变换平滑激活分布 | GPU 推理 |
| GGUF(llama.cpp) | W2-W8 | 否 | K-Quant / I-Quant 多级量化 | CPU 推理 |
| bitsandbytes | W4A16 | 否 | NF4 + 双量化 | QLoRA 训练 |
| FP8(E4M3 / E5M2) | W8A8 | 否 | H100/H200 原生支持 | 新一代 GPU |
GPTQ 原理:
# GPTQ 核心思想:逐层量化,最小化重构误差
# 对每层权重 W,目标是找到量化后 Ŵ 使得 ||W X - Ŵ X||² 最小
# 利用 Hessian H = 2 X X^T 做二阶信息,计算量化顺序
# 使用示例
from auto_gptq import AutoGPTQForCausalLM
model = AutoGPTQForCausalLM.from_quantized(
"TheBloke/Llama-2-7B-Chat-GPTQ",
quantize_config={"bits": 4, "group_size": 128}
)
AWQ 原理:
- 观察:约 1% 的显著权重(salient weights)决定了大部分激活值
- 不量化这些显著权重(保留 FP16),其余权重量化到 INT4
- 激活感知:依据激活分布自动寻找最优 per-channel 缩放因子
- 优势:相比 GPTQ 推理速度更快,相同精度下 group_size 更小
量化选型建议:
| 硬件 | 推荐方案 | 原因 |
|---|---|---|
| NVIDIA A100/H100 | FP16 / INT8 / FP8 | 原生支持,吞吐最高 |
| 消费级 4090/3090 | AWQ-INT4 / GPTQ | 显存 24GB 友好 |
| Apple Silicon | GGUF Q4_K_M | llama.cpp 优化 |
| 边缘端(手机/嵌入式) | INT4 / SmoothQuant | 内存与功耗严苛 |
12 推理优化:KV Cache 与 PagedAttention 的工作机制
答案:
LLM 推理是显存带宽与计算双重密集型任务,KV Cache 优化是吞吐提升的核心。
KV Cache 基础:
自回归推理时,每个新 token 需与历史所有 token 计算注意力。为避免重复计算历史 token 的 K、V 矩阵,将其缓存到显存:
# 推理时维护 KV Cache
class KVCache:
def __init__(self, batch_size, num_heads, seq_len, head_dim):
self.k = torch.zeros(batch_size, num_heads, seq_len, head_dim)
self.v = torch.zeros(batch_size, num_heads, seq_len, head_dim)
self.pos = 0
def update(self, k_new, v_new):
# 增量写入新 token 的 K、V
self.k[:, :, self.pos] = k_new
self.v[:, :, self.pos] = v_new
self.pos += 1
return self.k[:, :, :self.pos], self.v[:, :, :self.pos]
显存占用估算:
- 7B 模型(32 层,32 头,128 维):单 token KV Cache ≈ $2 \times 32 \times 32 \times 128 \times 2 \text{ bytes} = 0.5 \text{ MB}$
- 128K 上下文:$0.5 \text{ MB} \times 128000 = 64 \text{ GB}$(单序列单 batch)
- 并发 32 路:直接爆显存
传统 KV Cache 的问题:
- 预分配最大长度,内部碎片 25%-50%
- 不支持请求间共享(如 system prompt 重复)
- 显存利用率低,限制并发
PagedAttention 核心思想:
借鉴操作系统虚拟内存的页表机制,将 KV Cache 划分为固定大小的 Block(如 16 token / block),通过 Block Table 做逻辑到物理的映射。
graph TB
subgraph 请求1
A1[Seq 1: 48 tokens] --> BT1[Block Table: 0,2,5]
end
subgraph 请求2
A2[Seq 2: 32 tokens] --> BT2[Block Table: 1,3]
end
subgraph 物理Block池
B0[Block 0: tokens 0-15]
B1[Block 1: tokens 0-15]
B2[Block 2: tokens 16-31]
B3[Block 3: tokens 16-31]
B4[Block 4: Free]
B5[Block 5: tokens 32-47]
end
BT1 -.-> B0
BT1 -.-> B2
BT1 -.-> B5
BT2 -.-> B1
BT2 -.-> B3
关键技术收益:
| 指标 | 传统连续分配 | PagedAttention |
|---|---|---|
| 内部碎片 | 25%-50% | <4% |
| 显存利用率 | ~60% | ~96% |
| 并行采样(Parallel Sampling) | 每条独立分配全部 KV | 共享 Prompt 部分 Block |
| Prefix Caching | 不支持 | Block 级共享,多请求复用 system prompt |
| Preemption | 整段重算 | Swap 到 CPU,按需恢复 |
配套优化技术:
- Continuous Batching:请求完成即刻释放 slot,新请求即时插入,GPU 利用率最大化
- Chunked Prefill:长 prompt 切分为多 chunk,与 decode 请求混合调度
- Prefix Cache:自动识别相同前缀,共享 KV Block
- Speculative Decoding:用小模型 draft token,大模型 verify,加速 2-3x
- FlashAttention:IO-aware 分块注意力,HBM 读写次数从 $O(n^2)$ 降至 $O(n)$
- KV Cache 量化:FP8 / INT8 缓存,显存再降 2-4x
- MQA / GQA:减少 KV 头数,KV Cache 体积降 4-8x
推理引擎对比:
| 引擎 | 核心特性 | 部署形态 |
|---|---|---|
| vLLM | PagedAttention + Continuous Batching | 推理服务(OpenAI 兼容 API) |
| TGI(HuggingFace) | Rust 内核 + PagedAttention | 推理服务 |
| TensorRT-LLM | NVIDIA 深度优化编译 | 极致性能 |
| llama.cpp | GGUF + CPU/GPU 混合 | 本地/边缘 |
| SGLang | RadixAttention + 程序化提示 | 复杂 prompt 编排 |
| MLX | Apple Silicon 原生 | Mac 本地 |