关于人工智能:使用-DPO-微调-Llama-2

10次阅读

共计 4932 个字符,预计需要花费 13 分钟才能阅读完成。

简介

基于人类反馈的强化学习 (Reinforcement Learning from Human Feedback,RLHF) 事实上已成为 GPT-4 或 Claude 等 LLM 训练的最初一步,它能够确保语言模型的输入合乎人类在闲聊或安全性等方面的冀望。然而,它也给 NLP 引入了一些 RL 相干的复杂性: 既要构建一个好的处分函数,并训练一个模型用以预计每个状态的价值 (value) ; 又要留神最终生成的 LLM 不能与原始模型相差太远,如果太远的话会使得模型容易产生乱码而非有意义的文本。该过程非常复杂,波及到许多简单的组件,而这些组件自身在训练过程中又是动态变化的,因而把它们操持好并不容易。

Rafailov、Sharma、Mitchell 等人最近发表了一篇论文 Direct Preference Optimization,论文提出将现有办法应用的基于强化学习的指标转换为能够通过简略的二元穿插熵损失间接优化的指标,这一做法大大简化了 LLM 的提纯过程。

本文介绍了间接偏好优化 (Direct Preference Optimization,DPO) 法,该办法现已集成至 TRL 库 中。同时,咱们还展现了如何在 stack-exchange preference 数据集上微调最新的 Llama v2 7B 模型,stack-exchange preference 数据集中蕴含了各个 stack-exchange 门户上的各种问题及其排序后的答复。

DPO 与 PPO

在通过 RL 优化人类衍生偏好时,始终以来的传统做法是应用一个辅助处分模型来微调指标模型,以通过 RL 机制最大化指标模型所能取得的处分。直观上,咱们应用处分模型向待优化模型提供反馈,以促使它多生成高处分输入,少生成低处分输入。同时,咱们应用解冻的参考模型来确保输入偏差不会太大,且持续放弃输入的多样性。这通常须要在指标函数设计时,除了处分最大化指标外再增加一个绝对于参考模型的 KL 惩办项,这样做有助于避免模型学习舞弊或钻营处分模型。

DPO 绕过了建模处分函数这一步,这源于一个要害洞见: 从处分函数到最优 RL 策略的剖析映射。这个映射直观地度量了给定处分函数与给定偏好数据的匹配水平。有了它,作者就可与将基于处分和参考模型的 RL 损失间接转换为仅基于参考模型的损失,从而间接在偏好数据上优化语言模型!因而,DPO 从寻找最小化 RLHF 损失的最佳计划开始,通过扭转参量的形式推导出一个 仅需 参考模型的损失!

有了它,咱们能够间接优化该似然指标,而不须要处分模型或繁琐的强化学习优化过程。

如何应用 TRL 进行训练

如前所述,一个典型的 RLHF 流水线通常蕴含以下几个环节:

  1. 有监督微调 (supervised fine-tuning,SFT)
  2. 用偏好标签标注数据
  3. 基于偏好数据训练处分模型
  4. RL 优化

TRL 库蕴含了所有这些环节所需的工具程序。而 DPO 训练间接毁灭了处分建模和 RL 这两个环节 (环节 3 和 4),间接依据标注好的偏好数据优化 DPO 指标。

应用 DPO,咱们依然须要执行环节 1,但咱们仅需在 TRL 中向 DPOTrainer 提供环节 2 筹备好的偏好数据,而不再须要环节 3 和 4。标注好的偏好数据须要遵循特定的格局,它是一个含有以下 3 个键的字典:

  • prompt : 即推理时输出给模型的提醒
  • chosen : 即针对给定提醒的较优答复
  • rejected : 即针对给定提醒的较劣答复或非给定提醒的答复

例如,对于 stack-exchange preference 数据集,咱们能够通过以下工具函数将数据集中的样本映射至上述字典格局并删除所有原始列:

def return_prompt_and_responses(samples) -> Dict[str, str, str]:
    return {
        "prompt": [
            "Question:" + question + "\n\nAnswer:"
            for question in samples["question"]
        ],
        "chosen": samples["response_j"], # rated better than k
        "rejected": samples["response_k"], # rated worse than j
    }

dataset = load_dataset(
    "lvwerra/stack-exchange-paired",
    split="train",
    data_dir="data/rl"
)
original_columns = dataset.column_names

dataset.map(
    return_prompt_and_responses,
    batched=True,
    remove_columns=original_columns
)

一旦有了排序数据集,DPO 损失其实实质上就是一种有监督损失,其经由参考模型取得隐式处分。因而,从下层来看,DPOTrainer 须要咱们输出待优化的根底模型以及参考模型:

dpo_trainer = DPOTrainer(
    model, # 经 SFT 的根底模型
    model_ref, # 个别为经 SFT 的根底模型的一个拷贝
    beta=0.1, # DPO 的温度超参
    train_dataset=dataset, # 上文筹备好的数据集
    tokenizer=tokenizer, # 分词器
    args=training_args, # 训练参数,如: batch size, 学习率等
)

其中,超参 beta 是 DPO 损失的温度,通常在 0.10.5 之间。它管制了咱们对参考模型的关注水平,beta 越小,咱们就越疏忽参考模型。对训练器初始化后,咱们就能够简略调用以下办法,应用给定的 training_args 在给定数据集上进行训练了:

dpo_trainer.train()

基于 Llama v2 进行试验

在 TRL 中实现 DPO 训练器的益处是,人们能够利用 TRL 及其依赖库 (如 Peft 和 Accelerate) 中已有的 LLM 相干性能。有了这些库,咱们甚至能够应用 bitsandbytes 库提供的 QLoRA 技术 来训练 Llama v2 模型。

有监督微调

如上文所述,咱们先用 TRL 的 SFTTrainer 在 SFT 数据子集上应用 QLoRA 对 7B Llama v2 模型进行有监督微调:

# load the base model in 4-bit quantization
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
)

base_model = AutoModelForCausalLM.from_pretrained(
    script_args.model_name, # "meta-llama/Llama-2-7b-hf"
    quantization_config=bnb_config,
    device_map={"": 0},
    trust_remote_code=True,
    use_auth_token=True,
)
base_model.config.use_cache = False

# add LoRA layers on top of the quantized base model
peft_config = LoraConfig(
    r=script_args.lora_r,
    lora_alpha=script_args.lora_alpha,
    lora_dropout=script_args.lora_dropout,
    target_modules=["q_proj", "v_proj"],
    bias="none",
    task_type="CAUSAL_LM",
)
...
trainer = SFTTrainer(
    model=base_model,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
    peft_config=peft_config,
    packing=True,
    max_seq_length=None,
    tokenizer=tokenizer,
    args=training_args, # HF Trainer arguments
)
trainer.train()

DPO 训练

SFT 完结后,咱们保留好生成的模型。接着,咱们持续进行 DPO 训练,咱们把 SFT 生成的模型作为 DPO 的根底模型和参考模型,并在上文生成的 stack-exchange preference 数据上,以 DPO 为指标函数训练模型。咱们抉择对模型进行 LoRa 微调,因而咱们应用 Peft 的 AutoPeftModelForCausalLM 函数加载模型:

model = AutoPeftModelForCausalLM.from_pretrained(
    script_args.model_name_or_path, # location of saved SFT model
    low_cpu_mem_usage=True,
    torch_dtype=torch.float16,
    load_in_4bit=True,
    is_trainable=True,
)
model_ref = AutoPeftModelForCausalLM.from_pretrained(
    script_args.model_name_or_path, # same model as the main one
    low_cpu_mem_usage=True,
    torch_dtype=torch.float16,
    load_in_4bit=True,
)
...
dpo_trainer = DPOTrainer(
    model,
    model_ref,
    args=training_args,
    beta=script_args.beta,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
    tokenizer=tokenizer,
    peft_config=peft_config,
)
dpo_trainer.train()
dpo_trainer.save_model()

能够看出,咱们以 4 比特的形式加载模型,而后通过 peft_config 参数抉择 QLora 办法对其进行训练。训练器还会用评估数据集评估训练进度,并报告一些要害指标,例如能够抉择通过 WandB 记录并显示隐式处分。最初,咱们能够将训练好的模型推送到 HuggingFace Hub。

总结

SFT 和 DPO 训练脚本的残缺源代码可在该目录 examples/stack_llama_2 处找到,训好的已合并模型也已上传至 HF Hub (见 此处)。

你能够在 这儿 找到咱们的模型在训练过程的 WandB 日志,其中蕴含了 DPOTrainer 在训练和评估期间记录下来的以下处分指标:

  • rewards/chosen (较优答复的处分) : 针对较优答复,策略模型与参考模型的对数概率二者之差的均值,按 beta 缩放。
  • rewards/rejected (较劣答复的处分) : 针对较劣答复,策略模型与参考模型的对数概率二者之差的均值,按 beta 缩放。
  • rewards/accuracy (处分准确率) : 较优答复的处分大于相应较劣答复的处分的频率的均值
  • rewards/margins (处分余裕值) : 较优答复的处分与相应较劣答复的处分二者之差的均值。

直观上讲,在训练过程中,咱们心愿余裕值减少并且准确率达到 1.0,换句话说,较优答复的处分高于较劣答复的处分 (或余裕值大于零)。随后,咱们还能够在评估数据集上计算这些指标。

咱们心愿咱们代码的公布能够升高读者的入门门槛,让大家能够在本人的数据集上尝试这种大语言模型对齐办法,咱们急不可待地想看到你会用它做哪些事件!如果你想试试咱们训练进去的模型,能够玩玩这个 space: trl-lib/stack-llama。


英文原文: https://hf.co/blog/dpo-trl

原文作者: Kashif Rasul, Younes Belkada, Leandro von Werra

译者: Matrix Yao (姚伟峰),英特尔深度学习工程师,工作方向为 transformer-family 模型在各模态数据上的利用及大规模模型的训练推理。

审校 / 排版: zhongdongy (阿东)

正文完
 0