From 2c8b471f63d01949cb95f8ab08805274526779ab Mon Sep 17 00:00:00 2001 From: RangiLyu Date: Thu, 13 Jun 2024 13:14:00 +0800 Subject: [PATCH 1/5] rebase main --- docs/zh_cn/dpo/modify_settings.md | 83 +++++++++++++ docs/zh_cn/dpo/overview.md | 25 ++++ docs/zh_cn/dpo/quick_start.md | 71 +++++++++++ docs/zh_cn/index.rst | 17 +++ .../reward_model/images/preference_data.png | Bin 0 -> 27640 bytes .../reward_model/images/var_len_atten.png | Bin 0 -> 41318 bytes docs/zh_cn/reward_model/modify_settings.md | 100 ++++++++++++++++ docs/zh_cn/reward_model/overview.md | 29 +++++ docs/zh_cn/reward_model/preference_data.md | 110 ++++++++++++++++++ docs/zh_cn/reward_model/quick_start.md | 87 ++++++++++++++ 10 files changed, 522 insertions(+) create mode 100644 docs/zh_cn/dpo/modify_settings.md create mode 100644 docs/zh_cn/dpo/overview.md create mode 100644 docs/zh_cn/dpo/quick_start.md create mode 100644 docs/zh_cn/reward_model/images/preference_data.png create mode 100644 docs/zh_cn/reward_model/images/var_len_atten.png create mode 100644 docs/zh_cn/reward_model/modify_settings.md create mode 100644 docs/zh_cn/reward_model/overview.md create mode 100644 docs/zh_cn/reward_model/preference_data.md create mode 100644 docs/zh_cn/reward_model/quick_start.md diff --git a/docs/zh_cn/dpo/modify_settings.md b/docs/zh_cn/dpo/modify_settings.md new file mode 100644 index 000000000..abf513679 --- /dev/null +++ b/docs/zh_cn/dpo/modify_settings.md @@ -0,0 +1,83 @@ +## 修改 DPO 训练配置 + +本章节仅介绍与 DPO(Direct Preference Optimization)训练相关的配置参数,更多 XTuner 配置文件的细节,请参考[修改训练配置](https://xtuner.readthedocs.io/zh-cn/latest/training/modify_settings.html) + +### 损失函数 + +在 DPO 训练中,你可以根据需求选择不同的损失函数类型。XTuner 提供了多种损失函数选项,如 `sigmoid`、`hinge`、`ipo` 等。可以通过设置 `dpo_loss_type` 参数来选择使用的损失函数类型。 + +此外,你还可以通过调整 `loss_beta` 参数来控制损失函数中的温度系数。同时,`label_smoothing` 参数可以用于平滑标签。 + +```python +####################################################################### +# PART 1 Settings # +####################################################################### +# Model +dpo_loss_type = 'sigmoid' # One of ['sigmoid', 'hinge', 'ipo', 'kto_pair', 'sppo_hard', 'nca_pair', 'robust'] +loss_beta = 0.1 +label_smoothing = 0.0 +``` + +### 修改模型 + +用户可以修改 `pretrained_model_name_or_path` 对预训练模型进行修改。 + +```python +####################################################################### +# PART 1 Settings # +####################################################################### +# Model +pretrained_model_name_or_path = 'internlm/internlm2-chat-1_8b-sft' +``` + +### 训练数据 + +在 Reward Model 训练中,你可以通过 `max_length` 来指定单个样本序列的最大 token 数,XTuner 会自动对数据进行截断或是填充。 + +```python +# Data +max_length = 2048 +``` + +在配置文件中,我们通过 `train_dataset` 字段来指定训练数据集,你可以通过 `dataset` 字段指定数据集的加载方式,通过 `dataset_map_fn` 字段指定数据集的映射函数。 + +```python +####################################################################### +# PART 3 Dataset & Dataloader # +####################################################################### +sampler = SequenceParallelSampler \ + if sequence_parallel_size > 1 else DefaultSampler + +train_dataset = dict( + type=build_preference_dataset, + dataset=dict(type=load_dataset, path='mlabonne/orpo-dpo-mix-40k'), + tokenizer=tokenizer, + max_length=max_length, + dataset_map_fn=orpo_dpo_mix_40k_map_fn, + is_dpo=True, + is_reward=False, + reward_token_id=-1, + num_proc=32, + use_varlen_attn=use_varlen_attn, + max_packed_length=max_packed_length, + shuffle_before_pack=True, +) + +train_dataloader = dict( + batch_size=batch_size, + num_workers=dataloader_num_workers, + dataset=train_dataset, + sampler=dict(type=sampler, shuffle=True), + collate_fn=dict( + type=preference_collate_fn, use_varlen_attn=use_varlen_attn)) +``` + +上述配置中,我们使用了 `load_dataset` 来加载 huggingface 上的 `mlabonne/orpo-dpo-mix-40k` 数据集,使用 `orpo_dpo_mix_40k_map_fn` 作为数据集映射函数。 + +关于如何处理数据集以及如何编写数据集映射函数,请参考[偏好数据集章节](./preference_data.md)。 + +### 加速训练 + +在使用偏好数据训练时,我们推荐您开启[变长注意力机制](https://xtuner.readthedocs.io/zh-cn/latest/acceleration/varlen_flash_attn.html), 以避免单个偏好内的 chosen 和 rejected 的样本长度差异造成的显存浪费。你可以通过 `use_varlen_attn=True` 来开启变长注意力机制。 + +XTuner 中还支持了大量的训练加速方法,关于它们的使用方法,请参考[加速策略章节](https://xtuner.readthedocs.io/zh-cn/latest/acceleration/hyper_parameters.html)。 diff --git a/docs/zh_cn/dpo/overview.md b/docs/zh_cn/dpo/overview.md new file mode 100644 index 000000000..ab6e9c706 --- /dev/null +++ b/docs/zh_cn/dpo/overview.md @@ -0,0 +1,25 @@ +## DPO 介绍 + +### 简介 + +DPO(Direct Preference Optimization,直接偏好优化)是一种在大语言模型训练中用于直接优化模型偏好的方法。与传统的强化学习方法不同,DPO 直接使用人类偏好数据进行模型优化,从而提高生成内容的质量,使其更符合人类偏好。DPO 利用人类偏好数据,直接对模型进行优化,省略了训练Reward Model的训练过程,与PPO相比进一步省去了Critic Model,不但避免了复杂的强化学习算法,减少了训练开销,同时还提高了训练效率。 + +DPO 拥有大量的衍生算法,它们对DPO的损失函数进行了一定程度上的改进,我们在XTuner中除了DPO还实现了[Identity Preference Optimisation (IPO)](https://huggingface.co/papers/2310.12036),[Kahneman-Tversky Optimisation (KTO)](https://github.com/ContextualAI/HALOs)等论文中的损失函数,如需使用这些算法,请参考[修改DPO配置](./modify_settings.md)章节。 + +除了 DPO 之外,还出现了如 [ORPO](https://arxiv.org/abs/2403.07691) 等无需参考模型的对齐算法。ORPO 采用了对数比值(odds ratio)的概念来优化模型,通过在模型训练过程中惩罚那些被拒绝的样本,从而更有效地适应被选择的样本。ORPO 消除了对参考模型的依赖,使得训练过程更加简化且高效。 + +### XTuner 中 DPO 训练的优势 + +XTuner 中的 DPO 训练具备以下显著优势: + +1. **支持最新的算法**:XTuner除了支持标准的DPO之外,还支持了大量的衍生算法,同时也支持ORPO等不依赖参考模型的高效算法。 + +2. **减少显存浪费**:由于偏好数据中的 chosen 和 rejected 数据通常存在长度上的差异,因此在训练数据的拼接时会存在填充(padding token),造成显存浪费。在 XTuner 中,基于 Flash Attention2 中的[变长注意力](<>)功能,我们在训练过程中通过将偏好数据打包到同一个序列中,显著减少了由于 padding token 带来的显存浪费。这不仅提高了显存的利用效率,还使得在相同硬件条件下可以训练更大的模型或处理更多的数据。 + +![img](../reward_model/images/var_len_atten.png) + +3. **高效训练**:借助 XTuner 的 QLoRA 训练功能,参考模型能够被转化为移除LoRA适配器的语言模型,从而省去了参考模型权重的显存占用,大幅降低了DPO的训练开销。 + +### 开始训练 + +请参阅[快速上手](./quick_start.md)来了解最基本的概念,若希望了解更多训练参数配置相关的内容,请参考[修改DPO配置](./modify_settings.md)章节。 diff --git a/docs/zh_cn/dpo/quick_start.md b/docs/zh_cn/dpo/quick_start.md new file mode 100644 index 000000000..54a3911ca --- /dev/null +++ b/docs/zh_cn/dpo/quick_start.md @@ -0,0 +1,71 @@ +## DPO 快速上手 + +在本章节中,我们将介绍如何使用 XTuner 训练 1.8B 的 DPO(Direct Preference Optimization)模型,以帮助您快速上手。 + +### 准备预训练模型权重 + +我们使用经过 SFT 的语言模型[InternLM2-chat-1.8b-sft](<>)作为 DPO 模型的初始化模型来进行偏好对齐。 + +在训练配置文件中设置`pretrained_model_name_or_path = 'internlm/internlm2-chat-1_8b-sft'`,则会在启动训练时自动下载模型文件。若您需要手动下载模型权重,那么请参考[准备预训练模型权重](<>)章节,其中详细说明了如何从 Huggingface 或者是 Modelscope 下载模型权重的方法。这里我们附上模型的 HuggingFace 链接与 ModelScope 链接: + +- HuggingFace 链接位于:https://huggingface.co/internlm/internlm2-chat-1_8b-sft +- ModelScope 链接位于:https://modelscope.cn/models/Shanghai_AI_Laboratory/internlm2-chat-1_8b-sft/summary + +### 准备训练数据 + +在本教程中使用 Huggingface 上的[mlabonne/orpo-dpo-mix-40k](<>)数据集作为演示, + +```python +train_dataset = dict( + type=build_preference_dataset, + dataset=dict( + type=load_dataset, + path='mlabonne/orpo-dpo-mix-40k'), + dataset_map_fn=orpo_dpo_mix_40k_map_fn, + is_dpo=True, + is_reward=False, +) +``` + +在配置文件中使用以上配置,即可自动下载并处理该数据集。如果您希望使用其他 Huggingface 上的开源数据集或是使用自定义的数据集,请参阅[偏好数据集](../reward_model/preference_data.md)章节。 + +### 准备配置文件 + +XTuner 提供了多个开箱即用的配置文件,可以通过 `xtuner list-cfg` 查看。我们执行如下指令,以复制一个配置文件到当前目录。 + +```bash +xtuner copy-cfg internlm2_chat_1_8b_dpo_full . +``` + +打开复制后的配置文件,如果您选择自动下载模型和数据集,则无需修改配置。若您希望填入您预先下载的模型路径和数据集路径,请修改配置中的`pretrained_model_name_or_path`以及`train_dataset`中`dataset`的`path`参数。 + +更多的训练参数配置,请参阅[修改DPO训练配置](./modify_settings.md)章节。 + +### 启动训练 + +在完成上述操作后,便可以使用下面的指令启动训练任务了。 + +```bash +# 单机单卡 +xtuner train ./internlm2_chat_1_8b_dpo_full_copy.py +# 单机多卡 +NPROC_PER_NODE=${GPU_NUM} xtuner train ./internlm2_chat_1_8b_dpo_full_copy.py +# slurm 集群 +srun ${SRUN_ARGS} xtuner train ./internlm2_chat_1_8b_dpo_full_copy.py --launcher slurm +``` + +### 模型转换 + +XTuner 已经集成好了将模型转换为 HuggingFace 格式的工具,我们只需要执行 + +```bash +# 创建存放 hf 格式参数的目录 +mkdir work_dirs/internlm2_chat_1_8b_dpo_full_copy/iter_15230_hf + +# 转换格式 +xtuner convert pth_to_hf internlm2_chat_1_8b_dpo_full_copy.py \ + work_dirs/internlm2_chat_1_8b_dpo_full_copy.py/iter_15230.pth \ + work_dirs/internlm2_chat_1_8b_dpo_full_copy.py/iter_15230_hf +``` + +便能够将 XTuner 的 ckpt 转换为 Huggingface 格式的模型。 diff --git a/docs/zh_cn/index.rst b/docs/zh_cn/index.rst index afe6e1c76..4acf0e882 100644 --- a/docs/zh_cn/index.rst +++ b/docs/zh_cn/index.rst @@ -55,6 +55,23 @@ training/modify_settings.rst training/visualization.rst +.. toctree:: + :maxdepth: 2 + :caption: DPO + + dpo/overview.md + dpo/quick_start.md + dpo/modify_settings.md + +.. toctree:: + :maxdepth: 2 + :caption: Reward Model + + reward_model/overview.md + reward_model/quick_start.md + reward_model/modify_settings.md + reward_model/preference_data.md + .. toctree:: :maxdepth: 2 :caption: 加速训练 diff --git a/docs/zh_cn/reward_model/images/preference_data.png b/docs/zh_cn/reward_model/images/preference_data.png new file mode 100644 index 0000000000000000000000000000000000000000..a18ea64497b35cc5838febf3005c3bca9ec9ce7a GIT binary patch literal 27640 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 z

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 SNbp53h`h8Sv`ES*;J*M|;f&k> literal 0 HcmV?d00001 diff --git a/docs/zh_cn/reward_model/images/var_len_atten.png b/docs/zh_cn/reward_model/images/var_len_atten.png new file mode 100644 index 0000000000000000000000000000000000000000..3e60777d2063d925176799f49a1e821a24ff0b2f GIT binary patch literal 41318 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 yE&NZLEg*Z(@$vC-Y%R^c{V#R#KiDk$H-X) 作为 Reward Model 的偏好建模方式,你可以指定 `loss_type="ranking"` 来使用 ranking loss。XTuner 中也实现了 InternLM2 中提出的 focal 损失函数,它通过调整难易样本的权重来避免过拟合,可以设置 `loss_type="focal"` 来使用该损失函数。对于该损失函数的详细说明,请参考 [InternLM2 技术报告](https://arxiv.org/abs/2403.17297)。 + +另外,为了使 reward model 输出的 score 数值保持稳定,我们还在 loss 中额外增加了一个约束项,你可以指定 `penalty_type='log_barrier'` 或是 `penalty_type='L2'` 以启用对数约束或是L2约束。 + +```python +####################################################################### +# PART 1 Settings # +####################################################################### +# Model +loss_type = 'focal' # 'ranking' or 'focal' +penalty_type = 'log_barrier' # 'log_barrier' or 'L2' +``` + +### 修改模型 + +用户可以修改 `pretrained_model_name_or_path` 对预训练模型进行修改。 + +需要注意的是,由于 XTuner 通过对数据的末尾添加 `<|reward|>` 特殊 token 的方式计算 reward 得分,因此当切换模型的词表发生变化时,该特殊 token 的 id 也需要进行相应的修改,我们通常会使用词表末尾未使用的 token 作为 reward token。 + +例如,在 InternLM2 中我们使用 `[UNUSED_TOKEN_130]` 作为 reward token: + +```python +####################################################################### +# PART 1 Settings # +####################################################################### +# Model +pretrained_model_name_or_path = 'internlm/internlm2-chat-1_8b-sft' +reward_token_id = 92527 # use [UNUSED_TOKEN_130] as reward token +``` + +如果用户将模型切换为llama3,我们则可以使用 `<|reserved_special_token_0|>` 作为 reward token: + +```python +####################################################################### +# PART 1 Settings # +####################################################################### +# Model +pretrained_model_name_or_path = 'meta-llama/Meta-Llama-3-8B-Instruct' +reward_token_id = 128002 # use <|reserved_special_token_0|> as reward token +``` + +### 训练数据 + +在 Reward Model 训练中,你可以通过 `max_length` 来指定单个样本序列的最大 token 数,XTuner 会自动对数据进行截断或是填充。 + +```python +# Data +max_length = 2048 +``` + +在配置文件中,我们通过 `train_dataset` 字段来指定训练数据集,你可以通过 `dataset` 字段指定数据集的加载方式,通过 `dataset_map_fn` 字段指定数据集的映射函数。 + +```python +####################################################################### +# PART 3 Dataset & Dataloader # +####################################################################### +sampler = SequenceParallelSampler \ + if sequence_parallel_size > 1 else DefaultSampler + +train_dataset = dict( + type=build_preference_dataset, + dataset=dict( + type=load_dataset, + path='argilla/ultrafeedback-binarized-preferences-cleaned'), + tokenizer=tokenizer, + max_length=max_length, + dataset_map_fn=orpo_dpo_mix_40k_map_fn, + is_dpo=False, + is_reward=True, + reward_token_id=reward_token_id, + num_proc=32, + use_varlen_attn=use_varlen_attn, + max_packed_length=max_packed_length, + shuffle_before_pack=True, +) + +train_dataloader = dict( + batch_size=batch_size, + num_workers=dataloader_num_workers, + dataset=train_dataset, + sampler=dict(type=sampler, shuffle=True), + collate_fn=dict( + type=preference_collate_fn, use_varlen_attn=use_varlen_attn)) +``` + +上述配置中,我们使用了 `load_dataset` 来加载 huggingface 上的 `argilla/ultrafeedback-binarized-preferences-cleaned` 数据集,使用 `orpo_dpo_mix_40k_map_fn` 作为数据集映射函数(这是因为 `orpo_dpo_mix_40k` 与 `ultrafeedback-binarized-preferences-cleaned` 的格式相同,因此这里共用了同一个映射函数)。 + +关于如何处理数据集以及如何编写数据集映射函数,请参考[偏好数据集章节](./preference_data.md)。 + +### 加速训练 + +在使用偏好数据训练时,我们推荐您开启[变长注意力机制](https://xtuner.readthedocs.io/zh-cn/latest/acceleration/varlen_flash_attn.html), 以避免单个偏好内的 chosen 和 rejected 的样本长度差异造成的显存浪费。你可以通过 `use_varlen_attn=True` 来开启变长注意力机制。 + +XTuner 中还支持了大量的训练加速方法,关于它们的使用方法,请参考[加速策略章节](https://xtuner.readthedocs.io/zh-cn/latest/acceleration/hyper_parameters.html)。 diff --git a/docs/zh_cn/reward_model/overview.md b/docs/zh_cn/reward_model/overview.md new file mode 100644 index 000000000..2fc535102 --- /dev/null +++ b/docs/zh_cn/reward_model/overview.md @@ -0,0 +1,29 @@ +## Reward Model介绍 + +### 简介 + +Reward Model(奖励模型)是强化学习过程中一个关键的组成部分。它的主要任务是根据给定的输入和反馈来预测奖励值,从而指导学习算法的方向。在RLHF(Reinforcement Learning from Human Feedback)中,Reward Model 通过整合人类反馈,帮助强化学习算法更有效地优化策略。 + +在大语言模型训练中,Reward Model 通常指的是偏好模型(Preference Model)。通过在训练时提供相同提示词的好与坏(chosen&rejected)的回复来拟合人类的偏好,并在推理时预测出一个奖励值,以指导 RLHF 过程中 Actor 模型的优化过程。 + +Reward Model的应用场景包括但不限于: + +- **RLHF训练**:在使用 Proximal Policy Optimization(PPO)算法进行 RLHF 训练时,Reward Model提供奖励信号,指导模型优化策略,提高生成内容的质量并使其更贴近人类偏好。 +- **BoN采样**:在 Best-of-N(BoN)采样过程中,用户可以使用 Reward Model 对同一个提示词的多条回复进行打分,并选择奖励得分最高的生成结果,从而提升模型的输出效果。 +- **数据构造**:Reward Model 可以用于评估和过滤训练数据,或者也可以使用 Reward Model 替代人工标注来构造 DPO 训练数据。 + +### XTuner 中 Reward Model 训练的优势 + +XTuner 中的 Reward Model 训练具备以下显著优势: + +1. **使用最新的训练技巧**:XTuner 中集成了 InternLM2 中的 Reward Model 训练损失函数,可以稳定奖励得分的数值范围,也可以减少在简单样本上的过拟合(具体可参考[InternLM2技术报告](https://arxiv.org/abs/2403.17297))。 + +2. **减少显存浪费**:由于偏好数据中的 chosen 和 rejected 数据通常存在长度上的差异,因此在训练数据的拼接时会存在填充(padding token),造成显存浪费。在 XTuner 中,基于 Flash Attention2 中的变长注意力功能,我们在训练过程中通过将偏好数据打包到同一个序列中,显著减少了由于 padding token 带来的显存浪费。这不仅提高了显存的利用效率,还使得在相同硬件条件下可以训练更大的模型或处理更多的数据。 + +![img](./images/var_len_atten.png) + +3. **高效训练**:借助 XTuner 的 QLoRA 训练功能,我们能够仅对 Reward Model 的 Value Head 进行全参数训练,而对语言模型本身使用 QLoRA 微调,大幅降低了模型训练的显存开销。 + +### 开始训练 + +请参[阅快速上手](./quick_start.md)来了解最基本的概念,若希望了解更多训练参数配置相关的内容,请参考[修改Reward Model配置](./modify_settings.md)章节。 diff --git a/docs/zh_cn/reward_model/preference_data.md b/docs/zh_cn/reward_model/preference_data.md new file mode 100644 index 000000000..1dd296053 --- /dev/null +++ b/docs/zh_cn/reward_model/preference_data.md @@ -0,0 +1,110 @@ +## 偏好数据集 + +### 简介 + +XTuner 的 Reward Model 与 DPO、ORPO 等依赖偏好数据的算法都采用了同样的数据格式,偏好数据集中的每一条训练样本需要包含以下三个字段:`prompt`、`chosen`、`rejected`。其中每个字段的值都使用了 [OpenAI chat message](https://platform.openai.com/docs/api-reference/chat/create) 格式。一个具体的例子如下所示: + +```json +{ + "prompt": [ + { + "role": "system", + "content": "You are a helpful assistant." + }, + { + "role": "user", + "content": "Who won the world series in 2020?" + }, + { + "role": "assistant", + "content": "The Los Angeles Dodgers won the World Series in 2020." + }, + { + "role": "user", + "content": "Where was it played?" + } + ], + "chosen": [ + { + "role": "assistant", + "content": "The 2020 World Series was played at Globe Life Field in Arlington, Texas." + } + ], + "rejected": [ + { + "role": "assistant", + "content": "I don't know." + } + ] +} +``` + +当进行 Reward Model 训练或是 DPO 训练时,xtuner 会根据训练任务类型的不同,将偏好数据集处理为不同的训练标签。 + +![img](./images/preference_data.png) + +如上图所示,当进行 Reward Model 训练时,我们参考 ChatGPT 的训练方式,在对话数据的最后添加一个特殊的`<|reward|>` token,只对该 token 输出的 logits 计算损失。而当进行 DPO 系列算法的训练时,我们则会屏蔽掉 prompt 部分的 token,只对 chosen 和 rejected 的回复部分计算损失。在配置文件中,我们通过 dataset 中的 `is_reward` 和 `is_dpo` 两个字段来控制数据集的类型。 + +### 使用自定义数据进行训练 + +在理解了单个样本的数据格式之后,我们只需要将转换好的数据保存为[JSON Lines](https://jsonlines.org/)格式即可。文件中的每一行都是一个样本。 + +```json +{"prompt": [{"content": "How can I learn to better regulate my emotions as a grown adult?", "role": "user"}], "chosen": [{"content": "You could do emotional regulation exercises with me, and try to notice and practice ways that you could better control your emotions in real life.", "role": "assistant"}], "rejected": [{"content": "Do you mean how can you learn to be more emotionally mature?", "role": "assistant"}]} +{"prompt": [{"content": "What are some interesting things I can add to a grilled cheese sandwich?", "role": "user"}, {"content": "A tomato, a slice of pickle, some chopped pickles, some chopped green onions. Also, some mayonnaise if you like.", "role": "assistant"}, {"content": "Sounds great. Anything else?", "role": "user"}], "chosen": [{"content": "If you’d like something a little different, I recommend grating some horseradish on top of the sandwich.", "role": "assistant"}], "rejected": [{"content": "Maybe some nice fresh basil?", "role": "assistant"}]} +{"prompt": [{"content": "How do I bob for apples?", "role": "user"}], "chosen": [{"content": "Ah! Do you mean the game of “applesauce,” or do you mean the idea of “to bob for apples”?", "role": "assistant"}], "rejected": [{"content": "Sorry, I don’t know that term.", "role": "assistant"}]} +...... +``` + +在准备完自定义数据集之后,你需要将你保存的数据路径填入配置文件中的 `data_files` 字段,你可以同时加载多个 jsonl 数据进行训练。 + +```python +####################################################################### +# PART 3 Dataset & Dataloader # +####################################################################### +train_dataset = dict( + type=build_preference_dataset, + dataset=dict( + type=load_jsonl_dataset, + data_files=[ + '/your/jsonl/path/here.jsonl', + '/your/another/jsonl/path/here.jsonl' + ]), +) +``` + +### 使用开源数据集进行训练 + +与 XTuner 配置 SFT 数据一样,在使用 huggingface 上的开源数据集时,我们只需要定义映射函数 map_fn,将开源数据集格式处理为 XTuner 中的数据格式即可。 + +这里我们以 Intel/orca_dpo_pairs 为例,该数据集有 `system`、`question`、`chosen`、`rejected` 四个字段,并且每个字段的值为 text 而非 [OpenAI chat message](https://platform.openai.com/docs/api-reference/chat/create) 格式。因此我们需要为该数据集定义一个 map_fn: + +```python +def intel_orca_dpo_map_fn(example): + prompt = [{ + 'role': 'system', + 'content': example['system'] + }, { + 'role': 'user', + 'content': example['question'] + }] + chosen = [{'role': 'assistant', 'content': example['chosen']}] + rejected = [{'role': 'assistant', 'content': example['rejected']}] + return {'prompt': prompt, 'chosen': chosen, 'rejected': rejected} +``` + +通过代码可以看到,`intel_orca_dpo_map_fn` 对原数据中的四个字段进行处理,将其转换为了 `prompt`、`chosen`、`rejected` 三个字段,并且每个字段都处理为了[OpenAI chat message](https://platform.openai.com/docs/api-reference/chat/create) 格式,确保了后续数据处理流程的统一。 + +完成了 map_fn 的定义之后,需要在配置文件中 import 该函数,并在 `dataset_map_fn` 字段中进行配置。 + +```python +train_dataset = dict( + type=build_preference_dataset, + dataset=dict( + type=load_dataset, + path='Intel/orca_dpo_pairs'), + tokenizer=tokenizer, + max_length=max_length, + dataset_map_fn=intel_orca_dpo_map_fn, +) +``` diff --git a/docs/zh_cn/reward_model/quick_start.md b/docs/zh_cn/reward_model/quick_start.md new file mode 100644 index 000000000..b162638db --- /dev/null +++ b/docs/zh_cn/reward_model/quick_start.md @@ -0,0 +1,87 @@ +## Reward Model 快速上手 + +在本章节中,我们将介绍如何使用 XTuner 训练 1.8B 的 Reward Model,以帮助您快速上手。 + +### 准备预训练模型权重 + +依据[Training language models to follow instructions with human feedback](https://arxiv.org/abs/2203.02155)论文中的描述,我们使用进过 SFT 的语言模型作为Reward Model的初始化模型。这里我们使用[InternLM2-chat-1.8b-sft](<>)作为初始化模型。 + +在训练配置文件中设置`pretrained_model_name_or_path = 'internlm/internlm2-chat-1_8b-sft'`,则会在启动训练时自动下载模型文件。若您需要手动下载模型权重,那么请参考[准备预训练模型权重](<>)章节,其中详细说明了如何从Huggingface或者是Modelscope下载模型权重的方法。这里我们附上模型的 HuggingFace 链接与 ModelScope 链接: + +- HuggingFace 链接位于:https://huggingface.co/internlm/internlm2-chat-1_8b-sft + +- ModelScope 链接位于:https://modelscope.cn/models/Shanghai_AI_Laboratory/internlm2-chat-1_8b-sft/summary + +### 准备训练数据 + +在本教程中使用[UltraFeedback](<>)数据集作为演示,为了方便起见,我们使用huggingface上已经预处理过的[argilla/ultrafeedback-binarized-preferences-cleaned](<>)数据集, + +```python +train_dataset = dict( + type=build_preference_dataset, + dataset=dict( + type=load_dataset, + path='argilla/ultrafeedback-binarized-preferences-cleaned'), + dataset_map_fn=orpo_dpo_mix_40k_map_fn, + is_dpo=False, + is_reward=True, +) +``` + +在配置文件中使用以上配置,即可自动下载并处理该数据集。如果您希望使用其他huggingface上的开源数据集或是使用自定义的数据集,请参阅[偏好数据集](<>)章节。 + +### 准备配置文件 + +XTuner 提供了多个开箱即用的配置文件,可以通过 `xtuner list-cfg` 查看。我们执行如下指令,以复制一个配置文件到当前目录。 + +```bash +xtuner copy-cfg internlm2_chat_1_8b_reward_full_ultrafeedback . +``` + +打开复制后的配置文件,如果您选择自动下载模型和数据集,则无需修改配置。若您希望填入您预先下载的模型路径和数据集路径,请修改配置中的`pretrained_model_name_or_path`以及`train_dataset`中`dataset`的`path`参数。 + +更多的训练参数配置,请参阅[修改Reward训练配置](<>)章节。 + +### 启动训练 + +在完成上述操作后,便可以使用下面的指令启动训练任务了。 + +```bash +# 单机单卡 +xtuner train ./internlm2_chat_1_8b_reward_full_ultrafeedback_copy.py +# 单机多卡 +NPROC_PER_NODE=${GPU_NUM} xtuner train ./internlm2_chat_1_8b_reward_full_ultrafeedback_copy.py +# slurm 集群 +srun ${SRUN_ARGS} xtuner train ./internlm2_chat_1_8b_reward_full_ultrafeedback_copy.py --launcher slurm +``` + +正确的训练日志应当如下所示: + +``` +06/06 16:12:11 - mmengine - INFO - Iter(train) [ 10/15230] lr: 3.9580e-07 eta: 2:59:41 time: 0.7084 data_time: 0.0044 memory: 18021 loss: 0.6270 acc: 0.0000 chosen_score_mean: 0.0000 rejected_score_mean: 0.0000 num_samples: 4.0000 num_tokens: 969.0000 +06/06 16:12:17 - mmengine - INFO - Iter(train) [ 20/15230] lr: 8.3536e-07 eta: 2:45:25 time: 0.5968 data_time: 0.0034 memory: 42180 loss: 0.6270 acc: 0.5000 chosen_score_mean: 0.0013 rejected_score_mean: 0.0010 num_samples: 4.0000 num_tokens: 1405.0000 +06/06 16:12:22 - mmengine - INFO - Iter(train) [ 30/15230] lr: 1.2749e-06 eta: 2:37:18 time: 0.5578 data_time: 0.0024 memory: 32121 loss: 0.6270 acc: 0.7500 chosen_score_mean: 0.0016 rejected_score_mean: 0.0011 num_samples: 4.0000 num_tokens: 932.0000 +06/06 16:12:28 - mmengine - INFO - Iter(train) [ 40/15230] lr: 1.7145e-06 eta: 2:36:05 time: 0.6033 data_time: 0.0025 memory: 42186 loss: 0.6270 acc: 0.7500 chosen_score_mean: 0.0027 rejected_score_mean: 0.0016 num_samples: 4.0000 num_tokens: 994.0000 +06/06 16:12:35 - mmengine - INFO - Iter(train) [ 50/15230] lr: 2.1540e-06 eta: 2:41:03 time: 0.7166 data_time: 0.0027 memory: 42186 loss: 0.6278 acc: 0.5000 chosen_score_mean: 0.0031 rejected_score_mean: 0.0032 num_samples: 4.0000 num_tokens: 2049.0000 +06/06 16:12:40 - mmengine - INFO - Iter(train) [ 60/15230] lr: 2.5936e-06 eta: 2:33:37 time: 0.4627 data_time: 0.0023 memory: 30238 loss: 0.6262 acc: 1.0000 chosen_score_mean: 0.0057 rejected_score_mean: 0.0030 num_samples: 4.0000 num_tokens: 992.0000 +06/06 16:12:46 - mmengine - INFO - Iter(train) [ 70/15230] lr: 3.0331e-06 eta: 2:33:18 time: 0.6018 data_time: 0.0025 memory: 42186 loss: 0.6247 acc: 0.7500 chosen_score_mean: 0.0117 rejected_score_mean: 0.0055 num_samples: 4.0000 num_tokens: 815.0000 + +``` + +### 模型转换 + +XTuner 已经集成好了将模型转换为 HuggingFace 格式的工具,我们只需要执行 + +```bash +# 创建存放 hf 格式参数的目录 +mkdir work_dirs/internlm2_chat_1_8b_reward_full_ultrafeedback_copy/iter_15230_hf + +# 转换格式 +xtuner convert pth_to_hf internlm2_chat_1_8b_reward_full_ultrafeedback_copy.py.py \ + work_dirs/internlm2_chat_1_8b_reward_full_ultrafeedback_copy.py/iter_15230.pth \ + work_dirs/internlm2_chat_1_8b_reward_full_ultrafeedback_copy.py/iter_15230_hf +``` + +便能够将 XTuner 的 ckpt 转换为 Huggingface 格式的模型。 + +需要注意的是,由于 Reward Model 的类型并未在 transformers 官方库中集成,因此目前只有InternLM2模型训练得到的 Reward Model 会被转换为 InternLM2ForRewardModel 类型,而其他模型则会默认转换为 SequenceClassification 类型(例如 LLaMa3 会被转换为 LlamaForSequenceClassification 类型),但这并不影响其在 XTuner PPO 训练中的使用。 From 87fac1e4afafb5e915fb9f81dc55f944a191632c Mon Sep 17 00:00:00 2001 From: RangiLyu Date: Thu, 13 Jun 2024 14:02:25 +0800 Subject: [PATCH 2/5] refine link --- docs/zh_cn/dpo/overview.md | 2 +- docs/zh_cn/dpo/quick_start.md | 6 +++--- docs/zh_cn/reward_model/modify_settings.md | 2 +- docs/zh_cn/reward_model/quick_start.md | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/zh_cn/dpo/overview.md b/docs/zh_cn/dpo/overview.md index ab6e9c706..6bd19da67 100644 --- a/docs/zh_cn/dpo/overview.md +++ b/docs/zh_cn/dpo/overview.md @@ -14,7 +14,7 @@ XTuner 中的 DPO 训练具备以下显著优势: 1. **支持最新的算法**:XTuner除了支持标准的DPO之外,还支持了大量的衍生算法,同时也支持ORPO等不依赖参考模型的高效算法。 -2. **减少显存浪费**:由于偏好数据中的 chosen 和 rejected 数据通常存在长度上的差异,因此在训练数据的拼接时会存在填充(padding token),造成显存浪费。在 XTuner 中,基于 Flash Attention2 中的[变长注意力](<>)功能,我们在训练过程中通过将偏好数据打包到同一个序列中,显著减少了由于 padding token 带来的显存浪费。这不仅提高了显存的利用效率,还使得在相同硬件条件下可以训练更大的模型或处理更多的数据。 +2. **减少显存浪费**:由于偏好数据中的 chosen 和 rejected 数据通常存在长度上的差异,因此在训练数据的拼接时会存在填充(padding token),造成显存浪费。在 XTuner 中,基于 Flash Attention2 中的[变长注意力](https://xtuner.readthedocs.io/zh-cn/latest/acceleration/varlen_flash_attn.html)功能,我们在训练过程中通过将偏好数据打包到同一个序列中,显著减少了由于 padding token 带来的显存浪费。这不仅提高了显存的利用效率,还使得在相同硬件条件下可以训练更大的模型或处理更多的数据。 ![img](../reward_model/images/var_len_atten.png) diff --git a/docs/zh_cn/dpo/quick_start.md b/docs/zh_cn/dpo/quick_start.md index 54a3911ca..a92152b0f 100644 --- a/docs/zh_cn/dpo/quick_start.md +++ b/docs/zh_cn/dpo/quick_start.md @@ -4,16 +4,16 @@ ### 准备预训练模型权重 -我们使用经过 SFT 的语言模型[InternLM2-chat-1.8b-sft](<>)作为 DPO 模型的初始化模型来进行偏好对齐。 +我们使用经过 SFT 的语言模型[InternLM2-chat-1.8b-sft](https://huggingface.co/internlm/internlm2-chat-1_8b-sft)作为 DPO 模型的初始化模型来进行偏好对齐。 -在训练配置文件中设置`pretrained_model_name_or_path = 'internlm/internlm2-chat-1_8b-sft'`,则会在启动训练时自动下载模型文件。若您需要手动下载模型权重,那么请参考[准备预训练模型权重](<>)章节,其中详细说明了如何从 Huggingface 或者是 Modelscope 下载模型权重的方法。这里我们附上模型的 HuggingFace 链接与 ModelScope 链接: +在训练配置文件中设置`pretrained_model_name_or_path = 'internlm/internlm2-chat-1_8b-sft'`,则会在启动训练时自动下载模型文件。若您需要手动下载模型权重,那么请参考[准备预训练模型权重](https://xtuner.readthedocs.io/zh-cn/latest/preparation/pretrained_model.html)章节,其中详细说明了如何从 Huggingface 或者是 Modelscope 下载模型权重的方法。这里我们附上模型的 HuggingFace 链接与 ModelScope 链接: - HuggingFace 链接位于:https://huggingface.co/internlm/internlm2-chat-1_8b-sft - ModelScope 链接位于:https://modelscope.cn/models/Shanghai_AI_Laboratory/internlm2-chat-1_8b-sft/summary ### 准备训练数据 -在本教程中使用 Huggingface 上的[mlabonne/orpo-dpo-mix-40k](<>)数据集作为演示, +在本教程中使用 Huggingface 上的[mlabonne/orpo-dpo-mix-40k](https://huggingface.co/datasets/mlabonne/orpo-dpo-mix-40k)数据集作为演示, ```python train_dataset = dict( diff --git a/docs/zh_cn/reward_model/modify_settings.md b/docs/zh_cn/reward_model/modify_settings.md index 961236010..c56b04115 100644 --- a/docs/zh_cn/reward_model/modify_settings.md +++ b/docs/zh_cn/reward_model/modify_settings.md @@ -4,7 +4,7 @@ ### 损失函数 -XTuner 使用了 [Bradley–Terry 模型](<>) 作为 Reward Model 的偏好建模方式,你可以指定 `loss_type="ranking"` 来使用 ranking loss。XTuner 中也实现了 InternLM2 中提出的 focal 损失函数,它通过调整难易样本的权重来避免过拟合,可以设置 `loss_type="focal"` 来使用该损失函数。对于该损失函数的详细说明,请参考 [InternLM2 技术报告](https://arxiv.org/abs/2403.17297)。 +XTuner 使用了 [Bradley–Terry 模型](https://en.wikipedia.org/wiki/Bradley%E2%80%93Terry_model) 作为 Reward Model 的偏好建模方式,你可以指定 `loss_type="ranking"` 来使用 ranking loss。XTuner 中也实现了 InternLM2 中提出的 focal 损失函数,它通过调整难易样本的权重来避免过拟合,可以设置 `loss_type="focal"` 来使用该损失函数。对于该损失函数的详细说明,请参考 [InternLM2 技术报告](https://arxiv.org/abs/2403.17297)。 另外,为了使 reward model 输出的 score 数值保持稳定,我们还在 loss 中额外增加了一个约束项,你可以指定 `penalty_type='log_barrier'` 或是 `penalty_type='L2'` 以启用对数约束或是L2约束。 diff --git a/docs/zh_cn/reward_model/quick_start.md b/docs/zh_cn/reward_model/quick_start.md index b162638db..21c6efc37 100644 --- a/docs/zh_cn/reward_model/quick_start.md +++ b/docs/zh_cn/reward_model/quick_start.md @@ -4,9 +4,9 @@ ### 准备预训练模型权重 -依据[Training language models to follow instructions with human feedback](https://arxiv.org/abs/2203.02155)论文中的描述,我们使用进过 SFT 的语言模型作为Reward Model的初始化模型。这里我们使用[InternLM2-chat-1.8b-sft](<>)作为初始化模型。 +依据[Training language models to follow instructions with human feedback](https://arxiv.org/abs/2203.02155)论文中的描述,我们使用进过 SFT 的语言模型作为Reward Model的初始化模型。这里我们使用[InternLM2-chat-1.8b-sft](https://huggingface.co/internlm/internlm2-chat-1_8b-sft)作为初始化模型。 -在训练配置文件中设置`pretrained_model_name_or_path = 'internlm/internlm2-chat-1_8b-sft'`,则会在启动训练时自动下载模型文件。若您需要手动下载模型权重,那么请参考[准备预训练模型权重](<>)章节,其中详细说明了如何从Huggingface或者是Modelscope下载模型权重的方法。这里我们附上模型的 HuggingFace 链接与 ModelScope 链接: +在训练配置文件中设置`pretrained_model_name_or_path = 'internlm/internlm2-chat-1_8b-sft'`,则会在启动训练时自动下载模型文件。若您需要手动下载模型权重,那么请参考[准备预训练模型权重](https://xtuner.readthedocs.io/zh-cn/latest/preparation/pretrained_model.html)章节,其中详细说明了如何从Huggingface或者是Modelscope下载模型权重的方法。这里我们附上模型的 HuggingFace 链接与 ModelScope 链接: - HuggingFace 链接位于:https://huggingface.co/internlm/internlm2-chat-1_8b-sft @@ -14,7 +14,7 @@ ### 准备训练数据 -在本教程中使用[UltraFeedback](<>)数据集作为演示,为了方便起见,我们使用huggingface上已经预处理过的[argilla/ultrafeedback-binarized-preferences-cleaned](<>)数据集, +在本教程中使用[UltraFeedback](https://arxiv.org/abs/2310.01377)数据集作为演示,为了方便起见,我们使用huggingface上已经预处理过的[argilla/ultrafeedback-binarized-preferences-cleaned](https://huggingface.co/datasets/argilla/ultrafeedback-binarized-preferences-cleaned)数据集, ```python train_dataset = dict( @@ -28,7 +28,7 @@ train_dataset = dict( ) ``` -在配置文件中使用以上配置,即可自动下载并处理该数据集。如果您希望使用其他huggingface上的开源数据集或是使用自定义的数据集,请参阅[偏好数据集](<>)章节。 +在配置文件中使用以上配置,即可自动下载并处理该数据集。如果您希望使用其他huggingface上的开源数据集或是使用自定义的数据集,请参阅[偏好数据集](./preference_data.md)章节。 ### 准备配置文件 @@ -40,7 +40,7 @@ xtuner copy-cfg internlm2_chat_1_8b_reward_full_ultrafeedback . 打开复制后的配置文件,如果您选择自动下载模型和数据集,则无需修改配置。若您希望填入您预先下载的模型路径和数据集路径,请修改配置中的`pretrained_model_name_or_path`以及`train_dataset`中`dataset`的`path`参数。 -更多的训练参数配置,请参阅[修改Reward训练配置](<>)章节。 +更多的训练参数配置,请参阅[修改Reward训练配置](./modify_settings.md)章节。 ### 启动训练 From da6da57600dc38b6b851bcdc4df54dfed2a09729 Mon Sep 17 00:00:00 2001 From: RangiLyu Date: Thu, 13 Jun 2024 14:14:59 +0800 Subject: [PATCH 3/5] refine link --- docs/zh_cn/dpo/overview.md | 4 ++-- docs/zh_cn/reward_model/overview.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/zh_cn/dpo/overview.md b/docs/zh_cn/dpo/overview.md index 6bd19da67..dd025815d 100644 --- a/docs/zh_cn/dpo/overview.md +++ b/docs/zh_cn/dpo/overview.md @@ -4,9 +4,9 @@ DPO(Direct Preference Optimization,直接偏好优化)是一种在大语言模型训练中用于直接优化模型偏好的方法。与传统的强化学习方法不同,DPO 直接使用人类偏好数据进行模型优化,从而提高生成内容的质量,使其更符合人类偏好。DPO 利用人类偏好数据,直接对模型进行优化,省略了训练Reward Model的训练过程,与PPO相比进一步省去了Critic Model,不但避免了复杂的强化学习算法,减少了训练开销,同时还提高了训练效率。 -DPO 拥有大量的衍生算法,它们对DPO的损失函数进行了一定程度上的改进,我们在XTuner中除了DPO还实现了[Identity Preference Optimisation (IPO)](https://huggingface.co/papers/2310.12036),[Kahneman-Tversky Optimisation (KTO)](https://github.com/ContextualAI/HALOs)等论文中的损失函数,如需使用这些算法,请参考[修改DPO配置](./modify_settings.md)章节。 +DPO 拥有大量的衍生算法,它们对DPO的损失函数进行了一定程度上的改进,我们在XTuner中除了DPO还实现了[Identity Preference Optimisation (IPO)](https://huggingface.co/papers/2310.12036),[Kahneman-Tversky Optimisation (KTO)](https://github.com/ContextualAI/HALOs)等论文中的损失函数,如需使用这些算法,请参考[修改DPO配置](./modify_settings.md)章节。我们也提供了一些[示例配置](https://github.com/InternLM/xtuner/tree/main/xtuner/configs/dpo)用于参考。 -除了 DPO 之外,还出现了如 [ORPO](https://arxiv.org/abs/2403.07691) 等无需参考模型的对齐算法。ORPO 采用了对数比值(odds ratio)的概念来优化模型,通过在模型训练过程中惩罚那些被拒绝的样本,从而更有效地适应被选择的样本。ORPO 消除了对参考模型的依赖,使得训练过程更加简化且高效。 +除了 DPO 之外,还出现了如 [ORPO](https://arxiv.org/abs/2403.07691) 等无需参考模型的对齐算法。ORPO 采用了对数比值(odds ratio)的概念来优化模型,通过在模型训练过程中惩罚那些被拒绝的样本,从而更有效地适应被选择的样本。ORPO 消除了对参考模型的依赖,使得训练过程更加简化且高效。XTuner 中 ORPO 的训练方式与 DPO 非常类似,我们提供了一些 ORPO 的[示例配置](https://github.com/InternLM/xtuner/tree/main/xtuner/configs/orpo),用户可以参考DPO的教程对配置进行修改。 ### XTuner 中 DPO 训练的优势 diff --git a/docs/zh_cn/reward_model/overview.md b/docs/zh_cn/reward_model/overview.md index 2fc535102..f5a3a1b0b 100644 --- a/docs/zh_cn/reward_model/overview.md +++ b/docs/zh_cn/reward_model/overview.md @@ -1,4 +1,4 @@ -## Reward Model介绍 +## Reward Model 介绍 ### 简介 From 0cdc261217a9844ded7c633789448442ee198155 Mon Sep 17 00:00:00 2001 From: RangiLyu Date: Thu, 13 Jun 2024 16:25:45 +0800 Subject: [PATCH 4/5] resolve comments --- docs/zh_cn/dpo/modify_settings.md | 2 +- docs/zh_cn/dpo/overview.md | 10 +++++----- docs/zh_cn/reward_model/quick_start.md | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/zh_cn/dpo/modify_settings.md b/docs/zh_cn/dpo/modify_settings.md index abf513679..7b4672792 100644 --- a/docs/zh_cn/dpo/modify_settings.md +++ b/docs/zh_cn/dpo/modify_settings.md @@ -74,7 +74,7 @@ train_dataloader = dict( 上述配置中,我们使用了 `load_dataset` 来加载 huggingface 上的 `mlabonne/orpo-dpo-mix-40k` 数据集,使用 `orpo_dpo_mix_40k_map_fn` 作为数据集映射函数。 -关于如何处理数据集以及如何编写数据集映射函数,请参考[偏好数据集章节](./preference_data.md)。 +关于如何处理数据集以及如何编写数据集映射函数,请参考[偏好数据集章节](../reward_model/preference_data.md)。 ### 加速训练 diff --git a/docs/zh_cn/dpo/overview.md b/docs/zh_cn/dpo/overview.md index dd025815d..d1bfc4379 100644 --- a/docs/zh_cn/dpo/overview.md +++ b/docs/zh_cn/dpo/overview.md @@ -2,23 +2,23 @@ ### 简介 -DPO(Direct Preference Optimization,直接偏好优化)是一种在大语言模型训练中用于直接优化模型偏好的方法。与传统的强化学习方法不同,DPO 直接使用人类偏好数据进行模型优化,从而提高生成内容的质量,使其更符合人类偏好。DPO 利用人类偏好数据,直接对模型进行优化,省略了训练Reward Model的训练过程,与PPO相比进一步省去了Critic Model,不但避免了复杂的强化学习算法,减少了训练开销,同时还提高了训练效率。 +DPO(Direct Preference Optimization,直接偏好优化)是一种在大语言模型训练中用于直接优化模型偏好的方法。与传统的强化学习方法不同,DPO 直接使用人类偏好数据进行模型优化,从而提高生成内容的质量,使其更符合人类偏好。DPO 利用人类偏好数据,直接对模型进行优化,省略了训练 Reward Model 的训练过程,与 PPO 相比进一步省去了 Critic Model,不但避免了复杂的强化学习算法,减少了训练开销,同时还提高了训练效率。 -DPO 拥有大量的衍生算法,它们对DPO的损失函数进行了一定程度上的改进,我们在XTuner中除了DPO还实现了[Identity Preference Optimisation (IPO)](https://huggingface.co/papers/2310.12036),[Kahneman-Tversky Optimisation (KTO)](https://github.com/ContextualAI/HALOs)等论文中的损失函数,如需使用这些算法,请参考[修改DPO配置](./modify_settings.md)章节。我们也提供了一些[示例配置](https://github.com/InternLM/xtuner/tree/main/xtuner/configs/dpo)用于参考。 +DPO 拥有大量的衍生算法,它们对 DPO 的损失函数进行了一定程度上的改进,我们在 XTuner 中除了 DPO 还实现了[Identity Preference Optimisation (IPO)](https://huggingface.co/papers/2310.12036),[Kahneman-Tversky Optimisation (KTO)](https://github.com/ContextualAI/HALOs)等论文中的损失函数,如需使用这些算法,请参考[修改 DPO 配置](./modify_settings.md)章节。我们也提供了一些[示例配置](https://github.com/InternLM/xtuner/tree/main/xtuner/configs/dpo)用于参考。 -除了 DPO 之外,还出现了如 [ORPO](https://arxiv.org/abs/2403.07691) 等无需参考模型的对齐算法。ORPO 采用了对数比值(odds ratio)的概念来优化模型,通过在模型训练过程中惩罚那些被拒绝的样本,从而更有效地适应被选择的样本。ORPO 消除了对参考模型的依赖,使得训练过程更加简化且高效。XTuner 中 ORPO 的训练方式与 DPO 非常类似,我们提供了一些 ORPO 的[示例配置](https://github.com/InternLM/xtuner/tree/main/xtuner/configs/orpo),用户可以参考DPO的教程对配置进行修改。 +除了 DPO 之外,还出现了如 [ORPO](https://arxiv.org/abs/2403.07691) 等无需参考模型的对齐算法。ORPO 采用了对数比值(odds ratio)的概念来优化模型,通过在模型训练过程中惩罚那些被拒绝的样本,从而更有效地适应被选择的样本。ORPO 消除了对参考模型的依赖,使得训练过程更加简化且高效。XTuner 中 ORPO 的训练方式与 DPO 非常类似,我们提供了一些 ORPO 的[示例配置](https://github.com/InternLM/xtuner/tree/main/xtuner/configs/orpo),用户可以参考 DPO 的教程对配置进行修改。 ### XTuner 中 DPO 训练的优势 XTuner 中的 DPO 训练具备以下显著优势: -1. **支持最新的算法**:XTuner除了支持标准的DPO之外,还支持了大量的衍生算法,同时也支持ORPO等不依赖参考模型的高效算法。 +1. **支持最新的算法**:XTuner除了支持标准的 DPO 之外,还支持了大量的衍生算法,同时也支持ORPO等不依赖参考模型的高效算法。 2. **减少显存浪费**:由于偏好数据中的 chosen 和 rejected 数据通常存在长度上的差异,因此在训练数据的拼接时会存在填充(padding token),造成显存浪费。在 XTuner 中,基于 Flash Attention2 中的[变长注意力](https://xtuner.readthedocs.io/zh-cn/latest/acceleration/varlen_flash_attn.html)功能,我们在训练过程中通过将偏好数据打包到同一个序列中,显著减少了由于 padding token 带来的显存浪费。这不仅提高了显存的利用效率,还使得在相同硬件条件下可以训练更大的模型或处理更多的数据。 ![img](../reward_model/images/var_len_atten.png) -3. **高效训练**:借助 XTuner 的 QLoRA 训练功能,参考模型能够被转化为移除LoRA适配器的语言模型,从而省去了参考模型权重的显存占用,大幅降低了DPO的训练开销。 +3. **高效训练**:借助 XTuner 的 QLoRA 训练功能,参考模型能够被转化为移除LoRA适配器的语言模型,从而省去了参考模型权重的显存占用,大幅降低了 DPO 的训练开销。 ### 开始训练 diff --git a/docs/zh_cn/reward_model/quick_start.md b/docs/zh_cn/reward_model/quick_start.md index 21c6efc37..b0e603cf8 100644 --- a/docs/zh_cn/reward_model/quick_start.md +++ b/docs/zh_cn/reward_model/quick_start.md @@ -55,7 +55,7 @@ NPROC_PER_NODE=${GPU_NUM} xtuner train ./internlm2_chat_1_8b_reward_full_ultrafe srun ${SRUN_ARGS} xtuner train ./internlm2_chat_1_8b_reward_full_ultrafeedback_copy.py --launcher slurm ``` -正确的训练日志应当如下所示: +正确的训练日志应当如下所示(在单卡 A800 上运行): ``` 06/06 16:12:11 - mmengine - INFO - Iter(train) [ 10/15230] lr: 3.9580e-07 eta: 2:59:41 time: 0.7084 data_time: 0.0044 memory: 18021 loss: 0.6270 acc: 0.0000 chosen_score_mean: 0.0000 rejected_score_mean: 0.0000 num_samples: 4.0000 num_tokens: 969.0000 @@ -65,7 +65,6 @@ srun ${SRUN_ARGS} xtuner train ./internlm2_chat_1_8b_reward_full_ultrafeedback_c 06/06 16:12:35 - mmengine - INFO - Iter(train) [ 50/15230] lr: 2.1540e-06 eta: 2:41:03 time: 0.7166 data_time: 0.0027 memory: 42186 loss: 0.6278 acc: 0.5000 chosen_score_mean: 0.0031 rejected_score_mean: 0.0032 num_samples: 4.0000 num_tokens: 2049.0000 06/06 16:12:40 - mmengine - INFO - Iter(train) [ 60/15230] lr: 2.5936e-06 eta: 2:33:37 time: 0.4627 data_time: 0.0023 memory: 30238 loss: 0.6262 acc: 1.0000 chosen_score_mean: 0.0057 rejected_score_mean: 0.0030 num_samples: 4.0000 num_tokens: 992.0000 06/06 16:12:46 - mmengine - INFO - Iter(train) [ 70/15230] lr: 3.0331e-06 eta: 2:33:18 time: 0.6018 data_time: 0.0025 memory: 42186 loss: 0.6247 acc: 0.7500 chosen_score_mean: 0.0117 rejected_score_mean: 0.0055 num_samples: 4.0000 num_tokens: 815.0000 - ``` ### 模型转换 From 39d7401bfa16cfa9ea9c9bb307fa25696afe09ac Mon Sep 17 00:00:00 2001 From: RangiLyu Date: Thu, 13 Jun 2024 16:34:18 +0800 Subject: [PATCH 5/5] resolve comments --- docs/zh_cn/reward_model/overview.md | 2 +- docs/zh_cn/reward_model/quick_start.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/zh_cn/reward_model/overview.md b/docs/zh_cn/reward_model/overview.md index f5a3a1b0b..84b5ab14b 100644 --- a/docs/zh_cn/reward_model/overview.md +++ b/docs/zh_cn/reward_model/overview.md @@ -16,7 +16,7 @@ Reward Model的应用场景包括但不限于: XTuner 中的 Reward Model 训练具备以下显著优势: -1. **使用最新的训练技巧**:XTuner 中集成了 InternLM2 中的 Reward Model 训练损失函数,可以稳定奖励得分的数值范围,也可以减少在简单样本上的过拟合(具体可参考[InternLM2技术报告](https://arxiv.org/abs/2403.17297))。 +1. **使用最新的训练技巧**:XTuner 中集成了 InternLM2 中的 Reward Model 训练损失函数,可以稳定奖励得分的数值范围,也可以减少在简单样本上的过拟合(具体可参考 [InternLM2 技术报告](https://arxiv.org/abs/2403.17297))。 2. **减少显存浪费**:由于偏好数据中的 chosen 和 rejected 数据通常存在长度上的差异,因此在训练数据的拼接时会存在填充(padding token),造成显存浪费。在 XTuner 中,基于 Flash Attention2 中的变长注意力功能,我们在训练过程中通过将偏好数据打包到同一个序列中,显著减少了由于 padding token 带来的显存浪费。这不仅提高了显存的利用效率,还使得在相同硬件条件下可以训练更大的模型或处理更多的数据。 diff --git a/docs/zh_cn/reward_model/quick_start.md b/docs/zh_cn/reward_model/quick_start.md index b0e603cf8..3762a4e8c 100644 --- a/docs/zh_cn/reward_model/quick_start.md +++ b/docs/zh_cn/reward_model/quick_start.md @@ -4,9 +4,9 @@ ### 准备预训练模型权重 -依据[Training language models to follow instructions with human feedback](https://arxiv.org/abs/2203.02155)论文中的描述,我们使用进过 SFT 的语言模型作为Reward Model的初始化模型。这里我们使用[InternLM2-chat-1.8b-sft](https://huggingface.co/internlm/internlm2-chat-1_8b-sft)作为初始化模型。 +依据 [Training language models to follow instructions with human feedback](https://arxiv.org/abs/2203.02155) 论文中的描述,我们使用进过 SFT 的语言模型作为 Reward Model 的初始化模型。这里我们使用[InternLM2-chat-1.8b-sft](https://huggingface.co/internlm/internlm2-chat-1_8b-sft)作为初始化模型。 -在训练配置文件中设置`pretrained_model_name_or_path = 'internlm/internlm2-chat-1_8b-sft'`,则会在启动训练时自动下载模型文件。若您需要手动下载模型权重,那么请参考[准备预训练模型权重](https://xtuner.readthedocs.io/zh-cn/latest/preparation/pretrained_model.html)章节,其中详细说明了如何从Huggingface或者是Modelscope下载模型权重的方法。这里我们附上模型的 HuggingFace 链接与 ModelScope 链接: +在训练配置文件中设置`pretrained_model_name_or_path = 'internlm/internlm2-chat-1_8b-sft'`,则会在启动训练时自动下载模型文件。若您需要手动下载模型权重,那么请参考[准备预训练模型权重](https://xtuner.readthedocs.io/zh-cn/latest/preparation/pretrained_model.html)章节,其中详细说明了如何从 Huggingface 或者是 Modelscope 下载模型权重的方法。这里我们附上模型的 HuggingFace 链接与 ModelScope 链接: - HuggingFace 链接位于:https://huggingface.co/internlm/internlm2-chat-1_8b-sft @@ -14,7 +14,7 @@ ### 准备训练数据 -在本教程中使用[UltraFeedback](https://arxiv.org/abs/2310.01377)数据集作为演示,为了方便起见,我们使用huggingface上已经预处理过的[argilla/ultrafeedback-binarized-preferences-cleaned](https://huggingface.co/datasets/argilla/ultrafeedback-binarized-preferences-cleaned)数据集, +在本教程中使用 [UltraFeedback](https://arxiv.org/abs/2310.01377) 数据集作为演示,为了方便起见,我们使用 huggingface 上已经预处理过的 [argilla/ultrafeedback-binarized-preferences-cleaned](https://huggingface.co/datasets/argilla/ultrafeedback-binarized-preferences-cleaned) 数据集, ```python train_dataset = dict( @@ -28,7 +28,7 @@ train_dataset = dict( ) ``` -在配置文件中使用以上配置,即可自动下载并处理该数据集。如果您希望使用其他huggingface上的开源数据集或是使用自定义的数据集,请参阅[偏好数据集](./preference_data.md)章节。 +在配置文件中使用以上配置,即可自动下载并处理该数据集。如果您希望使用其他 huggingface 上的开源数据集或是使用自定义的数据集,请参阅[偏好数据集](./preference_data.md)章节。 ### 准备配置文件 @@ -38,7 +38,7 @@ XTuner 提供了多个开箱即用的配置文件,可以通过 `xtuner list-cf xtuner copy-cfg internlm2_chat_1_8b_reward_full_ultrafeedback . ``` -打开复制后的配置文件,如果您选择自动下载模型和数据集,则无需修改配置。若您希望填入您预先下载的模型路径和数据集路径,请修改配置中的`pretrained_model_name_or_path`以及`train_dataset`中`dataset`的`path`参数。 +打开复制后的配置文件,如果您选择自动下载模型和数据集,则无需修改配置。若您希望填入您预先下载的模型路径和数据集路径,请修改配置中的 `pretrained_model_name_or_path` 以及 `train_dataset` 中 `dataset` 的 `path` 参数。 更多的训练参数配置,请参阅[修改Reward训练配置](./modify_settings.md)章节。