这篇适合谁
适合已经会用 Hugging Face 训练大模型、想尝试强化学习(RL)微调推理模型的人。读完你应该能理解 GRPO 的基本流程,用 Unsloth 加载 Qwen3-8B 并挂上 LoRA,写出奖励函数,跑通一次 GRPO 训练,并用 SwanLab 记录结果。
强化学习和 GRPO 是什么
强化学习解决的是序贯决策问题,智能体通过与环境互动、根据奖励不断调整策略[1]。GRPO(Group Relative Policy Optimization,组相对策略优化)可以看作 PPO 的一种变体或改进,目标是提高训练效率和稳定性[1]。它尝试去掉或简化价值模型(Critic),靠组内的相对比较来估计每个输出的优势[1]。
GRPO 的流程大致是:对同一个 prompt 生成多组回答,用正确性检查或奖励函数给每个回答打分,算出组内平均分,再把每个回答和平均分比较,强化得分更高的回答[1]。规则化的奖励函数或验证器可以代替 LLM 奖励模型,从而节省训练成本和资源[1]。DeepSeek 的研究人员在用纯强化学习训练 R1-Zero 时,观察到模型无需人工指导,就学会了重新评估自己的方法并延长思考[1]。
为什么选 Unsloth
常见的 RL 框架有 TRL(Hugging Face 开发)、VeRL(字节跳动开发)和 Unsloth[1]。VeRL 面向分布式训练,两张卡及以上用起来才顺手;Ray 的学习成本对新手偏高[1]。Unsloth 则可以在单卡上以很低的资源占用对 8B 模型做 RL[1]。
Unsloth 称,它能把 Llama-3、Mistral、Phi-4 和 Gemma 的微调速度提高到 2 倍,内存占用降低 70%,且准确率没有下降[1]。对 Qwen3(8B),它称微调速度快 2 倍、显存减少 70%,并且支持的上下文长度是使用 Flash Attention 2 的环境的 8 倍[1]。项目地址是 docs.unsloth.ai 和 GitHub 上的 unslothai/unsloth[1][2]。
环境和依赖
本文的测试环境是单张 A100,显存 80 GB[1]。安装命令如下[1]:
pip install unsloth==2025.3.19 vllm==0.8.2加载模型并挂上 LoRA
用 FastLanguageModel.from_pretrained 加载 Qwen/Qwen3-8B。max_seq_length 设为 2048,fast_inference=True 表示启用 vLLM 推理;显存不够时,把 gpu_memory_utilization 调低[1]:
from unsloth import FastLanguageModel
import torch
max_seq_length = 2048 # 最大序列长度,可以增加以支持更长的推理轨迹
lora_rank = 32 # LoRA 的秩,秩越大模型可能越智能,但训练和推理速度会变慢
# 从预训练模型加载模型和分词器
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="Qwen/Qwen3-8B", # 要加载的预训练模型名称(官网版本/离线版本/unsloth版本均可)
max_seq_length=max_seq_length, # 设置模型的最大序列长度
load_in_4bit=False, # 是否以4位加载模型,对于LoRA 16位训练,设置为False
fast_inference=True, # 是否启用 vLLM 快速推理
max_lora_rank=lora_rank, # 设置 LoRA 的最大秩
gpu_memory_utilization=0.7, # GPU显存使用率,如果显存不足 (OOM),可以降低此值
)LoRA 通过 get_peft_model 添加,示例中秩为 32,lora_alpha 设为秩的两倍[1]:
# 为模型添加 PEFT (Parameter-Efficient Fine-Tuning) 配置,这里使用 LoRA
model = FastLanguageModel.get_peft_model(
model,
r=lora_rank, # LoRA 的秩 (r),选择任何大于0的数字!建议值为 8, 16, 32, 64, 128
target_modules=[ # 需要应用LoRA的模块名称列表
"q_proj", "k_proj", "v_proj", "o_proj", # 注意力机制中的查询、键、值、输出投影
"gate_proj", "up_proj", "down_proj", # 前馈网络中的门控、上行和下行投影
],
lora_alpha=lora_rank * 2, # LoRA 的 alpha 参数,设置为秩的2倍可以加速训练
use_gradient_checkpointing="unsloth", # 是否使用梯度检查点技术,"unsloth" 表示使用其优化版本以减少显存使用
random_state=3407, # 随机种子,用于确保结果的可复现性
)设置推理格式
训练前要让模型学会固定的输出格式。示例定义了四个标签:<start_working_out> 和 <end_working_out> 包住推理过程,<SOLUTION> 和 </SOLUTION> 包住最终答案[1]:
reasoning_start = "<start_working_out>" # 用于包裹模型的“思考过程”开始部分
reasoning_end = "<end_working_out>" # 用于包裹模型的“思考过程”结束部分
solution_start = "<SOLUTION>" # 用于包裹最终解答开始部分
solution_end = "</SOLUTION>" # 用于包裹最终解答结束部分系统提示词告诉模型先写推理过程,再给出答案[1]:
system_prompt = \
f"""You are given a problem.
Think about the problem and provide your working out.
Place it between {reasoning_start} and {reasoning_end}.
Then, provide your solution between {solution_start}{solution_end}"""先做 SFT,再做 GRPO
数据集的准备分两步。SFT 阶段用 unsloth/OpenMathReasoning-mini 的 cot 切分,只保留预期答案为数字的样本[1];随后只保留 token 长度在 90% 分位数以内的样本[1]。SFT 用 SFTTrainer,训练 2 个 epoch,学习率 2e-4,每张卡的 batch size 为 1[1]。
GRPO 阶段换成 open-r1/DAPO-Math-17k-Processed 的 en 配置、train 切分[1]。
设计奖励函数
GRPO 的效果很大程度取决于奖励函数。示例用了四个:
match_format_exactly:回答完整符合推理和答案的格式时得 3.0 分[1]。
def match_format_exactly(completions, **kwargs):
scores = [] # 用于保存每个 completion 的得分
for completion in completions:
score = 0
response = completion[0]["content"] # 获取模型输出内容(假设为 messages 列表中的第一个 assistant 回复)
# 如果输出内容能成功匹配指定格式(即包含完整 <start_working_out>...<SOLUTION>... 标签结构)
if match_format.search(response) is not None:
score += 3.0 # 匹配成功得 3 分(用于奖励格式正确的输出)
scores.append(score) # 保存该条 completion 的得分
return scores # 返回所有 completion 的格式匹配得分列表match_format_approximately:三个结束或答案标签各出现一次,每个加 0.5 分;出现次数不对则每个扣 1.0 分[1]。check_answer:答案与参考答案完全一致得 5.0 分,去掉空格后一致得 3.5 分,数值误差在 10% 以内得 2.0 分,在 20% 以内得 1.5 分,其余扣 2.5 分;无法解析为数字扣 4.5 分,没有提取到答案扣 2.0 分[1]。check_numbers:提取出的数字与答案完全相等得 3.5 分,否则扣 1.5 分;解析失败记 0 分,没提取到数字扣 2.5 分[1]。
GRPO 训练配置
示例中每个 prompt 生成 4 个回答,num_generations 越大效果可能越好,但显存占用也越高;显存不够时应减小它[1]。采样温度为 1.0,学习率 5e-6,max_steps 为 100,适合调试[1]:
training_args = GRPOConfig(
vllm_sampling_params = vllm_sampling_params, # 用于生成 completion 的采样策略
temperature = 1.0, # 生成的多样性控制(通常设为 0.7 ~ 1.0)
learning_rate = 5e-6, # 训练的学习率(较小以保证稳定收敛)
weight_decay = 0.01, # 权重衰减,用于防止过拟合
warmup_ratio = 0.1, # warmup 步数占总训练步数的比例(通常为 0.05 ~ 0.1)
lr_scheduler_type = "linear", # 学习率调度方式为线性下降
optim = "adamw_8bit", # 使用 bitsandbytes 的 8bit AdamW 优化器(省显存)
logging_steps = 1, # 每一步打印一次日志(适合 debug)
per_device_train_batch_size = 1, # 每张 GPU 的 batch size
gradient_accumulation_steps = 1, # 梯度累积步数(设为 4 可等效 batch size=4)
num_generations = 4, # 每个 prompt 生成多少个 response(越多越好,但显存消耗更大)
max_prompt_length = max_prompt_length, # 提示 token 最大长度(前面计算得出)
max_completion_length = max_completion_length, # 回答 token 最大长度(确保总长不超过模型限制)
# num_train_epochs = 1, # 可选参数:训练轮次,设置后可按 epoch 控制训练终止
max_steps = 100, # 最大训练步数(适合调试用,正式训练时可加大)
save_steps = 100, # 每 100 步保存一次模型(训练短时可以不存)
report_to = "none", # 不上传训练日志(可设置为 "wandb" 使用可视化工具)
output_dir = "outputs", # 模型输出路径训练器把四个奖励函数一起交给 GRPOTrainer[1]:
trainer = GRPOTrainer(
model = model, # 需要训练的语言模型(必须支持 causal LM 格式,如 GPT、LLaMA 等)
processing_class = tokenizer, # 用于生成 prompt、解码 response 的 tokenizer(需支持 chat_template)
reward_funcs = [ # 自定义奖励函数列表,用于计算每条样本的得分
match_format_exactly, # 检查是否严格符合 <start_working_out>...<SOLUTION> 格式,匹配得分高
match_format_approximately, # 检查是否大致有格式标签,宽松评分
check_answer, # 与参考答案对比,进行精确匹配、模糊匹配、近似数值匹配
check_numbers, # 提取数值进行比较(用于数学题)
],
args = training_args, # 训练参数配置(使用前面定义好的 GRPOConfig 对象)
train_dataset = dataset, # 实际用于训练的数据集
# 可选:如启用训练 + 验证评估,可替换为如下配置
# train_dataset = new_dataset["train"],
# eval_dataset = new_dataset["test"],
)保存和测试
训练结束后,用 model.save_lora("grpo_saved_lora") 保存 LoRA 权重,并检查保存的 safetensors 文件中没有全零的张量[1]:
model.save_lora("grpo_saved_lora")合并保存、只存 LoRA 以及 GGUF 格式(如 Q8_0、f16、q4_k_m)的代码都列在示例里,但都包在 if False: 中,默认不执行,需要时改成 True[1]。
用 SwanLab 记录实验
SwanLab 是开源的训练记录工具,提供训练可视化、自动日志、超参数记录、实验对比和多人协作等功能[1][3]。使用前先到 swanlab.cn 注册账号,然后在 SFT 和 GRPO 的配置里把 report_to 改为 "swanlab"[1]:
report_to = "swanlab", # 这里改成swanlab下一步
相关论文可以读 DeepSeek-R1 的论文,地址是 arxiv.org/abs/2501.12948[1]。Unsloth 的参数和更多训练方式见官方文档 docs.unsloth.ai。
本文改编自 Datawhale 开源项目 self-llm 中的教程《Qwen3-8B GRPO微调及通过swanlab可视化》[1],原文以 Apache-2.0 许可证发布。
来源
- 1Qwen3-8B GRPO微调及通过swanlab可视化Datawhale self-llm (Apache-2.0)
- 2unsloth (GitHub)Unsloth (GitHub)
- 3SwanLab (GitHub)SwanLab (GitHub)
引用本页: Qwen3 GRPO 强化学习微调. TellWorld, https://www.tellworld.net/zh/practice/practice-qwen3-grpo. 更新于 2026-11-14