当前位置: 首页 > news >正文

手写 RLHF(强化学习人类反馈):从零实现大模型对齐训练

一、引言2024-2025 年大语言模型经历了一场静悄悄的革命——模型的聪明不再仅仅取决于参数量和训练数据更取决于对齐Alignment。OpenAI 的 InstructGPT/ChatGPT、Anthropic 的 Claude、以及最近大火的 DeepSeek-R1其核心能力都建立在同一个技术基石之上基于人类反馈的强化学习RLHFReinforcement Learning from Human Feedback。然而翻开网上的教程大多数 RLHF 文章要么停留在概念层面要么直接调用 Hugging Face 的TRL库、transformers.Pipeline一行搞定——读者看完仍然不知道其中的梯度是怎么流的、策略是怎么更新的、奖励模型到底训练了什么。本文的目标就是从数学原理到代码实现一步一步手写 RLHF 的核心组件。你将亲手实现一个奖励模型Reward Model—— 从人类偏好排序中学习打分一个策略优化循环PPO—— 在 KL 散度约束下优化语言模型一个完整的RLHF 训练流程—— 把数据和梯度串起来无论你是想深入理解大模型对齐、准备相关面试还是想自己动手调教一个听话的模型这篇文章都会给你最硬核的参考。全文约 5500 字包含完整的 Python 伪代码和数学推导建议配合代码编辑器阅读。二、RLHF 的总览与核心直觉2.1 为什么需要 RLHF大语言模型的预训练目标是预测下一个 token学习的是语料中的统计规律。但人类的真实需求是模型输出有用Helpful模型输出诚实Honest模型输出无害Harmless这就是著名的HHH 原则。预训练损失函数交叉熵无法直接优化这三者因此需要引入人类反馈作为额外的监督信号。2.2 RLHF 的三个阶段标准的 RLHF 训练分为三步SFTSupervised Fine-Tuning用高质量的指令-回答对微调预训练模型让它学会遵循指令的格式。RMReward Modeling收集人类对模型输出的偏好排序数据训练一个奖励模型用来给任意文本输出打分。RLReinforcement Learning via PPO利用奖励模型的分数通过 PPOProximal Policy Optimization算法进一步微调 SFT 模型让模型不断输出高分回答。本文重点放在阶段 2奖励模型和阶段 3PPO 策略优化上。SFT 就是标准的监督微调不再赘述。2.3 一句话直觉RLHF 让模型自己生成回答 → 奖励模型打分 → 用分数更新模型 → 但不要偏离原始模型太远KL 约束奖励模型像一位阅卷老师PPO 则让学生不断做试卷、订正、提升成绩同时老师也禁止学生死记硬背偏离课本太远。三、奖励模型Reward Model教机器学会人类的品味3.1 问题定义我们有一个人工标注数据集对于同一个 prompt指令标记者比较了两个不同的模型回答 $y_1$ 和 $y_2$给出了偏好判断$y_1 \succ y_2$回答 1 优于回答 2。我们的目标是训练一个奖励函数 $r_\phi(x, y)$由参数 $\phi$ 定义的神经网络使得$$r_\phi(x, y_1) r_\phi(x, y_2) \quad \text{当且仅当} \quad y_1 \succ y_2$$3.2 Bradley-Terry 模型RLHF 中经典的偏好建模方法是Bradley-Terry 模型。它假设人类对 $y_1$ 优于 $y_2$ 的概率为$$P(y_1 \succ y_2 | x) \frac{\exp(r_\phi(x, y_1))}{\exp(r_\phi(x, y_1)) \exp(r_\phi(x, y_2))}$$这是一个逻辑回归形式的概率模型。直观理解两个回答的奖励分数差距越大人类偏好其中一个的概率就越高。3.3 损失函数给定标注数据集 $\mathcal{D} {(x^{(i)}, y_w^{(i)}, y_l^{(i)})}$其中 $y_w$ 是赢家、$y_l$ 是输家我们用负对数似然作为损失函数$$\mathcal{L}R(\phi) -\mathbb{E}{(x, y_w, y_l) \sim \mathcal{D}} \left[ \log \sigma(r_\phi(x, y_w) - r_\phi(x, y_l)) \right]$$其中 $\sigma$ 是 sigmoid 函数。这个损失函数的意义很清晰让赢家的分数尽可能高于输家。3.4 奖励模型架构在实践中奖励模型就是从 SFT 模型的基础上改造而来加载一个预训练或 SFT 过的语言模型例如 GPT-2、LLaMA 等移除语言模型的LM Head预测下一个 token 的分类层在最后一层的隐藏状态上取最后一个 token 的表示EOS token接一个线性层hidden_dim → 1输出一个标量分数关键设计选择为什么取最后一个 token因为在因果语言模型中最后一个 token 的隐藏状态聚合了整段序列的信息可以看作是模型对整个回答的综合评判。3.5 代码实现import torch import torch.nn as nn from transformers import AutoModel, AutoConfig class RewardModel(nn.Module): 基于语言模型构建的奖励模型 def __init__(self, base_model_name: str, dropout: float 0.1): super().__init__() # 加载基础模型无 LM Head self.config AutoConfig.from_pretrained(base_model_name) self.base_model AutoModel.from_pretrained(base_model_name) hidden_size self.config.hidden_size # 奖励头Value Head self.reward_head nn.Sequential( nn.Linear(hidden_size, hidden_size), nn.Dropout(dropout), nn.ReLU(), nn.Linear(hidden_size, 1) ) def forward( self, input_ids: torch.LongTensor, # (batch, seq_len) attention_mask: torch.LongTensor, # (batch, seq_len) ) - torch.Tensor: outputs self.base_model( input_idsinput_ids, attention_maskattention_mask, output_hidden_statesTrue ) # 取最后一个隐藏层 last_hidden outputs.last_hidden_state # (batch, seq_len, hidden_dim) # 找到每个序列的最后一个非 padding token 位置 # attention_mask 中值为 1 表示有效 token last_indices attention_mask.sum(dim1) - 1 # (batch,) # 收集最后一个有效 token 的隐藏状态 batch_indices torch.arange(last_indices.size(0), deviceinput_ids.device) last_token_hidden last_hidden[batch_indices, last_indices] # (batch, hidden_dim) # 通过奖励头得到标量分数 rewards self.reward_head(last_token_hidden) # (batch, 1) return rewards.squeeze(-1) # (batch,) def train_reward_model( model: RewardModel, dataloader: torch.utils.data.DataLoader, optimizer: torch.optim.Optimizer, epochs: int 1, device: str cuda ) - RewardModel: 训练奖励模型。 数据集格式: 每条记录包含 prompt_ids, chosen_ids, rejected_ids 及对应的 attention_mask。 model.train() loss_fn nn.BCEWithLogitsLoss() for epoch in range(epochs): total_loss 0.0 for batch in dataloader: chosen_ids batch[chosen_ids].to(device) chosen_mask batch[chosen_mask].to(device) rejected_ids batch[rejected_ids].to(device) rejected_mask batch[rejected_mask].to(device) # 前向传播 chosen_rewards model(chosen_ids, chosen_mask) rejected_rewards model(rejected_ids, rejected_mask) # 计算 Bradley-Terry 损失 # 目标: chosen_rewards rejected_rewards logits chosen_rewards - rejected_rewards # (batch,) # 标签全为 1chosen 应该优于 rejected labels torch.ones_like(logits) loss loss_fn(logits, labels) # 反向传播 optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() avg_loss total_loss / len(dataloader) print(fEpoch {epoch1}, Average Loss: {avg_loss:.4f}) return model3.6 训练细节与坑数据构建比较应当来自同一个 prompt的两个回答。在 InstructGPT 的数据集中标注者看到 prompt 和两个回答然后选出更优的那个。数据平衡尽量确保输家不永远是同一个模型生成的否则奖励模型容易学到模型 A 的答案不好这种偏差。过拟合奖励模型通常只有几万到几十万条偏好数据参数量却和基座模型一样大——极易过拟合。需要用dropout、权重衰减等方法控制。分数校准奖励模型的输出分数是相对的不是绝对刻度。2和-3之间的差距有意义但5的具体数值本身没有绝对意义。四、PPO 策略优化让模型学会讨好评委4.1 为什么要用 PPO有了奖励模型后最直接的想法是对模型做有监督的微调把奖励模型的评分当作标签去拟合。但这有两个问题1.分布偏移SFT 阶段模型从未见过自己生成的回答一旦模型开始生成新内容分布变化后奖励模型的准确性存疑。2.无法探索监督学习只能模仿已有数据无法探索新的、更优的输出策略。因此我们需要强化学习——让模型自己生成文本用奖励模型打分通过策略梯度更新。4.2 PPO 算法的核心公式PPO 在 RLHF 中的目标函数为$$\text{objective}(\theta) \mathbb{E}{(x, y) \sim \pi{\theta_{\text{old}}}} \left[ \min\left( \frac{\pi_\theta(y|x)}{\pi_{\text{old}}(y|x)} \cdot A(x,y), \ \text{clip}\left( \frac{\pi_\theta(y|x)}{\pi_{\text{old}}(y|x)}, 1-\epsilon, 1\epsilon \right) \cdot A(x,y) \right) \right]$$这个公式看起来复杂拆解开来看其实很直观$\frac{\pi_\theta}{\pi_{\text{old}}}$ 叫做重要性采样比importance ratio表示新策略下生成当前回答的概率相对于旧策略的比例。$A(x,y)$ 是优势函数Advantage表示当前回答相较于平均回答好多少。$\min$ 和 clip 一起限制了步长如果新策略和旧策略差异太大梯度会被截断防止一步迈太远。4.3 KL 散度约束PPO 的 clip 机制本身就能限制策略更新幅度但在 RLHF 中我们额外加入一个KL 惩罚项$$\text{reward}{\text{final}} r\phi(x, y) - \beta \cdot \text{KL}(\pi_{\text{ref}} \parallel \pi_\theta)$$这里的 $\pi_{\text{ref}}$ 是 SFT 阶段得到的参考模型冻结不动。这个 KL 惩罚确保模型不会为了讨好奖励模型而输出奇怪的内容如不断重复高分词汇模型的流畅性和语言能力不会退化保持参考模型的语言质量$\beta$ 是超参数控制对齐的强度在 PPO 实际实现中KL 项通常有两种加入方式-PPO-ptx在 PPO 目标中加入参考模型的交叉熵项保留预训练能力-Kullback-Leibler (KL) penalty在奖励信号中直接扣除 KL 散度4.4 代码实现 PPO 训练循环import torch import torch.nn.functional as F from transformers import AutoModelForCausalLM, AutoTokenizer class PPOConfig: PPO 训练超参数 def __init__(self): self.learning_rate 1.5e-5 self.batch_size 16 self.gradient_accumulation_steps 4 self.ppo_epochs 4 # 每个 batch 内 PPO 更新轮数 self.clip_epsilon 0.2 # PPO clip 范围 self.kl_coef 0.02 # KL 惩罚系数 self.value_coef 0.1 # Value loss 系数 self.max_grad_norm 1.0 # 梯度裁剪 self.max_length 512 # 生成长度 self.temperature 0.7 # 采样温度 class PPOTrainer: PPO 训练器管理策略模型 价值函数 奖励模型的交互。 def __init__( self, policy_model: AutoModelForCausalLM, # 正在训练的策略模型 ref_model: AutoModelForCausalLM, # 冻结的参考模型SFT 后 reward_model: RewardModel, # 训练好的奖励模型 tokenizer: AutoTokenizer, config: PPOConfig, device: str cuda ): self.policy_model policy_model.to(device) self.ref_model ref_model.to(device) self.reward_model reward_model.to(device) self.tokenizer tokenizer self.config config self.device device # 冻结参考模型和奖励模型 for param in self.ref_model.parameters(): param.requires_grad False for param in self.reward_model.parameters(): param.requires_grad False # 优化器只优化策略模型 self.optimizer torch.optim.AdamW( self.policy_model.parameters(), lrconfig.learning_rate ) def _get_log_probs( self, model: AutoModelForCausalLM, input_ids: torch.LongTensor, attention_mask: torch.LongTensor ) - torch.Tensor: 计算给定序列的 token 级对数概率之和 outputs model(input_ids, attention_maskattention_mask) logits outputs.logits # (batch, seq_len, vocab_size) # shift 操作logits 预测下一个 tokenlabels 是实际下一个 token shift_logits logits[:, :-1, :].contiguous() shift_labels input_ids[:, 1:].contiguous() shift_mask attention_mask[:, 1:].contiguous() # 计算每个 token 的 log_prob log_probs F.log_softmax(shift_logits, dim-1) # (batch, seq_len-1, vocab_size) per_token_log_probs log_probs.gather( dim-1, indexshift_labels.unsqueeze(-1) ).squeeze(-1) # (batch, seq_len-1) # 对有效 token 求和带 mask sum_log_probs (per_token_log_probs * shift_mask).sum(dim-1) # (batch,) return sum_log_probs def _compute_advantages_and_returns( self, rewards: torch.Tensor, # (batch,) 奖励模型给出的分数 values: torch.Tensor, # (batch,) 价值函数预测的分数 gamma: float 1.0, lam: float 0.95 ) - tuple: 计算 GAEGeneralized Advantage Estimation优势和折扣回报。 GAE 公式: A_t δ_t (γλ)δ_{t1} (γλ)^2δ_{t2} ... 其中 δ_t r_t γV(s_{t1}) - V(s_t) # 简单版本单步奖励无时间维度展开 # 在 RLHF 中整个序列只有一个奖励值终端奖励 advantages rewards - values # (batch,) returns rewards # 简单情况终端回报就是奖励 return advantages, returns def train_step(self, batch: dict) - dict: 单步 PPO 训练。 batch 包含: - query_ids: prompt 的 token ids - query_mask: prompt 的 attention mask query_ids batch[query_ids].to(self.device) query_mask batch[query_mask].to(self.device) # 阶段 1: 生成回答 # 策略模型生成回答 with torch.no_grad(): gen_outputs self.policy_model.generate( input_idsquery_ids, attention_maskquery_mask, max_new_tokensself.config.max_length - query_ids.size(1), temperatureself.config.temperature, do_sampleTrue, pad_token_idself.tokenizer.pad_token_id, return_dict_in_generateTrue, output_scoresTrue ) response_ids gen_outputs.sequences # (batch, full_seq_len) response_mask (response_ids ! self.tokenizer.pad_token_id).long() # 阶段 2: 计算各模型的输出对数概率 with torch.no_grad(): # 旧策略的对数概率用于重要性采样比的分母 old_log_probs self._get_log_probs( self.policy_model, response_ids, response_mask ) # 参考模型的对数概率用于 KL 计算 ref_log_probs self._get_log_probs( self.ref_model, response_ids, response_mask ) # 奖励模型打分 rewards self.reward_model(response_ids, response_mask) # 阶段 3: 多轮 PPO 更新 total_stats { policy_loss: 0.0, value_loss: 0.0, kl_div: 0.0, clip_frac: 0.0 } for _ in range(self.config.ppo_epochs): # 新策略的对数概率 new_log_probs self._get_log_probs( self.policy_model, response_ids, response_mask ) # 计算重要性采样比 ratio torch.exp(new_log_probs - old_log_probs) # 计算 KL 散度近似形式 # KL(policy || ref) ≈ log(ref/policy) - 1 policy/ref 的一种形式 kl_div old_log_probs - ref_log_probs # 从奖励中扣除 KL 惩罚 penalized_rewards rewards - self.config.kl_coef * kl_div # 计算优势和回报 # 这里用简单版本reward 即为回报优势 reward - baseline # 实际上还需要学习 value functioncritic这里从简 advantages penalized_rewards - penalized_rewards.mean() if advantages.std() 0: advantages advantages / (advantages.std() 1e-8) # PPO 策略损失clipped surrogate objective pg_loss1 -advantages * ratio pg_loss2 -advantages * torch.clamp( ratio, 1.0 - self.config.clip_epsilon, 1.0 self.config.clip_epsilon ) pg_loss torch.max(pg_loss1, pg_loss2).mean() # 总损失简化版不含 value loss loss pg_loss # 反向传播 self.optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_( self.policy_model.parameters(), self.config.max_grad_norm ) self.optimizer.step() # 统计信息 total_stats[policy_loss] pg_loss.item() total_stats[kl_div] kl_div.mean().item() total_stats[clip_frac] ( (torch.abs(ratio - 1.0) self.config.clip_epsilon).float().mean().item() ) # 平均统计 for key in total_stats: total_stats[key] / self.config.ppo_epochs return total_stats4.5 价值函数Critic的角色上面的简化实现中我跳过了 value network 的细节。在完整的 PPO 中模型还有一个价值头value head与奖励模型类似在最后一层隐藏状态上接一个线性层输出标量用来估计状态的价值 $V(x, y_{t})$。价值函数的作用1. 提供baseline用于计算优势函数 $A r - V$降低梯度方差2. 用于GAEGeneralized Advantage Estimation在多步时序下更精确地估计优势价值函数通过均方误差MSE损失训练$$\mathcal{L}_{\text{value}} \mathbb{E}\left[ \left( V(x, y) - \text{return} \right)^2 \right]$$4.6 训练技巧与超参数超参数推荐值说明learning_rate1e-5 ~ 3e-5比标准微调更小策略不应变化太快clip_epsilon0.2PPO clip 范围标准值kl_coef0.01 ~ 0.05KL 惩罚权重越大越保守ppo_epochs3 ~ 4每个 batch 的 PPO 更新次数batch_size16 ~ 64取决于显存和数据规模temperature0.7 ~ 1.0回答生成的采样温度最重要的经验法则监控 KL 散度。如果 KL 散度持续增长超过 10 nats 甚至数百 nats说明模型正在离开参考模型的分布——这通常意味着奖励模型被欺骗了模型的输出在语义上虽获高分但语言质量已严重下降。五、完整的 RLHF 训练流程5.1 数据准备def prepare_rlhf_dataset( prompts: list[str], tokenizer: AutoTokenizer, max_length: int 512 ) - torch.utils.data.Dataset: 将 prompts 转化为模型输入格式 class PromptDataset(torch.utils.data.Dataset): def __init__(self, prompts, tokenizer, max_length): self.prompts prompts self.tokenizer tokenizer self.max_length max_length def __len__(self): return len(self.prompts) def __getitem__(self, idx): prompt self.prompts[idx] encoded self.tokenizer( prompt, max_lengthself.max_length, truncationTrue, paddingTrue, return_tensorspt ) return { query_ids: encoded[input_ids][0], query_mask: encoded[attention_mask][0] } return PromptDataset(prompts, tokenizer, max_length)5.2 主训练循环def run_rlhf_training( sft_model_name: str, reward_model_path: str, prompts: list[str], ppo_config: PPOConfig ): 完整的 RLHF 训练流程 device torch.device(cuda if torch.cuda.is_available() else cpu) tokenizer AutoTokenizer.from_pretrained(sft_model_name) # 加载模型 policy AutoModelForCausalLM.from_pretrained(sft_model_name) ref_model AutoModelForCausalLM.from_pretrained(sft_model_name) reward_model RewardModel.load_from_checkpoint(reward_model_path) # 准备数据 dataset prepare_rlhf_dataset(prompts, tokenizer) dataloader torch.utils.data.DataLoader( dataset, batch_sizeppo_config.batch_size, shuffleTrue ) # 创建 PPO 训练器 trainer PPOTrainer( policy_modelpolicy, ref_modelref_model, reward_modelreward_model, tokenizertokenizer, configppo_config, devicedevice ) # 训练循环 for step, batch in enumerate(dataloader): stats trainer.train_step(batch) if step % 10 0: print(fStep {step}: fpolicy_loss{stats[policy_loss]:.4f}, fkl_div{stats[kl_div]:.4f}, fclip_frac{stats[clip_frac]:.2%}) return trainer.policy_model5.3 三步走的时间顺序SFT1 ~ 3 天128 张 GPU用 1 万到 10 万条指令-回答对微调基础模型。这是 RLHF 的起点一个合格但不够优秀的模型。RM 训练1 ~ 2 天64 张 GPU收集 10 万到 100 万条人类偏好对训练奖励模型。奖励模型的大小通常和 SFT 模型相同。PPO 训练1 ~ 3 天128 张 GPU用 PPO 循环优化策略模型。这是最昂贵的阶段因为每个 step 都需要模型生成、推理奖励模型、做多次前向/反向传播。在 DeepSeek 的 R1 论文中他们实际上使用了更复杂的变体——Group Relative Policy OptimizationGRPO思路是在同一个 prompt 上采样多个回答用这些回答的奖励相对排序替代 value network进一步简化了架构。六、RLHF 的常见问题与最佳实践6.1 奖励过度优化Reward Hacking这是 RLHF 中最臭名昭著的问题模型学会了欺骗奖励模型而不是真正提升回答质量。典型案例- 模型生成非常长的回答因为奖励模型偏好长回答- 模型不断重复高分词汇当然必须的毫无疑问- 模型输出自我标榜的表述这是一个非常好的问题解决方案1.KL 惩罚$\beta$ 不应太小确保模型不离参考模型太远2.奖励模型校准定期用人类评估奖励模型的预测质量3.多样本采样同一个 prompt 采样多个回答取平均奖励4.正则化对回答长度做惩罚或归一化6.2 模式坍塌Mode Collapse模型变得过于一致——所有回答都是同一个模板缺乏多样性。解决方案- 训练奖励模型时使用多样化的偏好数据- PPO 中使用更高的采样温度- 加入 Top-p / Top-k 采样限制6.3 训练不稳定PPO 训练本身就不稳定加上大模型后更容易出现 loss 爆炸或梯度消失。解决方案- 梯度范数裁剪clip_grad_norm ≤ 1.0- 学习率预热warmup- 监控 KL 散度和 clip fraction- 定期保存 checkpoint6.4 奖励模型的评估与迭代训练好的奖励模型本身也需要持续评估否则 PPO 训练就是在用一个不准的温度计测体温。奖励模型的评估可以从以下几个维度进行1. 准确率Accuracy在保留的测试集上计算奖励模型正确预测人类偏好的比例。一个成熟的奖励模型准确率通常在 65%~75% 之间人类标注者之间的一致性也在 70%~80%。2. 校准度Calibration奖励模型给出的分数差是否对应合理的人类偏好概率例如当奖励模型认为 $y_1$ 比 $y_2$ 高出 2 分时人类是否真的有 ~88%sigmoid(2) ≈ 0.88的概率选择 $y_1$校准度差意味着模型对某些类型的回答过度自信或不够自信。3. 维度分解更先进的评估方法是将偏好分解为多个维度分别评估-有用性Helpfulness回答是否解决了用户的问题-准确性Factuality回答中的事实是否准确-安全性Safety回答是否包含有害或偏见内容有些工作将奖励模型设计为多任务输出每个维度一个分数这样奖励模型的泛化能力和可解释性都会大幅提升。4. 迭代策略奖励模型不是一次性训练就完事的。在实践中通常采用迭代式数据收集SFT 模型 → 采样回答 → 人类标注 → 训练 RM1 RM1 → PPO 训练 π1 → π1 采样新回答 → 人类标注 → 训练 RM2 RM2 → PPO 训练 π2 → ...每轮迭代中之前的 PPO 模型会暴露出新的回答模式奖励模型必须适应这些新模式。这正是 InstructGPT/ChatGPT 论文中提到的迭代 RLHF的核心思想。6.5 RLHF 的变体与发展方法核心思想代表工作PPO带 clip 的策略梯度 KL 约束InstructGPTDPO直接从偏好数据优化策略无需显式奖励模型DPO (Rafailov et al.)GRPO用组内相对奖励替代 value networkDeepSeek-R1RLOOREINFORCE with Leave-One-Out baselineREINFORCE 变体KTO只需要好/坏标签不需要成对比较KTO (Ethayarajh et al.)DPODirect Preference Optimization是 2024-2025 年最受关注的 RLHF 替代方案。它证明了偏好信息可以直接用于优化策略模型不需要显式的奖励模型也不需要复杂的 PPO 循环。其目标函数为$$\mathcal{L}{\text{DPO}}(\pi\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]$$DPO 的实现比 PPO 简单至少 2~3 倍且训练稳定得多。但 PPO 仍然有一个独特优势可以在训练过程中采样新回答而 DPO 只能使用静态数据集中的回答。七、实战指南从零搭建你自己的 RLHF 系统7.1 实战案例用 Anthropic HH-RLHF 数据集跑通 PPOAnthropic HH-RLHF是目前最常用的开源 RLHF 数据集包含约 16 万条人类偏好比较对。下面是一个端到端的运行命令模板# Step 1: 克隆 TRL 库示例 git clone https://github.com/huggingface/trl cd trl/examples/scripts # Step 2: 使用 GPT-2 在 HH-RLHF 上训练奖励模型 python reward_modeling.py \ --model_name gpt2 \ --dataset_name Anthropic/hh-rlhf \ --output_dir ./rm-gpt2 \ --num_train_epochs 3 \ --per_device_train_batch_size 8 \ --gradient_accumulation_steps 4 \ --learning_rate 1e-5 # Step 3: PPO 训练 python ppo.py \ --reward_model_path ./rm-gpt2 \ --policy_model_name gpt2 \ --dataset_name Anthropic/hh-rlhf \ --output_dir ./ppo-gpt2 \ --ppo_epochs 4 \ --kl_coef 0.02 \ --clip_epsilon 0.27.2 使用 LoRA 降低显存开销全参数 PPO 训练 7B 模型至少需要 4×A100但通过 LoRALow-Rank Adaptation可以将显存需求降到 1×A100from peft import LoraConfig, get_peft_model def prepare_policy_with_lora(base_model): 在策略模型上应用 LoRA 适配器 lora_config LoraConfig( r16, lora_alpha32, target_modules[q_proj, v_proj, k_proj, o_proj], lora_dropout0.05, biasnone, task_typeCAUSAL_LM ) model get_peft_model(base_model, lora_config) model.print_trainable_parameters() return modelLoRA 在 RLHF 中的实践要点- 只对策略模型应用 LoRA参考模型和奖励模型保持全精度- LoRA rank 推荐 16~64rank 过大反而容易过拟合- 训练结束后可以合并 LoRA 权重到基础模型或单独保存 adapter7.3 从 PPO 到 GRPODeepSeek 的创新在 DeepSeek-R1 中他们使用了Group Relative Policy OptimizationGRPO核心思想是对同一个 prompt 采样一组回答group_size8~64用这组回答的奖励分数计算相对优势组内归一化完全省略 value networkCritic减少一半的显存开销GRPO 的优势函数为$$A_i \frac{r_i - \text{mean}(r_{\text{group}})}{\text{std}(r_{\text{group}})}$$这不仅节省了显存还让训练更加稳定因为优势估计不受 value network 训练质量的影响。7.4 环境准备pip install torch transformers datasets accelerate trl wandbHugging Face 的TRLTransformer Reinforcement Learning库封装了上述大部分逻辑。但我还是强烈建议你先手写一遍再做封装调用——只会调 API 的人永远无法 Debug 梯度问题。7.5 推荐的学习路径本周用 LLaMA-1B 或 GPT-2 在 Anthropic HH-RLHF 数据集上跑通 PPO下周尝试 DPO比较两种方法的训练速度和最终效果下月用你自己的数据训练奖励模型然后在 7B 规模上做对齐7.6 硬件要求奖励模型训练单卡 24GB VRAM可训 1B 模型PPO 训练7B 模型至少 4 × A100-80GB如果资源有限可以使用 LoRA 微调、减少 batch size、使用梯度累积7.7 评估指标RLHF 之后的模型评估不能只看基准分数如 MMLU、GSM8K还需要评估对齐质量Human Evaluation人类评分者对模型回答做 A/B 测试最贵但最准确LLM-as-a-Judge用 GPT-4 / DeepSeek 作为裁判打分成本低、可复现Reward Score奖励模型自身打分有偏差但方便快速迭代KL 散度对齐前后的分布变化安全测试红队攻击、越狱测试特别地LLM-as-a-Judge 在实践中需要设计完善的评估 Prompt包括系统指令你是一位公正的 AI 评估员。请从以下维度对回答评分1-5分 1. 有用性Helpfulness回答是否直接解决了用户的问题 2. 准确性Accuracy回答中包含的事实是否正确 3. 完整性Completeness回答是否覆盖了问题的所有方面 4. 清晰度Clarity表述是否清晰、易于理解 请逐维度给出分数和简要理由。使用 LLM 评估时需要注意-位置偏差如果同时比较两个回答交换 A/B 顺序后评估结果应基本一致。如果偏差显著需要做多次交换取平均。-自偏好偏差裁判模型可能偏向与自己风格相似的回答。使用不同的裁判模型做交叉验证可以缓解。-长度偏差模型倾向于给更长的回答更高分。可以将回答截断到相同长度或在评估指令中明确要求忽略长度因素。八、总结本文从数学原理到代码实现完整走了一遍 RLHF 的技术路线直觉层面RLHF 奖励模型打分 PPO 策略更新 KL 约束奖励模型基于 Bradley-Terry 偏好模型用交叉熵损失训练一个评分器PPO 优化重要性采样比 clip 约束 KL 惩罚稳定地提升模型性能完整流程SFT → RM → PPO三步缺一不可RLHF 不是银弹——它昂贵、不稳定、容易奖励过拟合。但它确实是过去两年大模型能力跃升的核心引擎。从 ChatGPT 到 Claude 到 DeepSeek-R1这些模型之所以好用不是因为它们更大而是因为它们更对齐。理解 RLHF就是理解大模型从会说话到会干活的那最后一跃。 延伸阅读- DeepSeek 实战指南从入门到生产部署 → 了解更多关于 RLHF 在 DeepSeek 模型中的实际应用- 回复手写系列查看更多从零实现的技术教程本文是「手写系列」的第 9 篇。前 8 篇覆盖了 Transformer、RAG、LoRA、DeepSeek 推理加速、向量检索、AI 评估等主题。持续更新欢迎关注。
http://www.rkmt.cn/news/1376304.html

相关文章:

  • xLSTM与迁移学习在ADS-B入侵检测中的实战应用与性能分析
  • Unity RTS开发脚手架:工业级单位编队与视野遮蔽实现
  • 将vCenter(VCSA)的默认证书替换为自己企业CA的证书
  • 5步轻松配置:碧蓝航线自动化脚本新手完整指南
  • 红队实战中的Kali高级配置与隐蔽性设计
  • Burp Suite Galaxy插件实战:上下文感知解密中枢搭建指南
  • 合肥成人书法培训,真的能快速提升书写水平吗?
  • 解锁iOS设备无限可能:2026最新越狱技术深度解析与实战指南
  • 百度网盘下载速度太慢?Python脚本帮你获取高速直链
  • 华硕笔记本性能优化终极指南:如何用G-Helper替代Armoury Crate提升体验
  • 天翼云S6通用服务器深度评测:4核8G5Mpbs年付590元起,性价比之王?
  • 机器学习生存分析实战:从XGBoost-AFT到临床预测模型构建
  • 仓库管理流程全拆解:手把手教你落地一套高效的仓库管理流程
  • ESP32嵌入式Wi-Fi安全验证:WPA2-PSK四次握手捕获与PMK推导
  • 模拟器每次改完代码都要重连?一个菜单就搞定,90%的人不知道
  • 2026实测:宁波十大小学语文小升初机构横评
  • 吉林做幕墙工程公司哪家性价比高?恒基幕墙工程上榜 - mypinpai
  • Go二进制逆向实战:破解IDA Pro无法识别的Golang符号与runtime机制
  • Spring boot 特性和自写Reids组件
  • claude 或 codex接入临时补充api
  • Codex CLI 上手前,先补上这条可回滚的验收链路
  • 如何高效使用Iwara视频下载神器:一键批量下载的完整指南
  • WordPress AI: 7.0如何为AI驱动的网站奠定基础
  • 如何在Blender中实现专业级MMD模型动画制作:5步完整解决方案
  • OpenCV模板匹配遇到旋转就抓瞎?一个Python脚本帮你搞定0°到360°全角度识别
  • YOLO训练结果可视化避坑指南:手把手教你处理v5的CSV和v7的TXT格式差异
  • 推荐!2026年靠谱的沙盘模型设计公司 - mypinpai
  • ARM SVE2指令集与UADDLB/UADDLT指令详解
  • AlwaysOnTop:终极Windows窗口置顶工具完整使用指南
  • LED闪灯电路板学习 过程