已合并
补充 Ray PPO Readme #2111
AtomGit-Bot创建于 2024年12月27日
补充 Ray PPO Readme #2111
已合并
从refs/pull/2111/head合入到master
共 6 个文件变更+171-3
| @@ -490,7 +490,7 @@ MindSpeed-LLM当前已支持和预置大模型列表: | |||
| 490 | <td>【昇腾】</td> | 490 | <td>【昇腾】</td> |
| 491 | </tr> | 491 | </tr> |
| 492 | <tr> | 492 | <tr> |
| 493 | - <td rowspan="2">偏好对齐</td> | 493 | + <td rowspan="3">偏好对齐</td> |
| 494 | <td><a href="./docs/features/offline_dpo.md">Offline DPO</a></td> | 494 | <td><a href="./docs/features/offline_dpo.md">Offline DPO</a></td> |
| 495 | <td>✅</td> | 495 | <td>✅</td> |
| 496 | <td>❌</td> | 496 | <td>❌</td> |
| @@ -502,6 +502,12 @@ MindSpeed-LLM当前已支持和预置大模型列表: | |||
| 502 | <td>❌</td> | 502 | <td>❌</td> |
| 503 | <td>【China Mobile Cloud】</td> | 503 | <td>【China Mobile Cloud】</td> |
| 504 | </tr> | 504 | </tr> |
| 505 | +<tr> | ||
| 506 | + <td><a href="./docs/features/ray_ppo.md">Ray PPO</a></td> | ||
| 507 | + <td>✅</td> | ||
| 508 | + <td>❌</td> | ||
| 509 | + <td>【昇腾】</td> | ||
| 510 | + </tr> | ||
| 505 | <tr> | 511 | <tr> |
| 506 | <td rowspan="2">奖励模型</td> | 512 | <td rowspan="2">奖励模型</td> |
| 507 | <td><a href="./docs/features/outcome_reward_model.md">ORM</a></td> | 513 | <td><a href="./docs/features/outcome_reward_model.md">ORM</a></td> |
| @@ -0,0 +1,162 @@ | |||
| 1 | +# 后训练方法 Ray PPO | ||
| 2 | + | ||
| 3 | +[PPO(Proximal Policy Optimization)](https://arxiv.org/abs/1707.06347)是一种强化对齐微调方法,常用于人类反馈强化学习(Reinforcement Learning with Human Feedback, RLHF)任务。 | ||
| 4 | + | ||
| 5 | +PPO方法中包含了四个模型:Actor,Critic,Reference,Reward。其中Actor/Reference模型是经过预训练和指令微调(Supervised Fine-Tuning,SFT)得到的大语言模型,Critic和Reward是训练得到的奖励模型。PPO 的训练目标是使得 Actor 模型的回答可以更加符合人类偏好。 | ||
| 6 | + | ||
| 7 | +# 使用说明 | ||
| 8 | + | ||
| 9 | +## 环境配置 | ||
| 10 | + | ||
| 11 | +配置MindSpeed-LLM基础环境: 参考[MindSpeed-LLM 使用指南-前期准备](https://gitee.com/ascend/MindSpeed-LLM/blob/master/docs/USER_GUIDE.md#%E5%89%8D%E6%9C%9F%E5%87%86%E5%A4%87) | ||
| 12 | + | ||
| 13 | +## 数据预处理 | ||
| 14 | + | ||
| 15 | +数据集转换参考脚本:MindSpeed-LLM\examples\mcore\llama3\data_convert_llama3_ppo.sh | ||
| 16 | +以 [descriptiveness 数据集](https://huggingface.co/datasets/trl-internal-testing/descriptiveness-sentiment-trl-style/tree/main/data) 为例。 | ||
| 17 | + | ||
| 18 | +```bash | ||
| 19 | +source /usr/local/Ascend/ascend-toolkit/set_env.sh | ||
| 20 | +mkdir ./dataset/llama3-hf/ | ||
| 21 | + | ||
| 22 | +python ./preprocess_data.py \ | ||
| 23 | + --input ./dataset/descriptiveness-00000-of-00001.parquet \ | ||
| 24 | + --tokenizer-name-or-path ./model_from_hf/llama3-hf/ \ | ||
| 25 | + --output-prefix ./dataset/llama3-hf/descriptiveness \ | ||
| 26 | + --workers 16 \ | ||
| 27 | + --log-interval 1000 \ | ||
| 28 | + --tokenizer-type PretrainedFromHF \ | ||
| 29 | + --handler-name PPOAlpacaStyleInstructionHandler \ | ||
| 30 | + --prompt-type llama3 \ | ||
| 31 | + --map-keys '{"prompt":"prompt", "query":"", "response": "prompt", "system":""}' | ||
| 32 | +``` | ||
| 33 | + | ||
| 34 | +## 模型权重转换 | ||
| 35 | + | ||
| 36 | +根据 PPO 算法要求,Actor 和 Reference 模型应该使用 SFT 微调后的模型进行初始化,Critic 和 Reward 模型应该使用奖励模型训练后的模型进行初始化。PPO算法模型权重均使用Megatron-mcore格式,其他格式的权重需要进行模型权重转换,具体可参考[MindSpeed-LLM 使用指南-权重转换](https://gitee.com/ascend/MindSpeed-LLM/blob/master/docs/USER_GUIDE.md#%E6%9D%83%E9%87%8D%E4%B8%8B%E8%BD%BD%E5%8F%8A%E8%BD%AC%E6%8D%A2)。 | ||
| 37 | + | ||
| 38 | +## 启动方式 | ||
| 39 | + | ||
| 40 | +### 单机 | ||
| 41 | + | ||
| 42 | +通过 --config-name 传递选取的 config 文件名(不添加.yaml后缀),可以通过下列命令直接启动训练(Llama32 1B 模型可单机运行)。 | ||
| 43 | +目前已支持的配置文件放置在 configs/rlxf/ 文件夹下。配置文件的具体说明见下文。 | ||
| 44 | + | ||
| 45 | +```bash | ||
| 46 | +python ray_gpt.py --config-name ppo_trainer_llama32_1b | ||
| 47 | +``` | ||
| 48 | + | ||
| 49 | +### 多机 | ||
| 50 | + | ||
| 51 | +多机运行程序时,需要首先进入对应目录,并激活conda或docker环境: | ||
| 52 | + | ||
| 53 | +```bash | ||
| 54 | +cd MindSpeed-LLM | ||
| 55 | +conda activate xxx | ||
| 56 | +``` | ||
| 57 | + | ||
| 58 | +然后,在主节点上启动 Ray 集群: | ||
| 59 | + | ||
| 60 | +```bash | ||
| 61 | +# 配置最大文件描述符环境变量 | ||
| 62 | +ulimit -n 32768 | ||
| 63 | +# 创建一个集群,端口6344,dashboard端口8260,有8个NPU | ||
| 64 | +ray start --head --port 6344 --dashboard-host=0.0.0.0 --dashboard-port=8260 --resources='{"NPU": 8}' | ||
| 65 | +``` | ||
| 66 | + | ||
| 67 | +随后,在其他节点加入主节点的集群 | ||
| 68 | + | ||
| 69 | +```bash | ||
| 70 | +# 配置最大文件描述符环境变量 | ||
| 71 | +ulimit -n 32768 | ||
| 72 | +# IP_ADDRESS 处填写主节点 IP 地址 | ||
| 73 | +ray start --address="IP_ADDRESS:6344" --resources='{"NPU": 8}' | ||
| 74 | +``` | ||
| 75 | + | ||
| 76 | +在完成 Ray 集群构建后,在主节点启动运行程序即可(Llama3 8B 模型可双机运行) | ||
| 77 | + | ||
| 78 | +```bash | ||
| 79 | +python ray_gpt.py --config-name ppo_trainer_llama3_8b | ||
| 80 | +``` | ||
| 81 | + | ||
| 82 | +## 配置文件 | ||
| 83 | + | ||
| 84 | +由于 PPO 训练过程中涉及 4 个模型,通过将模型参数和训练配置解耦的层级化参数配置,来简化 PPO 训练的参数配置过程。RLXF 训练涉及到的所有配置文件均存储在 configs/rlxf 路径下,其中 model 文件夹下存储了模型结构相关的配置文件,PPO训练相关的模型参数文件以ppo_trainer_{模型名}.yaml方式命名。 | ||
| 85 | + | ||
| 86 | +在每个 ppo_trainer 配置文件中,需要包含defaults,training,resource_pool,algorithm等字段,以及 PPO 训练过程中涉及到的 4 个角色 actor,critic,reward,ref的配置。其中: | ||
| 87 | + | ||
| 88 | +1. defaults 负责引入模型配置文件,在 defaults 中应列举本配置文件中所需要用到的所有模型配置,模型配置可以在下方四个角色的具体配置中通过 model 字段进行选择。 | ||
| 89 | +2. training 字段设置的参数为所有 4 个角色通用的默认参数,这些参数可以在下方进一步被角色的单独配置所覆盖。 | ||
| 90 | +3. resource_pool 字段指定了各个角色所需的 NPU 资源数量。 | ||
| 91 | +4. algorithm 字段配置计算PPO中advantages算法的相关参数。 | ||
| 92 | +5. actor,critic,reward,ref 字段分别指定了PPO算法中四个角色训练相关的参数配置。 | ||
| 93 | + | ||
| 94 | +## 参数解析 | ||
| 95 | + | ||
| 96 | +相较于普通模型训练,PPO增加一些特殊参数: | ||
| 97 | + | ||
| 98 | +### `training:` | ||
| 99 | + | ||
| 100 | +* `stage`:用于指定训练算法,使用 Ray PPO 训练须设置为`ray_ppo`; | ||
| 101 | + | ||
| 102 | +### `actor_rollout:` | ||
| 103 | + | ||
| 104 | +* `do_sample`:控制 Actor 模型进行推理时是否采样,默认为 False ; | ||
| 105 | +* `ppo_mini_batch_size`:Actor 模型的 mini_batch_size,默认为1; | ||
| 106 | +* `max_prompt_length`:PPO 训练中最大 prompt 长度,默认为512; | ||
| 107 | +* `num_samples_per_step`:Actor 推理时每个step的推理样本数量,默认为1; | ||
| 108 | +* `ppo_epochs`:Actor 训练对同一批经验数据的重复次数,默认为1; | ||
| 109 | +* `clip_ratio`:Actor模型训练计算损失函数时的clip比例,默认为0.2; | ||
| 110 | +* `shuffle_minibatch`:Actor 训练时是否对 minibatch 进行 shuffle,默认为 False; | ||
| 111 | +* `num_gpus_for_train` :Actor 模型分配给训练部分的显卡数量; | ||
| 112 | +* `num_gpus_for_infer` :Actor 模型分配给推理部分的显卡数量; | ||
| 113 | + | ||
| 114 | +### `critic:` | ||
| 115 | + | ||
| 116 | +* `cliprange_value`:Critic 模型计算损失函数时 clip 范围,默认为3.0; | ||
| 117 | +* `critic_mini_batch_size`:Critic 模型设置的 mini_batch_size,默认为1; | ||
| 118 | +* `critic_update_epochs`:Critic 训练对同一批经验数据的重复次数,默认为1; | ||
| 119 | + | ||
| 120 | +### `algorithm:` | ||
| 121 | + | ||
| 122 | +* `adv_estimator`:advantages计算的方式,通常采用gae(广义优势估计Generalized Advantage Estimation, GAE); | ||
| 123 | +* `gamma`:计算 advantage 时的折扣因子; | ||
| 124 | +* `lam`:GAE 优势计算的 lambda 值; | ||
| 125 | +* `kl_penalty`:KL 散度计算方式; | ||
| 126 | +* `kl_ctrl:` | ||
| 127 | + * `kl_coef`:施加 KL 散度惩罚的系数; | ||
| 128 | + * `type`:KL 散度惩罚的系数类型; | ||
| 129 | +* `missing_eos_penalty`:缺少序列结束符EOS时的惩罚系数; | ||
| 130 | + | ||
| 131 | +### `resource_pool:` | ||
| 132 | + | ||
| 133 | +* `actor_rollout`:给 Actor 模型训练和推理总共分配的显卡数量; | ||
| 134 | +* `ref`:给 Reference 模型分配的显卡数量; | ||
| 135 | +* `critic`:给 Critic 模型分配的显卡数量; | ||
| 136 | +* `reward`:给 Reward 模型分配的显卡数量; | ||
| 137 | + | ||
| 138 | +# 精度对比 | ||
| 139 | + | ||
| 140 | +我们与 HuggingFace 的强化学习开源仓库 [TRL](https://github.com/huggingface/trl/) 进行了精度对比,来辅助验证算法实现的正确性。为了与基准方法进行精度对齐,在 Actor 推理时采用贪婪(greedy)策略去除随机性,训练过程中的 critic loss和 actor loss对比如下图所示。 | ||
| 141 | + | ||
| 142 | +<figure> | ||
| 143 | + <img src="../../sources/images/ray_ppo/llama32_1b_trl_compare_generate_response.png" alt="Image description" /> | ||
| 144 | + <figcaption style="text-align: center;">未固定 responses 时 loss 对比图 (左为 actor loss,右为 critc loss)</figcaption> | ||
| 145 | +</figure> | ||
| 146 | + | ||
| 147 | +然而,由于 greedy 方法的策略为选取 logits 最大的 token,当如果两个 token 的 logits 值十分接近时,可能会导致选取的 token 的结果产生偏差。这种误差会被多次迭代逐步累计放大,最终影响到 loss 精度对齐。 | ||
| 148 | + | ||
| 149 | +因此,我们额外补充了固定 responses 方式进行精度对齐的实验。可以看到,固定 responses 后 loss 能够较好地实现对齐。 | ||
| 150 | + | ||
| 151 | +<figure> | ||
| 152 | + <img src="../../sources/images/ray_ppo/llama32_1b_trl_compare_fixed_response.png" alt="Image description" /> | ||
| 153 | + <figcaption style="text-align: center;">固定 responses 后的 loss 对比图 (左为 actor loss,右为 critc loss)</figcaption> | ||
| 154 | +</figure> | ||
| 155 | + | ||
| 156 | +注: 为了验证 actor loss 的精度对齐效果,这里并未直接对比 PPO 算法中记录的 actor loss。这是由于 PPO 算法在计算 advantages 时,为保证算法的稳定性,会在 Actor 训练过程中在 minibatch 间做白化操作(将其分布的均值调整为0,方差调整为1)。这导致 Actor 虽然在进行梯度更新时使用每个 minibatch 计算的 loss,但记录下来的 minibatch 间的 loss 均值接近于 0 。因此,我们选择了记录 Actor 每个 minibatch loss 绝对值的均值,来验证精度对齐效果。 | ||
| 157 | + | ||
| 158 | + | ||
| 159 | +# 参考文献 | ||
| 160 | + | ||
| 161 | +[PPO](https://arxiv.org/abs/1707.06347) | ||
| 162 | + | ||
| @@ -90,7 +90,7 @@ class MegatronPPOReference(BaseTrainer): | |||
| 90 | 90 | ||
| 91 | self.timers('model-setup', log_level=0).start(barrier=True) | 91 | self.timers('model-setup', log_level=0).start(barrier=True) |
| 92 | 92 | ||
| 93 | - self.model = get_model(self.model_provider, self.model_type) | 93 | + self.model = get_model(self.model_provider, self.model_type, wrap_with_ddp=False) |
| 94 | unwrapped_model = unwrap_model(self.model) | 94 | unwrapped_model = unwrap_model(self.model) |
| 95 | if self.args.stage == "ray_online_dpo": | 95 | if self.args.stage == "ray_online_dpo": |
| 96 | self.args.micro_batch_size *= 2 | 96 | self.args.micro_batch_size *= 2 |
| @@ -93,7 +93,7 @@ class MegatronPPORM(RMTrainer): | |||
| 93 | self.args.micro_batch_size *= 2 | 93 | self.args.micro_batch_size *= 2 |
| 94 | self.timers('model-setup', log_level=0).start(barrier=True) | 94 | self.timers('model-setup', log_level=0).start(barrier=True) |
| 95 | 95 | ||
| 96 | - model = get_model(self.model_provider, self.model_type) | 96 | + model = get_model(self.model_provider, self.model_type, wrap_with_ddp=False) |
| 97 | unwrapped_model = unwrap_model(model) | 97 | unwrapped_model = unwrap_model(model) |
| 98 | 98 | ||
| 99 | if self.args.load is not None or self.args.pretrained_checkpoint is not None: | 99 | if self.args.load is not None or self.args.pretrained_checkpoint is not None: |