mcpField
G
grpo-rl-training

grpo-rl-training

github.com/orchestra-research开发14.1k972 次安装19 周前更新
代码仓库

关于 grpo-rl-training

此技能为使用 TRL 实现 GRPO 强化学习训练提供指导,适合需要通过自定义奖励微调语言模型的从业者。内容涵盖数据准备、奖励函数、训练配置、监控与排错。

主要功能

  1. 01GRPO 训练流程指导
  2. 02自定义组合奖励函数
  3. 03TRL 配置与模型训练示例
  4. 04奖励指标监控建议
  5. 05训练故障排查模式

适用场景

  1. 01按客观正确性训练数学模型
  2. 02约束模型生成结构化格式
  3. 03针对领域行为微调模型
  4. 04同时优化正确性、格式与风格

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/orchestra-research/ai-research-skills --skill grpo-rl-training

安装后在 Claude Code 中输入/grpo-rl-training确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
examples/
reward_functions_library.py
templates/
basic_grpo_training.py

详细介绍

使用 TRL 进行 GRPO/RL 训练

使用 Transformer Reinforcement Learning (TRL) 库实现组相对策略优化 (GRPO) 的专家级指南。本技能提供经过实战检验的模式、关键见解以及用于通过自定义奖励函数微调语言模型的生产就绪工作流。

何时使用本技能

当你需要完成以下任务时,可使用 GRPO 训练:

  • 强制特定输出格式(例如 XML 标签、JSON、结构化推理)
  • 教授可验证任务,并带有客观的正确性指标(数学、编码、事实核查)
  • 提升推理能力,通过奖励思维链模式
  • 让模型对齐领域特定行为,无需标注偏好数据
  • 同时优化多个目标(格式 + 正确性 + 风格)

不要将 GRPO 用于:

  • 简单的监督微调任务(改用 SFT)
  • 没有明确奖励信号的任务
  • 当你已经拥有高质量偏好对时(改用 DPO/PPO)

核心概念

1. GRPO 算法基础

关键机制:

  • 为每个提示词生成多个补全(组大小:4-16)
  • 使用奖励函数比较每组内的补全
  • 更新策略,使其相对于组内更偏好高奖励回答

与 PPO 的关键区别:

  • 不需要单独的奖励模型
  • 样本效率更高(从组内比较中学习)
  • 实现和调试更简单

数学直觉:

For each prompt p:
  1. Generate N completions: {c₁, c₂, ..., cₙ}
  2. Compute rewards: {r₁, r₂, ..., rₙ}
  3. Learn to increase probability of high-reward completions
     relative to low-reward ones in the same group
2. 奖励函数设计理念

黄金法则:

  1. 组合多个奖励函数 - 每个函数处理一个方面(格式、正确性、风格)
  2. 适当缩放奖励 - 权重越高 = 信号越强
  3. 使用增量奖励 - 对部分符合给予部分得分
  4. 独立测试奖励 - 单独调试每个奖励函数

奖励函数类型:

类型用例示例权重
正确性可验证任务(数学、代码)2.0(最高)
格式强制严格结构0.5-1.0
长度鼓励冗长/简洁0.1-0.5
风格惩罚不想要的模式-0.5 到 0.5

实现工作流

步骤 1:数据集准备

关键要求:

  • 提示词采用聊天格式(包含 'role' 和 'content' 的 dict 列表)
  • 包含系统提示词以设定预期
  • 对于可验证任务,将真实答案作为额外列包含在内

示例结构:

from datasets import load_dataset, Dataset

SYSTEM_PROMPT = """
Respond in the following format:
<reasoning>
[Your step-by-step thinking]
</reasoning>
<answer>
[Final answer]
</answer>
"""

def prepare_dataset(raw_data):
    """
    Transform raw data into GRPO-compatible format.

    Returns: Dataset with columns:
    - 'prompt': List[Dict] with role/content (system + user messages)
    - 'answer': str (ground truth, optional but recommended)
    """
    return raw_data.map(lambda x: {
        'prompt': [
            {'role': 'system', 'content': SYSTEM_PROMPT},
            {'role': 'user', 'content': x['question']}
        ],
        'answer': extract_answer(x['raw_answer'])
    })

专业提示:

  • 对于复杂格式,在系统提示词中使用 one-shot 或 few-shot 示例
  • 保持提示词简洁(max_prompt_length:256-512 个 token)
  • 在训练前验证数据质量(垃圾进 = 垃圾出)
步骤 2:奖励函数实现

模板结构:

def reward_function_name(
    prompts,        # List[List[Dict]]: Original prompts
    completions,    # List[List[Dict]]: Model generations
    answer=None,    # Optional: Ground truth from dataset
    **kwargs        # Additional dataset columns
) -> list[float]:
    """
    Evaluate completions and return rewards.

    Returns: List of floats (one per completion)
    """
    # Extract completion text
    responses = [comp[0]['content'] for comp in completions]

    # Compute rewards
    rewards = []
    for response in responses:
        score = compute_score(response)
        rewards.append(score)

    return rewards

示例 1:正确性奖励(数学/编码)

def correctness_reward(prompts, completions, answer, **kwargs):
    """Reward correct answers with high score."""
    responses = [comp[0]['content'] for comp in completions]
    extracted = [extract_final_answer(r) for r in responses]
    return [2.0 if ans == gt else 0.0
            for ans, gt in zip(extracted, answer)]

示例 2:格式奖励(结构化输出)

import re

def format_reward(completions, **kwargs):
    """Reward XML-like structured format."""
    pattern = r'<reasoning>.*?</reasoning>\s*<answer>.*?</answer>'
    responses = [comp[0]['content'] for comp in completions]
    return [1.0 if re.search(pattern, r, re.DOTALL) else 0.0
            for r in responses]

示例 3:增量格式奖励(部分得分)

def incremental_format_reward(completions, **kwargs):
    """Award partial credit for format compliance."""
    responses = [comp[0]['content'] for comp in completions]
    rewards = []

    for r in responses:
        score = 0.0
        if '<reasoning>' in r:
            score += 0.25
        if '</reasoning>' in r:
            score += 0.25
        if '<answer>' in r:
            score += 0.25
        if '</answer>' in r:
            score += 0.25
        # Penalize extra text after closing tag
        if r.count('</answer>') == 1:
            extra_text = r.split('</answer>')[-1].strip()
            score -= len(extra_text) * 0.001
        rewards.append(score)

    return rewards

关键见解: 组合 3-5 个奖励函数以实现稳健训练。顺序的重要性低于信号的多样性。

步骤 3:训练配置

内存优化配置(小 GPU)

from trl import GRPOConfig

training_args = GRPOConfig(
    output_dir="outputs/grpo-model",

    # Learning rate
    learning_rate=5e-6,          # Lower = more stable
    adam_beta1=0.9,
    adam_beta2=0.99,
    weight_decay=0.1,
    warmup_ratio=0.1,
    lr_scheduler_type='cosine',

    # Batch settings
    per_device_train_batch_size=1,
    gradient_accumulation_steps=4,  # Effective batch = 4

    # GRPO-specific
    num_generations=8,            # Group size: 8-16 recommended
    max_prompt_length=256,
    max_completion_length=512,

    # Training duration
    num_train_epochs=1,
    max_steps=None,               # Or set fixed steps (e.g., 500)

    # Optimization
    bf16=True,                    # Faster on A100/H100
    optim="adamw_8bit",          # Memory-efficient optimizer
    max_grad_norm=0.1,

    # Logging
    logging_steps=1,
    save_steps=100,
    report_to="wandb",            # Or "none" for no logging
)

高性能配置(大 GPU)

training_args = GRPOConfig(
    output_dir="outputs/grpo-model",
    learning_rate=1e-5,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=2,
    num_generations=16,           # Larger groups = better signal
    max_prompt_length=512,
    max_completion_length=1024,
    num_train_epochs=1,
    bf16=True,
    use_vllm=True,                # Fast generation with vLLM
    logging_steps=10,
)

关键超参数:

参数影响调优建议
num_generations用于比较的组大小从 8 开始,如果 GPU 允许则增加到 16
learning_rate收敛速度/稳定性5e-6(安全),1e-5(更快,风险更高)
max_completion_length输出冗长度匹配你的任务(推理用 512,简短答案用 256)
gradient_accumulation_steps有效批次大小如果 GPU 内存有限则增加
步骤 4:模型设置与训练

标准设置(Transformers)

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig
from trl import GRPOTrainer

# Load model
model_name = "Qwen/Qwen2.5-1.5B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16,
    attn_implementation="flash_attention_2",  # 2-3x faster
    device_map="auto"
)

tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token

# Optional: LoRA for parameter-efficient training
peft_config = LoraConfig(
    r=16,                         # Rank (higher = more capacity)
    lora_alpha=32,               # Scaling factor (typically 2*r)
    target_modules=[
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj"
    ],
    task_type="CAUSAL_LM",
    lora_dropout=0.05,
)

# Initialize trainer
trainer = GRPOTrainer(
    model=model,
    processing_class=tokenizer,
    reward_funcs=[
        incremental_format_reward,
        format_reward,
        correctness_reward,
    ],
    args=training_args,
    train_dataset=dataset,
    peft_config=peft_config,      # Remove for full fine-tuning
)

# Train
trainer.train()

# Save
trainer.save_model("final_model")

Unsloth 设置(快 2-3 倍)

from unsloth import FastLanguageModel

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="google/gemma-3-1b-it",
    max_seq_length=1024,
    load_in_4bit=True,
    fast_inference=True,
    max_lora_rank=32,
)

model = FastLanguageModel.get_peft_model(
    model,
    r=32,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    lora_alpha=32,
    use_gradient_checkpointing="unsloth",
)

# Rest is identical to standard setup
trainer = GRPOTrainer(model=model, ...)
trainer.train()

关键训练见解

1. 损失行为(预期模式)
  • 损失从接近 0 开始,并在训练过程中增加
  • 这是正确的——损失衡量的是相对于初始策略的 KL 散度
  • 模型正在学习(偏离原始行为以优化奖励)
  • 通过监控奖励指标而非损失来观察进展
2. 奖励跟踪

需要关注的关键指标:

  • reward:所有完成结果的平均值
  • reward_std:组内多样性(应保持 > 0)
  • kl:与参考模型的 KL 散度(应适度增长)

健康的训练模式:

Step   Reward    Reward_Std   KL
100    0.5       0.3          0.02
200    0.8       0.25         0.05
300    1.2       0.2          0.08  ← Good progression
400    1.5       0.15         0.12

警告信号:

  • 奖励标准差 → 0(模型坍缩为单一响应)
  • KL 爆炸(> 0.5)(偏离过多,降低 LR)
  • 奖励停滞(奖励函数过于严苛或模型容量问题)
3. 常见陷阱与解决方案
问题症状解决方案
模式坍缩所有完成结果完全相同增加 num_generations,添加多样性惩罚
无学习效果奖励曲线平坦检查奖励函数逻辑,增加 LR
OOM 错误GPU 显存超出减少 num_generations,启用梯度检查点
训练缓慢< 1 it/s启用 use_vllm=True,使用 Unsloth,减少序列长度
格式被忽略模型不遵循结构增加格式奖励权重,添加增量奖励

高级模式

1. 多阶段训练

对于复杂任务,分阶段训练:

# Stage 1: Format compliance (epochs=1)
trainer_stage1 = GRPOTrainer(
    model=model,
    reward_funcs=[incremental_format_reward, format_reward],
    ...
)
trainer_stage1.train()

# Stage 2: Correctness (epochs=1)
trainer_stage2 = GRPOTrainer(
    model=model,
    reward_funcs=[format_reward, correctness_reward],
    ...
)
trainer_stage2.train()
2. 自适应奖励缩放
class AdaptiveReward:
    def __init__(self, base_reward_func, initial_weight=1.0):
        self.func = base_reward_func
        self.weight = initial_weight

    def __call__(self, *args, **kwargs):
        rewards = self.func(*args, **kwargs)
        return [r * self.weight for r in rewards]

    def adjust_weight(self, success_rate):
        """Increase weight if model struggling, decrease if succeeding."""
        if success_rate < 0.3:
            self.weight *= 1.2
        elif success_rate > 0.8:
            self.weight *= 0.9
3. 自定义数据集集成
def load_custom_knowledge_base(csv_path):
    """Example: School communication platform docs."""
    import pandas as pd
    df = pd.read_csv(csv_path)

    dataset = Dataset.from_pandas(df).map(lambda x: {
        'prompt': [
            {'role': 'system', 'content': CUSTOM_SYSTEM_PROMPT},
            {'role': 'user', 'content': x['question']}
        ],
        'answer': x['expert_answer']
    })
    return dataset

部署与推理

保存并合并 LoRA
# Merge LoRA adapters into base model
if hasattr(trainer.model, 'merge_and_unload'):
    merged_model = trainer.model.merge_and_unload()
    merged_model.save_pretrained("production_model")
    tokenizer.save_pretrained("production_model")
推理示例
from transformers import pipeline

generator = pipeline(
    "text-generation",
    model="production_model",
    tokenizer=tokenizer
)

result = generator(
    [
        {'role': 'system', 'content': SYSTEM_PROMPT},
        {'role': 'user', 'content': "What is 15 + 27?"}
    ],
    max_new_tokens=256,
    do_sample=True,
    temperature=0.7,
    top_p=0.9
)
print(result[0]['generated_text'])

最佳实践清单

训练前:

  • 验证数据集格式(提示词为 List[Dict])
  • 在样本数据上测试奖励函数
  • 从数据中计算预期的 max_prompt_length
  • 根据 GPU 显存选择合适的 num_generations
  • 设置日志记录(推荐 wandb)

训练中:

  • 监控奖励进展(应逐渐增加)
  • 检查 reward_std(应保持 > 0.1)
  • 留意 OOM 错误(必要时减少批次大小)
  • 每 50-100 步采样生成结果
  • 在留出集上验证格式合规性

训练后:

  • 如果使用 PEFT,合并 LoRA 权重
  • 在多样化的提示词上测试
  • 与基线模型对比
  • 记录奖励权重和超参数
  • 保存可复现性配置

故障排查指南

调试工作流
  1. 隔离奖励函数——分别独立测试每个函数
  2. 检查数据分布——确保提示词的多样性
  3. 降低复杂度——从单一奖励开始,逐步添加
  4. 监控生成结果——每 N 步打印样本
  5. 验证提取逻辑——确保答案解析正常工作
快速修复
# Debug reward function
def debug_reward(completions, **kwargs):
    responses = [comp[0]['content'] for comp in completions]
    for i, r in enumerate(responses[:2]):  # Print first 2
        print(f"Response {i}: {r[:200]}...")
    return [1.0] * len(responses)  # Dummy rewards

# Test without training
trainer = GRPOTrainer(..., reward_funcs=[debug_reward])
trainer.generate_completions(dataset[:1])  # Generate without updating

参考资料与资源

官方文档:

示例仓库:

推荐阅读:

  • Agent 指令的渐进式披露模式
  • RL 中的奖励塑造(Ng 等)
  • LoRA 论文(Hu 等,2021)

Agent 使用说明

加载此技能后:

  1. 在实现 GRPO 训练之前,请阅读本文件的全部内容
  2. 从最简单的奖励函数开始(例如基于长度的),以验证配置是否正确
  3. 使用 templates/ 目录中的模板作为起点
  4. 参考 examples/ 中的示例来实现特定任务的实现
  5. 按顺序遵循工作流(不要跳过步骤)
  6. 逐步调试——每次只添加一个奖励函数

重要提醒:

  • 始终使用多个奖励函数(3-5 个为最佳)
  • 监控奖励指标,而不是损失
  • 在训练前测试奖励函数
  • 从小规模开始(num_generations=4),逐步扩大规模
  • 频繁保存检查点(每 100 步)

本技能专为 专家级实现 而设计。初学者应先从监督微调开始,然后再尝试 GRPO。

评论

支持 Markdown