ORPO偏好优化:性能和DPO一样好并且更简单的对齐方法

人工智能
现在有许多方法可以使大型语言模型(LLM)与人类偏好保持一致。以人类反馈为基础的强化学习(RLHF)是最早的方法之一,并促成了ChatGPT的诞生,但RLHF的成本非常高。与RLHF相比,DPO、IPO和KTO的成本明显更低,因为它们不需要奖励模型。

现在有许多方法可以使大型语言模型(LLM)与人类偏好保持一致。以人类反馈为基础的强化学习(RLHF)是最早的方法之一,并促成了ChatGPT的诞生,但RLHF的成本非常高。与RLHF相比,DPO、IPO和KTO的成本明显更低,因为它们不需要奖励模型。

虽然DPO和IPO的成本较低,但它们仍需训练两个不同的模型。首先是监督微调(SFT)步骤,即训练模型按指令回答问题,然后使用SFT模型作为初始化和参考,以使模型与人类偏好一致。

ORPO是另一种新的LLM对齐方法,这种方法甚至不需要SFT模型。通过ORPO,LLM可以同时学习回答指令和满足人类偏好。

在本文中,我将解释ORPO并介绍其相关的内容,最后将展示如何使用消费级硬件将Mistral 7B转换为聊天模型。

ORPO:Monolithic Preference Optimization without Reference Model

作者通过展示SFT步骤在对齐流程中并不理想来很好地论证了ORPO的动机。虽然在指令数据集上微调模型确实使模型适应在特定领域回答指令,但生成人类可能拒绝的答案的概率也增加了。

被选中和被拒绝的响应可能有很多共同点:相同的领域、相同的格式等,因此生成与任务相关但不正确的答案的概率增加。而DPO可以降低被拒绝响应的概率,同时增加被选择响应的概率,即在上图中的曲线之间增大差距。偏好优化技术是在包含以下内容的数据集上训练的:

提示
 选择的答案
 被拒绝的答案

对于STF,它是在与选择的答案配对的提示上进行训练的。用于sft的数据集可以与偏好优化使用的相同,但不包括"被拒绝"的答案。所以可以直观地认为,应该能够微调一个基础LLM,使其在学习如何回答指令的同时,也学会惩罚和偏好某些答案。

ORPO就是在这个理论基础上建立的,ORPO简单地通过添加负对数似然损失与OR损失(OR代表奇异比)来修改训练损失:

OR损失对被拒绝的答案进行弱惩罚,而对选择的答案进行强有力的奖励。这里包含了一个超参数lambda用于加权OR损失。

lambda设为0.1似乎效果不错。如果设置为0.5,虽然区分选择和拒绝输出的能力更强,但选择答案的概率也降低了。所以为了对于在拒绝错误答案比获取正确答案更为关键的特定应用,可能将lambda设置为0.5会更好。

通过ORPO的损失,模型在学习了SFT期间的内容的同时,也学会了人类偏好。

但这种方法的一个缺点是,它可能需要更大的偏好数据集。

使用TRL运行ORPO

虽然这是今年3月分刚发布的论文,但是ORPO 已经可以在Hugging Face库上使用了,并且它因为只修改了损失函数,所以可以很好的与现有的Lora方法集成,这里我们就演示如何将它与GaLora进行结合,训练我们自己的模型。

首先,安装依赖:

pip install -q -U bitsandbytes
 pip install --upgrade -q -U transformers
 pip install -q -U peft
 pip install -q -U accelerate
 pip install -q -U datasets
 pip install -q -U git+https://github.com/huggingface/trl.git

然后,导入库:

import torch, multiprocessing
 from datasets import load_dataset
 from peft import LoraConfig, prepare_model_for_kbit_training
 from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    TrainingArguments,
 )
 from trl import ORPOTrainer, ORPOConfig

还需要运行以下代码,确保如果GPU支持的话则使用FlashAttention和bfloat16:

import os
 major_version, minor_version = torch.cuda.get_device_capability()
 if major_version >= 8:
  os.system("pip install flash-attn")
  torch_dtype = torch.bfloat16
  attn_implementatinotallow='flash_attention_2'
  print("Your GPU is compatible with FlashAttention and bfloat16.")
 else:
  torch_dtype = torch.float16
  attn_implementatinotallow='eager'
  print("Your GPU is not compatible with FlashAttention and bfloat16.")

然后,加载数据集。这里使用“HuggingFaceH4/ ultrafeedback_binalized”(MIT许可)来训练Zephyr模型。我将一个聊天模板应用到“被选中”和“被拒绝”列上,以对JSON进行字符串化。

dataset = load_dataset("HuggingFaceH4/ultrafeedback_binarized", split=["train_prefs","test_prefs"])
 
 def process(row):
    row["chosen"] = tokenizer.apply_chat_template(row["chosen"], tokenize=False)
    row["rejected"] = tokenizer.apply_chat_template(row["rejected"], tokenize=False)
    return row
 
 dataset[0] = dataset[0].map(
    process,
    num_proc= multiprocessing.cpu_count(),
    load_from_cache_file=False,
 )
 
 dataset[1] = dataset[1].map(
    process,
    num_proc= multiprocessing.cpu_count(),
    load_from_cache_file=False,
 )

剩下就是加载标记器,配置并加载模型。

model_name = "mistralai/Mistral-7B-v0.1"
 #Tokenizer
 tokenizer = AutoTokenizer.from_pretrained(model_name, add_eos_token=True, use_fast=True)
 tokenizer.pad_token = tokenizer.eos_token
 tokenizer.padding_side = 'left' #Necessary for FlashAttention compatibility
 
 bnb_config = BitsAndBytesConfig(
        load_in_4bit=True,
        bnb_4bit_quant_type="nf4",
        bnb_4bit_compute_dtype=torch_dtype,
        bnb_4bit_use_double_quant=True,
 )
 model = AutoModelForCausalLM.from_pretrained(
          model_name, torch_dtype=torch_dtype, quantization_cnotallow=bnb_config, device_map={"": 0}, attn_implementatinotallow=attn_implementation
 )
 model = prepare_model_for_kbit_training(model)
 #Configure the pad token in the model
 model.config.pad_token_id = tokenizer.pad_token_id

该模型使用bitsandbytes的NF4数据类型(使用BitsAndBytesConfig配置)动态量化。记得设置“prepare_model_for_kbit_training”,因为它支持梯度检查点并节省大量内存。

对于LoRA的配置,使用标准超参数。如果增加“r”可能会有更好的结果,但这也会增加内存消耗,所以这里就不进行超参数得调整了。

peft_config = LoraConfig(
        lora_alpha=16,
        lora_dropout=0.05,
        r=16,
        bias="none",
        task_type="CAUSAL_LM",
        target_modules= ['k_proj', 'q_proj', 'v_proj', 'o_proj', "gate_proj", "down_proj", "up_proj"]
 )

还可以在LoraConfig中设置“use_dora=True”,以使用DoRA训练更好(但更慢)的适配器。

最后就是设置ORPOConfig并开始训练:

orpo_config = ORPOConfig(
    output_dir="./results/",
    evaluation_strategy="steps",
    do_eval=True,
    optim="paged_adamw_8bit",
    per_device_train_batch_size=2,
    gradient_accumulation_steps=4,
    per_device_eval_batch_size=2,
    log_level="debug",
    logging_steps=20,
    learning_rate=8e-6,
    eval_steps=20,
    max_steps=100,
    save_steps=20,
    save_strategy='epoch',
    warmup_ratio=0.1,
    lr_scheduler_type="linear",
    beta=0.1, #beta is ORPO's lambda
    max_length=1024,
 )
 
 trainer = ORPOTrainer(
        model=model,
        train_dataset=dataset[0],
        eval_dataset=dataset[1],
        peft_cnotallow=peft_config,
        args=orpo_config,
        tokenizer=tokenizer,
 )
 
 trainer.train()

ORPOTrainer与SFTTrainer和DPOTrainer的不同之处在于,它似乎不接受trainingargument作为参数。所以需要传递一个“ORPOConfig”。这里有个注意点:ORPOConfig中提到的“beta”是论文中描述的“lambda”,它衡量OR损失。

以下是在Colab 测试得结果:

训练和验证损失都减少了。说明模型正在学习,也就是说这个方法是有效得。让我们再看看论文中ORPO的学习曲线:

从曲线中可以清楚地看出,ORPO需要数千个训练步骤来学习如何区分选择的响应和拒绝的响应。为了获得类似的结果,应该训练ORPO至少2000步,总批大小为64(如论文所述)。这样看来使用一个高端消费级GPU,例如RTX 4090是可行的,但可能需要几天的时间。

总结

ORPO是一种单步微调和对准指令llm的新方法。它不需要任何奖励或SFT模型,并且ORPO比DPO和RLHF更简单。根据论文ORPO的性能与DPO相当或略好。但是ORPO需要几千个训练步骤来学习好的和坏的反应之间的区别。

应该从现在开始使用ORPO吗?

如果想要一个简单有效的方法,ORPO是可以得。但是想要得到最好的结果,ORPO还不能完全的得到验证。因为目前还没有一个偏好优化方法的全面比较。但是我们可以从ORPO开始,因为他毕竟比较简单。

责任编辑:华轩 来源: DeepHub IMBA
相关推荐

2024-02-19 00:10:00

AI模型

2009-11-26 09:47:35

SharePoint2

2013-12-31 09:19:23

Python调试

2021-12-22 07:31:18

RedisNoSQL数据库

2012-03-07 17:24:10

戴尔咨询

2011-02-28 10:38:13

Windows 8

2012-12-20 10:17:32

IT运维

2020-11-30 15:33:33

访问数组

2009-06-12 15:26:02

2015-08-25 09:52:36

云计算云计算产业云计算政策

2013-01-11 18:10:56

软件

2020-03-02 10:56:41

办公电脑疫情

2020-05-19 10:02:58

CIOIPD集成产品开发

2009-12-08 14:26:13

大型网络运维

2021-12-23 15:11:46

Web 3.0元宇宙Metaverse

2015-08-28 09:20:07

app推广方法

2011-03-14 16:51:24

2016-09-21 13:17:31

LibreOfficeJava缓冲区

2012-10-26 12:33:58

视频会议视频通信华为

2021-10-02 10:36:00

YAML编程语言软件开发
点赞
收藏

51CTO技术栈公众号