1. 项目概述
在人工智能领域,大语言模型(LLM)的强化学习微调是一个热门研究方向。本次实践基于Internlm2.5-1.8B模型,使用ms-swift框架和GRPO算法,在gsm8k数学推理数据集上进行强化学习微调实验。通过对比RL前后的模型表现,我们验证了GRPO算法在数学推理任务上的有效性。
实验结果显示,经过GRPO微调后,模型在gsm8k测试集上的准确率从19.86%提升到了34.50%,取得了显著的性能提升。这一结果证明了强化学习微调对于提升大语言模型在特定领域任务上的表现具有重要价值。
1.1 核心目标与技术选型
本次实验的核心目标是验证GRPO算法在数学推理任务上的有效性,并探索强化学习微调对大语言模型性能的提升效果。我们选择了以下技术方案:
- 基础模型:Internlm2.5-1.8B,这是一个18亿参数的中等规模语言模型,在数学推理任务上具有较好的基础能力
- 微调框架:ms-swift,一个高效的大模型微调框架,支持多种强化学习算法
- 强化学习算法:GRPO(Group Relative Policy Optimization),一种专为推理任务设计的强化学习算法
- 数据集:gsm8k,一个包含7473个小学数学问题的数据集,适合评估模型的数学推理能力
选择这一技术路线主要基于以下考虑:
- GRPO算法相比传统PPO减少了Critic网络的需求,降低了显存占用
- ms-swift框架对国产大模型有良好的支持,且提供了丰富的强化学习工具
- gsm8k数据集规模适中,能够快速验证算法效果
2. 技术原理详解
2.1 强化学习算法对比
在语言模型微调领域,主要有三种强化学习算法:PPO、DPO和GRPO。它们各有特点,适用于不同场景。
2.1.1 PPO(Proximal Policy Optimization)
PPO是OpenAI在2017年提出的强化学习算法,其核心特点包括:
- 基于Actor-Critic架构,同时优化策略和价值函数
- 使用策略裁剪机制限制更新幅度,保证训练稳定性
- 包含三个关键损失项:
- 策略裁剪损失(LCLIP):控制策略更新幅度
- 价值函数损失(LVF):优化Critic网络
- 熵正则项(S):鼓励探索
PPO的优势在于通用性强、训练稳定,但缺点是需要维护多个模型(策略、价值、奖励、参考),计算开销较大。
2.1.2 DPO(Direct Preference Optimization)
DPO是Stanford在2023年提出的算法,它将偏好优化转化为监督学习问题:
- 不需要显式的奖励模型或RL训练
- 直接优化策略模型使其符合人类偏好
- 核心公式:
code复制L_DPO = -E[logσ(βlog(πθ(yw|x)/πref(yw|x)) - βlog(πθ(yl|x)/πref(yl|x)))]
DPO训练简单高效,但依赖高质量偏好对数据,且对任务形式较为敏感。
2.1.3 GRPO(Group Relative Policy Optimization)
GRPO是DeepSeek在2024年提出的算法,专为推理类任务设计:
- 对同一指令生成G个回答
- 计算组内奖励的均值(r̄)和标准差(σr)
- 归一化优势:A_i = (r_i - r̄)/σr
- 使用PPO裁剪损失更新策略
GRPO省去了Critic模型,显存占用低,特别适合数学、代码等有明确评判标准的推理任务。但它的通用性不如PPO,且依赖可靠的奖励信号。
2.2 GRPO算法流程详解
GRPO的核心创新在于用组内相对优势替代了传统的价值网络估计。具体实现步骤如下:
- 采样阶段:对于每个输入问题,模型生成多个(通常4-8个)不同的回答
- 评分阶段:使用奖励函数对每个回答进行评分
- 标准化处理:
- 计算组内评分的均值(r̄)和标准差(σr)
- 对每个回答计算标准化优势:A_i = (r_i - r̄)/σr
- 策略更新:
- 使用PPO的裁剪损失函数,但用标准化优势替代传统优势估计
- 同时加入熵正则项鼓励探索
这种设计使得GRPO在数学推理等任务上:
- 避免了训练不稳定的价值网络
- 通过组内比较获得更稳定的梯度信号
- 减少了显存占用,可以在更大批次下训练
3. 实验环境准备
3.1 硬件与系统配置
本次实验使用以下硬件和系统环境:
- GPU:NVIDIA A100(40GB显存),实际使用约50%显存
- CUDA版本:12.6
- 操作系统:Linux
- Python版本:3.10
3.2 软件环境搭建
使用conda创建隔离的Python环境:
bash复制conda create -n ms-swift python=3.10 -y
conda activate ms-swift
安装必要的Python包:
bash复制pip install uv
uv pip install -U \
ms-swift \
torch==2.8.0 \
torchvision \
torchaudio \
transformers==4.57.1 \
modelscope>=1.23 \
"peft>=0.11,<0.19" \
trl==0.23.1 \
deepspeed==0.17.6 \
vllm==0.11.0 \
lmdeploy==0.10.2 \
evalscope>=1.0 \
gradio==5.32.1 \
math_verify==0.5.2 \
-i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
关键包说明:
ms-swift:大模型微调框架vllm:高效推理引擎math_verify:数学表达式验证工具trl:Transformer强化学习库
3.3 数据集准备
gsm8k数据集已预置在开发机的/share/datasets/gsm8k_datas/目录下,无需额外下载。数据集包含训练集和测试集,格式为parquet。
创建项目目录:
bash复制mkdir gsm8k_rl
cd ./gsm8k_rl
4. 数据处理与模型训练
4.1 数据预处理
将原始数据集转换为GRPO训练所需的格式:
python复制import re
from datasets import Dataset
import os
import json
SYSTEM_PROMPT = "You are a meticulous mathematical reasoning assistant."
def parse_gsm8k_final_number(raw_answer: str) -> str:
s = "" if raw_answer is None else str(raw_answer).strip()
try:
tail = s.split("####")[-1].strip()
m = re.search(r"(-?\d+(?:\.\d+)?(?:/\d+(?:\.\d+)?)?)", tail)
return m.group(1) if m else tail
except:
print("ERROR")
def to_target_schema(ex):
q = (ex.get("question") or "").strip()
a = ex.get("answer")
ans = parse_gsm8k_final_number(a)
return {
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content":"Please reason step by step, and put your final answer within \\boxed{}\n" + q},
],
"solution": f"\\boxed{{{ans}}}",
}
def load_split(split: str):
path = f"/share/datasets/gsm8k_datas/main/{split}-00000-of-00001.parquet"
ds = Dataset.from_parquet(path)
out = ds.map(to_target_schema, remove_columns=ds.column_names)
return out
train_ds = load_split("train")
test_ds = load_split("test")
def save_as_jsonl(dataset, save_path):
os.makedirs(os.path.dirname(save_path), exist_ok=True)
with open(save_path, "w", encoding="utf-8") as f:
for item in dataset:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
train_out = "./data/train.jsonl"
test_out = "./data/test.jsonl"
save_as_jsonl(train_ds, train_out)
save_as_jsonl(test_ds, test_out)
处理后的数据格式示例:
json复制{
"messages": [
{
"role": "system",
"content": "You are a meticulous mathematical reasoning assistant."
},
{
"role": "user",
"content": "Please reason step by step, and put your final answer within \\boxed{}\nNatalia sold clips to 48 of her friends in April, and then she sold half as many clips in May. How many clips did Natalia sell altogether in April and May?"
}
],
"solution": "\\boxed{72}"
}
4.2 奖励函数设计
GRPO训练需要定义奖励函数来评估模型生成的质量。我们使用了两个奖励函数:
-
accuracy:swift框架自带,比较模型输出与标准答案的匹配程度
- 完全匹配:1.0分
- 不匹配:0.0分
-
box_reward:自定义奖励,检查输出是否包含
\boxed{}格式- 包含:1.0分
- 不包含:0.0分
box_reward实现代码:
python复制import re
from typing import List
from swift.plugin import ORM, orms
class BoxedReward(ORM):
"""Reward: check whether output contains \\boxed{...}"""
def __call__(self, completions, **kwargs) -> List[float]:
pattern = re.compile(r"\\boxed\s*\{.*?\}", re.DOTALL)
return [1.0 if pattern.search(str(c)) else 0.0 for c in completions]
orms["box_reward"] = BoxedReward
两个奖励函数的权重各设为0.5,既考虑答案正确性,也鼓励模型使用标准格式输出。
4.3 模型训练
使用以下脚本启动GRPO训练:
bash复制#!/bin/bash
set -e
LOG_DIR="./logs"
mkdir -p "$LOG_DIR"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
LOG_FILE="$LOG_DIR/[GRPO]internlm2_5-1_8b_${TIMESTAMP}.log"
export OMP_NUM_THREADS=1
export CUDA_VISIBLE_DEVICES=0
export MASTER_PORT=$((10000 + RANDOM % 50000))
export VLLM_WORKER_MULTIPROC_METHOD=spawn
export VLLM_LOGGING_LEVEL=INFO
{
echo "===== Training start: $(date) ====="
echo "Log file: $LOG_FILE"
echo "Using port: $MASTER_PORT"
echo "CUDA_VISIBLE_DEVICES=$CUDA_VISIBLE_DEVICES"
echo "Enable vLLM: true"
} >> "$LOG_FILE"
nohup swift rlhf \
--rlhf_type grpo \
--model '/share/new_models/Shanghai_AI_Laboratory/internlm2_5-1_8b' \
--dataset './data/train.jsonl#4000' \
--external_plugins ./box_reward.py \
--reward_funcs accuracy box_reward \
--reward_weights 0.5 0.5 \
--eval_steps 50 \
--train_type lora \
--target_modules all-linear \
--max_completion_length 768 \
--torch_dtype bfloat16 \
--num_train_epochs 1 \
--per_device_train_batch_size 8 \
--per_device_eval_batch_size 4 \
--learning_rate 5e-6 \
--warmup_ratio 0.05 \
--gradient_accumulation_steps 4 \
--save_steps 50 \
--save_total_limit 5 \
--gradient_checkpointing_kwargs '{"use_reentrant": false}' \
--logging_steps 5 \
--max_length 2048 \
--output_dir ./grpo_out \
--dataset_num_proc 8 \
--dataloader_num_workers 0 \
--freeze_vit true \
--log_completions true \
--use_vllm true \
--vllm_gpu_memory_utilization 0.50 \
--vllm_max_model_len 2048 \
--vllm_tensor_parallel_size 1 \
--vllm_enforce_eager false \
--vllm_mode colocate \
> "$LOG_FILE" 2>&1 &
关键训练参数说明:
--rlhf_type grpo:指定使用GRPO算法--reward_funcs accuracy box_reward:使用两个奖励函数--train_type lora:采用LoRA微调方式--num_train_epochs 1:训练1个epoch--learning_rate 5e-6:相对较小的学习率保证稳定性--use_vllm true:使用vLLM加速推理
训练完成后,需要合并LoRA适配器到基础模型:
bash复制swift export \
--adapter "/root/gsm8k_rl/grpo_out/v0-20260117-220412/checkpoint-1000" \
--merge_lora True
5. 模型评估与分析
5.1 评估方法
使用vLLM进行模型评估,主要脚本如下:
python复制import json
import os
from typing import List, Dict, Any
from tqdm import tqdm
from vllm import LLM, SamplingParams
from datasets import Dataset
class MathAccuracy:
"""数学准确率评估器,使用math_verify包进行LaTeX解析和验证"""
def __init__(self):
import importlib.util
assert importlib.util.find_spec('math_verify') is not None, (
"The math_verify package is required but not installed. "
"Please install it using 'pip install math_verify'.")
def __call__(self, completions: List[str], solution: List[str], **kwargs) -> List[float]:
from latex2sympy2_extended import NormalizationConfig
from math_verify import LatexExtractionConfig, parse, verify
rewards = []
for content, sol in zip(completions, solution):
gold_parsed = parse(sol, extraction_mode='first_match',
extraction_config=[LatexExtractionConfig()])
if len(gold_parsed) != 0:
answer_parsed = parse(
content,
extraction_config=[
LatexExtractionConfig(
normalization_config=NormalizationConfig(
nits=False,
malformed_operators=False,
basic_latex=True,
boxed=True,
units=True,
),
boxed_match_priority=0,
try_extract_without_anchor=False,
)
],
extraction_mode='first_match',
)
reward = float(verify(answer_parsed, gold_parsed))
else:
reward = 1.0
return rewards
def run_evaluation(
model_path: str,
data_path: str,
output_path: str = None,
tensor_parallel_size: int = 1,
temperature: float = 0.0,
max_tokens: int = 2048,
batch_size: int = 32,
seed: int = 42,
):
# 加载数据集和模型
dataset = load_dataset(data_path)
llm = LLM(
model=model_path,
tensor_parallel_size=tensor_parallel_size,
seed=seed,
dtype="half",
trust_remote_code=True,
)
# 配置采样参数
sampling_params = SamplingParams(
temperature=temperature,
max_tokens=max_tokens,
stop=["<|endoftext|>", "<|im_end|>"],
)
# 评估并计算准确率
evaluator = MathAccuracy()
rewards = evaluator(all_completions, solutions)
accuracy = sum(rewards) / len(rewards) * 100
return accuracy
评估时设置temperature=0进行贪婪解码,确保结果可复现。
5.2 评估结果
- 基础模型评估:
bash复制python eval.py \
--model_path /share/new_models/Shanghai_AI_Laboratory/internlm2_5-1_8b \
--data_path /root/gsm8k_rl/data/test.jsonl \
--output_path ./result/base.json \
--batch_size 32 \
--max_tokens 1024
基础模型准确率:19.86%
- GRPO微调后评估:
bash复制python eval.py \
--model_path /root/gsm8k_rl/grpo_out/v0-20260117-220412/checkpoint-1000-merged \
--data_path /root/gsm8k_rl/data/test.jsonl \
--output_path ./result/grpo.json \
--batch_size 32 \
--max_tokens 1024
微调后模型准确率:34.50%
5.3 结果分析
通过GRPO微调,模型在gsm8k测试集上的准确率从19.86%提升到了34.50%,相对提升了73.7%。这一显著提升说明:
- GRPO算法有效:能够引导模型学习更好的数学推理策略
- 奖励函数设计合理:accuracy和box_reward的组合有效指导了模型优化方向
- 训练配置适当:学习率、批次大小等超参数设置合理
值得注意的是,虽然准确率提升显著,但34.5%的绝对准确率仍有很大提升空间。可能的改进方向包括:
- 增加训练epoch数
- 优化奖励函数设计
- 尝试更大的基础模型
- 使用更多样化的训练数据
6. 常见问题与解决方案
6.1 训练过程中的显存不足
问题现象:训练时出现CUDA out of memory错误
解决方案:
- 减小
per_device_train_batch_size - 启用梯度检查点:
--gradient_checkpointing_kwargs '{"use_reentrant": false}' - 降低
vllm_gpu_memory_utilization(默认0.5) - 使用更小的基础模型
6.2 评估结果不稳定
问题现象:相同模型多次评估结果差异较大
解决方案:
- 评估时设置
temperature=0确保确定性输出 - 检查数据加载是否每次都相同
- 增加评估样本量
- 确保没有启用dropout等随机性操作
6.3 奖励函数设计问题
问题现象:模型过度优化某个奖励而忽视其他方面
解决方案:
- 调整奖励权重,找到更平衡的组合
- 设计更全面的奖励函数,覆盖更多评估维度
- 设置奖励裁剪,防止单一奖励主导优化
6.4 训练收敛慢
问题现象:训练多轮后指标提升不明显
解决方案:
- 适当增大学习率
- 检查奖励函数是否提供了足够的梯度信号
- 增加组大小(group size),提供更丰富的比较信息
- 检查模型架构是否适合当前任务
7. 经验总结与优化建议
通过本次GRPO微调实践,我们获得了以下宝贵经验:
-
奖励函数设计至关重要:好的奖励函数应该:
- 与最终评估指标一致
- 提供丰富的梯度信号
- 平衡不同优化目标
-
组大小影响训练效果:较大的组大小能提供更稳定的优势估计,但会增加计算开销。实践中需要权衡,一般4-8为宜。
-
训练稳定性技巧:
- 使用较小的初始学习率
- 逐步增加批次大小
- 定期保存检查点
-
评估注意事项:
- 使用独立的测试集
- 评估指标应与奖励函数有所区别
- 进行人工检查确保质量
对于希望复现或扩展本实验的开发者,建议:
- 从小规模实验开始,快速验证思路
- 使用wandb等工具监控训练过程
- 尝试不同的奖励函数组合
- 在更大模型上验证GRPO效果
GRPO作为一种新兴的强化学习算法,在数学推理等有明确评估标准的任务上展现出了良好潜力。随着技术的不断发展,它有望在更多领域得到应用和完善。
