大模型的对齐(Alignment)训练是让预训练基座模型从「续写文本」变为「遵循人类意图」的关键环节。从2022年ChatGPT采用RLHF(Reinforcement Learning from Human Feedback)一鸣惊人,到2023年DPO(Direct Preference Optimization)以更简洁的方式实现同等效果,再到2024-2025年DeepSeek提出GRPO(Group Relative Policy Optimization)大幅降低训练成本,对齐算法经历了三次范式跃迁。本文将深入解析三种算法的数学原理、工程实现差异以及生产级部署实践。

一、为什么需要对齐训练:从预训练到人类偏好
预训练阶段的大模型通过预测下一个Token学习了语言的统计规律,但它并不「理解」人类的偏好。一个预训练模型可能生成有害内容、偏离主题、或者无法遵循指令格式。对齐训练的目标就是让模型的行为与人类期望对齐——更安全、更有用、更诚实。
对齐训练通常分为三个阶段:
- SFT(Supervised Fine-Tuning):用高质量的指令-回复对进行监督微调,让模型学会「指令跟随」的基本能力
- 偏好学习:通过人类标注的偏好数据(A优于B),让模型学会区分好坏回复
- 策略优化:基于偏好信号对模型策略进行优化,使模型倾向于生成高质量回复
RLHF、DPO和GRPO三种算法主要差异在于第二和第三阶段的实现方式。下面逐一深入解析。
二、RLHF:基于PPO的经典对齐方案
2.1 RLHF的三阶段架构
RLHF由OpenAI在2022年提出,是ChatGPT成功的核心技术。它包含三个独立训练的模型:
| 阶段 | 模型 | 目标 | 训练数据 |
|---|---|---|---|
| 1. SFT | 策略模型(Actor) | 指令跟随 | 指令-回复对 |
| 2. 奖励模型 | 奖励模型(Reward Model) | 打分预测 | 偏好对(chosen, rejected) |
| 3. RL优化 | Actor + Critic + RM | 策略优化 | Prompt + RM打分 |
2.2 奖励模型的训练
奖励模型的核心任务是根据一个Prompt和对应回复,输出一个标量奖励值。训练数据来自人类标注者的偏好判断:给定同一Prompt的两个回复A和B,标注者判断哪个更好。奖励模型的损失函数基于Bradley-Terry模型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 # 奖励模型训练损失(简化版)
import torch
import torch.nn.functional as F
def reward_model_loss(reward_chosen, reward_rejected):
"""
reward_chosen: [batch] 好回复的奖励值
reward_rejected: [batch] 差回复的奖励值
"""
# Bradley-Terry模型:P(chosen > rejected) = sigmoid(r_chosen - r_rejected)
logits = reward_chosen - reward_rejected
loss = -F.logsigmoid(logits).mean()
return loss
# 训练循环示例
for batch in dataloader:
chosen_rewards = reward_model(batch['prompt'], batch['chosen_response'])
rejected_rewards = reward_model(batch['prompt'], batch['rejected_response'])
loss = reward_model_loss(chosen_rewards, rejected_rewards)
loss.backward()
optimizer.step()
2.3 PPO策略优化
PPO(Proximal Policy Optimization)是RLHF最核心也最复杂的部分。它需要在训练时同时维护四个模型:
- Actor(策略模型):正在优化的模型,生成回复
- Critic(价值模型):估计状态价值,用于计算优势函数
- Reference Model(参考模型):冻结的SFT模型,用于计算KL散度惩罚
- Reward Model(奖励模型):冻结的奖励模型,提供奖励信号
PPO的优化目标是最大化期望奖励,同时通过KL散度约束防止策略模型偏离参考模型太远(避免奖励黑客攻击):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 # PPO核心优化逻辑(基于trl库的简化实现)
import torch
import torch.nn.functional as F
def ppo_step(actor, critic, ref_model, reward_model, batch, config):
"""
PPO单步优化
"""
prompts = batch['prompts']
responses = batch['responses']
# 1. 计算旧策略的log_probs(用于ratio计算)
with torch.no_grad():
old_log_probs = actor.get_log_probs(prompts, responses)
# 2. 计算参考模型的log_probs(用于KL惩罚)
with torch.no_grad():
ref_log_probs = ref_model.get_log_probs(prompts, responses)
# 3. 获取奖励值
with torch.no_grad():
rewards = reward_model.get_rewards(prompts, responses)
# KL惩罚项
kl_penalty = config.kl_coef * (old_log_probs - ref_log_probs)
rewards = rewards - kl_penalty
# 4. 计算优势函数(GAE)
with torch.no_grad():
values = critic.get_values(prompts, responses)
advantages = compute_gae(rewards, values, config.gamma, config.lam)
# 优势归一化
advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-8)
returns = advantages + values
# 5. PPO Clip目标
new_log_probs = actor.get_log_probs(prompts, responses)
ratio = torch.exp(new_log_probs - old_log_probs)
surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1 - config.clip_range, 1 + config.clip_range) * advantages
policy_loss = -torch.min(surr1, surr2).mean()
# 6. 价值函数损失
new_values = critic.get_values(prompts, responses)
value_loss = F.mse_loss(new_values, returns)
# 7. 总损失
total_loss = policy_loss + config.vf_coef * value_loss - config.entropy_coef * entropy
return total_loss
2.4 RLHF的工程痛点
RLHF虽然效果出色,但在工程实践中存在显著的复杂性:
- 显存爆炸:同时加载4个模型,对于7B模型需要约4×7B=28B参数的显存,加上梯度、优化器状态,实际需要约8张A100(80GB)
- 训练不稳定:PPO的超参数极其敏感,学习率、KL系数、clip范围、GAE参数都需要精细调优
- 奖励黑客:模型可能学会利用奖励模型的弱点获取高分但不实际的回复
- 采样效率低:需要在训练过程中实时生成回复(rollout),严重拖慢训练速度

三、DPO:绕过强化学习的偏好优化
3.1 DPO的核心洞察
2023年斯坦福大学提出的DPO(Direct Preference Optimization)是对RLHF的一次深刻简化。其核心洞察是:RLHF中奖励模型和策略优化可以统一为一个单一的优化目标,无需显式训练奖励模型,也无需PPO采样。
DPO的数学推导从RLHF的KL约束最优策略出发。在RLHF中,最优策略可以表示为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 # DPO推导核心
# RLHF最优策略: pi*(y|x) = (1/Z(x)) * pi_ref(y|x) * exp(r(x,y)/beta)
# 由此可反解出奖励函数:
# r(x,y) = beta * log(pi*(y|x) / pi_ref(y|x)) + beta * log(Z(x))
# 代入Bradley-Terry偏好模型:
# P(chosen > rejected) = sigmoid(beta * [log(pi(y_w)/pi_ref(y_w)) - log(pi(y_l)/pi_ref(y_l))])
# DPO直接优化这个目标!
import torch
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损失函数
policy_chosen_logps: 策略模型对chosen回复的log概率
policy_rejected_logps: 策略模型对rejected回复的log概率
ref_chosen_logps: 参考模型对chosen回复的log概率
ref_rejected_logps: 参考模型对rejected回复的log概率
beta: KL约束强度
"""
chosen_logratios = policy_chosen_logps - ref_chosen_logps
rejected_logratios = policy_rejected_logps - ref_rejected_logps
logits = beta * (chosen_logratios - rejected_logratios)
loss = -F.logsigmoid(logits).mean()
return loss
3.2 DPO的训练流程
DPO的训练流程极其简洁,只需要两个模型(策略模型 + 参考模型),且无需在线采样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 # 使用TRL库进行DPO训练的完整流程
from trl import DPOTrainer, DPOConfig
from transformers import AutoModelForCausalLM, AutoTokenizer
from datasets import Dataset
def train_dpo(model_name, dataset_path, output_dir):
# 1. 加载策略模型和参考模型
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
device_map="auto"
)
ref_model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
device_map="auto"
)
# 2. 准备偏好数据集
# 格式: {"prompt": str, "chosen": str, "rejected": str}
dataset = Dataset.from_json(dataset_path)
# 3. DPO训练配置
config = DPOConfig(
output_dir=output_dir,
beta=0.1, # KL约束强度
learning_rate=5e-7, # 比SFT更小的学习率
per_device_train_batch_size=4,
gradient_accumulation_steps=8,
num_train_epochs=3,
max_length=2048,
max_prompt_length=1024,
warmup_ratio=0.1,
lr_scheduler_type="cosine",
save_strategy="epoch",
bf16=True,
)
# 4. 启动训练
trainer = DPOTrainer(
model=model,
ref_model=ref_model,
args=config,
train_dataset=dataset,
processing_class=tokenizer,
)
trainer.train()
trainer.save_model(output_dir)
3.3 DPO vs RLHF的工程对比
| 维度 | RLHF (PPO) | DPO |
|---|---|---|
| 所需模型数 | 4个(Actor, Critic, RM, Ref) | 2个(Policy, Ref) |
| 显存需求 | 极高(7B需8×A100-80G) | 中(7B需2×A100-80G) |
| 在线采样 | 需要(rollout) | 不需要(离线数据) |
| 超参数敏感度 | 高(PPO clip, GAE, KL等) | 低(主要调beta) |
| 训练稳定性 | 易发散 | 稳定 |
| 训练速度 | 慢(需实时生成) | 快(约10倍加速) |
| 效果上限 | 高(可探索新策略) | 中(受限于离线数据) |
四、GRPO:DeepSeek的分组相对策略优化
4.1 GRPO的设计动机
GRPO(Group Relative Policy Optimization)由DeepSeek团队在DeepSeek-Math和DeepSeek-R1中提出并大规模使用。它解决了一个核心问题:PPO需要训练一个Critic(价值模型)来估计优势函数,这在LLM场景下非常昂贵。GRPO的核心创新是用一组采样的相对奖励来替代Critic模型,从而减少一半的模型显存。
GRPO的工作原理如下:对于同一个Prompt,从当前策略中采样G个不同的回复,用奖励模型对每个回复打分,然后在这组回复内计算相对优势(组内归一化),直接用这个相对优势作为PPO的优势函数。

4.2 GRPO算法详解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 # GRPO核心算法实现(简化版)
import torch
import torch.nn.functional as F
def grpo_loss(actor, ref_model, reward_model, prompt, beta=0.04,
group_size=8, clip_range=0.2):
"""
GRPO单步优化
"""
# 1. 从当前策略采样G个回复
responses = []
old_log_probs = []
for _ in range(group_size):
response, log_prob = actor.generate_with_logprob(prompt)
responses.append(response)
old_log_probs.append(log_prob)
old_log_probs = torch.stack(old_log_probs) # [G]
# 2. 用奖励模型对每个回复打分
with torch.no_grad():
rewards = torch.stack([
reward_model.get_reward(prompt, resp)
for resp in responses
]) # [G]
# 3. 计算组内相对优势(替代Critic)
mean_reward = rewards.mean()
std_reward = rewards.std() + 1e-8
advantages = (rewards - mean_reward) / std_reward # [G] 归一化
# 4. 计算KL惩罚(使用参考模型)
with torch.no_grad():
ref_log_probs = torch.stack([
ref_model.get_log_prob(prompt, resp)
for resp in responses
])
# 5. 新策略的log_probs
new_log_probs = torch.stack([
actor.get_log_prob(prompt, resp)
for resp in responses
])
# 6. PPO Clip + KL惩罚
ratio = torch.exp(new_log_probs - old_log_probs)
surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1 - clip_range, 1 + clip_range) * advantages
policy_loss = -torch.min(surr1, surr2).mean()
# KL散度惩罚(直接加到loss中)
kl = (torch.exp(ref_log_probs - new_log_probs) -
(ref_log_probs - new_log_probs) - 1).mean()
total_loss = policy_loss + beta * kl
return total_loss
4.3 GRPO的核心优势
GRPO相比传统PPO有几个关键优势:
- 无需Critic模型:省去价值模型训练,显存减少约25%,且避免了Critic训练不稳定的问题
- 天然适合推理任务:对于有明确正确答案的推理任务(如数学、代码),可以用规则验证替代奖励模型,实现无RM训练
- 更好的探索能力:组内采样天然具有多样性,比DPO的离线数据更有探索性
- 可验证奖励:DeepSeek-R1使用GRPO + 规则奖励(代码执行验证、答案匹配)实现了超强推理能力
4.4 DeepSeek-R1中的GRPO实践
DeepSeek-R1是GRPO最成功的应用案例。它通过GRPO + 可验证奖励(Verifiable Rewards)实现了接近OpenAI o1的推理能力:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 # DeepSeek-R1风格的GRPO训练配置(基于verl框架)
# 规则奖励函数示例
def math_reward_fn(prompt: str, response: str) -> float:
"""数学推理的规则奖励:检查最终答案是否正确"""
# 提取 \boxed{} 中的答案
import re
match = re.search(r'\\boxed\{([^}]+)\}', response)
if not match:
return -1.0 # 未给出答案,惩罚
predicted = match.group(1).strip()
ground_truth = extract_ground_truth(prompt)
if predicted == ground_truth:
return 1.0 # 正确
else:
return 0.0 # 错误但不惩罚太重
def code_reward_fn(prompt: str, response: str) -> float:
"""代码生成奖励:实际执行并检查测试用例"""
code = extract_code(response)
test_cases = extract_test_cases(prompt)
passed = 0
for test_input, expected in test_cases:
try:
result = execute_code(code, test_input, timeout=5)
if result == expected:
passed += 1
except Exception:
continue
return passed / len(test_cases) # 通过率作为奖励
# GRPO训练配置
grpo_config = {
"actor_model": "deepseek-llm-7b-base",
"reference_model": "deepseek-llm-7b-base", # 冻结
# 无需reward_model!使用规则奖励
"reward_fn": [math_reward_fn, code_reward_fn],
"group_size": 64, # 每个prompt采样64个回复
"beta": 0.04, # KL惩罚系数
"clip_range": 0.2,
"learning_rate": 1e-6,
"temperature": 1.0, # 采样温度,控制探索
"max_turns": 1,
"kl_penalty_type": "k3", # KL惩罚类型
}
五、三种算法的生产级部署实践
5.1 算法选型决策
不同场景下三种算法各有优势,选型应基于以下决策矩阵:
| 场景 | 推荐算法 | 理由 |
|---|---|---|
| 快速对齐实验 | DPO | 训练快、稳定、资源需求低 |
| 通用对话模型 | DPO或GRPO | DPO足够且高效 |
| 推理增强模型 | GRPO | 支持可验证奖励,探索能力强 |
| 追求极限效果 | RLHF(PPO) | 在线探索能力最强 |
| 资源受限 | DPO | 仅需2个模型 |
| 数学/代码任务 | GRPO | 规则奖励无需RM |
5.2 显存优化策略
无论选择哪种算法,显存管理都是生产级训练的核心挑战。以下策略适用于所有方案:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 # 常用显存优化策略组合
# 1. LoRA微调 - 减少可训练参数
from peft import LoraConfig, get_peft_model
lora_config = LoraConfig(
r=64, # LoRA秩
lora_alpha=128,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
# 2. 梯度检查点 - 用计算换显存
model.gradient_checkpointing_enable()
# 3. 参考模型使用LoRA适配器(DPO特有优化)
# 只需要一个基座模型 + 两个LoRA适配器
# ref_model实际上就是禁用LoRA适配器的base model
dpo_config.ref_model = None # TRL支持此优化
dpo_config.ref_model_adapters = None # 自动处理
# 4. vLLM加速rollout(GRPO/PPO专用)
# 使用vLLM进行高效批量生成
from vllm import LLM, SamplingParams
llm = LLM(model=model_path,
tensor_parallel_size=4,
gpu_memory_utilization=0.3, # 预留显存给训练
enable_prefix_caching=True) # 前缀缓存加速
sampling_params = SamplingParams(
n=group_size, # GRPO分组大小
temperature=1.0,
max_tokens=2048,
)
# 批量生成G个回复
outputs = llm.generate(prompts, sampling_params)
5.3 数据质量控制
偏好数据质量直接决定对齐效果。以下是生产级数据质量控制的关键点:
- 偏好一致性:同一标注者对相似样本的偏好应保持一致,使用一致性过滤剔除矛盾标注
- 偏好强度过滤:标注者信心不足的样本(犹豫时间短或频繁修改)应降权或剔除
- 多样性保障:确保偏好数据覆盖不同长度、难度、类型的Prompt
- 质量分层:将偏好数据按质量分层,优先使用高质量数据训练
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 # 偏好数据质量过滤示例
import json
from collections import Counter
def filter_preference_data(data_path, output_path):
"""多维度偏好数据质量过滤"""
with open(data_path) as f:
data = [json.loads(line) for line in f]
filtered = []
for item in data:
prompt = item['prompt']
chosen = item['chosen']
rejected = item['rejected']
# 1. 长度合理性检查
if len(chosen) < 50 or len(chosen) > 4096:
continue
if len(rejected) < 50 or len(rejected) > 4096:
continue
# 2. 答案不能完全相同
if chosen.strip() == rejected.strip():
continue
# 3. 相似度过滤(太相似的偏好对信息量低)
from difflib import SequenceMatcher
sim = SequenceMatcher(None, chosen, rejected).ratio()
if sim > 0.95:
continue
# 4. Prompt去重
# 5. 标注者一致性检查(如有元数据)
if item.get('annotator_agreement', 1.0) < 0.7:
continue
filtered.append(item)
print(f"过滤前: {len(data)}, 过滤后: {len(filtered)}")
with open(output_path, 'w') as f:
for item in filtered:
f.write(json.dumps(item, ensure_ascii=False) + '\n')
六、效果评估与常见问题
6.1 对齐效果评估体系
对齐训练的效果评估不应仅看单一指标,需要多维度评估:
| 评估维度 | 评估方法 | 关键指标 |
|---|---|---|
| 指令遵循 | IFEval / MT-Bench | 遵循率 |
| 安全性 | Red Teaming / HarmBench | 拒绝率、越狱率 |
| 推理能力 | MATH / GPQA / HumanEval | 准确率 |
| 人类偏好 | AlpacaEval / Chatbot Arena | 胜率 |
| 有用性 | MT-Bench / Arena-Hard | 评分 |
6.2 常见训练问题与解决方案
对齐训练中经常遇到以下问题:
问题1:模型输出变得过于保守(过度对齐)
- 原因:KL惩罚过大或偏好数据中rejected过于极端
- 方案:降低beta值,增加偏好数据多样性,使用混合质量的数据
问题2:DPO训练后模型能力退化
- 原因:学习率过大导致SFT能力遗忘
- 方案:使用更小学习率(5e-7以下),增加训练数据量,使用SimPO等改进变体
问题3:GRPO训练不收敛
- 原因:组内奖励方差过大或采样温度不当
- 方案:调整采样温度(0.7-1.2),增加group_size到32-64,使用奖励裁剪
问题4:奖励黑客行为
- 原因:奖励模型泛化能力不足,被策略模型利用
- 方案:使用多个RM集成,增加KL惩罚,定期更新RM,使用规则验证
七、未来趋势:在线DPO与自我对弈
对齐算法仍在快速演进,几个值得关注的方向:
- Online DPO:结合DPO的简洁性和PPO的在线探索能力,在训练过程中动态生成新的偏好对
- Self-Play:模型自己生成回复并自我评估偏好,减少人工标注需求
- SPIN(Self-Play fIne-tuNing):模型与自身旧版本对弈,逐步提升
- 宪法AI(Constitutional AI):使用AI自身基于规则集进行偏好标注,实现可扩展监督
- 多目标对齐:同时优化有用性、无害性、诚实性等多个目标,避免单一目标过度优化
从RLHF到DPO再到GRPO,对齐训练算法的演进趋势是从复杂到简洁、从在线到离线再回到在线。每一次范式跃迁都降低了对齐训练的工程门槛,让更多团队能够构建高质量的对齐模型。理解这三种算法的原理和工程实践,是构建生产级大模型应用的核心能力。
在实际项目中,建议从DPO开始快速验证效果,根据需求决定是否升级到GRPO或RLHF。对于推理增强场景,GRPO + 可验证奖励是当前最优选择。随着算法和工具链的成熟,对齐训练的门槛会进一步降低,但对偏好数据质量和评估体系的建设始终是核心竞争力。
汤不热吧