TRL 文档
ORPO 训练器
并获得增强的文档体验
开始使用
ORPO 训练器
概览
赔率比偏好优化 (ORPO) 由 ORPO: Monolithic Preference Optimization without Reference Model 一文介绍,作者为 Jiwoo Hong、Noah Lee 和 James Thorne。
该论文的摘要如下
虽然最近用于语言模型的偏好对齐算法已经展示了有希望的结果,但有监督的微调 (SFT) 仍然是实现成功收敛的必要条件。在本文中,我们研究了 SFT 在偏好对齐背景下的关键作用,强调对不受欢迎的生成风格的轻微惩罚就足以实现偏好对齐的 SFT。在此基础上,我们引入了一种直接且创新的无参考模型的单片赔率比偏好优化算法 ORPO,消除了对额外偏好对齐阶段的需求。我们通过实证和理论证明,对于从 125M 到 7B 的各种尺寸,赔率比是在 SFT 期间对比受欢迎和不受欢迎风格的明智选择。具体来说,仅在 UltraFeedback 上使用 ORPO 对 Phi-2 (2.7B)、Llama-2 (7B) 和 Mistral (7B) 进行微调,就超越了参数超过 7B 和 13B 的最先进语言模型的性能:在 AlpacaEval_{2.0} 上达到 12.20%(图 1),在 IFEval 上达到 66.19%(指令级宽松,表 6),在 MT-Bench 上达到 7.32(图 12)。我们发布了 Mistral-ORPO-alpha (7B) 和 Mistral-ORPO-beta (7B) 的代码和模型检查点。
它研究了 SFT 在偏好对齐背景下的关键作用。该方法使用偏好数据,认为对不受欢迎的生成进行轻微惩罚,再加上通过附加到 NLL 损失的简单对数赔率比项来强烈适应所选响应的信号,就足以实现偏好对齐的 SFT。
因此,ORPO 是一种无参考模型的偏好优化算法,消除了对额外偏好对齐阶段的需求,从而节省了计算和内存。
官方代码可以在 xfactlab/orpo 中找到。
这种后训练方法由 Kashif Rasul、Lewis Tunstall 和 Alvaro Bartolome 贡献。
快速开始
此示例演示了如何使用 ORPO 方法训练模型。我们使用 Qwen 0.5B 模型 作为基础模型。我们使用来自 UltraFeedback 数据集 的偏好数据。您可以在此处查看数据集中的数据
以下是训练模型的脚本
# train_orpo.py
from datasets import load_dataset
from trl import ORPOConfig, ORPOTrainer
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
train_dataset = load_dataset("trl-lib/ultrafeedback_binarized", split="train")
training_args = ORPOConfig(output_dir="Qwen2-0.5B-ORPO", logging_steps=10)
trainer = ORPOTrainer(model=model, args=training_args, processing_class=tokenizer, train_dataset=train_dataset)
trainer.train()
使用以下命令执行脚本
accelerate launch train_orpo.py
在 8 个 GPU 上分布式运行,训练大约需要 30 分钟。您可以通过查看奖励图来验证训练进度。奖励幅度的增加趋势表明模型正在改进,并且随着时间的推移生成更好的响应。
要查看 训练好的模型 的性能,您可以使用 Transformers Chat CLI。
$ transformers-cli chat --model_name_or_path trl-lib/Qwen2-0.5B-ORPO
<quentin_gallouedec>:
What is the best programming language?
<trl-lib/Qwen2-0.5B-ORPO>:
It's challenging to determine the best programming language as no one language is perfect, as the complexity of a task and the type of project are significant factors. Some popular languages include Java, Python, JavaScript, and
C++. If you have specific needs or requirements for a specific project, it's important to choose the language that best suits those needs.
Here are some other factors to consider when choosing a programming language for a project:
• Language proficiency: A good programming language is more likely to be easy to understand and use, and will allow developers to collaborate on projects more efficiently.
• Ease of use: There are tools and libraries available to make programming more accessible, so developers should choose a language that can help them get started easier.
• Code readability: A clear and concise codebase should be easy to read and understand, especially when working with large projects.
• Tool and framework support: There are numerous libraries available for Python, Java, and JavaScript, along with tools like IDEs and static code analysis tools.
• Accessibility: Some languages and tools have features that make them more accessible to developers with disabilities, such as support for screen readers.
• Version control: As your projects grow and complexity increases, version control tools can be beneficial for tracking changes.
预期数据集类型
ORPO 需要一个 偏好数据集。ORPOTrainer 支持 对话式 和 标准 数据集格式。当提供对话式数据集时,训练器将自动将聊天模板应用于数据集。
虽然 ORPOTrainer 同时支持显式和隐式提示,但我们建议使用显式提示。如果提供隐式提示数据集,训练器将自动从 "chosen"
和 "rejected"
列中提取提示。有关更多信息,请参阅 偏好风格 部分。
示例脚本
我们提供了一个示例脚本,用于使用 ORPO 方法训练模型。该脚本位于 examples/scripts/orpo.py
中
要使用 Qwen2 0.5B 模型 在 UltraFeedback 数据集 上测试 ORPO 脚本,请运行以下命令
accelerate launch examples/scripts/orpo.py \ --model_name_or_path Qwen/Qwen2-0.5B-Instruct \ --dataset_name trl-lib/ultrafeedback_binarized \ --num_train_epochs 1 \ --logging_steps 25 \ --output_dir Qwen2-0.5B-ORPO
使用技巧
对于混合专家模型:启用辅助损失
如果专家之间的负载大致均匀分布,则 MOE 是最有效的。
为了确保我们在偏好调整期间类似地训练 MOE,将来自负载均衡器的辅助损失添加到最终损失中是有益的。
通过在模型配置中设置 output_router_logits=True
(例如 MixtralConfig
)来启用此选项。
要缩放辅助损失对总损失的贡献程度,请在模型配置中使用超参数 router_aux_loss_coef=...
(默认值:0.001
)。
记录的指标
在训练和评估期间,我们记录以下奖励指标
rewards/chosen
:策略模型对所选响应的平均对数概率,按 beta 缩放rewards/rejected
:策略模型对拒绝响应的平均对数概率,按 beta 缩放rewards/accuracies
:所选奖励 > 相应拒绝奖励的平均频率rewards/margins
:所选奖励与相应拒绝奖励之间的平均差值log_odds_chosen
:所选响应相对于拒绝响应的平均对数赔率比log_odds_ratio
:log(sigmoid(log_odds_chosen))
的平均值nll_loss
:来自损失的 SFT 部分的平均负对数似然损失,针对所选响应
ORPOTrainer
class trl.ORPOTrainer
< source >( model: typing.Union[transformers.modeling_utils.PreTrainedModel, torch.nn.modules.module.Module, str, NoneType] = None args: typing.Optional[trl.trainer.orpo_config.ORPOConfig] = None data_collator: typing.Optional[transformers.data.data_collator.DataCollator] = None train_dataset: typing.Optional[datasets.arrow_dataset.Dataset] = None eval_dataset: typing.Union[datasets.arrow_dataset.Dataset, dict[str, datasets.arrow_dataset.Dataset], NoneType] = None processing_class: typing.Union[transformers.tokenization_utils_base.PreTrainedTokenizerBase, transformers.image_processing_utils.BaseImageProcessor, transformers.feature_extraction_utils.FeatureExtractionMixin, transformers.processing_utils.ProcessorMixin, NoneType] = None model_init: typing.Optional[typing.Callable[[], transformers.modeling_utils.PreTrainedModel]] = None callbacks: typing.Optional[list[transformers.trainer_callback.TrainerCallback]] = None optimizers: tuple = (None, None) preprocess_logits_for_metrics: typing.Optional[typing.Callable[[torch.Tensor, torch.Tensor], torch.Tensor]] = None peft_config: typing.Optional[dict] = None compute_metrics: typing.Optional[typing.Callable[[transformers.trainer_utils.EvalLoopOutput], dict]] = None )
参数
- model (
transformers.PreTrainedModel
) — 要训练的模型,最好是AutoModelForSequenceClassification
。 - args (
ORPOConfig
) — 用于训练的 ORPO 配置参数。 - data_collator (
transformers.DataCollator
) — 用于训练的数据整理器。如果未指定,将使用默认数据整理器 (DPODataCollatorWithPadding
),它将根据批次中序列的最大长度,将序列填充到最大长度,给定成对序列的数据集。 - train_dataset (
datasets.Dataset
) — 用于训练的数据集。 - eval_dataset (
datasets.Dataset
) — 用于评估的数据集。 - processing_class (
PreTrainedTokenizerBase
或BaseImageProcessor
或FeatureExtractionMixin
或ProcessorMixin
, 可选) — 用于处理数据的处理类。如果提供,将用于自动处理模型的输入,并将与模型一起保存,以便更容易地重新运行中断的训练或重用微调模型。 - model_init (
Callable[[], transformers.PreTrainedModel]
) — 用于训练的模型初始化器。如果未指定,将使用默认模型初始化器。 - callbacks (
list[transformers.TrainerCallback]
) — 用于训练的回调函数列表。 - optimizers (
tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR]
) — 用于训练的优化器和调度器。 - preprocess_logits_for_metrics (
Callable[[torch.Tensor, torch.Tensor], torch.Tensor]
) — 用于在计算指标之前预处理 logits 的函数。 - peft_config (
dict
, defaults toNone
) — 用于训练的 PEFT 配置。如果传递 PEFT 配置,模型将被包装在 PEFT 模型中。 - compute_metrics (
Callable[[EvalPrediction], dict]
, optional) — 用于计算指标的函数。必须接受EvalPrediction
并返回一个从字符串到指标值的字典。
Initialize ORPOTrainer.
Llama tokenizer does satisfy enc(a + b) = enc(a) + enc(b)
. It does ensure enc(a + b) = enc(a) + enc(a + b)[len(enc(a)):]
. Reference: https://github.com/EleutherAI/lm-evaluation-harness/pull/531#issuecomment-1595586257
Run the given model on the given batch of inputs, concatenating the chosen and rejected inputs together.
We do this to avoid doing two forward passes, because it’s faster for FSDP.
concatenated_inputs
< source >( batch: dict is_encoder_decoder: bool = False label_pad_token_id: int = -100 padding_value: int = 0 device: typing.Optional[torch.device] = None )
Concatenate the chosen and rejected inputs into a single tensor.
create_model_card
< source >( model_name: typing.Optional[str] = None dataset_name: typing.Optional[str] = None tags: typing.Union[str, list[str], NoneType] = None )
Creates a draft of a model card using the information available to the Trainer
.
evaluation_loop
< source >( dataloader: DataLoader description: str prediction_loss_only: typing.Optional[bool] = None ignore_keys: typing.Optional[list[str]] = None metric_key_prefix: str = 'eval' )
Overriding built-in evaluation loop to store metrics for each batch. Prediction/evaluation loop, shared by Trainer.evaluate()
and Trainer.predict()
.
Works both with or without labels.
Generate samples from the model and reference model for the given batch of inputs.
get_batch_logps
< source >( logits: FloatTensor labels: LongTensor average_log_prob: bool = False label_pad_token_id: int = -100 is_encoder_decoder: bool = False )
参数
- logits — 模型的 Logits(未归一化)。形状:(batch_size, sequence_length, vocab_size)
- labels — 用于计算对数概率的标签。值为 label_pad_token_id 的标签 token 将被忽略。形状:(batch_size, sequence_length)
- average_log_prob — 如果为 True,则返回每个(非掩码)token 的平均对数概率。否则,返回(非掩码)token 的对数概率之和。
- label_pad_token_id — 标签填充 token id。
- is_encoder_decoder — 模型是否为编码器-解码器模型。
Compute the log probabilities of the given labels under the given logits.
get_batch_loss_metrics
< source >( model batch: dict train_eval: typing.Literal['train', 'eval'] = 'train' )
计算给定输入批次的 ORPO 损失和其他指标,用于训练或测试。
日志
< source >( logs: dict start_time: typing.Optional[float] = None )
在监视训练的各种对象上记录 logs
,包括存储的指标。
优势比率损失
< source >( policy_chosen_logps: FloatTensor policy_rejected_logps: FloatTensor ) → 三个张量的元组
参数
- policy_chosen_logps — 策略模型对选定响应的对数概率。形状:(batch_size,)
- policy_rejected_logps — 策略模型对拒绝响应的对数概率。形状:(batch_size,)
Returns
包含三个张量的元组
(losses, chosen_rewards, rejected_rewards)。losses 张量包含批次中每个样本的 ORPO 损失。chosen_rewards 和 rejected_rewards 张量分别包含选定和拒绝响应的奖励。用于记录目的的选定响应与拒绝响应比率的对数优势比。用于记录目的的 log(sigmoid(log_odds_chosen))
。
计算一批策略和参考模型对数概率的 ORPO 优势比率 (OR) 损失。
tokenize_row
< source >( feature model: typing.Union[transformers.modeling_utils.PreTrainedModel, torch.nn.modules.module.Module, NoneType] = None )
从 ORPO 特定数据集中标记化单行数据。
在此阶段,我们尚不转换为 PyTorch 张量;我们仅处理截断,以防 prompt + 选定或 prompt + 拒绝响应过长。首先我们截断 prompt;如果仍然过长,我们将截断选定/拒绝响应。
我们还为选定/拒绝响应创建标签,其长度等于 prompt 和选定/拒绝响应的长度之和,其中 prompt 标记的标签为 label_pad_token_id。
ORPOConfig
class trl.ORPOConfig
< source >( output_dir: typing.Optional[str] = None overwrite_output_dir: bool = False do_train: bool = False do_eval: bool = False do_predict: bool = False eval_strategy: typing.Union[transformers.trainer_utils.IntervalStrategy, str] = 'no' prediction_loss_only: bool = False per_device_train_batch_size: int = 8 per_device_eval_batch_size: int = 8 per_gpu_train_batch_size: typing.Optional[int] = None per_gpu_eval_batch_size: typing.Optional[int] = None gradient_accumulation_steps: int = 1 eval_accumulation_steps: typing.Optional[int] = None eval_delay: typing.Optional[float] = 0 torch_empty_cache_steps: typing.Optional[int] = None learning_rate: float = 1e-06 weight_decay: float = 0.0 adam_beta1: float = 0.9 adam_beta2: float = 0.999 adam_epsilon: float = 1e-08 max_grad_norm: float = 1.0 num_train_epochs: float = 3.0 max_steps: int = -1 lr_scheduler_type: typing.Union[transformers.trainer_utils.SchedulerType, str] = 'linear' lr_scheduler_kwargs: typing.Union[dict, str, NoneType] = <factory> warmup_ratio: float = 0.0 warmup_steps: int = 0 log_level: typing.Optional[str] = 'passive' log_level_replica: typing.Optional[str] = 'warning' log_on_each_node: bool = True logging_dir: typing.Optional[str] = None logging_strategy: typing.Union[transformers.trainer_utils.IntervalStrategy, str] = 'steps' logging_first_step: bool = False logging_steps: float = 500 logging_nan_inf_filter: bool = True save_strategy: typing.Union[transformers.trainer_utils.SaveStrategy, str] = 'steps' save_steps: float = 500 save_total_limit: typing.Optional[int] = None save_safetensors: typing.Optional[bool] = True save_on_each_node: bool = False save_only_model: bool = False restore_callback_states_from_checkpoint: bool = False no_cuda: bool = False use_cpu: bool = False use_mps_device: bool = False seed: int = 42 data_seed: typing.Optional[int] = None jit_mode_eval: bool = False use_ipex: bool = False bf16: bool = False fp16: bool = False fp16_opt_level: str = 'O1' half_precision_backend: str = 'auto' bf16_full_eval: bool = False fp16_full_eval: bool = False tf32: typing.Optional[bool] = None local_rank: int = -1 ddp_backend: typing.Optional[str] = None tpu_num_cores: typing.Optional[int] = None tpu_metrics_debug: bool = False debug: typing.Union[str, list[transformers.debug_utils.DebugOption]] = '' dataloader_drop_last: bool = False eval_steps: typing.Optional[float] = None dataloader_num_workers: int = 0 dataloader_prefetch_factor: typing.Optional[int] = None past_index: int = -1 run_name: typing.Optional[str] = None disable_tqdm: typing.Optional[bool] = None remove_unused_columns: typing.Optional[bool] = True label_names: typing.Optional[list[str]] = None load_best_model_at_end: typing.Optional[bool] = False metric_for_best_model: typing.Optional[str] = None greater_is_better: typing.Optional[bool] = None ignore_data_skip: bool = False fsdp: typing.Union[list[transformers.trainer_utils.FSDPOption], str, NoneType] = '' fsdp_min_num_params: int = 0 fsdp_config: typing.Union[dict, str, NoneType] = None tp_size: typing.Optional[int] = 0 fsdp_transformer_layer_cls_to_wrap: typing.Optional[str] = None accelerator_config: typing.Union[dict, str, NoneType] = None deepspeed: typing.Union[dict, str, NoneType] = None label_smoothing_factor: float = 0.0 optim: typing.Union[transformers.training_args.OptimizerNames, str] = 'adamw_torch' optim_args: typing.Optional[str] = None adafactor: bool = False group_by_length: bool = False length_column_name: typing.Optional[str] = 'length' report_to: typing.Union[NoneType, str, list[str]] = None ddp_find_unused_parameters: typing.Optional[bool] = None ddp_bucket_cap_mb: typing.Optional[int] = None ddp_broadcast_buffers: typing.Optional[bool] = None dataloader_pin_memory: bool = True dataloader_persistent_workers: bool = False skip_memory_metrics: bool = True use_legacy_prediction_loop: bool = False push_to_hub: bool = False resume_from_checkpoint: typing.Optional[str] = None hub_model_id: typing.Optional[str] = None hub_strategy: typing.Union[transformers.trainer_utils.HubStrategy, str] = 'every_save' hub_token: typing.Optional[str] = None hub_private_repo: typing.Optional[bool] = None hub_always_push: bool = False gradient_checkpointing: bool = False gradient_checkpointing_kwargs: typing.Union[dict, str, NoneType] = None include_inputs_for_metrics: bool = False include_for_metrics: list = <factory> eval_do_concat_batches: bool = True fp16_backend: str = 'auto' push_to_hub_model_id: typing.Optional[str] = None push_to_hub_organization: typing.Optional[str] = None push_to_hub_token: typing.Optional[str] = None mp_parameters: str = '' auto_find_batch_size: bool = False full_determinism: bool = False torchdynamo: typing.Optional[str] = None ray_scope: typing.Optional[str] = 'last' ddp_timeout: typing.Optional[int] = 1800 torch_compile: bool = False torch_compile_backend: typing.Optional[str] = None torch_compile_mode: typing.Optional[str] = None include_tokens_per_second: typing.Optional[bool] = False include_num_input_tokens_seen: typing.Optional[bool] = False neftune_noise_alpha: typing.Optional[float] = None optim_target_modules: typing.Union[NoneType, str, list[str]] = None batch_eval_metrics: bool = False eval_on_start: bool = False use_liger_kernel: typing.Optional[bool] = False eval_use_gather_object: typing.Optional[bool] = False average_tokens_across_devices: typing.Optional[bool] = False max_length: typing.Optional[int] = 1024 max_prompt_length: typing.Optional[int] = 512 max_completion_length: typing.Optional[int] = None beta: float = 0.1 disable_dropout: bool = True label_pad_token_id: int = -100 padding_value: typing.Optional[int] = None truncation_mode: str = 'keep_end' generate_during_eval: bool = False is_encoder_decoder: typing.Optional[bool] = None model_init_kwargs: typing.Optional[dict[str, typing.Any]] = None dataset_num_proc: typing.Optional[int] = None )
参数
- learning_rate (
float
, 可选, 默认为1e-6
) —AdamW
优化器的初始学习率。默认值会替换TrainingArguments
中的默认值。 - max_length (
int
或None
, 可选, 默认为1024
) — 批次中序列(prompt + completion)的最大长度。如果您想使用默认数据整理器,则此参数是必需的。 - max_prompt_length (
int
或None
, 可选, 默认为512
) — prompt 的最大长度。如果您想使用默认数据整理器,则此参数是必需的。 - max_completion_length (
int
或None
, 可选, 默认为None
) — completion 的最大长度。如果您想使用默认数据整理器,并且您的模型是 encoder-decoder 模型,则此参数是必需的。 - beta (
float
, 可选, 默认为0.1
) — 用于控制 ORPO 损失中相对比率损失权重的参数。在 论文 中,它用 λ 表示。在 代码 中,它用alpha
表示。 - disable_dropout (
bool
, 可选, 默认为True
) — 是否禁用模型中的 dropout。 - label_pad_token_id (
int
, 可选, 默认为-100
) — Label pad token id。如果您想使用默认数据整理器,则此参数是必需的。 - padding_value (
int
或None
, 可选, 默认为None
) — 要使用的填充值。如果为None
,则使用 tokenizer 的填充值。 - truncation_mode (
str
, 可选, 默认为"keep_end"
) — 当 prompt 过长时使用的截断模式。可能的值为"keep_end"
或"keep_start"
。如果您想使用默认数据整理器,则此参数是必需的。 - generate_during_eval (
bool
, 可选, 默认为False
) — 如果为True
,则在评估期间从模型生成完成结果并记录到 W&B 或 Comet。 - is_encoder_decoder (
bool
或None
, 可选, 默认为None
) — 当使用model_init
参数 (可调用对象) 来实例化模型而不是model
参数时,您需要指定可调用对象返回的模型是否为 encoder-decoder 模型。 - model_init_kwargs (
dict[str, Any]
或None
, 可选, 默认为None
) — 当从字符串实例化模型时,传递给AutoModelForCausalLM.from_pretrained
的关键字参数。 - dataset_num_proc (
int
或None
, 可选, 默认为None
) — 用于处理数据集的进程数。
用于 ORPOTrainer 的配置类。
使用 HfArgumentParser
我们可以将此类转换为 argparse 参数,这些参数可以在命令行中指定。