From bc0185b45400fb1da20148cb6f0bddf33af501ba Mon Sep 17 00:00:00 2001 From: hhh2210 Date: Thu, 17 Apr 2025 21:52:17 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitingore | 2 + Auto_Program/my_progress.md | 10 +++ README.zh-CN.md | 93 +++++++++++++++++++++++++ __pycache__/ref_server.cpython-310.pyc | Bin 0 -> 3434 bytes grpo_vllm_one.py | 4 +- my_progtess.md | 73 +++++++++++++++++++ ref_server.py | 4 +- 7 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 .gitingore create mode 100644 Auto_Program/my_progress.md create mode 100644 README.zh-CN.md create mode 100644 __pycache__/ref_server.cpython-310.pyc create mode 100644 my_progtess.md diff --git a/.gitingore b/.gitingore new file mode 100644 index 0000000..7ce12e5 --- /dev/null +++ b/.gitingore @@ -0,0 +1,2 @@ +__pycache__/ +simple_GRPO/__pycache__/ diff --git a/Auto_Program/my_progress.md b/Auto_Program/my_progress.md new file mode 100644 index 0000000..ff745b5 --- /dev/null +++ b/Auto_Program/my_progress.md @@ -0,0 +1,10 @@ +# Prompt +system_prompt_0312.txt 包含一个完整的示例,而 system_prompt_0312_zero.txt 没有示例。 +system_prompt_0312.txt 是一个 Few-Shot (实际上是 One-Shot) 提示,因为它提供了一个样本来指导模型。 +system_prompt_0312_zero.txt 是一个 Zero-Shot 提示,它只给出指令,不提供样本。 + +这两个文件都定义了与 LLM 交互的格式规范,特别是针对需要模型在推理过程中生成并利用代码执行结果的任务。它们在 hjy_grpo_program.py 脚本中被用作 系统提示,在向模型发起请求时放在最前面,用以设定模型的行为模式。 +指导模型生成: 它们告诉模型应该如何构建其响应,包括思考过程、代码嵌入、结果声明和最终答案的格式。 +支持代码执行流程: 标签和结果声明的格式是 hjy_grpo_program.py 中 get_completions 和 run 函数能够正确提取代码、执行代码并将结果反馈给模型的关键。 + +# RL \ No newline at end of file diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..ad9335b --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,93 @@ +# 🚀🚀🚀 simple_GRPO 🚀🚀🚀 + +一个简单的GRPO实现,用于复现类r1的LLM思考能力。本项目是一个简单的开源实现,使用了来自Hugging Face的trl的核心损失计算公式。 + +我们构建了最简单的代码库,支持: +- 节省GPU内存,实现可行且高效的训练 +- 从教学角度快速理解GRPO等RL过程 +- 快速尝试多种改进,如多答案生成、重组、KL惩罚和参数调优 +- 在模型训练早期观察到"顿悟时刻" + +## ✨最新更新 +- 2025/02/19: 添加了loss triton实现,略有加速,可选择不使用。详见*simple_grpo_v1*文件夹 +- 2025/02/19: 添加了重组版本,在ref_server上实现了生成数据采样。详见*regroup_ver*文件夹 +- 2025/02/27: 添加了vllm包以加速推理 +- 2025/03/24: 添加了reinforce++算法,使用方式与之前相同 + +## 🌟 特点 + +### 💡 简洁性 +项目代码简单,仅有约200行代码分布在2个文件中。只依赖_deepspeed_和_torch_等标准库,无需依赖ray等。设计支持更复杂的干预。 + +### 🤖 拆分的参考模型 +参考模型部分被解耦,允许在不同GPU上运行(甚至可在配有4090的不同机器上运行)。避免了将参考模型和训练模型放在同一GPU上,防止torch多进程创建多个副本,实现在80G A800上训练7B模型。 + +### 💃 性能 +在1台A800 GPU上训练不到1小时完成。Qwen2.5-7B和Qwen2.5-3B均在前30步优化中出现"顿悟时刻"。 + +### 🥳 核心损失计算 +损失计算公式基于Hugging Face的trl。感谢Hugging Face的贡献。 + +## 🙌 环境 +运行环境在requirements.txt中,执行: +```bash +pip install -r requirements.txt +``` +至少需要两个GPU。 + +## 使用方法 +### 如果有三个或更多GPU,您将有更好的选择! +运行以下命令: +```bash +CUDA_VISIBLE_DEVICES=7 python ref_server.py +``` +这仅使用一个GPU来收集并运行参考模型。 + +在*grpo_vllm_one.py*中,设置相对于下一步可见设备的生成设备索引: +```bash +gen_device = 1 +``` +然后,打开另一个bash: +```bash +CUDA_VISIBLE_DEVICES=2,3,4,5,6 deepspeed grpo_vllm_one.py +``` + +## ✨ 实验结果 + +1. 运行环境 +- 硬件设置:2×A800 (80GB) GPU +- 配置: + - 训练:1个GPU,采用Zero-Stage 2优化 + - 推理:专用1个GPU(兼容3090/4090) + +2. 训练性能 + +| 模型 | 步数 | 时间 | +|------------|------|------------| +| Qwen2.5-3B | 60 | 12分34秒 | +| Qwen2.5-7B | 60 | 16分40秒 | + +2.1 Qwen2.5-3B +- 准确率: + - 5步优化后稳定在60%以上 + - 峰值约70% +- 格式合规性: + - 30步后达到约100% + +2.2 Qwen2.5-7B +- 准确率: + - 整个训练过程中保持>90% +- 格式合规性: + - 30步内达到100% +- 收敛性:两项指标均显示快速收敛 + +## 顿悟时刻 +1. Qwen2.5-3B(第20步)模型在思考过程中意识到初始计算错误,开始重新考虑问题。 + +2. Qwen2.5-7B(第20步)模型发现计算结果与总数不符,自我校正并重新评估。 + +## 😊 待办事项 +- 答案生成可能无效,因为一个组可能包含全部错误答案或全部正确答案。需要组重组和更好的答案生成 +- 如果生成长思考链(CoT),GPU内存仍然紧张。必须分割组以使批次更小 + +我们已经实现并正在测试这些功能,很快将推出。 \ No newline at end of file diff --git a/__pycache__/ref_server.cpython-310.pyc b/__pycache__/ref_server.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df70148a8613072e93190cb7d7f83e7e7215baec GIT binary patch literal 3434 zcmZuzTXPi074Gi2>_w~FN(dwb1`8WDTox4DAr+D`VC)bJK|raLCr(X`)UDlFc4wBJ zSqZ6DPcm)_TICF#ooXjv9z33NhLWcjK18`l+_m}pOSrh=)?bTG|FHxdVuV5T!0%-Xq1yV{ux z=Em#Qpla9WgLz9&1SjOA;;jWab&JXA;G~*vol+-Np|vPy6bMpL4F{sgfC8o{|eg=M*)n&P|$mE4Z7F;haFZ_fNLn9Fkym(;>j`wo1GYK$66Ni%I_A)!X%k(L=9ndf>Mu9K`!gFuh^ff2V%S`K3K(>zTNk17CSt0&MRmA$cpZ4#*Lc) zR(9j<>Nr=%X{*E(- z9uEy~f2e+SBz-++6pRS=e~^?Y;H;!M(>%!bi6s-(Fk0w|4)@ zgZ-ttjPmG4y&0wT4sprqU*BJQaP=2=>PSD?jd0_H&BjpXdtGJb!zj;_&?Z$!C3%#` zX=2>1o{YYJ&sP+d$D#Qb4043uA#cG~eX1brg{>d9MSt5_1U~dJZ&$E3!mtAdKZ+b7 zi_-$0JoCLca1PwQ({d2l&P#safe+tv=>p?Xbr2VRUkr+U-{RFu?hZ;6 z;7C$${pdBGBT8vl%aL)XCiGP@wihQ-J+GBbiQvK-#rW;C5$Bn4_T#RZz#z=h?Yt8` zALX^*i1H?!QR*cbtgiNzDU3Us;Lmtj9&J6X6-+5ky1hJ%Wu~vw!gN;=0KuCfxOOvB z9^Dk`bk|HCZ??k{-P_gaW@e_2kJyP1<7*C(8GgX|GOo)g%F29+&m+XD2(o2-Wv+jX z>HR{z*Cl*1?xTlK2*Ui1%A2X&!s78mKy;jeKVm=-I1s>gHi8>am;5aG4%f31fGClL z$TKG0MPzDBxmv);qsoFv@NY3YE{w-Tj7JZEC$HjtDjNEEXwR8s#vX}FVLd9naVxAM4Qi1L&d_ltqiH+k&QD8<%qCo zH$=c7K^=sJ@w%Fz#><*fm&%=x@CGI5hYl{uG#q*BSKyqTMN<|t13UAol;2vW(U755hW0 zN=8{=`g;&~!-pTpe!h%UCrvi=ZOr2)R6urX5>ffX;D8SrD;kjQw5@7g4Y#4dC~1nD+X&G7#ur9_ zbDgem6@I_W6cv;hP7fP8lA*;6Q(B9+)8A77>)+AUkI@AF9cyd-Cpzruw3jOze%9aC zn8x3*(pg;8xu#NXxHOYMH-uLRi#8zKRGi>XPbFK*!Y~Z4cH%9iZ(7E(*8yJoSVrB_ z4>32!+UGW7zNLw`za4Kwv3Lvm_uA?=l` 标签内,最终答案放在 `` 标签内。 +2. **代码嵌入:** 在 `` 标签内部,如果需要计算,模型应生成 Python 代码块,并将其包裹在 `\n\`\`\`python\n...\n\`\`\`\n` 结构中。 +3. **模拟执行与结果注入:** 提示明确要求模型在代码块之后写上 "The result of executing this Python code is: \[code output]"。这强烈暗示了训练流程中存在一个外部组件(可能是 `ref_server.py` 或 `hjy_grpo_program.py` 内的逻辑)会**实际执行**模型生成的 Python 代码,并将执行结果(`[code output]` 部分)反馈给模型,或者在评估/奖励计算中使用这个结果。 +4. **逐步推理:** 模型需要根据代码执行的结果继续其推理过程。 + +这与标准的“工具调用”范式非常相似,其中“工具”是 **Python 代码执行器**。模型学习的不是调用一个抽象的 API 名称,而是直接生成可执行的代码。 +`config.py` 主要定义了训练过程的超参数,如批大小、学习率、保存路径、WandB 配置、Deepspeed 配置等。 + +与我们讨论的“代码执行”相关的关键配置是: + +* `"ref_server": "http://localhost:59807"`: 指定了参考模型服务(`ref_server.py`)的地址。这证实了训练脚本会与参考模型进行通信。 +* `"port": 59807`: 参考模型服务的端口。 + +好的,`hjy_grpo_program.py` 的开头部分(前 200 行)揭示了关键的实现细节: + +1. **代码执行逻辑 (`run` 函数, L122-L151):** + * 这个函数明确地实现了从模型的生成文本中**提取 Python 代码块**(位于 ` ```python ... ``` ` 之间)。 + * 它使用 `exec()` 来**执行提取的代码**。 + * **关键:** 它通过 `io.StringIO` 和 `sys.stdout` 重定向来捕获代码的**标准输出** (`print` 语句的结果)。 + * 它包含**超时处理** (`signal.alarm`) 和基本的**异常捕获**,将错误信息作为字符串返回。 + * 如果代码没有输出或找不到代码块,它会返回错误信息。 + +2. **递归生成与代码注入 (`get_completions` 函数, L153-L172):** + * 这个函数使用 `vllm` 生成模型的续写。 + * **核心机制:** 它设定了一个停止标志 `stop_sentences = "The result of executing this Python code is:"`。 + * 当模型的生成文本包含这个停止标志时,它会: + * 调用 `run()` 函数执行刚刚生成的代码块。 + * 将 `run()` 返回的执行结果(或错误信息)**追加**到当前生成的文本后面。 + * 将追加了结果的文本作为新的提示,**递归调用 `get_completions`** 继续生成。 + * 这实现了系统提示中要求的“在代码块后明确说明执行结果”的流程,并将结果反馈给模型用于后续推理。 + +3. **生成工作进程 (`gen_worker` 函数, L100):** + * 这个函数运行在一个单独的进程中,负责使用 `vllm` 模型生成答案 (`gen_answers` 函数调用 `get_completions`)。 + * 它**计算奖励分数**: + * `reward_correct` (L190): 比较最终答案(`` 标签内 `\boxed{}` 部分)与基准答案 (`ground_truth`) 的正确性。它使用了 `math_verify` 库,暗示了任务可能与数学问题求解相关(如 GSM8K 数据集)。 + * `reward_format` (L208, 代码未完全显示): 可能检查生成的文本是否符合 `//` 的整体格式要求。 + * 它将生成的数据(prompt, answer, rewards, logprobs 等)**发送给参考模型服务器** (`ref_server`) 存储,供主训练进程 (`GRPO_step`) 拉取。 + +4. **GRPO 训练步骤 (`GRPO_step` 函数, L67-L95):** + * 从 `ref_server` 获取批次数据 (`get_batch`, L43),包含输入、奖励、参考模型 logprobs 等。 + * 计算当前策略模型(正在训练的模型)的 logprobs。 + * 计算 KL 散度惩罚 (`per_token_kl`),防止模型偏离太远。 + * 计算 GRPO 损失,结合了优势(从奖励计算得出)、策略模型 logprobs、生成模型 logprobs(如果 `compute_gen_logps` 为 True)和 KL 惩罚。 + +5. **参考服务器角色 (`ref_server.py`):** + * 根据 `hjy_grpo_program.py` 中的交互方式 (`get_batch` 拉取数据,`gen_worker` 推送数据),`Auto_Program` 目录下的 `ref_server.py` 主要扮演**数据存储和分发**的角色。它存储由 `gen_worker` 生成并评估好的样本(包含提示、生成的答案、计算好的奖励、logprobs 等),然后按批次提供给主训练进程进行 GRPO 更新。它本身**不执行** Python 代码或计算奖励。 + +**总结与回答:** + +是的,`Learn/Train/simple_GRPO/Auto_Program` 文件夹确实是在**训练模型进行一种特定形式的“工具调用”**,这里的“工具”是**Python 代码执行器**。 + +**工作原理:** + +1. **任务定义:** 模型被要求遵循 `.........` 的格式解决问题(可能是数学题)。 +2. **代码生成:** 模型在 `` 块内生成 Python 代码片段(`` 标签内)。 +3. **代码执行与结果注入:** `hjy_grpo_program.py` 中的 `gen_worker` 进程使用 `vllm` 生成文本,当遇到特定停止标志时,提取并执行 Python 代码 (`run` 函数),捕获其输出。 +4. **递归生成:** 将代码执行结果追加到生成文本后,作为新的提示继续让模型生成,模拟推理过程。 +5. **奖励计算:** `gen_worker` 根据最终答案的正确性 (`reward_correct`) 和格式合规性 (`reward_format`) 计算奖励。 +6. **数据存储:** `gen_worker` 将生成的样本(包含提示、答案、奖励、logprobs)发送到 `ref_server.py` 存储。 +7. **GRPO 训练:** 主训练进程从 `ref_server.py` 获取带奖励的样本批次,并执行 `GRPO_step` 来更新模型参数,鼓励模型生成能得出正确答案且格式正确的代码和推理链。 + +这种方法巧妙地将代码执行能力集成到了模型的思考链中,并通过强化学习(GRPO)进行优化。 diff --git a/ref_server.py b/ref_server.py index 883df6d..9bb465d 100644 --- a/ref_server.py +++ b/ref_server.py @@ -1,4 +1,4 @@ - +# 运行一个固定的参考模型 (reference model),并提供一个网络服务来计算由当前训练模型生成的序列的对数概率 (log probabilities) import json, os, shutil, re, random, io, time import torch @@ -33,7 +33,7 @@ def bytes_list_to_list(b): import bottle, threading, queue os.environ['TOKENIZERS_PARALLELISM'] = 'true' - model_path = "/data2/Qwen/Qwen2.5-7B" + model_path = "/data1/hao/models/FILM-7B/" ref_model = AutoModelForCausalLM.from_pretrained(model_path, torch_dtype=torch.bfloat16, _attn_implementation="sdpa").to('cuda') From f8d61fba0b984750453878068c4ed03897522284 Mon Sep 17 00:00:00 2001 From: hhh2210 Date: Thu, 17 Apr 2025 22:02:08 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20auto=5Fprogram=20?= =?UTF-8?q?=E7=9A=84=E7=AC=94=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Auto_Program/my_progress.md | 98 ++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/Auto_Program/my_progress.md b/Auto_Program/my_progress.md index ff745b5..a901980 100644 --- a/Auto_Program/my_progress.md +++ b/Auto_Program/my_progress.md @@ -7,4 +7,100 @@ system_prompt_0312_zero.txt 是一个 Zero-Shot 提示,它只给出指令, 指导模型生成: 它们告诉模型应该如何构建其响应,包括思考过程、代码嵌入、结果声明和最终答案的格式。 支持代码执行流程: 标签和结果声明的格式是 hjy_grpo_program.py 中 get_completions 和 run 函数能够正确提取代码、执行代码并将结果反馈给模型的关键。 -# RL \ No newline at end of file + +# RL-GRPO + +## 主要组件 + +### 配置文件 (config.py) +- 定义所有训练参数和配置 +- 不包含执行逻辑,而是定义了两组重要的配置: + - `train_config`: 训练相关的超参数和设置 + - 路径配置: `model_path`, `save_path`, `data_path` + - Weights & Biases 日志配置: `wandb_*` + - 生成设备: `gen_device` (指定用于文本生成的 GPU 设备 ID) + - 算法参数: + - `beta`: GRPO/PPO 中 KL 散度惩罚项的系数 + - `clip_param`: PPO/GRPO 损失函数中的裁剪参数 + - 批次设置: + - `Q_batch_size`, `num_pre_Q`: 每次采样的问题数量和每个问题生成的候选答案数量 + - `train_batch_size`: 实际用于训练更新的批次大小 + - 更新控制: `gen_update_steps` (每隔多少训练步数更新生成模型) + - 算法开关: `compute_gen_logps` (是否计算生成模型的 logprobs) + - 服务器配置: `ref_server`, `port` + - DeepSpeed 配置: `ds_config` (包含 ZeRO 优化、梯度累积、优化器等设置) + +### Reference Server (ref_server.py) +- 作为独立的 Reference Server 运行 +- 负责计算参考模型的对数概率并作为数据中转站 +- 核心功能: + - 加载参考模型: 固定的 LLM 用于计算参考 logprobs + - 接收数据: 通过 `/upload` POST 端点接收生成数据 + - 计算参考 Logprobs: 使用固定模型计算每个 token 的对数概率 + - 缓存与分发: 将数据与 ref_logps 打包放入队列 + - 提供数据: 通过 `/get` 端点向训练进程提供完整数据包 +- 数据流: gen_worker → /upload → raw_queue → 参考模型计算 → result_queue → /get → 主训练进程 + +### 主训练与生成脚本 (hjy_grpo_program.py) +包含主要的训练逻辑(模型更新)和独立的生成逻辑(数据收集) + +#### 生成工作进程 (gen_worker) +- 启动: 在 rank 0 进程中使用 `torch.multiprocessing` 启动 +- 模型加载: 使用 vLLM 加载模型以高效生成文本 +- 核心循环: + - 从数据集采样问题 + - 周期性更新模型权重 + - 生成答案: + - 使用 system_prompt 格式化输入 + - 生成多个候选答案 + - 递归代码执行: 生成代码 → 执行 → 将结果反馈给模型 → 继续生成 + - 计算奖励: + - `reward_correct`: 验证答案正确性 + - `reward_format`: 检查输出格式 + - `call_python`: 根据代码执行成功次数给予奖励 + - 计算生成 Logprobs (如果启用) + - 发送数据到 ref_server + +#### 主训练进程 (Main Process) +- 分布式设置: 使用 DeepSpeed 初始化分布式环境 +- 模型加载: 加载基础模型并用 DeepSpeed 包装 +- 训练循环: + - 获取批次数据: 从 ref_server 获取处理好的数据 + - 计算 GRPO 损失: + - 对输入进行前向传播获取当前策略的 logits 和 logprobs + - 计算 KL 散度惩罚: 当前策略与参考策略之间 + - 计算策略比率: 当前策略与生成策略之间 + - 应用 PPO 裁剪目标函数 + - 最终损失: 结合裁剪目标和 KL 散度惩罚 + - 反向传播与优化 + - 周期性更新生成模型 + - 日志记录与保存 + +## 整体训练流程 + +1. **启动**: + - 启动 ref_server.py + - 启动 hjy_grpo_program.py (初始化分布式环境并启动 gen_worker) + +2. **收集数据** (gen_worker): + - 使用 vLLM 生成带有代码执行的答案 + - 计算奖励和生成 logprobs + - 将数据发送到 ref_server + +3. **参考 Logprobs 计算** (ref_server): + - 接收数据并计算参考 logprobs + - 将完整数据包放入队列 + +4. **模型训练** (主进程): + - 获取完整数据包 + - 计算 GRPO 损失 + - 使用 DeepSpeed 更新模型参数 + +5. **模型同步**: + - 主进程定期将更新后的模型权重发送给 gen_worker + +6. **循环**: + - 步骤 2-5 持续进行指定的训练步数 + +这个系统通过分离生成和训练、使用参考模型稳定训练、利用 vLLM 加速生成以及 DeepSpeed 支持分布式训练,实现了一个高效且复杂的 RL 训练系统,专门用于优化需要代码执行能力的 LLM。 +