“StackLLaMA”: 用 RLHF 训练 LLaMA 的手把手教程

这篇具有很好参考价值的文章主要介绍了“StackLLaMA”: 用 RLHF 训练 LLaMA 的手把手教程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

如 ChatGPT,GPT-4,Claude语言模型 之强大,因为它们采用了 基于人类反馈的强化学习 (Reinforcement Learning from Human Feedback, RLHF) 来使之更符合我们的使用场景。

本博客旨在展示用 RLHF 训练一个 LLaMA 模型,以回答 Stack Exchange 上的问题。具体而言,包含以下几个方面:

  • 有监督的微调 (Supervised Fine-tuning,SFT)。
  • 奖励 / 偏好建模 (Reward / preference modeling,RM)。
  • 基于人类反馈的强化学习 (RLHF)。

摘自 InstructGPT 论文,Ouyang, Long, et al. “Training language models to follow instructions with human feedback.” arXiv preprint arXiv:2203.02155 (2022).

结合了上述方法,我们发布了 StackLLaMA 模型,该模型在 🤗 Hub 上开源 (访问链接查看 Meta 的原始 LLaMA ),整个 训练的流程 已经集成到了 Hugging Face TRL 库中 。你可以通过下面的 demo 来尝试该模型。

LLaMA 模型

在实践 RLHF 时,选取一个合适的模型很重要: RLHF 只是一个让模型满足我们交互形式的需求的微调过程 。所以我们选取了最近上线的 LLaMA 模型。LLaMA 模型是 Mata AI 最近推出的大语言模型。其参数量大小涵盖 7B 到 65B,以及训练在 1T 和 1.4T 的 token 上,这让其很实用。我们这里采用 7B 的模型。(请填写 Meta AI 的这份 表单 来下载模型)。

Stack Exchange 数据集

收集人类的反馈数据集是很复杂且昂贵的劳动。为了做到这个,并且还能保证模型的有效性,我们使用 StackExchange 数据集。该数据集涵盖了 StackExchange 平台上的问题和答案 (包含 StackOverflow 的编程等话题下的)。这很适合我们的实践,因为其包含了每个答案的赞和踩的数量。

我们按照 Askell et al. 2021 中的方法,给每个答案赋分:

score = log2 (1 + upvotes) rounded to the nearest integer, plus 1 if the questioner accepted the answer (we assign a score of −1 if the number of upvotes is negative).

对奖励模型,我们将看到每个问题总是需要两个答案对比。有些问题有很多答案,可以产生很多对,我们只取十个以限制每个问题的数据量。最后,我们把格式从 HTML 转化到 Markdown 以提高输出的可读性。你可以看到数据集和处理过程的 [笔记本]。(https://huggingface.co/datasets/lvwerra/stack-exchange-paired。)

高效训练策略

即使是最小 LLaMA 模型的训练,都需要大量内存。估算一下: 以 bf16 半精度,每个参数用 2 个字节 (以 fp32 精度四字节的标准),训练时需要 8 个字节 (例如 Adam 优化器,参见 Tramsformers 的 性能文档)。可见 7B 参数量的模型将用 (2+8)* 7B = 70 GB 的内存,并且还可能需要更多用于计算诸如注意力分数的中间值。所以很难在一张 80GB 显存的 A100 上训练。或许你可以使用一些技巧,比如用更高效的半精度训练的优化器来压缩内存,但溢出是迟早的。

另外的可能是 参数高效的微调(Parameter-Efficient Fine-Tuning, PEFT) 技术,比如 peft 库,它可以对使用 8-bit 加载的模型做 低秩优化(Low-Rank Adaptation,LoRA)。

线性层的低秩优化: 额外参数 (橙色) 被加在 Frozen 层 (蓝色),编码后的隐藏状态与 Frozen 层的隐藏状态叠加在一起。

以 8bit 加载模型会大幅降低内存占用,因为每个参数只要一字节 (比如 7B LLaMA 是 7GB 内存)。与直接训练原始模型不同,LoRA 在特定层 (一般是注意力层) 添加少量新参数,大幅降低了需要训练的参数。

此情此景,一个衡量标准是 1B 的参数在整个微调过程中占 ~1.2-1.4GB (和具体 batch size 及序列长度有关)。在参考的博客中具体讨论了,这使得低成本下微调较大参数规模的模型成为可能 (比如在一张 A100 上微调 50-60B 的参数)。

这些技术能让微调大模型的任务,在消费级设备和 Google Colab 上执行。这里提供一些值得关注的演示 demo: facebook/opt-6.7b (在 float16 精度下 13GB) 和 openai/whisper-large
跑在 Google Colab (15GB 显存) 上。欲了解 peft 的使用,请参见 github 仓库 或者之前的 博客介绍: 在客户端训练 20B 参数量的模型。

现在我们能在一张 GPU 上微调很大的模型了,但训练还是会很慢。此时最简单的策略便是并行化: 把一个训练同时放到不同的 GPU 上,各 GPU 接受不同的 batch。这样我们可以并行执行前向传播和后向传播,通过增加 GPU 的数量实现并行能力提升。

我们可以选用 trainsformers.Traineraccelerate,因为它们都支持无代码变更进行数据并行化。只需注意调用 torchrun 或者 accelerate launch 脚本时的参数即可实现。比如以下就是在一个 8 显卡的机器上分别用 accelerate launchtorchrun的方法:

accelerate launch --multi_gpu --num_machines 1  --num_processes 8 my_accelerate_script.py
torchrun --nnodes 1  --nproc_per_node 8 my_torch_script.py

有监督的微调

在训练奖励模型和用 RL 之前,模型若是已经在我们感兴趣的方面表现好将会很有帮助。在我们的示例中,我们想要其能回答问题,而其他时候,我们可能它能听指令 (这时对指令执行的微调是理想的)。实现这个最简单的方法便是面向该语言任务,用该任务和领域的文本,继续训练。StackExchange 数据集 含 10M 的指令量,所以我们能用其子集很容易地训练。

在用 RLHF 之前的模型微调没有特别的,就是一般的面向语言任务的预训练模型微调。为了高效利用数据,我们采用了称之为 打包 的技术: 与 batch 中的每个样本均由单一文本组成,最后基于最长的文本来 padding (填充),我们把很多文本拼接起来,用 EOS token 来隔开,然后分割成一些 chunk (切块) 来做成 batch,避免 padding。

该方法大大提高了效率,因为模型输入的所有 token 都对 loss 有所训练,而非 padding 作为掩码被丢弃了。如果你没有足够数据,并且担心随意地分开 token 会失去上下文语义,你也可以用传统的数据加载器
ConstantLengthDataset 解决了 打包技术,并且我们能在用 peft 加载模型后用 Trainer。首先,我们用 int8 加载模型,准备训练,然后加入 LoRA 微调器。

# load model in 8bit
model = AutoModelForCausalLM.from_pretrained(
        args.model_path,
        load_in_8bit=True,
        device_map={"": Accelerator().local_process_index}
    )
model = prepare_model_for_int8_training(model)

# add LoRA to model
lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
)

model = get_peft_model(model, config)

我们根据相应的语言任务,对模型训练几千个 step (步),并保存模型。由于我们将会有其他微调模型的目的,我们将 LoRA 的微调器权重合并到原模型中。

声明: 因为 LLaMA 的许可证规定,我们只能发布微调器的权重,你需要填 Meta AI 的 表格 来获取模型,然后用这个 脚本 来转成 🤗 Transformers 格式。注意 🤗 Transformers 应该从源码安装,或者 v4.28 版。

现在我们已经微调好了模型,可以训练奖励模型了。

奖励模型和人类偏好

原则上,我们可以直接用人类标注来对模型做 RLHF 微调。然而,这将需要我们给人类发送一些样本,在每轮优化后计分。这是贵且慢的,因为收敛需要的训练样本量大,而人类阅读和标注的速度有限。

一个比直接反馈更好的策略是,在进入 RL 循环之前用人类标注集来训练一个奖励模型。奖励模型的目的是模拟人类对文本的打分。构建奖励模型有许多能用的策略: 最直接的便是预测标注 (比如根据好与坏,输出比分或者布尔值)。最佳实践是,预测结果的排序,即对每个 prompt (输入文本) 对应的两个结果 \((y_k, y_j)\),模型预测人类标注的比分哪个更高。

或者表示为 loss (损失) 函数:

\[\mbox{loss}(\theta) = - E_{(x, y_j, y_k)~D} [ \mbox{log}( \sigma( r_\theta (x, y_j) - r_\theta(x, y_k)) ) ] \]

其中 \(r\) 是模型对可能的标注 \(y_j\) 的预测分数。

在 StackExchange 数据集上,我们能得到两个答案的受欢迎程度。有了这个信息和上面的损失函数,我们就能自定义 loss 来改 transformers.Trainer 了。


class RewardTrainer(Trainer):
    def compute_loss(self, model, inputs, return_outputs=False):
        rewards_j = model(input_ids=inputs["input_ids_j"], attention_mask=inputs["attention_mask_j"])[0]
        rewards_k = model(input_ids=inputs["input_ids_k"], attention_mask=inputs["attention_mask_k"])[0]
        loss = -nn.functional.logsigmoid(rewards_j - rewards_k).mean()
        if return_outputs:
            return loss, {"rewards_j": rewards_j, "rewards_k": rewards_k}
        return loss

我们用数据集中的 100000 对,并在 50000 对上评估。在比较小的 batch size,为 4 下,我们用 LoRA 的 peft 微调器来训练 LLaMA 模型,在 BF16 精度下用 Adam 优化器。我们的 LoRA 设置是:

peft_config = LoraConfig(
    task_type=TaskType.SEQ_CLS,
    inference_mode=False,
    r=8,
    lora_alpha=32,
    lora_dropout=0.1,
)

训练用 Weights & Biases 来记日志,并在 🤗 训练集群上,用 8 卡 A-100,要数小时,最后准确率为 67%。尽管看上去可能低了,但想想这个任务的难度。

如下文要细说的,训练结果将作为固定参数,以供下游使用。

基于人类反馈的强化学习

现在我们手头有了微调的语言模型和奖励模型,可以开始执行 RL 循环了: 这个过程大致分为三步

  1. 生成对 prompt (输入文本) 的反馈。
  2. 用奖励模型来对反馈评分。
  3. 对评分,进行一轮策略优化的强化学习。

在被 token 化并输入奖励模型前,提问和回答的 prompt 模版如下:

Question: <Query>
Answer: <Response>

在有监督训练 (SFT),奖励模型训练 (RM) 和 RLHF 的阶段都用此模版。

用 RL 训练语言模型出现的常见问题是,模型可能学会胡说八道以糊弄奖励模型,后者可能给高分。为了权衡,我们对奖励增加惩罚: 留一份没有训练的模型,如何比较两者输出的 KL 散度

\[\mbox{R}(x, y) = \mbox{r}(x, y) - \beta \mbox{KL}(x,y) \]

其中 \(r\) 是奖励模型的结果,\(\mbox{KL}(x,y)\) 是当前模型和对比模型的 KL 散度差。

再提一遍,我们用 peft 来实现内存高效的训练,其对 RLHF 阶段提供了优势。这里参考的模型和训练的模型用同一个基底,也就是有监督训练 (SFT) 的结果,它是用 8-bit 来加载,并且自始自终是固定的。我们仅用 PPO 方法优化最终模型的 LoRA 权重,同时全部共享一个基底模型。

for epoch, batch in tqdm(enumerate(ppo_trainer.dataloader)):
    question_tensors = batch["input_ids"]
        
    # sample from the policy and generate responses
    response_tensors = ppo_trainer.generate(
        question_tensors,
        return_prompt=False,
        length_sampler=output_length_sampler,
        **generation_kwargs,
    )
    batch["response"] = tokenizer.batch_decode(response_tensors, skip_special_tokens=True)

    # Compute sentiment score
    texts = [q + r for q, r in zip(batch["query"], batch["response"])]
    pipe_outputs = sentiment_pipe(texts, **sent_kwargs)
    rewards = [torch.tensor(output[0]["score"] - script_args.reward_baseline) for output in pipe_outputs]

    # Run PPO step
    stats = ppo_trainer.step(question_tensors, response_tensors, rewards)
    # Log stats to WandB
    ppo_trainer.log_stats(stats, batch, rewards)

我们用 🤗 集群,在 3x8 A100-80GB 的机器上训练了 20h,但一个差不多的结果很快 (大概,在 8 A100-80GB 上训练 20h)。所有的训练过程都在 Weight & Biases 上找到。

每个 batch 的奖励,对每步的训练,在 ~1000 步时模型的效果最好。

所以模型训好了能干啥嘞 ? 我们拭目以待 !

尽管我们不该太相信其结果,至少目前。但结果已经很好了,甚至附上了 Google 链接。我们来看看训练时的挑战。

挑战,不稳定和突破口

用 RL 训练 LLM (Large Language Models,大语言模型) 不总是一帆风顺的,你看到的本文也是经历无数实验,无数失败和无数调参的。即便如此,该模型也不能说变现完美。这儿,我们分享一些遇到的观察和问题。

奖励更高代表更好表现?

天呐,这个实验肯定表现很好 ! 看奖励的曲线多甜啊 !

在 RL 中,一般而言,奖励越高越好。在 RLHF 中,我们用了一个奖励模型,它不完美,所以留给了 PPO 算法捡漏的机会。这能导致奖励突然上升,然而当检查文本结果时,却充斥了字符 “```”,因为奖励模型对含有代码 stack exchange 的答案更信任。幸运的是,该问题碰到的很少,应该是采取的 KL 散度的惩罚项起到了作用。

KL 散度总是正的?

如我们前面所提到的,一个 KL 惩罚项被用来保证训练后的分布和原始分布接近。一般地 , KL 散度来度量两个分布的相似程度,并且总是正的。然而,在 trl 我们用了一个 KL 的近似,期望值和真的 KL 散度相同。

\[KL_{pen} (x, y) = \mbox{log} (\pi_\phi^\mbox{RL}(y | x) / \pi^{\mbox{SFT}}(y|x)) \]

显然,当训练中一个 token 比原始模型概率低,这会导致 KL 散度为负,合适的取样和平均总能得到正的。但是一些采样的生成策略导致了不匀称的采样。比如,当生成被 padding 的序列 batch 时和当设置 EOS token 被压缩的最小长度是,模型会有很大/很小的概率到负 KL 散度的 token。同时 PPO 算法是面向奖励优化的,模型就会追逐负的惩罚,导致训练不稳定。

对生成和采样,你需要特别小心。我们建议一开始用最简单的方式,如何在逐渐复杂。

任然存在的问题

任然有很多问题我们不懂,比如下面,loss 间断地跳跃,导致之后的不稳定

一旦我们解决了这些问题,我们就会上传变化到 trl 上,以保证社区受益。

总结

在本博客,我们走过了 RLHF 训练的整个流程,从准备人类标注的数据集开始,调整语言模型到特定领域,训练奖励模型,并最终用 RL 训练一个模型。

通过使用 peft,任何人都能在一张 GPU 上跑我们的实验 ! 如果训练慢了,可以用数据并行化的方法,不需要改任何代码,或者用多张 GPU 并行提高训练速度。

对实际应用,这仅仅是第一步 ! 一旦你有了模型,你就要和其他模型比较优劣。这个可以用一个面向不同模型的排名生成做到,和我们训练奖励数据集类似。

一旦你加入了评估的步骤,好玩的就开始了: 你可以在原数据集上反复炼丹,也可以增加数据集或者对原数据集提纯。另外,你可以对奖励模型和生成试不同大小和结构的模型,这需要时间。

我们在积极提高 TRL 以保证 RLHF 的每一步都可见,并且十分激动能看到人们用它来构建的东西。如果你想有所贡献,欢迎看我们的 Github Issue。

引用

@misc {beeching2023stackllama,
    author = { Edward Beeching and
                     Younes Belkada and
                     Kashif Rasul and
                     Lewis Tunstall and
                     Leandro von Werra and
                     Nazneen Rajani and
                     Nathan Lambert
                   },
    title = { StackLLaMA: An RL Fine-tuned LLaMA Model for Stack Exchange Question and Answering },
    year = 2023,
    url = { https://huggingface.co/blog/stackllama },
    doi = { 10.57967/hf/0513 },
    publisher = { Hugging Face Blog }
}

感谢

我们感谢 Philipp Schmid 分享了他对文本生成绝妙的 demo, 我们的 demo 也是基于他的。我们也感谢 Omar Sanseviero 和 Louis Castricato 对我们博客的草稿提供宝贵详尽的反馈。


英文原文: https://hf.co/blog/stackllama

作者: Edward Beeching, Kashif Rasul, Younes Belkada, Lewis Tunstall, Leandro von Werra Nazneen Rajani, Nathan Lambert

译者: Vermillion-Qi(张奇), zhongdongy (阿东)文章来源地址https://www.toymoban.com/news/detail-434119.html

到了这里,关于“StackLLaMA”: 用 RLHF 训练 LLaMA 的手把手教程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 省钱!NewBing硬核新玩法;手把手教你训练AI模特;用AI替代同事的指南;B站最易上手AI绘画教程 | ShowMeAI日报

    👀 日报周刊合集 | 🎡 生产力工具与行业应用大全 | 🧡 点赞关注评论拜托啦! 社区同学分享了两种NewBing的新用法,不仅准确高效,而且免免免费!简直不要太爽! 第一种硬核玩法直接秒掉「ChatPDF」「ChatDOC」这类文档工具。如果,对于在浏览器中打开的文档,NewBing不仅可

    2024年02月01日
    浏览(49)
  • Llama 3 开源!手把手带你进行大模型推理,部署,微调和评估

    节前,我们组织了一场算法岗技术面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学,针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 基于大模型实践和技术交流,我们

    2024年04月26日
    浏览(35)
  • 手把手教你使用Segformer训练自己的数据

    使用Transformer进行语义分割的简单高效设计。 将 Transformer 与轻量级多层感知 (MLP) 解码器相结合,表现SOTA!性能优于SETR、Auto-Deeplab和OCRNet等网络 相比于ViT,Swin Transfomer计算复杂度大幅度降低,具有输入图像大小线性计算复杂度。Swin Transformer随着深度加深,逐渐合并图像块来

    2024年01月20日
    浏览(76)
  • 手把手教你用MindSpore训练一个AI模型!

    首先我们要先了解深度学习的概念和AI计算框架的角色( https://zhuanlan.zhihu.com/p/463019160 ),本篇文章将演示怎么利用MindSpore来训练一个AI模型。和上一章的场景一致,我们要训练的模型是用来对手写数字图片进行分类的LeNet5模型 请参考( http://yann.lecun.com/exdb/lenet/ )。 图1 M

    2024年02月04日
    浏览(60)
  • YOLOV7训练自己的数据集以及训练结果分析(手把手教你)

    YOLOV7训练自己的数据集整个过程主要包括:环境安装----制作数据集----参数修改----模型测试----模型推理 labelme标注的数据格式是VOC,而YOLOv7能够直接使用的是YOLO格式的数据,因此下面将介绍如何将自己的数据集转换成可以直接让YOLOv7进行使用。 1. 创建数据集 在data目录下新建

    2023年04月20日
    浏览(53)
  • 零基础手把手训练实践-图像分类模型-基于达摩院modelscope

    -基于达摩院modelscope 导读:图像分类模型是最简单的,也是最基础的计算机视觉任务,应用非常广泛。本文将手把手介绍零基础训练图像分类模型的实践过程。文章主要介绍如何在标注好的数据集基础上,进行微调,使模型能够在新的数据上重新适配一个新的分类任务。 阅读

    2024年02月13日
    浏览(37)
  • 手把手调参 YOLOv8 模型之 训练|验证|推理配置-详解

    YOLO系列模型在目标检测领域有着十分重要的地位,随着版本不停的迭代,模型的性能在不断地提升,源码提供的功能也越来越多,那么如何使用源码就显得十分的重要,接下来通过文章带大家手把手去了解Yolov8(最新版本)的每一个参数的含义,并且通过具体的图片例子让大

    2024年02月05日
    浏览(48)
  • 手把手教你训练一个VAE生成模型一生成手写数字

    VAE(Variational Autoencoder)变分自编码器是一种使用变分推理的自编码器,其主要用于生成模型。 VAE 的编码器是模型的一部分,用于将输入数据压缩成潜在表示,即编码。 VAE 编码器包括两个子网络:一个是推断网络,另一个是生成网络。推断网络输入原始输入数据,并输出两

    2024年02月06日
    浏览(59)
  • 手把手教你如何使用YOLOV5训练自己的数据集

    YOLOV5是目前最火热的目标检测算法之一。YOLOV5为一阶段检测算法因此它的速度非常之快。可以在复杂场景中达到60祯的实时检测频率。 接下来本文将详细的讲述如何使用YOLOV5去训练自己的数据集 YOLOV5中使用了Tensorboard和Wandb来可视化训练,其中Wandb配置可以看这篇文章: Wand

    2024年02月05日
    浏览(70)
  • 如何运用yolov5训练自己的数据(手把手教你学yolo)

    在这篇博文中,我们对YOLOv5模型进行微调,用于自定义目标检测的训练和推理。 深度学习领域在2012年开始快速发展。在那个时候,这个领域还比较独特,编写深度学习程序和软件的人要么是深度学习实践者,要么是在该领域有丰富经验的研究人员,或者是具备优秀编码技能

    2024年02月07日
    浏览(142)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包