微调 LLM (RLHF + DPO)
使用强化学习(RL)根据人类反馈微调大语言模型(即RLHF)的方法,以及一种更有效的改进方法(即DPO)。
一、GPT-3与InstructGPT
2020年,OpenAI发布了GPT-3,这是一种大型语言模型(LLM),只需查看几个示例即可执行任意自然语言处理(NLP)任务。这包括为模型编写巧妙的输入(即提示),使其执行所需的任务(例如翻译、问答和完形填空任务)。
尽管GPT-3具有开创性的性能,但它与我们今天看到的实用大语言模型仍有很大差距。换句话说,要将GPT-3转变为ChatGPT,还需要额外的训练。
GPT-3论文发布两年后,OpenAI发布了InstructGPT,这是GPT-3的微调版本,更符合人类的偏好。这是至关重要的一步,因为GPT-3基础模型通常不会生成对用户有价值的补全内容(除非用户是熟练的提示编写者)。此外,由于它是在来自互联网的各种数据上进行训练的,其回复可能带有偏见或冒犯性。
另一方面,InstructGPT是一个直观的聊天机器人,能够生成有用且无害的回复[2]。这是通过两轮微调实现的:首先,对聊天数据(即输入-响应对)进行监督微调(SFT);其次,利用人类反馈强化学习(RLHF)。在这里,我们将重点关注后一种方法。
二、人类反馈强化学习(RLHF)
强化学习(RL)允许模型通过试错来学习。这包括在训练期间为模型预测分配奖励,并根据这些奖励更新其参数。
人类反馈强化学习(RLHF)是一种大规模根据人类偏好训练模型的方法[2]。在训练过程中,不是由人类实时为模型输出分配奖励,而是基于人类偏好以监督学习的方式训练一个奖励模型,并将其作为奖励分配的代理。
(一)奖励模型
为了训练奖励模型,针对单个提示,从GPT-3生成多个响应,然后人工标注员根据(详细的)有用性和无害性准则对这些响应进行排名[2]。这是一个重要的细节,因为对人类来说,评估响应的相对质量(即对它们进行排名)比从头开始编写最佳响应要容易(且快速)得多。这个过程的结果是为奖励模型提供了更高质量和数量的训练数据。
(二)近端策略优化(PPO)
然后,通过近端策略优化(PPO)使用奖励模型来训练InstructGPT。这是一种高效的强化学习算法,它根据奖励值更新模型参数[3]。
与强化学习的真正目标(即找到一个策略(模型),使在所有可能的环境轨迹中的预期累积奖励最大化)相比,PPO结合了两个关键思想,以创建一个更简单、更稳定的优化方法。
首先,它使用代理目标,即近似真实目标的目标。其次,它采用裁剪技术,确保优化步骤不会太大。PPO的目标如下所述:
三、RLHF的局限性
尽管在RLHF之后,InstructGPT在人类偏好指标上优于GPT-3及其监督微调检查点[2],但它有一个关键的局限性。即最终模型的质量取决于用于创建奖励模型的训练数据。
这个上限与其他强化学习方法形成对比,其他方法(原则上)可以通过额外的训练无限期地提高性能[4]。仔细思考这一点,可能会引发疑问。
如果RLHF从根本上受到用于创建奖励的训练数据的限制,我们能否直接使用这些偏好数据来训练大语言模型呢?虽然这个逻辑有些简单直接,但答案是肯定的。
四、直接策略优化(DPO)
直接策略优化(DPO)将RLHF重新表述为一个简单的文本分类问题[5]。其作者表明,在KL散度约束下,RLHF的最优策略可以以封闭形式推导出来。
因此,我们可以最小化模型的对数概率与关于首选-非首选响应训练对的最优策略之间的差异。这个过程的相应损失函数如下[5]:
DPO的主要优点是,我们可以通过明显更简单的训练设置获得与RLHF相同的性能提升。为了演示这一点,让我们来看一个具体的例子。
五、示例代码:基于视频标题偏好微调Qwen2.5–0.5B
大语言模型在内容创作方面往往缺乏品味。这就是为什么当有人使用人工智能撰写博客文章或任何其他类型的内容时,很容易被察觉。
在这里,我将展示如何通过使用DPO微调Qwen2.5–0.5B-Instruct,根据我的偏好生成YouTube视频标题,从而改善这种情况。
(一)步骤1:整理偏好数据
这个过程中最重要且最耗时的部分是生成偏好数据集。我的操作过程如下:
- 编写一个包含114个视频创意的列表。
- 使用Qwen2.5–7B-Instruct(通过Together AI的API)为每个创意生成5个标题。
- 对于每个创意,创建10个(即5选2)标题对。
- 手动查看所有1140个标题对,并标记出哪个更好。
我花了大约3个小时手动标记所有1140对标题。完成后,我将数据重新格式化为三列:prompt(提示)、chosen(选择的标题)和rejected(被拒绝的标题),以匹配trl库的DPOTrainer所需的格式。
你可以在此处查看完整的数据集。用于生成标题对的代码可在此处免费获得。
(二)步骤2:使用DPO进行微调
准备好偏好数据后,我们就可以进行相对简单的部分:微调模型。首先,我们导入一些有用的库:
from datasets import load_dataset
from trl import DPOConfig, DPOTrainer
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
import torch
接下来,我们将导入训练数据集(来自步骤1)和基础模型(Qwen2.5–0.5B-Instruct):
dataset = load_dataset("shawhin/youtube-titles-dpo")
model_name = "Qwen/Qwen2.5-0.5B-Instruct"
model = AutoModelForCausalLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token
在微调此模型之前,了解其标题编写能力会很有帮助。所以,让我们使用验证数据集中的提示生成一个标题:
def format_chat_prompt(user_input):
"""
将用户输入格式化为带有<|im_start|>和<|im_end|>标签的聊天模板格式。
参数:
user_input (str): 用户输入的文本。
返回:
str: 为模型格式化后的提示。
"""
user_prompt = f"<|im_start|>user\n{user_input}<|im_end|>\n"
assistant_prompt = "<|im_start|>assistant\n"
formatted_prompt = user_prompt + assistant_prompt
return formatted_prompt
generator = pipeline("text-generation", model=model, tokenizer=tokenizer)
prompt = format_chat_prompt(dataset['valid']['prompt'][0][0]['content'])
outputs = generator(prompt, max_length=100, truncation=True, num_return_sequences=1, temperature=0.7)
print(outputs[0]['generated_text'])
假设我们的输入如下:
<|im_start|>user
Given the YouTube video idea write an engaging title.
**Video Idea**: intro independent component analysis
**Additional Guidance**:
- Title should be between 30 and 75 characters long
- Only return the title idea, nothing else!<|im_end|>
<|im_start|>assistant
模型输出可能是:
"Unlocking Independent Component Analysis: The Key to Understanding Your Data!"
我们可以看到这个标题不是很好,它太长了,并且包含模糊的信息,例如“了解数据的关键!”。这与我实际用于独立成分分析(ICA)视频的标题形成鲜明对比:“独立成分分析(ICA) | 脑电分析示例代码”。
为了使模型生成的内容更符合我的偏好,让我们对其进行微调。首先,我们需要定义DPO的训练参数。在这里,我使用批量大小8并训练3个epoch。在每个epoch保存一个检查点,并在最后加载最佳的检查点:
ft_model_name = model_name.split('/')[1].replace("Instruct", "DPO")
training_args = DPOConfig(
output_dir=ft_model_name,
logging_steps=25,
per_device_train_batch_size=8,
per_device_eval_batch_size=8,
num_train_epochs=3,
load_best_model_at_end=True,
metric_for_best_model="eval_loss",
save_strategy="epoch",
eval_strategy="epoch",
eval_steps=1,
)
接下来,我们可以使用DPOTrainer训练模型:
trainer = DPOTrainer(
model=model,
args=training_args,
processing_class=tokenizer,
train_dataset=dataset['train'],
eval_dataset=dataset['valid'],
)
trainer.train()
模型在epoch 2之后开始过拟合,因此我们将使用该检查点作为最终模型。以下是微调后的模型针对之前相同的视频创意生成的内容:
<|im_start|>user
Given the YouTube video idea write an engaging title.
**Video Idea**: intro independent component analysis
**Additional Guidance**:
- Title should be between 30 and 75 characters long
- Only return the title idea, nothing else!<|im_end|>
<|im_start|>assistant
模型输出为:
Independent Component Analysis for Beginners
虽然它与我实际使用的标题不同,但与基础模型生成的内容相比,仍然有了显著的改进。
(三)步骤3:评估微调模型
虽然查看单个示例可以让我们对新模型的性能有一个大致的感受,但这并不是一个可靠的评估策略。一种方法是比较基础模型和微调模型生成的标题。
不幸的是,像品味和偏好这样的因素很难在标准评估中体现(这就是我们首先使用DPO的原因)。此外,我无法有效地使用判断大语言模型进行自动化评估(尽管使用GPT-4o进行了多次尝试)。
这就是为什么我再次进行了一些手动数据标注。我的过程如下:
- 从我的初始列表中随机挑选50个视频标题创意。
- 对于每个创意,分别使用基础模型和微调模型生成标题。
- 手动查看所有50对标题,并分配偏好标签。
- 计算微调模型生成的标题被偏好的频率。
由于我只考虑了50对标题,因此这花费的时间明显较少(约10分钟)。最终结果是,68%的情况下,微调后的标题更受青睐。这些数据的快照如下:
六、局限性
尽管三个微调模型在标题生成方面有了显著改进,但仍有一些改进的空间:
- Qwen的7b版本生成了偏好数据,而0.5b版本进行了微调。未来应该使用相同的模型来简化学习任务。
- 偏好数据集中的几个标题对都不太好。未来的工作应该尝试删除这些对,并观察性能变化。
- 一些生成的标题是中文的,这可能是因为中文在Qwen的训练数据中占比较大。下次,我将尝试使用其他模型,如Llama或Gemma。
- 这里我使用了一个5亿参数的模型,以便它可以在我的笔记本电脑上快速运行。然而,更大的模型可能在这个任务上表现得更好。
- DPO直接应用于经过指令调整的模型。未来的工作应该尝试先在真实的标题示例上进行一轮监督微调,然后再使用DPO进行偏好调整。
八、参考文献
[1] arXiv:2005.14165 [cs.CL]
[2] arXiv:2203.02155 [cs.CL]
[3] arXiv:1707.06347 [cs.LG]
[4] arXiv:2305.18290 [cs.LG]
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。