LLaMA-Factory PPO 训练实战:从 SFT 到 RLHF 完整指南

一、前言:为什么需要 PPO?

在大语言模型(LLM)的后训练阶段,监督微调(SFT) 只能让模型学会"模仿"人类的表达形式,但无法真正理解人类的偏好价值观 。这就是 RLHF(Reinforcement Learning from Human Feedback) 的用武之地。

PPO(Proximal Policy Optimization)作为 RLHF 的核心算法,通过奖励模型(Reward Model)的反馈来优化策略,使模型生成更符合人类期望的回复。

本文将基于 LLaMA-Factory 框架,完整演示从 SFT → Reward Model → PPO 的三阶段训练流程。


二、训练流程架构

复制代码
┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   预训练模型     │ --> │   SFT 训练        │ --> │   SFT 模型      │
│  (Qwen2.5-7B)   │     │  学习指令遵循      │     │  (有对话能力)    │
└─────────────────┘     └──────────────────┘     └────────┬────────┘
                                                         │
                              ┌──────────────────────────┘
                              ▼
                    ┌──────────────────┐
                    │  构建偏好数据      │
                    │ (chosen/rejected) │
                    └────────┬─────────┘
                              ▼
                    ┌──────────────────┐     ┌─────────────────┐
                    │   Reward Model   │ --> │   奖励模型       │
                    │    训练阶段       │     │  (学会打分)      │
                    └──────────────────┘     └────────┬────────┘
                                                       │
                              ┌────────────────────────┘
                              ▼
                    ┌──────────────────┐     ┌─────────────────┐
                    │    PPO 训练      │ --> │   最终模型       │
                    │  强化学习优化      │     │  (符合人类偏好)  │
                    └──────────────────┘     └─────────────────┘

三、环境准备

3.1 安装 LLaMA-Factory

bash 复制代码
# 克隆仓库
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory

# 创建虚拟环境
conda create -n llama-factory python=3.10 -y
conda activate llama-factory

# 安装依赖(推荐使用最新版本)
pip install -e ".[torch,metrics]"

3.2 验证安装

bash 复制代码
llamafactory-cli version

# 预期输出:显示 LLaMA-Factory 版本信息

四、阶段一:SFT 监督微调

SFT 是 RLHF 的基础,目标是让模型具备基本的指令遵循能力。

4.1 配置文件 sft_config.yaml

yaml 复制代码
### 模型配置
model_name_or_path: Qwen/Qwen3-4B  # 基础模型
trust_remote_code: true

### 训练方法
stage: sft
do_train: true
finetuning_type: lora           # 使用 LoRA 节省显存
lora_target: all
lora_rank: 8
lora_alpha: 16
lora_dropout: 0.05

### 数据集配置
dataset: alpaca_gpt4_zh,sharegpt_gpt4  # 混合多个数据集
template: qwen
cutoff_len: 2048
max_samples: 100000
overwrite_cache: true
preprocessing_num_workers: 16

### 输出配置
output_dir: saves/qwen-sft-lora
logging_steps: 10
save_steps: 500
plot_loss: true
overwrite_output_dir: true

### 训练参数
per_device_train_batch_size: 1
gradient_accumulation_steps: 8    # 有效 batch size = 8
learning_rate: 5.0e-5
num_train_epochs: 3.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true                          # A100/V100 启用
# fp16: true                        # 旧显卡启用

### 优化器配置
optim: adamw_torch
weight_decay: 0.1
max_grad_norm: 1.0

### 系统配置
ddp_timeout: 180000000
seed: 42

4.2 启动训练

bash 复制代码
# 单卡训练
llamafactory-cli train configs/sft_config.yaml

# 多卡训练(DeepSpeed)
CUDA_VISIBLE_DEVICES=0,1,2,3 llamafactory-cli train configs/sft_config.yaml \
    --deepspeed configs/deepspeed/ds_z2_config.json

4.3 合并 LoRA 权重(关键步骤)

PPO 训练需要加载完整的 SFT 模型,因此必须先合并:

bash 复制代码
llamafactory-cli export \
    --model_name_or_path saves/qwen-sft-lora \
    --adapter_name_or_path saves/qwen-sft-lora \
    --template qwen \
    --finetuning_type lora \
    --export_dir saves/qwen-sft-merged \
    --export_size 2 \
    --export_device cpu \
    --export_legacy_format false

注意: 合并后的模型目录应包含 config.jsonpytorch_model.binmodel.safetensors 等文件。


五、阶段二:Reward Model 训练

奖励模型的作用是给模型的输出"打分",学习人类偏好。

5.1 准备偏好数据

数据格式(JSON):

json 复制代码
[
  {
    "instruction": "请解释机器学习中的过拟合现象",
    "input": "",
    "output": [
      "过拟合是指模型在训练数据上表现很好,但在新数据上表现差的现象。这通常是因为模型过于复杂,记住了训练数据的噪声而不是学习通用规律。解决方法包括增加数据、正则化、早停等。",
      "过拟合就是模型训练得太好了,所以有问题。"
    ]
  }
]

关键: output 字段是一个数组,第一个元素是 chosen (优选),第二个是 rejected(劣选)。

5.2 配置文件 reward_config.yaml

yaml 复制代码
### 模型配置
model_name_or_path: saves/qwen-sft-merged  # 使用合并后的 SFT 模型

### 训练方法
stage: rm                       # reward modeling
do_train: true
finetuning_type: lora
lora_target: all
lora_rank: 8
lora_alpha: 16

### 数据集配置
dataset: comparison_gpt4_zh     # 偏好数据集
template: qwen
cutoff_len: 2048
max_samples: 50000

### 输出配置
output_dir: saves/qwen-reward-lora
logging_steps: 10
save_steps: 300
plot_loss: true
overwrite_output_dir: true

### 训练参数
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 1.0e-5          # RM 使用较小学习率
num_train_epochs: 2.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true

### RM 特定配置
# 奖励模型使用回归头,自动添加 value_head

5.3 启动训练与合并

bash 复制代码
# 训练奖励模型
llamafactory-cli train configs/reward_config.yaml

# 合并奖励模型
llamafactory-cli export \
    --model_name_or_path saves/qwen-reward-lora \
    --adapter_name_or_path saves/qwen-reward-lora \
    --template qwen \
    --finetuning_type lora \
    --export_dir saves/qwen-reward-merged \
    --export_size 2 \
    --export_device cpu

验证奖励模型:

python 复制代码
from transformers import AutoModelForSequenceClassification, AutoTokenizer

model = AutoModelForSequenceClassification.from_pretrained(
    "saves/qwen-reward-merged",
    trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained("saves/qwen-reward-merged")

# 测试打分
text = "这是一个很好的回答!"
inputs = tokenizer(text, return_tensors="pt")
score = model(**inputs).logits
print(f"Reward score: {score.item()}")

六、阶段三:PPO 强化学习训练

这是 RLHF 的核心阶段,使用 PPO 算法优化策略。

6.1 准备 Prompt 数据

PPO 只需要 prompts,模型会自己生成 responses:

json 复制代码
[
  {
    "instruction": "请用简单的语言解释什么是区块链",
    "input": ""
  },
  {
    "instruction": "写一段 Python 代码计算斐波那契数列",
    "input": ""
  }
]

6.2 配置文件 ppo_config.yaml

yaml 复制代码
### 模型配置
model_name_or_path: saves/qwen-sft-merged    # Actor 模型(SFT 模型)
reward_model: saves/qwen-reward-merged       # Critic/Reward 模型

### 训练方法
stage: ppo
do_train: true
finetuning_type: lora
lora_target: all
lora_rank: 8
lora_alpha: 16

### 数据集配置
dataset: ppo_prompts_zh                      # 只有 prompts 的数据集
template: qwen
cutoff_len: 2048
max_samples: 20000

### PPO 核心参数
ppo_epochs: 1                # 每次数据迭代中 PPO 更新次数
ppo_buffer_size: 1           # 经验回放缓冲区大小
ppo_target_kl: 0.1           # KL 散度目标值(防止策略偏离太远)
ppo_clip_eps: 0.2            # PPO 裁剪参数
ppo_score_norm: true         # 奖励分数归一化(重要!)
ppo_score_clip: 10.0         # 奖励分数裁剪范围
ppo_temperature: 1.0         # 采样温度

### 生成配置(影响采样质量)
top_k: 0                     # 0 表示禁用 top-k
top_p: 0.9                   # nucleus sampling
temperature: 0.7             # 生成多样性
max_new_tokens: 512          # 每次生成长度
do_sample: true              # 必须启用采样

### 输出配置
output_dir: saves/qwen-ppo-lora
logging_steps: 5
save_steps: 100
plot_loss: true
overwrite_output_dir: true

### 训练参数
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 1.0e-5        # PPO 通常需要较小学习率
num_train_epochs: 1.0        # PPO 通常 1-3 个 epoch 即可
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true

### 优化器
optim: adamw_torch
max_grad_norm: 1.0

6.3 PPO 参数详解

参数 作用 调参建议
ppo_target_kl 控制新旧策略的 KL 散度 0.05-0.1,越大允许偏离越多
ppo_score_norm 对奖励分数进行归一化 强烈建议开启,稳定训练
ppo_score_clip 限制奖励分数范围 5-10,防止极端值影响
temperature 生成采样的温度 0.5-1.0,影响探索能力
ppo_buffer_size 经验缓冲区大小 根据显存调整,1-4

6.4 启动 PPO 训练

bash 复制代码
# 单卡训练(需要 24GB+ 显存)
llamafactory-cli train configs/ppo_config.yaml

# 多卡训练(推荐)
CUDA_VISIBLE_DEVICES=0,1,2,3 llamafactory-cli train configs/ppo_config.yaml \
    --deepspeed configs/deepspeed/ds_z3_config.json

七、关键问题与解决方案

7.1 网络连接错误(Hugging Face)

现象:

复制代码
MaxRetryError: HTTPSConnectionPool(host='huggingface.co', port=443): 
Max retries exceeded with url: ... Network is unreachable

解决:

yaml 复制代码
# 使用本地绝对路径,而非 Hugging Face Hub 路径
model_name_or_path: /absolute/path/to/saves/qwen-sft-merged  # ✅
# model_name_or_path: saves/qwen-sft-merged                    # ❌ 会被识别为 HF Hub

或设置镜像:

bash 复制代码
export HF_ENDPOINT=https://hf-mirror.com

7.2 显存不足(OOM)

方案 A:启用 QLoRA

yaml 复制代码
quantization_bit: 4           # 4-bit 量化
quantization_method: bitsandbytes

方案 B:减小生成长度

yaml 复制代码
max_new_tokens: 256           # 从 512 减小
cutoff_len: 1024              # 从 2048 减小

方案 C:使用 DeepSpeed ZeRO-3

bash 复制代码
# configs/deepspeed/ds_z3_config.json
{
  "train_batch_size": "auto",
  "train_micro_batch_size_per_gpu": "auto",
  "gradient_accumulation_steps": "auto",
  "zero_optimization": {
    "stage": 3,
    "offload_optimizer": {
      "device": "cpu",
      "pin_memory": true
    },
    "offload_param": {
      "device": "cpu",
      "pin_memory": true
    }
  }
}

7.3 KL 散度过大(训练不稳定)

现象: kl 值迅速增大,奖励分数震荡

解决:

yaml 复制代码
ppo_target_kl: 0.05           # 从 0.1 减小
learning_rate: 5.0e-6          # 从 1.0e-5 减小
ppo_score_clip: 5.0            # 从 10.0 减小

7.4 奖励模型分数异常

现象: 所有样本奖励分数接近,无法区分好坏

解决:

yaml 复制代码
ppo_score_norm: true            # 确保开启归一化
# 或检查奖励模型是否训练充分

八、完整训练脚本

bash 复制代码
#!/bin/bash
set -e

# ========== 配置 ==========
BASE_MODEL="Qwen/Qwen3-4B"
PROJECT_NAME="qwen-rlhf"
STEPS=("sft" "rm" "ppo")

# ========== 阶段 1: SFT ==========
echo ">>> Stage 1: SFT Training"
llamafactory-cli train configs/sft_config.yaml

echo ">>> Merging SFT LoRA"
llamafactory-cli export \
    --model_name_or_path saves/${PROJECT_NAME}-sft-lora \
    --adapter_name_or_path saves/${PROJECT_NAME}-sft-lora \
    --template qwen \
    --finetuning_type lora \
    --export_dir saves/${PROJECT_NAME}-sft-merged \
    --export_device cpu

# ========== 阶段 2: Reward Model ==========
echo ">>> Stage 2: Reward Model Training"
llamafactory-cli train configs/reward_config.yaml

echo ">>> Merging Reward Model"
llamafactory-cli export \
    --model_name_or_path saves/${PROJECT_NAME}-reward-lora \
    --adapter_name_or_path saves/${PROJECT_NAME}-reward-lora \
    --template qwen \
    --finetuning_type lora \
    --export_dir saves/${PROJECT_NAME}-reward-merged \
    --export_device cpu

# ========== 阶段 3: PPO ==========
echo ">>> Stage 3: PPO Training"
llamafactory-cli train configs/ppo_config.yaml

echo ">>> Merging Final PPO Model"
llamafactory-cli export \
    --model_name_or_path saves/${PROJECT_NAME}-ppo-lora \
    --adapter_name_or_path saves/${PROJECT_NAME}-ppo-lora \
    --template qwen \
    --finetuning_type lora \
    --export_dir saves/${PROJECT_NAME}-ppo-merged \
    --export_device cpu

echo ">>> RLHF Training Complete!"
echo "Final model: saves/${PROJECT_NAME}-ppo-merged"

九、模型评估与部署

9.1 快速测试

bash 复制代码
# 启动 Web UI
llamafactory-cli webui

# 或使用命令行推理
llamafactory-cli chat saves/qwen-ppo-merged --template qwen

9.2 部署为 API

bash 复制代码
llamafactory-cli api \
    --model_name_or_path saves/qwen-ppo-merged \
    --template qwen \
    --infer_backend vllm \
    --vllm_maxlen 4096

十、总结与最佳实践

阶段 关键要点 常见陷阱
SFT 数据质量 > 数量,多轮对话需正确处理 忘记合并 LoRA 直接用于下一阶段
RM 偏好对要清晰可区分,数据平衡 奖励模型过拟合,分数饱和
PPO 小学习率、KL 约束、分数归一化 KL 爆炸、训练不稳定、生成质量下降

核心建议:

  1. 渐进式训练:每个阶段都保存 checkpoint,便于回滚调试
  2. 监控指标 :重点关注 klrewardloss 三个指标的变化曲线
  3. 数据质量:RLHF 的效果上限由数据质量决定,算法只是放大器
  4. 充分验证:PPO 训练后务必进行人工评估,奖励分数高 ≠ 实际效果好

参考资源


本文基于 LLaMA-Factory v0.9.0 版本编写,不同版本配置可能略有差异。

相关推荐
接着奏乐接着舞。4 小时前
5分钟本地跑起大模型
人工智能·llama
liuze40819 小时前
Ollama安装
llama
小超同学你好19 小时前
Transformer 14. DeepSeekMoE 架构解析:与 LLaMA 以及 Transformer 架构对比
语言模型·架构·transformer·llama
小超同学你好1 天前
Transformer 15: DeepSeek-V2 架构解析:MLA + DeepSeekMoE 与主流架构对比
语言模型·架构·transformer·llama
品克缤2 天前
Trading-Analysis:基于“规则+LLM”的行情分析终端(兼谈 Vibe Coding 实战感)
前端·后端·node.js·vue·express·ai编程·llama
seaside20032 天前
llama.cpp 部署qwen3.5 2B 高通芯片安卓实战
llama·qwen3.5·高通soc
JAdroid2 天前
LLM大模型操作比特币
llama
踏歌~3 天前
LLaMA Factory简介和使用方法
llama
魔乐社区3 天前
在魔乐社区使用llama-factory微调Qwen3.5-4B模型
微调·llama·qwen3.5