快速入门:在 GSM8K 数据集上进行 PPO 训练 — verl documentation
1.基本信息
- 目标:训练一个LLM,用基于函数的奖励处理GSM8k任务。
- 数据集:GSM8k是一个数学问题集. prompt是小学数学问题,LLM被要求解决数学问题。
2.步骤
1.准备数据集
对数据集进行预处理(parquet 格式),以便它包含计算 RL 奖励的必要字段,且读取速度更快。
python3 examples/data_preprocess/gsm8k.py --local_dir ~/data/gsm8k
运行报错,提示很多模块缺失。看起来与之前安装依赖项时的报错有关,一部分原因是GitHub难以直接访问,所以此时还缺了很多模块;依次重新安装;torch始终安装失败,显示空间不足。把pip换成conda,安装成功。
显示无法访问某个文件,但文件存在,路径也是正确的。
Original error was: libcblas.so.3: cannot open shared object file: No such file or directory
根据gpt的回答,这个问题常见于:
- 从非 Conda 安装源装了 NumPy,缺少系统依赖;
- 使用系统 Python / Miniconda 时,某些 .so 文件缺失;
- 安装了错的 BLAS 库(如 MKL 和 OpenBLAS 冲突);
因此推荐用 Conda 管理这些低层依赖,尤其是 NumPy、SciPy、PyTorch 这类依赖 C 库的包。
一行命令修复所有相关数学库:
conda install -n verl -c conda-forge numpy scipy blas=*=openblas
更新 NumPy 到官方支持版本:
conda install numpy -n verl
再次运行后发现不再报相同的错误。仍然有部分模块缺失,继续补充安装;pip不行换conda,conda不行换pip,都出现解决不了的问题就重新创建conda环境,最终不再出现模块缺失报错。
ConnectionError: Couldn't reach 'openai/gsm8k' on the Hub (LocalEntryNotFoundError)
国内很难直接访问Hugging Face,只能把数据集下载到本地,再从本地加载。
(verl) XX@XX:/XX$ python3 examples/data_preprocess/gsm8k.py
Generating train split: 100%|██████████████████████████████████| 7473/7473 [00:00<00:00, 224025.17 examples/s]
Generating test split: 100%|███████████████████████████████████| 1319/1319 [00:00<00:00, 222331.99 examples/s]
Map: 100%|██████████████████████████████████████████████████████| 7473/7473 [00:00<00:00, 21730.41 examples/s]
Map: 100%|██████████████████████████████████████████████████████| 1319/1319 [00:00<00:00, 21416.08 examples/s]
Creating parquet from Arrow format: 100%|██████████████████████████████████████| 8/8 [00:00<00:00, 302.72ba/s]
Creating parquet from Arrow format: 100%|██████████████████████████████████████| 2/2 [00:00<00:00, 401.87ba/s]
这个脚本的作用是将 GSM8K 数据集处理成结构化格式,并保存为 .parquet 文件到local_dir下。脚本中设置local_dir为home目录下:
为了方便起见,在verl文件夹下创建一个新的文件夹用于存放parquet文件,把前面已经得到的文件删掉。
ls -lh ~/data/gsm8k/*.parquet #列出指定目录下的文件
rm ~/data/gsm8k/*.parquet #删除指定文件
2.下载模型以进行后续训练
python3 -c "import transformers; transformers.pipeline('text-generation', model='Qwen/Qwen2.5-0.5B-Instruct')"
同理,hug的模型也需要下载到本地。为了方便查看进度,先只下载指针文件,再拉取。
GIT_LFS_SKIP_SMUDGE=1 git clone https://hf-mirror.com/Qwen/Qwen2.5-0.5B-Instruct
cd Qwen2.5-0.5B-Instruct/
git lfs pull
Error: Failed to call git rev-parse --git-dir: exit status 128
Not in a git repository.
说明当前的目录 xx/Qwen2.5-0.5B-Instruct 并不是一个有效的 Git 仓库,也就是说.git/ 目录可能缺失或被意外删除了。但这里的问题是Git 检测到当前仓库的拥有者(owner)与当前的用户不一致,出于安全考虑拒绝操作。Git 的提示也给出了解决方法:
git config --global --add safe.directory /xx/Qwen2.5-0.5B-Instruct
再git lfs pull即可。故修改这一步的代码为:
python3 -c "import transformers; transformers.pipeline('text-generation', model='/xx/Qwen2.5-0.5B-Instruct')"
这里出现了一个问题,显示Device set to use cpu。运行代码import torch;
print(torch.cuda.is_available())输出false,说明当前 Python 环境下 PyTorch 无法检测到 GPU。可能是pytorch版本不对,需要重装一下。
总是显示磁盘空间不足。最后解决:用 --no-cache-dir,禁止pip缓存 .whl文件,减少磁盘占用
pip install --no-cache-dir torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
Device set to use cuda:0 #正常了
3.使用 instruct 模型执行 PPO 训练
奖励模型/函数
使用一个预定义的基于规则的奖励模型。强制模型在解答中使用 #### 符号后给出最终答案。我们通过正则表达式匹配,从参考解答和模型输出中提取最终答案。对于奖励的分配方式如下:如果答案正确,则奖励为1;如果答案错误,则奖励为 0.0;如果没有答案,则奖励为 0。
更多详情参考 verl/utils/reward_score/gsm8k.py。
训练脚本
#!/bin/bash
PYTHONUNBUFFERED=1 python3 -m verl.trainer.main_ppo \ #设置 Python 输出不缓存,实时显示输出?
data.train_files=/x/data/gsm8k/train.parquet \ #训练数据集
data.val_files=/x/data/gsm8k/test.parquet \ #验证数据集
data.train_batch_size=256 \
data.max_prompt_length=512 \
data.max_response_length=256 \
actor_rollout_ref.model.path=/xx/Qwen2.5-0.5B-Instruct \ #用于初始化actor模型的预训练模型路径
actor_rollout_ref.actor.optim.lr=1e-6 \
actor_rollout_ref.actor.ppo_mini_batch_size=64 \
actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=4 \
actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=8 \
actor_rollout_ref.rollout.tensor_model_parallel_size=1 \
actor_rollout_ref.rollout.gpu_memory_utilization=0.4 \
actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=4 \
critic.optim.lr=1e-5 \
critic.model.path=Qwen/Qwen2.5-0.5B-Instruct \
critic.ppo_micro_batch_size_per_gpu=4 \
algorithm.kl_ctrl.kl_coef=0.001 \
trainer.logger=['console'] \
trainer.val_before_train=False \
trainer.default_hdfs_dir=null \
trainer.n_gpus_per_node=1 \
trainer.nnodes=1 \
trainer.save_freq=10 \
trainer.test_freq=10 \
trainer.total_epochs=15 2>&1 | tee verl_demo.log
把所有代码直接粘到命令行,没法跑通,所以单独写了一个demo.sh。运行发现有缺很多module,继续补充安装。安装模块的时候老出现这样的报错:
根据gpt的回答,这个问题常常出现在cuda版本上。查看nvcc:
nvcc -V
各种各样的问题。。。反正不兼容。重装conda环境,逐条运行安装脚本里的命令试一下;反正报错不一样了,就当这个问题解决了。
一番努力之后跑起来了,但一直报磁盘空间不足的错误。更改tmp的默认路径当前目录下:
mkdir -p /xx/tmp/ray
export RAY_TMPDIR=/xx/tmp/ray
看起来跑动了,每个step下都有一长串:
step总数为435;应该是由样本数、训练epoch数计算得到的(train_epoch从15改到5,435-->145)。不知为什么改了之后跑的时候会冒出来几个文件夹,文件夹里有代码文件。(尝试删掉了,暂时没出大问题)
训练得到的结果存在文件夹:
4.跑通examples里的grpo算法
运行examples/grpo_trainer/下的脚本run_qwen2-7b.sh;和PPO同理,修改脚本中模型和数据的路径。出现报错:
raise HFValidationError( huggingface_hub.errors.HFValidationError: Repo id must be in the form 'repo_name' or 'namespace/repo_name':
经反复检查,纯粹是路径写错了。又出现了磁盘空间不足的问题,重复前面ppo中提到的设置。
运行出现cuda out of memory报错,调整参数(向ppo的例子靠拢)直到可以顺利跑动。具体来说改了以下部分:
data.train_batch_size=1024-->256 \
data.max_response_length=1024-->256 \
actor_rollout_ref.actor.ppo_mini_batch_size=256-->64 \
actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=40-->4 \
actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=40-->4 \
actor_rollout_ref.rollout.n=5-->3 \
trainer.total_epochs=15-->3 $@
代码可以正常运行;结果如下: