预训练(Pretraining)是大语言模型生命周期的第一阶段,其目标是在海量无标注语料上通过自监督学习训练模型掌握语言的基本规律、知识和推理能力。预训练阶段产生的模型称为"基座模型"(Base Model),具备通用的文本理解和生成能力,但不具备特殊的指令遵循或任务执行能力。
预训练的核心训练目标是语言建模(Language Modeling),即最大化模型对训练文本的预测概率。根据模型架构的不同,有两种主要变体:
因果语言建模(Causal Language Modeling, CLM):用于自回归解码器模型(如 GPT、LLaMA、Qwen)。模型只能根据当前 token 之前的 token 来预测当前 token:
\mathcal{L}_{\text{CLM}} = -\sum_{t=1}^{T} \log P(x_t | x_{<t}; \theta)其中 \theta 是模型参数,x_{<t} 是位置 t 之前的所有 token。这种单向建模是自回归生成的基础。
掩码语言建模(Masked Language Modeling, MLM):用于编码器模型(如 BERT、RoBERTa)。输入序列中随机遮盖 15% 的 token,模型需要根据上下文(包括左右两侧)预测被遮盖的 token:
\mathcal{L}_{\text{MLM}} = -\sum_{t \in \mathcal{M}} \log P(x_t | x_{\setminus \mathcal{M}}; \theta)其中 \mathcal{M} 是被遮盖位置的集合,x_{\setminus \mathcal{M}} 是未遮盖的上下文 token。
预训练数据的规模和质量直接决定模型能力:
| 数据类别 | 占比 | 来源 | 说明 |
|---|---|---|---|
| Web 文本 | 60-80% | Common Crawl, RefinedWeb | 规模最大但噪声多 |
| 高质量文章 | 10-20% | Wikipedia, Books | 知识密度高 |
| 代码 | 5-15% | GitHub | 提升推理和编程能力 |
| 多语言数据 | 5-10% | 各语言维基百科、新闻 | 覆盖低资源语言 |
训练数据规模演进:GPT-1 (5GB) → GPT-3 (570GB) → LLaMA-2 (2TB) → DBRX (12TB) → DeepSeek-V3 (14.8T tokens)。每一代模型的数据量增长约 3-10 倍。
预训练是计算成本最高的环节。以 DeepSeek-V3(671B 总参/37B 活跃参数)为例:
预训练是微调和对齐的基础。预训练向模型"注入"了世界知识和语言能力,而后续的微调和对齐则负责"塑造"模型的行为方式。没有充分的预训练,后续微调的效果将受到根本制约。
微调(Fine-tuning)是在预训练基座模型的基础上,使用特定领域或特定任务的数据进行有监督训练的进阶过程。微调的核心思想是:预训练模型已经学习了通用的语言表示,微调只需在此基础上进行"定向调整"即可适应下游任务。
全量微调更新模型的所有参数。对于 70B 参数的模型,全量微调需要使用约 560GB 的 GPU 显存(FP16 精度),通常需要 8-16 张 A100-80G 或 H100-80G 显卡。
数学形式:
\theta^* = \arg\min_{\theta} \frac{1}{N}\sum_{i=1}^{N} \mathcal{L}(f(x_i; \theta), y_i)其中 \theta 是模型的所有参数,N 是训练样本数,\mathcal{L} 是任务相关的损失函数(如交叉熵),f(x_i; \theta) 是模型输出,y_i 是目标标签。
优点:理论上可达到最优适应效果。
缺点:训练成本极高,每个下游任务需要训练并存储完整的模型副本,不便于多任务部署。
PEFT(Parameter-Efficient Fine-Tuning)只更新一小部分额外参数,冻结预训练参数不变。典型方法包括:
LoRA(Low-Rank Adaptation):
W = W_0 + BA, \quad \text{其中 } B \in \mathbb{R}^{d \times r}, A \in \mathbb{R}^{r \times k}, r \ll \min(d, k)W_0 是冻结的预训练权重,BA 是低秩分解的增量矩阵,秩 r 通常取 8、16 或 32。训练时只更新 B 和 A,W_0 保持不变。推理时可将 BA 合并入 W_0 中,不增加推理延迟。
AdaLoRA:动态分配参数预算到不同层,重要层分配更多参数,不重要层分配更少。
Prefix Tuning:在输入序列前添加可学习的连续向量(soft prompt),只更新这些向量。
| 特性 | 全量微调 | LoRA | AdaLoRA | Prefix Tuning |
|---|---|---|---|---|
| 可训练参数量 | 100% | 0.1-1% | 0.1-1% | <0.1% |
| 显存需求 | 100%(基线) | 30-40% | 30-40% | 20-30% |
| 模型存储 | 每个任务一个完整副本 | 共用基础模型 + 小权重 | 同 LoRA | 共用基础模型 |
| 推理延迟 | 无增加 | 可合并为原始权重 | 同 LoRA | 需拼接 prefix |
| 任务适应能力 | 最强 | 接近全量微调 | 优于 LoRA | 略弱于 LoRA |
| 适用场景 | 重要任务、资源充足 | 多适配器部署 | 资源优化 | 快速实验 |
vLLM 支持动态加载多个 LoRA 适配器,无需重启服务:
# 启动 vLLM 服务,支持 LoRA
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-2-7b-hf \
--enable-lora \
--max-loras 4 \
--max-lora-rank 64
# 请求时指定 LoRA 适配器
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-2-7b-hf",
"messages": [{"role": "user", "content": "翻译成英文:今天天气很好"}],
"extra_body": {"lora": "path/to/translation-adapter"}
}'
vLLM 的 LoRA 支持基于 punica kernel 实现批量 LoRA 计算,同一 batch 中的不同请求可以使用不同的 LoRA 适配器,互不干扰。
Instruction Tuning(指令微调)是一种特殊的微调方法,通过在大量"指令-回答"对(instruction-response pairs)上训练模型,使其具备理解和遵循人类指令的能力。这是从基座模型(Base Model)到对话模型(Chat Model / Assistant Model)的关键转变。
指令微调的训练数据采用对话格式,通常包含多轮交互。最常用的格式是 ChatML(Chat Markup Language):
<|im_start|>system
你是一个乐于助人的 AI 助手。
<|im_end|>
<|im_start|>user
请解释量子计算的基本原理。
<|im_end|>
<|im_start|>assistant
量子计算利用量子力学中的两个核心原理——叠加(superposition)和纠缠(entanglement)...
<|im_end|>
ChatML 格式的关键元素:
<|im_start|>:标记角色(system/user/assistant)的开始<|im_end|>:标记对话回合的结束损失掩码(Loss Masking):在指令微调的损失计算中,只对 assistant 回复部分的 token 计算损失,对 user 输入和 system prompt 部分进行掩码:
\mathcal{L}_{\text{inst}} = -\sum_{t \in \mathcal{A}} \log P(y_t | x, y_{<t}; \theta)其中 \mathcal{A} 是 assistant 回复部分的 token 位置集合。这种掩码防止模型在训练中"学习"复述用户的输入,确保模型只学习如何做出有用的回复。
高质量的指令微调数据通常包含:
| 数据类型 | 示例 | 占比 | 作用 |
|---|---|---|---|
| 通用问答 | 知识性问题、解释 | 30-40% | 知识检索与解释能力 |
| 创作任务 | 写作、翻译、改写 | 15-20% | 文本生成能力 |
| 推理任务 | 数学推理、逻辑分析 | 10-15% | 推理能力 |
| 编程任务 | 代码生成、调试 | 10-15% | 代码能力 |
| 多轮对话 | 上下文连贯交互 | 10-15% | 对话管理能力 |
| 安全对齐 | 拒绝不当请求 | 5-10% | 安全与无害性 |
Instruction Tuning 是 RLHF 的前置步骤。在完整的 RLHF 流程中,Stage 1 就是指令微调(SFT)。基座模型在指令微调后虽然能遵循指令,但输出的"风格"可能不正确——缺乏对齐、容易胡编乱造。RLHF 的后续阶段(Reward Modeling + RL Optimization)负责进一步将模型输出与人类偏好对齐。
RLHF(基于人类反馈的强化学习)是当前最有效的 LLM 对齐技术之一。它将人类偏好信息通过奖励模型引入强化学习流程,使模型生成结果与人类期望(有用性、诚实性、无害性)保持一致。OpenAI 的 InstructGPT/ChatGPT、Anthropic 的 Claude 等最早采用了这一范式。
RLHF 包含以下四个阶段:
Phase 1: SFT(监督微调)
预训练模型 + 高质量指令数据 → 指令遵循模型
Phase 2: 偏好数据收集
从 SFT 模型采样多个回复 → 人工标注偏好 (good/bad)
Phase 3: Reward Model 训练
SFT 模型 + 偏好比较数据 → 奖励模型
数据格式: (prompt, completion_A, completion_B, preference)
Phase 4: RL 优化(PPO)
SFT 模型 + 奖励模型 → 对齐人类偏好的最终模型
目标: 最大化奖励,同时保持与 SFT 模型的 KL 散度约束
奖励模型的训练目标是最大化人类偏好数据的似然。给定一个 prompt x 和两个输出 y_w(被偏好)、y_l(不被偏好),Bradley-Terry 模型假设人类偏好概率与奖励值的差异呈逻辑斯蒂分布:
P(y_w \succ y_l | x) = \sigma(r_\phi(x, y_w) - r_\phi(x, y_l))其中 r_\phi(x, y) 是奖励模型(通常基于 SFT 模型,去掉最后的 unembedding 层,加上一个线性层输出标量),\sigma 是 sigmoid 函数。
训练损失:
\mathcal{L}_{\text{RM}} = -\mathbb{E}_{(x, y_w, y_l) \sim \mathcal{D}} \left[ \log \sigma(r_\phi(x, y_w) - r_\phi(x, y_l)) \right]这个损失函数的直觉是:最大化偏好回复与不偏好回复之间的奖励差距。
PPO(Proximal Policy Optimization)是 RLHF 中使用的强化学习算法。其目标函数为:
\mathcal{L}_{\text{PPO}}(\theta) = \mathbb{E}_{t} \left[ \min\left( \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_\text{old}}(a_t|s_t)} A_t,\ \text{clip}\left( \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_\text{old}}(a_t|s_t)}, 1-\epsilon, 1+\epsilon \right) A_t \right) \right]对于 LLM 的 RLHF 场景,PPO 损失进一步包含两个约束:
R(x, y) = r_\phi(x, y) - \beta \cdot D_{\text{KL}}(\pi_\theta(y|x) \| \pi_{\text{ref}}(y|x))D_{\text{KL}}(\pi_\theta \| \pi_{\text{ref}}) = \mathbb{E}_{y \sim \pi_\theta} \left[ \log \frac{\pi_\theta(y|x)}{\pi_{\text{ref}}(y|x)} \right]完整 PPO 损失:
\mathcal{L}(\theta) = \mathcal{L}_{\text{PPO}}(\theta) + \alpha \cdot \mathcal{L}_{\text{KL}}(\theta)其中 \beta 控制 KL 惩罚的强度,\alpha 决定了两种损失之间的权衡。
vLLM 原生支持奖励模型推理(用于 RLHF 训练中的奖励计算或评估):
# 加载奖励模型
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-Math-RM-72B \
--task token_classify \
--pooler-config '{"pooling_type": "ALL", "step_tag_id": null}'
# 使用 Score API 获得奖励分数
curl http://localhost:8000/v1/score \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen2.5-Math-RM-72B",
"text_1": "What is 2+2?",
"text_2": "The answer is 4."
}'
# 返回: {"score": 2.34} (越高表示回复越好)
过程奖励模型(PRM)的 StepPool 实现:
class StepPool(AllPool):
def forward(self, hidden_states, pooling_metadata):
# 1. 调用 AllPool 获得全 token 输出
pooled_data = super().forward(hidden_states, pooling_metadata)
# 2. 只提取 step_tag_id 位置的隐藏状态
step_mask = (input_ids == step_tag_id)
return pooled_data[step_mask]
\beta 的调节需要大量实验DPO(直接偏好优化)由 Rafailov 等人于 2023 年提出,是一种无需独立奖励模型的偏好优化方法。DPO 的核心洞察是:RLHF 中的奖励函数可以表示为策略模型和参考模型的概率比,因此可以直接在偏好数据上优化策略模型,无需训练单独的奖励模型。
DPO 的出发点是将 RLHF 的最优策略解表示为闭式形式。经典的 RLHF 目标是:
\max_{\pi_\theta} \mathbb{E}_{x \sim \mathcal{D}, y \sim \pi_\theta(y|x)} \left[ r(x, y) \right] - \beta \cdot D_{\text{KL}}(\pi_\theta(y|x) \| \pi_{\text{ref}}(y|x))这个优化问题有已知的闭式解:
\pi^*(y|x) = \frac{1}{Z(x)} \pi_{\text{ref}}(y|x) \exp\left( \frac{1}{\beta} r(x, y) \right)其中 Z(x) 是配分函数。反过来,奖励函数可以用策略比率表示:
r(x, y) = \beta \log \frac{\pi^*(y|x)}{\pi_{\text{ref}}(y|x)} + \beta \log Z(x)将奖励函数代入 Bradley-Terry 偏好模型:
P(y_w \succ y_l | x) = \sigma\left( r(x, y_w) - r(x, y_l) \right)配分函数 Z(x) 消掉了!得到:
P(y_w \succ y_l | x) = \sigma\left( \beta \log \frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)} - \beta \log \frac{\pi_\theta(y_l|x)}{\pi_{\text{ref}}(y_l|x)} \right)最终 DPO 损失函数:
\mathcal{L}_{\text{DPO}}(\theta) = -\mathbb{E}_{(x, y_w, y_l) \sim \mathcal{D}} \left[ \log \sigma\left( \beta \log \frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)} - \beta \log \frac{\pi_\theta(y_l|x)}{\pi_{\text{ref}}(y_l|x)} \right) \right]| 特性 | RLHF (PPO) | DPO |
|---|---|---|
| 需要独立奖励模型 | 是 | 否 |
| 训练计算成本 | 高(需同时运行 4 个模型) | 低(只需策略模型 + 参考模型) |
| 训练稳定性 | 超参数敏感,容易崩溃 | 相对稳定 |
| 数学基础 | 强化学习(策略梯度) | 直接偏好优化(闭式解) |
| 对数据质量要求 | 中等 | 高(偏好数据需要更精确) |
| 性能天花板 | 理论上更高 | 可能受限于闭式解假设 |
| 代表模型 | GPT-4, Claude-3 | Llama-3, DBRX, Qwen-2 |
DPO 的一个关键假设是存在一个在偏好数据上表现良好且无需进一步 KL 正则化的最优策略。当偏好数据存在噪声或不一致时,DPO 可能不如 PPO 稳健。此外,DPO 不支持在线采样——它只能从已有的偏好数据中学习,无法像 PPO 那样通过与环境的交互动态采样和优化。
GRPO(Group Relative Policy Optimization)是 DeepSeek-R1 中使用的强化学习方法,是 PPO 的一种变体。它的核心创新是:在组/群组内对多个采样回复进行相对比较,替代了 PPO 中需要价值网络(critic/value model)来估计优势函数的方法。
GRPO 的工作流程如下:
1. 给定 prompt x,从当前策略 π_θ 采样 G 个回复 {y_1, y_2, ..., y_G}
2. 为每个回复计算奖励 r(x, y_i)
3. 在组内计算优势函数:
A_i = (r(x, y_i) - mean(r)) / std(r)
4. 使用组内优势更新策略 π_θ
5. 加入 KL 散度约束,防止偏离参考模型
GRPO 的优势函数基于组内统计量,而非价值网络的估计:
A_i = \frac{r(x, y_i) - \mu_G}{\sigma_G}其中 \mu_G = \frac{1}{G}\sum_{j=1}^G r(x, y_j),\sigma_G = \frac{1}{G}\sum_{j=1}^G (r(x, y_j) - \mu_G)^2 是组内奖励的均值和标准差。
关键洞察:通过组内标准化,GRPO 消除了对价值网络的需求。价值网络是 PPO 中最易崩溃的组件之一,因为它需要为每个 token 位置估计未来回报的期望值。GRPO 将这个复杂问题简化为组内比较,显著提高了训练稳定性。
\mathcal{L}_{\text{GRPO}}(\theta) = -\frac{1}{G} \sum_{i=1}^{G} \left[ A_i \cdot \min\left( \frac{\pi_\theta(y_i|x)}{\pi_{\theta_\text{old}}(y_i|x)},\ \text{clip}\left( \frac{\pi_\theta(y_i|x)}{\pi_{\theta_\text{old}}(y_i|x)}, 1-\epsilon, 1+\epsilon \right) \right) - \beta \cdot D_{\text{KL}}(\pi_\theta \| \pi_{\text{ref}}) \right]| 特性 | PPO | DPO | GRPO |
|---|---|---|---|
| 需要价值网络 | 是 | 否 | 否 |
| 需要奖励模型 | 是 | 否 | 是 |
| 在线采样 | 是 | 否(离线) | 是 |
| 组内比较 | 否 | 否 | 是 |
| KL 正则形式 | 显式 | 隐式(通过参考模型对比) | 显式 |
| 训练稳定性 | 低 | 中 | 高 |
| 代表实现 | InstructGPT | Llama-3 | DeepSeek-R1 |
| 范式 | 训练目标 | 数据需求 | 计算成本 | 输出模型类型 | 典型应用 |
|---|---|---|---|---|---|
| Pretraining | 语言建模(最小化 PPL) | 海量无标注文本(TB 级) | 极高(百万 GPU 时) | 基座模型 | 通用语言理解与生成 |
| Full Fine-tuning | 任务损失最小化 | 任务标注数据(万-百万级) | 高(全参数更新) | 任务专用模型 | 分类、NER、翻译 |
| LoRA / PEFT | 任务损失最小化 | 少量标注数据 | 低(约 30% 显存) | 可插拔适配器 | 多任务部署、个性化 |
| Instruction Tuning | 对话损失最小化 | 指令-回答对(万-百万级) | 中等 | 指令遵循模型 | 对话助手、客服 |
| RLHF (PPO) | 奖励最大化 + KL 约束 | 偏好数据 + 奖励模型 | 极高(4 个模型同时运行) | 对齐模型 | ChatGPT, Claude |
| DPO | 隐式偏好优化 | 偏好数据(无需奖励模型) | 中等 | 对齐模型 | Llama-3 |
| GRPO | 组内偏好优化 + KL 约束 | 偏好数据 + 奖励模型 | 较高(在线采样) | 对齐模型 | DeepSeek-R1 |
Perplexity(困惑度)是语言模型最基础、最常用的内在评估指标。它衡量模型对测试文本的"惊讶程度"——perplexity 越低,模型对文本的预测越准确。
对于包含 T 个 token 的测试序列 x = (x_1, x_2, \ldots, x_T):
\text{PPL}(x) = \exp\left( -\frac{1}{T} \sum_{t=1}^{T} \log P(x_t | x_{<t}) \right)等价地,perplexity 是平均交叉熵损失的指数:
\text{PPL} = \exp(\mathcal{L}_{\text{CE}})其中 \mathcal{L}_{\text{CE}} = -\frac{1}{T} \sum_{t=1}^{T} \log P(x_t | x_{<t})。
交叉熵与编码长度:交叉熵 H(P_{\text{data}}, P_{\text{model}}) 表示使用模型 P_{\text{model}} 的编码方案来编码真实分布 P_{\text{data}} 中的数据所需的平均比特数。Perplexity 是将这个比特数转换为"有效选择数"的结果。
Perplexity 作为分支因子:
与信息量的关系:
\log_2(\text{PPL}) = \frac{1}{T} \sum_{t=1}^{T} (-\log_2 P(x_t | x_{<t}))这是每个 token 的平均信息量(以比特计),也称为模型的压缩率。
| 模型 | 参数量 | 数据集 | Perplexity |
|---|---|---|---|
| GPT-2 | 117M | WikiText-2 | ~29 |
| GPT-3 | 175B | Various | ~20 |
| LLaMA-2 7B | 7B | WikiText-2 | ~12 |
| LLaMA-3 8B | 8B | Various | ~10 |
| DeepSeek-V3 | 671B | Various | ~8-9 |
注意:perplexity 与词汇表大小和 tokenizer 密切相关,不同模型之间的 perplexity 比较必须在相同 tokenizer 和相同测试集上进行才有意义。
BLEU 最初用于机器翻译评估,通过比较模型输出与参考翻译的 n-gram 重叠来衡量质量。
修正精度(Modified Precision):
p_n = \frac{\sum_{\text{n-gram} \in \text{candidate}} \min(\text{count}_{\text{candidate}}, \text{count}_{\text{reference}})}{\sum_{\text{n-gram} \in \text{candidate}} \text{count}_{\text{candidate}}}简短惩罚(Brevity Penalty, BP):
\text{BP} = \begin{cases} 1 & \text{if } c > r \\ e^{1 - r/c} & \text{if } c \leq r \end{cases}最终 BLEU 分数:
\text{BLEU} = \text{BP} \cdot \exp\left( \sum_{n=1}^{N} w_n \log p_n \right)通常 N=4,w_n = 1/N(即 BLEU-4)。
ROUGE 主要用于文本摘要评估,侧重于召回率而非精确率。
ROUGE-N:
\text{ROUGE-N} = \frac{\sum_{\text{n-gram} \in \text{reference}} \text{count}_{\text{match}}}{\sum_{\text{n-gram} \in \text{reference}} \text{count}_{\text{reference}}}ROUGE-L(基于最长公共子序列):
\text{ROUGE-L} = \frac{(1 + \beta^2) R_{\text{LCS}} P_{\text{LCS}}}{R_{\text{LCS}} + \beta^2 P_{\text{LCS}}}| 特性 | BLEU | ROUGE |
|---|---|---|
| 主要用途 | 机器翻译 | 文本摘要 |
| 侧重 | 精确率(Precision) | 召回率(Recall) |
| N-gram 连续性 | 要求连续 n-gram | 同 BLEU(ROUGE-N)或 LCS(ROUGE-L) |
| 简短惩罚 | 有(BP) | 通常无 |
| 多参考支持 | 是 | 是 |
MMLU 由 Hendrycks 等人于 2021 年提出,覆盖 57 个学科约 15,908 道选择题(4 选 1),是评估 LLM 知识和推理能力的权威基准。
测试方式:格式化选择题 prompt → 模型选择 A/B/C/D → 计算正确率。
| 分数区间 | 水平 |
|---|---|
| ~25% | 随机猜测(4 选 1) |
| 40-50% | 高中水平 |
| 60-70% | 大学入门水平 |
| 80-90% | 专家水平 |
| >90% | 超越人类专家 |
代表性模型分数:
| 模型 | MMLU (5-shot) |
|---|---|
| GPT-3 (175B) | 43.9% |
| LLaMA-2 70B | 69.8% |
| GPT-4 | 86.4% |
| Gemini Ultra | 90.0% |
| DeepSeek-V3 | 88.5% |
| GPT-4o | 88.7% |
HumanEval 由 OpenAI 于 2021 年提出,包含 164 个手写 Python 编程问题,每个问题包含函数签名、docstring 和测试用例。
评估方式:Pass@k 指标——生成 k 个候选代码,至少有一个通过所有测试用例即算成功:
\text{Pass@k} = \mathbb{E}_{\text{Problems}} \left[ 1 - \frac{\binom{n-c}{k}}{\binom{n}{k}} \right]其中 n 是生成样本数,c 是通过测试的样本数。
MBPP 包含约 974 个 Python 编程问题,相比 HumanEval 更基础(涉及字符串操作、列表处理等)。
MBPP vs HumanEval:
| 特性 | HumanEval | MBPP |
|---|---|---|
| 题目数 | 164 | 974 |
| 难度 | 中等偏难 | 中低 |
| 函数类型 | 算法题为主 | 编程基本功 |
| 测试用例 | 每个问题 7-10 个 | 每个问题 3 个 |
| 数据污染风险 | 低(手写) | 中 |
GPQA 由纽约大学等机构于 2023 年发布,包含 448 道研究生级别的多项选择题。题目由领域专家撰写,覆盖物理、化学、生物学等。
特点:
MATH 包含 12,500 道数学竞赛题目,覆盖代数、几何、微积分等 7 个类别。
评估方式:5 levels of difficulty。每个问题需要模型生成带最终答案的完整解题过程。评估时提取最终答案与标准答案精确匹配。
GSM8K 包含约 8,500 道小学数学应用题,每道题需要 2-8 步推理。使用 CoT(Chain-of-Thought)可显著提升分数:
| 模型 | GSM8K (CoT) |
|---|---|
| GPT-3.5 | ~57% |
| GPT-4 | ~92% |
| DeepSeek-V3 | ~90% |
MT-Bench 由 LMSYS 组织于 2023 年提出,包含 80 个高质量的多轮对话问题,覆盖 8 个类别(写作、推理、数学、编程、提取、STEM、人文、角色扮演)。
评估方式:使用 LLM-as-a-Judge(通常是 GPT-4)对模型回复进行 1-10 分的打分。每个问题包含两轮对话,更考验模型的对话连贯性。
局限性:
Chatbot Arena(又称 LMSYS Chatbot Arena)是一个众包的 LLM 评估平台,采用匿名 A/B 测试 + ELO Rating 的方式进行模型排名。
ELO Rating 系统:
E_A = \frac{1}{1 + 10^{(R_B - R_A) / 400}}R_A' = R_A + K \cdot (S_A - E_A)R_A、R_B 是当前评分,E_A 是 A 获胜的期望概率,S_A 是实际结果(1 表示胜,0 表示负,0.5 表示平),K 是 K 因子(控制评分更新幅度)。特点:
| 基准 | 测试内容 | 评估方式 | 题目数 | 局限性 |
|---|---|---|---|---|
| Perplexity | 语言概率建模 | 交叉熵指数 | 任意文本 | 不反映生成质量 |
| BLEU | 翻译准确性 | n-gram 精确率 | 翻译测试集 | 语义盲区,低相关性 |
| ROUGE | 摘要内容覆盖 | n-gram 召回率 | 摘要测试集 | 同 BLEU |
| MMLU | 多学科知识 | 选择题正确率 | 15,908 道 | 选择题限制,数据污染 |
| HumanEval | Python 代码 | Pass@k | 164 道 | 单一语言,样本少 |
| MBPP | Python 基础编程 | Pass@k | 974 道 | 难度较低 |
| GPQA | 研究生级推理 | 选择题正确率 | 448 道 | 样本少 |
| MATH | 数学竞赛推理 | 答案精确匹配 | 12,500 道 | 仅数学 |
| GSM8K | 数学应用推理 | 答案精确匹配 | 8,500 道 | 小学难度 |
| MT-Bench | 多轮对话质量 | LLM 评分 1-10 | 80 道 | LLM Judge 偏差 |
| Chatbot Arena | 综合对话质量 | 众包 A/B + ELO | 持续收集 | 众包质量参差 |
CUDA Core 是 NVIDIA GPU 中的通用标量计算单元。每个 CUDA Core 可以执行一个浮点或整数运算(如加法、乘法、逻辑运算)。它们基于 SIMT(Single Instruction, Multiple Threads)架构,32 个线程组成一个 warp,以 warp 为单位执行相同的指令。
典型规格:A100 有 6,912 个 CUDA Core,H100 有 16,896 个 CUDA Core。CUDA Core 的算力通常以 TFLOPS(FP32)衡量:A100 约 19.5 TFLOPS,H100 约 67 TFLOPS。
Tensor Core 是 NVIDIA 从 Volta 架构(V100,2017 年)引入的专用矩阵计算单元,专门加速矩阵乘加运算(D = A \times B + C)。Tensor Core 可以在一个时钟周期内完成 4x4 矩阵的乘加操作,在 AI 训练和推理中发挥关键作用。
精度支持演进:
| 架构 | Tensor Core 代际 | 支持精度 |
|---|---|---|
| Volta (V100) | 第 1 代 | FP16 |
| Turing (T4) | 第 2 代 | FP16, INT8, INT4 |
| Ampere (A100) | 第 3 代 | FP16, BF16, TF32, INT8, INT4 |
| Hopper (H100) | 第 4 代 | 新增 FP8 (E4M3/E5M2) |
| Blackwell (B200) | 第 5 代 | 新增 FP4, FP6 |
Tensor Core 关键算力(以 FP16 FMA 计算,TFLOPS):
| GPU | FP32 (CUDA Core) | FP16 (Tensor Core) | FP8 (Tensor Core) | INT8 (Tensor Core) |
|---|---|---|---|---|
| A100 (SXM) | 19.5 | 312 | N/A | 624 |
| H100 (SXM) | 67 | 989 | 1,979 | 1,979 |
| B200 | 90 | 2,250 | 4,500 | 4,500 |
关键差异:Tensor Core 的 FP16 算力是 CUDA Core FP32 的 15-30 倍。这意味着当运算可以被量化为低精度矩阵乘法时(如 Attention 的 QK^T 和 PV 计算),性能可以提升 1-2 个数量级。
在 vLLM 中,KV Cache 量化(FP8)和模型权重量化(FP8/INT4)依赖于 Tensor Core 的高效低精度计算:
# vLLM 中的 FP8 量化推理
# 使用 torch._scaled_mm(底层调用 Tensor Core 的 FP8 MMA)
import torch
# 假设 weight 和 input 都被量化为 FP8
weight_fp8 = weight.to(torch.float8_e4m3fn)
input_fp8 = input.to(torch.float8_e4m3fn)
# Tensor Core 的 FP8 矩阵乘法
# Hopper GPU 上比 FP16 快 2x
output = torch._scaled_mm(input_fp8, weight_fp8.t())
vLLM 的 float8_e4m3fn 支持需要 Hopper GPU(H100/H200)的 Tensor Core。在 Ampere GPU(A100)上只支持 INT8 Tensor Core 加速。
HBM(高带宽显存)是 GPU 使用的主要内存技术,与传统的 GDDR(Graphics DDR)显存相比,HBM 通过 3D 堆叠技术和更宽的内存总线实现远超 GDDR 的带宽。
HBM 通过硅通孔(Through-Silicon Via, TSV)技术将多个 DRAM 芯片垂直堆叠,并通过中间层(interposer)连接到 GPU 核心。这种 3D 堆叠架构实现了:
| 规格 | HBM2e | HBM3 | HBM3e |
|---|---|---|---|
| 带宽/堆叠 | ~460 GB/s | ~819 GB/s | ~1.2 TB/s |
| 最大容量/堆叠 | 24 GB | 24-64 GB | 24-144 GB |
| 运行频率 | 2.0-2.4 Gbps | 6.4 Gbps | 9.8 Gbps |
| 总线宽度/堆叠 | 1024-bit | 1024-bit | 1024-bit |
| 使用 GPU | A100 | H100 | H200, B200 |
实际 GPU 中的 HBM 配置:
| GPU | HBM 版本 | 显存容量 | HBM 带宽 | 带宽提升 |
|---|---|---|---|---|
| A100 (SXM) | HBM2e | 80 GB | 2.0 TB/s | 基线 |
| H100 (SXM) | HBM3 | 80 GB | 3.35 TB/s | +67% |
| H200 (SXM) | HBM3e | 141 GB | 4.8 TB/s | +140% |
| B200 | HBM3e | 192 GB | 8.0 TB/s | +300% |
HBM 带宽是 LLM 推理最重要的硬件瓶颈。Decode 阶段是典型的内存带宽密集型操作:每次前向传播需要读取完整的模型参数(如 70B 模型约 140GB FP16 权重),而计算量却很小。在 A100(2 TB/s)上,理论最小 TPOT 约为 70ms/token(70B 模型,无 batching 时)。
这就是为什么 H200(4.8 TB/s)相比 H100(3.35 TB/s)在 LLM 推理中可获得 30-50% 的 token 生成速度提升——更高的 HBM 带宽直接缩短了每次读取权重的时间。
GPU 的内存层次从上到下依次为:
寄存器 (Register)
↓ 延迟: <1ns, 带宽: ~50 TB/s
SRAM (共享内存 / L1 缓存)
↓ 延迟: ~5-10ns, 带宽: ~10-20 TB/s
HBM (高带宽显存)
↓ 延迟: ~200-300ns, 带宽: 2-8 TB/s
DRAM (系统主内存, CPU 端)
↓ 延迟: ~100-200ns(通过 PCIe/NVLink), 带宽: 50-200 GB/s
SSD (磁盘存储)
↓ 延迟: ~10-100μs, 带宽: 2-14 GB/s
| 层级 | 典型大小 | 延迟 | 带宽 | 位置 |
|---|---|---|---|---|
| 寄存器 | 256 KB / SM | <1 ns | >50 TB/s | 芯片内 |
| SRAM (L1/Shared) | 128-256 KB / SM | ~5-10 ns | 10-20 TB/s | 芯片内 |
| L2 Cache | 40-60 MB (GPU 全局) | ~50-100 ns | 5-10 TB/s | 芯片内 |
| HBM | 80-192 GB | ~200-300 ns | 2-8 TB/s | 芯片封装内 |
| DRAM (CPU) | 256-2048 GB | ~100-200 ns | 50-200 GB/s | 主板 |
| SSD | 1-30 TB | ~10-100 μs | 2-14 GB/s | 磁盘 |
FlashAttention 是 HBM-SRAM 层次利用的典范。传统 Attention 的计算模式是"从 HBM 读取 → 计算 → 写回 HBM",而 FlashAttention 通过 Tiling(分块)技术将计算限制在 SRAM 中:
# 伪代码:FlashAttention 的 Tiling 策略
def flash_attention(Q, K, V, block_size):
# 将 Q, K, V 切分为 B_m 和 B_n 大小的块
for i in range(0, n, B_m): # Q 块
for j in range(0, n, B_n): # K, V 块
# 将 Q_i, K_j, V_j 从 HBM 加载到 SRAM
qi = load_to_sram(Q[i:i+B_m])
kj = load_to_sram(K[j:j+B_n])
vj = load_to_sram(V[j:j+B_n])
# 在 SRAM 中进行矩阵乘法和 softmax
s_ij = qi @ kj.T # SRAM 运算
p_ij = softmax(s_ij) # SRAM 运算
o_i += p_ij @ vj # SRAM 累积
# 最终结果写回 HBM
save_to_hbm(o_i)
IO 复杂度对比:
O(n^2 \cdot d) HBM 访问O(n^2 \cdot d / M) HBM 访问,其中 M 是 SRAM 大小实际中通常获得 2-4 倍的 attention 计算加速。
Memory Wall(内存墙) 是指计算速度增长远快于内存带宽增长导致的性能瓶颈。在 LLM 推理中,Memory Wall 意味着:GPU 的计算能力远远超过其从 HBM 读取数据的能力,因此大部分推理时间花在数据搬运上,而非计算上。
定量分析(以 H100 + LLaMA-70B 为例):
一次 forward pass 的计算量约为 2 \times (L \times n \times d \times 4) \approx 2 \times 80 \times 4096 \times 8192 \times 4 \approx 21.5 TFLOPs
计算时间:21.5 TFLOPS / 989 TFLOPS ≈ 22 ms
实际时间 = max(42ms, 22ms) ≈ 42ms → 受限于内存带宽!
这就是为什么增加 HBM 带宽(如 H100→H200,带宽从 3.35→4.8 TB/s)比增加计算能力(H100→B200)对 LLM 推理的收益更直接。
| 规格 | A100 (SXM) | H100 (SXM) | H200 (SXM) | B200 |
|---|---|---|---|---|
| 架构 | Ampere (GA100) | Hopper (GH100) | Hopper (GH100) | Blackwell (GB200) |
| CUDA Core | 6,912 | 16,896 | 16,896 | ~20,000+ |
| Tensor Core | 432 (第 3 代) | 528 (第 4 代) | 528 (第 4 代) | 第 5 代 |
| FP32 (CUDA) | 19.5 TFLOPS | 67 TFLOPS | 67 TFLOPS | 90 TFLOPS |
| FP16 (Tensor) | 312 TFLOPS | 989 TFLOPS | 989 TFLOPS | 2,250 TFLOPS |
| FP8 (Tensor) | N/A | 1,979 TFLOPS | 1,979 TFLOPS | 4,500 TFLOPS |
| 显存容量 | 80 GB HBM2e | 80 GB HBM3 | 141 GB HBM3e | 192 GB HBM3e |
| 显存带宽 | 2.0 TB/s | 3.35 TB/s | 4.8 TB/s | 8.0 TB/s |
| NVLink 带宽 | 600 GB/s | 900 GB/s | 900 GB/s | 1,800 GB/s |
| 互联方式 | NVLink 3 | NVLink 4 | NVLink 4 | NVLink 5 |
| TDP | 400W | 700W | 700W | 1,000W |
| 支持 FP8 | 否 | 是 | 是 | 是 |
Compute Capability(计算能力)是 NVIDIA 用来标识 GPU 硬件特性版本的编号系统,格式为 X.Y,其中 X 是主要架构代际,Y 是同一代内的特性变体。它决定了 GPU 支持哪些 CUDA 特性(如硬件功能、精度格式、指令集等)。
| Compute Capability | 架构 | 代表 GPU | 关键特性 |
|---|---|---|---|
| 7.0 | Volta | V100 | 第 1 代 Tensor Core |
| 7.5 | Turing | T4, RTX 2080 | INT8/INT4 Tensor Core |
| 8.0 | Ampere | A100 | BF16, TF32, 第 3 代 Tensor Core |
| 8.6 | Ampere | RTX 3090 | 消费级 Ampere |
| 8.9 | Ada Lovelace | RTX 4090 | FP8 转换支持 |
| 9.0 | Hopper | H100 | FP8 Tensor Core, Dynamic Programming |
| 10.0 | Blackwell | B200 | FP4/FP6, 第 5 代 Tensor Core |
vLLM 在编译和运行时检查 Compute Capability 来决定启用哪些优化:
# vLLM 中的 Compute Capability 检查(简化)
import torch
capability = torch.cuda.get_device_capability() # e.g., (8, 0) for A100
if capability >= (9, 0): # Hopper
# 启用 FP8 量化
use_fp8 = True
elif capability >= (8, 0): # Ampere
# 使用 BF16 和 TF32
use_bf16 = True
allow_tf32 = True
if capability >= (8, 0):
# 启用 FlashAttention(需要 Ampere 以上架构)
use_flash_attn = True
在分布式训练中,GPU 服务器之间需要高速网络传输梯度和模型参数。两种主流的高速互联方案是 InfiniBand 和 RoCE(RDMA over Converged Ethernet)。
InfiniBand 是为高性能计算(HPC)设计的专用网络技术,提供极低延迟和高带宽:
| 代际 | 单链路速率 | 典型配置 | 有效带宽 |
|---|---|---|---|
| InfiniBand EDR | 25 Gbps | 4x EDR | 100 Gbps |
| InfiniBand HDR | 50 Gbps | 4x HDR | 200 Gbps |
| InfiniBand NDR | 100 Gbps | 4x NDR | 400 Gbps |
| InfiniBand XDR | 200 Gbps | 4x XDR | 800 Gbps |
特点:
RoCE 是在标准以太网上实现 RDMA 的协议。RoCEv2 将 RDMA 数据包封装在 UDP 中,运行在标准以太网上。
| 版本 | 封装 | 延迟 | 部署复杂度 | 成本 |
|---|---|---|---|---|
| RoCEv1 | 链路层 | 低 | 低 | 低 |
| RoCEv2 | UDP/IP | 中等 | 中等 | 中等 |
特点:
| 维度 | InfiniBand | RoCE |
|---|---|---|
| 延迟 | ~0.5μs | ~1-3μs |
| 带宽 | 400-800 Gbps | 100-400 Gbps |
| 成本 | 高(2-3x) | 中 |
| 部署复杂度 | 低(专用网络) | 高(需要 PFC 配置) |
| 可用性 | 需要专用硬件 | 标准以太网交换机 |
| 生态支持 | NVIDIA NCCL 原生支持 | NCCL 支持(需配置) |
vLLM 使用 NCCL(NVIDIA Collective Communications Library)进行 GPU 间通信。NCCL 自动选择最快的通信路径:
NVLink (同节点 GPU 间) > InfiniBand (跨节点) > RoCE (跨节点) > TCP/IP (兜底)
在多节点场景下(节点间通过 InfiniBand/RoCE),带宽大幅下降(典型 100-400 Gbps = 12.5-50 GB/s),TP 的通信开销会显著增加,因此通常限制 TP size 不超过单节点 GPU 数量。跨节点的分布式策略通常使用 PP(流水线并行)或 DP(数据并行),而非 TP(张量并行)。
RAG(Retrieval-Augmented Generation,检索增强生成)是一种将信息检索与文本生成相结合的架构。它在 LLM 生成过程中引入外部知识库的检索结果,使模型能够基于检索到的相关信息进行回答,而不是仅依赖其训练时学到的参数化知识。
RAG 的核心流程为 Retrieve → Augment → Generate:
输入查询
│
▼
[检索阶段] → Query Encoding → Vector Search / BM25 Search → 获取 Top-K 文档
│
▼
[增强阶段] → 将检索文档与原始查询拼接为增强 prompt
│
▼
[生成阶段] → LLM 基于增强 prompt 生成回复
详细步骤:
Step 1: Retrieve(检索)
Query: "什么是 LoRA 微调?"
→ Embedding Model 编码查询向量
→ 向量数据库近似最近邻搜索 (ANN)
→ 返回 Top-3 最相关文档:
doc_1: "LoRA(Low-Rank Adaptation)..."
doc_2: "Parameter-Efficient Fine-Tuning..."
doc_3: "QLoRA: 4-bit 量化的 LoRA..."
Step 2: Augment(增强)
{{context}}
根据以下资料回答问题:
{doc_1}
{doc_2}
{doc_3}
问题:什么是 LoRA 微调?
Step 3: Generate(生成)
LLM 基于增强 prompt 生成:
"LoRA 微调是一种参数高效微调方法..."
| 优势 | 说明 | 与纯 LLM 对比 |
|---|---|---|
| 知识更新 | 修改知识库即可,无需重新训练 | 纯 LLM 需重新训练或微调 |
| 幻觉缓解 | 提供具体引用来源,减少胡编乱造 | 纯 LLM 完全依赖参数化记忆 |
| 事实准确性 | 可验证每个声称的来源 | 纯 LLM 无法追溯信息来源 |
| 长尾知识 | 可检索不常见或领域特有知识 | 纯 LLM 在大训练数据之外表现差 |
| 隐私合规 | 可控制知识库访问权限 | 纯 LLM 可能记忆并泄露训练数据 |
Chunking(文本分块)是将知识库文档切分为适合检索和嵌入处理的文本片段。分块策略直接影响检索效果:分块太大会增加噪声和计算成本,且可能超过 LLM 的上下文窗口;分块太小会导致语义不完整,检索时缺乏上下文信息。
| 策略 | 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 固定大小 | 按字符/token 数切分,可重叠 | 实现简单、性能稳定 | 可能切断语义边界 | 结构简单的文档 |
| 语义分块 | 基于 embedding 相似度分割 | 保持语义完整性 | 计算成本高 | 需要高质量检索的场景 |
| 递归分块 | 按段落→句子→短语层次切分 | 自动适应文档结构 | 依赖分割规则质量 | 结构清晰的文档 |
| 基于文档结构 | 按 Markdown 标题/HTML 标签 | 保持自然逻辑单元 | 依赖输入格式 | 结构化文档 |
最简单的方法,按固定的 token 数(如 256、512 或 1024)切割文档:
# 固定大小分块(带重叠)
def chunk_fixed(text, chunk_size=512, overlap=50):
tokens = tokenizer.encode(text)
chunks = []
for i in range(0, len(tokens), chunk_size - overlap):
chunk = tokens[i:i + chunk_size]
chunks.append(tokenizer.decode(chunk))
return chunks
重叠(overlap) 参数(通常为 chunk_size 的 10-20%)用于缓解切割点处语义断裂的问题。
利用文档的标题层级(Markdown #、HTML <h1> 等)作为自然的分块边界:
# 递归字符文本分割器(LangChain 实现)
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=50,
separators=["\n\n", "\n", ".", "!", "?", ",", " ", ""],
)
chunks = text_splitter.split_text(document)
基于语义相似度检测"主题边界"的分块策略:
# 语义分块(简化版)
def semantic_chunk(text, sentence_model, threshold=0.8):
sentences = split_into_sentences(text)
chunks = []
current_chunk = [sentences[0]]
for i in range(1, len(sentences)):
# 计算当前句与 chunk 平均 embedding 的相似度
sim = cosine_similarity(
sentence_model.encode(sentences[i]),
sentence_model.encode(" ".join(current_chunk))
)
if sim > threshold:
current_chunk.append(sentences[i])
else:
chunks.append(" ".join(current_chunk))
current_chunk = [sentences[i]]
return chunks
Chunking 质量评估指标:
Embedding 模型(嵌入模型)将文本映射到连续向量空间 \mathbb{R}^d,在 RAG 中用于 query 和 document 的编码,是向量检索的基石。在 vLLM 中,嵌入模型通过 Pooling Model 的 embed 任务提供服务。
| 模型 | 维度 | 最大长度 | 架构 | 特点 |
|---|---|---|---|---|
| bge-large-en-v1.5 | 1024 | 512 | BERT-large | 中文友好,BAAI 出品 |
| e5-mistral-7b-instruct | 4096 | 4096 | Mistral-7B | 长文本支持,高质量 |
| instructor-xl | 768 | 512 | INSTRUCTOR | 任务感知嵌入 |
| jina-embeddings-v3 | 1024 | 8192 | Jina BERT | 多语言,超长上下文 |
| gte-large-en-v1.5 | 1024 | 8192 | GTE | 业界领先的双编码器 |
vLLM 支持的嵌入模型:
_EMBEDDING_MODELS = {
"BertModel": ("bert", "BertEmbeddingModel"),
"RobertaModel": ("roberta", "RobertaEmbeddingModel"),
"RobertaForMaskedLM": ("roberta", "RobertaEmbeddingModel"),
"JinaEmbeddingsV5Model": ("jina", "JinaEmbeddingsV5Model"),
}
intfloat/e5-mistral-7b-instruct)进行降维(如 1024→256),同时保持好的检索效果。向量数据库(Vector Database)专门用于存储和检索高维向量数据,通过近似最近邻搜索(ANN)算法实现高效的相似性搜索。
| 特性 | Milvus | Pinecone | Weaviate | Qdrant |
|---|---|---|---|---|
| 部署方式 | 自托管 / 云 | 云托管 | 自托管 / 云 | 自托管 / 云 |
| 索引算法 | IVF, HNSW, DiskANN | HNSW | HNSW | HNSW |
| 过滤能力 | 标量过滤 + 向量搜索 | 元数据过滤 | GraphQL 过滤 | Payload 过滤 |
| 多模态 | 否 | 否 | 否 | 否 |
| 分布式 | 原生支持 | 托管 | Kubernetes | 集群模式 |
| 持久化 | MinIO/S3 | 自动 | 文件系统/S3 | 文件系统/S3 |
| 中文社区 | 活跃(中文文档) | 一般 | 一般 | 一般 |
| 适合场景 | 大规模生产 | 快速原型 | 知识图谱 + RAG | 高性能搜索 |
HNSW(Hierarchical Navigable Small World):当前向量数据库的主流 ANN 算法,构建多层图结构,高层图提供快速导航,低层图提供精确搜索。
# HNSW 搜索示意图
# 第 3 层(最高): 🟢 → 🟢 → 🟢 (稀疏连接,长距离跳跃)
# 第 2 层: 🟢 → 🟢 → 🟢 → 🟢
# 第 1 层: 🟢 → 🟢 → 🟢 → 🟢 → 🟢 → 🟢
# 第 0 层(最密集): 全连接图
HNSW 的搜索过程:从最顶层开始,贪婪地走向最近邻,然后下降到下一层重复,直到到达最底层。
IVF(Inverted File Index):将向量空间划分为 N 个 Voronoi 单元,搜索时只搜索查询向量所在的单元和邻近单元。需要训练一个 K-means 聚类器来定义单元中心。
vLLM 本身不做检索(不包含向量数据库),而是作为生成引擎。典型的 vLLM + RAG 架构为:
RAG 系统
├── 检索层: Milvus/Pinecone 等向量数据库
│ ├── Chunking: 文档切分为检索单元
│ ├── Embedding: 调用 vLLM 的 embed 接口编码
│ └── Indexing: 向量索引构建 (HNSW/IVF)
├── 精排层: vLLM 的 Rerank Model (cross-encoder)
│ └── Score API 的 classify 任务
└── 生成层: vLLM 的 LLM 推理
└── Chat Completions API + 检索上下文
两阶段检索策略(在 vLLM 中的 Score API 支持):
# 阶段 1: Bi-encoder 粗排(余弦相似度)
query_embedding = embed_model.embed([user_query])
doc_embeddings = embed_model.embed(doc_chunks)
scores = cosine_similarity(query_embedding, doc_embeddings)
top_k_indices = argsort(scores)[-50:] # 粗排保留 Top-50
# 阶段 2: Cross-encoder 精排(vLLM Score API classify 任务)
pairs = [(user_query, doc_chunks[i]) for i in top_k_indices]
rerank_scores = rerank_model.score(pairs) # cross-encoder 精排
final_indices = argsort(rerank_scores)[-5:] # 精排保留 Top-5
Hybrid Search 结合了密集检索(Dense Retrieval)和稀疏检索(Sparse Retrieval,BM25)的优点,在语义理解和精确关键词匹配之间取得平衡。
使用神经网络嵌入模型(如 bge、e5)将文本编码为稠密向量,通过向量相似度搜索:
基于词频-逆文档频率(TF-IDF)的改进算法:
\text{BM25}(q, d) = \sum_{t \in q} \text{IDF}(t) \cdot \frac{\text{TF}(t, d) \cdot (k_1 + 1)}{\text{TF}(t, d) + k_1 \cdot (1 - b + b \cdot \frac{|d|}{\text{avgdl}})}Hybrid Search 通常使用加权融合或重排序融合:
# 加权融合
def hybrid_search(query, alpha=0.5):
dense_scores = dense_search(query) # 向量相似度
sparse_scores = bm25_search(query) # BM25 分数
# 先对两种分数进行归一化
dense_scores = min_max_normalize(dense_scores)
sparse_scores = min_max_normalize(sparse_scores)
# 加权融合
final_scores = alpha * dense_scores + (1 - alpha) * sparse_scores
return argsort(final_scores)
代表性重排序模型:RRF(Reciprocal Rank Fusion):
\text{RRF}(d) = \sum_{m \in \text{methods}} \frac{1}{k + \text{rank}_m(d)}其中 \text{rank}_m(d) 是文档 d 在方法 m 中的排名,k 是常数(通常 60)。RRF 不需要分数归一化,对分数分布不敏感。
| 指标 | 全称 | 含义 | 计算方式 |
|---|---|---|---|
| Hit Rate | 命中率 | 检索结果是否包含至少一个相关文档 | \frac{\text{有相关文档的查询数}}{\text{总查询数}} |
| MRR | Mean Reciprocal Rank | 第一个相关文档在结果中的位置 | \frac{1}{N}\sum_{i=1}^{N}\frac{1}{\text{rank}_i} |
| NDCG | Normalized Discounted Cumulative Gain | 考虑多级相关性和位置的加权指标 | \frac{\text{DCG}}{\text{IDCG}} |
| Recall@k | Top-k 召回率 | 前 k 个结果中相关文档的比例 | — |
| Precision@k | Top-k 精确率 | 前 k 个结果中相关文档的占比 | — |
假设有 3 个查询,每个查询的检索结果中相关文档的位置如下:
| 查询 | 相关文档位置 | Hit Rate | MRR |
|---|---|---|---|
| Q1 | 第 1 位 | 1 | 1/1 = 1.0 |
| Q2 | 第 3 位 | 1 | 1/3 ≈ 0.33 |
| Q3 | 未找到 | 0 | 0 |
整体:Hit Rate = 2/3 ≈ 66.7%, MRR = (1.0 + 0.33 + 0) / 3 ≈ 0.44
NDCG 的额外价值:NDCG 考虑多个相关文档的位置,而非只关注第一个。例如如果 Q1 在位置 1 和 5 有相关文档,NDCG 会同时奖励两个位置(按折扣累加),而 MRR 只考虑位置 1。
| 方法 | 核心思想 | 解决的问题 | 原理 |
|---|---|---|---|
| Self-RAG | 模型自我判断是否需要检索 | 统一了"检索"和"不检索"的决策 | 训练模型输出反思 token(如 [Retrieve]、[No Retrieve]、[Relevant]、[Not Relevant]),让模型自主决定何时检索和如何使用检索结果 |
| CRAG | 检索后纠错和校正 | 检索结果质量不高时的兜底策略 | 对检索结果进行评估,如果检索结果差则触发大规模 web 搜索作为补充;如果检索结果好则保留和使用 |
| RAPTOR | 构建文档的层次摘要树 | 同时支持具体细节和宏观理解的检索 | 将文档递归摘要为层次结构(叶子=原文段落,内部节点=摘要),检索时同时搜索所有层级 |
| HyDE | 用假设文档编码查询 | 弥合查询和文档之间的语义鸿沟 | 先用 LLM 生成一个"假设文档"(hypothetical document),然后用其嵌入进行检索,而非直接用查询嵌入 |
Query: "What is the capital of France?"
→ LLM generates hypothetical document: "France's capital is Paris..."
→ Embed hypothetical document (not the query)
→ Search with this embedding
→ Better than query embedding because the embedding space alignment improves