为视觉语言多模态模型进行偏好优化

news/2024/12/25 13:44:59/文章来源:https://www.cnblogs.com/huggingface/p/18306310

为视觉语言多模态模型进行偏好优化

训练模型使得它能够理解并预测人类偏好是一项比较复杂的任务。诸如 SFT (Supervised finetuning) 的传统的方法一般都需要耗费较大成本,因为这些算法需要对数据打上特定的标签。而偏好优化 (Preference Optimization) 作为一种替代选项,通常可以简化这一过程,并产出更准确的结果。通过对候选回答的对比和排序,而不是赋予固定的标签,偏好优化使得模型能更高效地捕捉人类偏好中的细微差别。

偏好优化已经在大语言模型中广泛使用了,但现在,它也可以用在视觉语言模型 (VLM) 上。得益于 TRL 的开发,现在我们可以 使用 TRL 对 VLM 进行直接偏好优化 (Direct Preference Optimization)。本文将会介绍使用 TRL 和 DPO 对视觉语言模型进行训练的全过程。

偏好数据集

进行偏好优化,首先我们需要有一个能体现用户偏好的数据集。在双项选择的设定下,相应的数据一般包含一个提示词 (Prompt) 和两个候选回答,两个回答中一个被记为选中 (chosen),另一个被记为淘汰 (rejected)。模型将要去学习着给出选中的回答,而不是被淘汰的那个。下图就是一个例子:

图片来自 openbmb/RLAIF-V-Dataset 数据集

图片来自 openbmb/RLAIF-V-Dataset 数据集

❔ Question: How many families?

  • ❌ Rejected: The image does not provide any information about families.
  • ✅ Chosen: The image shows a Union Organization table setup with 18,000 families.

需要注意的是,尽管选中的回答也不是完全正确的 (回答 18000 个家庭还是不对,应该是 18000000),但它也好于那个被淘汰的回答。

本文将使用 openbmb/RLAIF-V-Dataset 作为示例数据集,它包含了超过 83000 条标注的数据。可以通过下面代码查看一下数据集:

>>> from datasets import load_dataset
>>> dataset = load_dataset("openbmb/RLAIF-V-Dataset", split="train[:1%]")
>>> sample = dataset[1]
>>> sample["image"].show()
>>> sample["question"]
'how many families?'
>>> sample["rejected"]
'The image does not provide any information about families.'
>>> sample["chosen"]
'The image shows a Union Organization table setup with 18,000 families.'

我们将要训练的 VLM 模型需要文本和图像同时作为输入,所以这里的第一步还是要对数据集格式进行改造。一条数据应该被结构化成能模拟人机对话的形式。用户提供一个提示语,其中包含一张图片和一个问题,然后模型需要能够给出一个回答。我们用以下代码实现格式转换:

from datasets import features
from transformers import AutoProcessorprocessor = AutoProcessor.from_pretrained("HuggingFaceM4/idefics2-8b", do_image_splitting=False)def format(example):# Prepare the input for the chat templateprompt = [{"role": "user","content": [{"type": "image"}, {"type": "text", "text": example["question"]}],},]chosen = [{"role": "assistant","content": [{"type": "text", "text": example["chosen"]}],},]rejected = [{"role": "assistant","content": [{"type": "text", "text": example["rejected"]}],},]# Apply the chat templateprompt = processor.apply_chat_template(prompt, tokenize=False)chosen = processor.apply_chat_template(chosen, tokenize=False)rejected = processor.apply_chat_template(rejected, tokenize=False)# Resize the image to ensure it fits within the maximum allowable# size of the processor to prevent OOM errors.max_size = processor.image_processor.size["longest_edge"]example["image"].thumbnail((max_size, max_size))return {"images": [example["image"]], "prompt": prompt, "chosen": chosen, "rejected": rejected}# Apply the formatting function to the dataset,
# remove columns to end up with only "images", "prompt", "chosen", "rejected" columns
dataset = dataset.map(format, remove_columns=dataset.column_names)# Make sure that the images are decoded, it prevents from storing bytes.
# More info here https://github.com/huggingface/blog/pull/2148#discussion_r1667400478
f = dataset.features
f["images"] = features.Sequence(features.Image(decode=True)) # to avoid bytes
dataset = dataset.cast(f)

完成了格式转换,我们来看看第一条数据:

>>> dataset[1]
{'images': [<PIL.JpegImagePlugin.JpegImageFile image mode=L size=980x812 at 0x154505570>],'prompt': 'User:<image>how many families?<end_of_utterance>\n','rejected': 'Assistant: The image does not provide any information about families.<end_of_utterance>\n','chosen': 'Assistant: The image shows a Union Organization table setup with 18,000 families.<end_of_utterance>\n'}

OK!接下来准备好 GPU,训练马上开始。

训练

我们将使用 Idefics2-8b 作为我们的示例模型,但 TRL 里的 DPO 也是能用在像 Llava 1.5 和 PaliGemma 这样的模型上的 (可参考这篇文章: Finetuning Llava 1.5, PaliGemma and others)。不过训练之前,我们先检查一下我们的 GPU 显存是否够用:

训练需要多大的 GPU 显存?

一个 80GB VRAM 的 GPU 足够用来对 Idefics2-8b 进行 DPO 训练吗?我们可以先计算一下:

我们用 $ N $ 表示参数的数量,用 $ P $ 表示训练使用的精度。训练过程中,下列部分需要共同放入显存中:

  • 要训练的模型: $ N \times P $
  • 用以防止模型产生偏离的参考模型: 和要训练的模型一样大,所以也是 $ N \times P $
  • 梯度: 我们对所有参数都进行训练,所以每个参数都有梯度: $ N \times P $
  • 优化器的状态量: 我们使用 AdamW,一个参数会保存两个状态量,所以需要: $ 2 \times N \times P $

Idefics2-8b 有 80 亿 (8B) 参数,我们使用 float32 精度,每个参数占 4 个字节。所以总的显存需求是:

参数来源 计算公式 显存需求
要训练的模型 $ 8 \times 10^9 \times 4 $ 32 GB
参考模型 $ 8 \times 10^9 \times 4 $ 32 GB
梯度 $ 8 \times 10^9 \times 4 $ 32 GB
优化器状态量 $ 2 \times 8 \times 10^9 \times 4 $ 64 GB
合计 160 GB

这远超我们前面说的 80GB 显存了!幸运的是,我们可以使用量化、LoRA 等技术来大幅度地减少显存需求,让训练可以进行。接下来我们将介绍这些技术。

量化

量化会降低模型权重和激活值的精度,但也同时显著减少内存需求。将精度从 float32 改为 bfloat16 ,会让每个参数需要的比特数从 4 比特减少到 2 比特。这一策略不仅能减少内存使用,还会显著加速训练,确保以最小代价保证足够高的性能。具体做法如下:

import torch
from transformers import AutoModelForVision2Seqmodel = AutoModelForVision2Seq.from_pretrained("HuggingFaceM4/idefics2-8b", torch_dtype=torch.bfloat16)

通过如下 bf16=True 的设置, bfloat16 也可以被用在优化器上:

from transformers import TrainingArgumentstraining_args = TrainingArguments(..., bf16=True)

LoRA

LoRA 对参数矩阵进行低秩分解; 在训练时,固定住原参数矩阵,仅训练分解出的两个矩阵。是一种大规模减少 LLM 训练参数的方法。LoRA 已被集成在了 PEFT 库里,使用非常方便:

  from transformers import AutoModelForVision2Seq
+ from peft import get_peft_model, LoraConfigmodel = AutoModelForVision2Seq.from_pretrained("HuggingFaceM4/idefics2-8b")
+ peft_config = LoraConfig(target_modules="all-linear")
+ model = get_peft_model(model, peft_config)

PEFT 像是给原模型进行了一次封装 (代码中称为 adapter )。训练时,实际上是这个 adapter 在被训练,而原有的模型保持不动。我们现在算算 LoRA 帮我们减少了多少要训练的参数:

>>> model.print_trainable_parameters()
trainable params: 55,348,736 || all params: 8,458,116,848 || trainable%: 0.6543860411799315

它帮我们把要训练的参数从八十亿降到了五千五百万!差距真大!这将显著减少显存需求。

使用 bfloat16 和 LoRA 后的显存需求

现在我们来算算新的显存需求:

参数来源 计算公式 显存需求
要训练的模型 $ 8 \mathrm{G} \times 2 $ 16 GB
参考模型 $ 8 \mathrm{G} \times 2 $ 16 GB
梯度 $ 55 \mathrm{M} \times 2 $ 0.1 GB
优化器状态量 $ 2 \times 55 \mathrm{M} \times 2 $ 0.2 GB
合计 32.3 GB

现在我们仅需 32GB 的显存就可以训练我们的 Idefics2-8b 模型了。这合理多了,用 80GB 显存的 GPU 就可以训练了。

PEFT 文档 和 谷歌这篇关于 LoRA 和 QLoRA 文章 也提供了很多关于显存优化的帮助指南,读者感兴趣可以阅读。

训练时 batch size 怎么设定?

上述关于显存占用的计算还不算准确,因为实际训练时,激活值也需要占用显存。激活值是神经网络各层的输出。作为中间产物,它们的显存占用量取决于模型结构和训练时的 batch size。准确计算这些显存需求还是很困难的,我们一般依赖实验观察。

若想找到一个合适的 batch size ( per_device_train_batch_size ),你可以先随便选取一个你认为合适的数值 (比如 64) 然后试着开始训练。当然这大多数情况下会爆显存 (OOM)。如果这样,你可以减半 batch size,同时将 gradient_accumulation_steps 翻倍,以获得和原先 batch size 设定相同的效果。反复重复这一过程,最终当 OOM 不再出现时,你就可以训练了。我们的实验参数是: per_device_train_batch_size 设为 2, gradient_accumulation_steps 设为 32。

你还可以使用 gradient_checkpointing 来减少激活值所需的内存。这一技术在计算梯度时,会重新计算一遍前向过程,而不是在前向过程中保存用于计算梯度的中间结果。需要使用时,设置 gradient_checkpointing=True 即可。

完整训练代码

一切就绪,我们可以开始训练了。下面是我们的完整训练代码。除了上面提到的部分外,我们还设置了 dataset_num_procdataloader_num_workers 等参数,用于加速数据预处理。

# dpo_idefics2-8b.py
from datasets import features, load_dataset
from transformers import AutoModelForVision2Seq, AutoProcessor
import torch
from trl import DPOConfig, DPOTrainer
from peft import LoraConfigdef main():# Load the model and processormodel = AutoModelForVision2Seq.from_pretrained("HuggingFaceM4/idefics2-8b", torch_dtype=torch.bfloat16)processor = AutoProcessor.from_pretrained("HuggingFaceM4/idefics2-8b", do_image_splitting=False)# Load the datasetdataset = load_dataset("openbmb/RLAIF-V-Dataset", split="train")def format(example):# Prepare the input for the chat templateprompt = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": example["question"]}]}]chosen = [{"role": "assistant", "content": [{"type": "text", "text": example["chosen"]}]}]rejected = [{"role": "assistant", "content": [{"type": "text", "text": example["rejected"]}]}]# Apply the chat templateprompt = processor.apply_chat_template(prompt, tokenize=False)chosen = processor.apply_chat_template(chosen, tokenize=False)rejected = processor.apply_chat_template(rejected, tokenize=False)# Resize the image to ensure it fits within the maximum allowable# size of the processor to prevent OOM errors.max_size = processor.image_processor.size["longest_edge"]// 2example["image"].thumbnail((max_size, max_size))return {"images": [example["image"]], "prompt": prompt, "chosen": chosen, "rejected": rejected}# Apply the formatting function to the datasetdataset = dataset.map(format, remove_columns=dataset.column_names, num_proc=32)# Make sure that the images are decoded, it prevents from storing bytes.# More info here https://github.com/huggingface/blog/pull/2148#discussion_r1667400478f = dataset.featuresf["images"] = features.Sequence(features.Image(decode=True))dataset = dataset.cast(f)# Train the modeltraining_args = DPOConfig(output_dir="idefics2-8b-dpo",bf16=True,gradient_checkpointing=True,per_device_train_batch_size=2,gradient_accumulation_steps=32,num_train_epochs=1,dataset_num_proc=32, # tokenization will use 32 processesdataloader_num_workers=32, # data loading will use 32 workerslogging_steps=10,)trainer = DPOTrainer(model,ref_model=None, # not needed when using peftargs=training_args,train_dataset=dataset,tokenizer=processor,peft_config=LoraConfig(target_modules="all-linear"),)trainer.train()if __name__ == "__main__":main()

启动脚本开始训练,接下来就等待结果吧 🚀

accelerate launch dpo_idefics2-8b.py

结果

训练需要几小时的时间。当训练完成后,我们可以看看训练相关指标的变化曲线:

Learning curves

In DPO, we focus on several metrics to assess the quality of the training:

在 DPO 中,为了评估训练,我们关注这几个指标:

  • 精度 (Accuracy): 在训练样本中,模型更愿意输出被选中的回答而不是被淘汰的回答,这个比率有多少。我们可以看到随着训练,精度在提升,这是个好的信号。
  • 奖励 (Rewards): 这一指标与一个回答 (选中或淘汰) 被选中的概率呈正相关,读者可以参考 DPO 论文 , 第 5 部分。我们希望被选中的回答对应的奖励高于被淘汰的回答。我们可以通过两者奖励的差值 ( reward margin ) 来看: 图中这一差值逐渐变大, 这也是个好的信号。

评测

推理代码

训练完成后,我们接下来就要在一些样本上评测一下了。这会让我们了解模型学习得怎么样、预测有效性如何。下面的代码可以用来在测试样本上进行评测:

from transformers import AutoModelForVision2Seq, AutoProcessor
from PIL import Imagemodel = AutoModelForVision2Seq.from_pretrained("HuggingFaceM4/idefics2-8b").to("cuda")
processor = AutoProcessor.from_pretrained("HuggingFaceM4/idefics2-8b", do_image_splitting=False)
model.load_adapter("HuggingFaceH4/idefics2-8b-dpo-rlaif-v-v0.3") # <-- Load the adapter we've just trained# Process
user_message = ...
image_path = ...
data = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": user_message}]}]
prompts = processor.apply_chat_template(data, add_generation_prompt=True) # add_generation_prompt=True to end the prompt with "ASSISTANT:"
images = [Image.open(image_path)]
inputs = processor(prompts, images, return_tensors="pt")
inputs = {k: v.to("cuda") for k, v in inputs.items()}# Generate
generated_ids = model.generate(**inputs, max_new_tokens=500)
response_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
print(response_text)

前面提到的 openbmb/RLAIF-V-Dataset 这个数据集是用来减少大模型幻觉的。但真实训练效果如何呢?我们可以使用 AMBER benchmark 这个评测基准,该数据集专门被用来评估 VLM 的幻觉情况。我们列出 Idefics2 和 Idefics2+DPO 的结果,并和其它模型对比。

Accuracy F1
GPT-4o 88.8 91.6
Idefics2+DPO 85.9 89.4
Idefics2 85.8 89.1
GPT-4v 83.4 87.4
MiniGemini 82.6 87.6
LLaVA-NeXT 81.4 85.4
QWEN-VL 81.9 86.4
LURE 73.5 77.7
OPERA 75.2 78.3
Less-is-more 72.4 75.8
VCD 71.8 74.9

总的来看,有点作用!幻觉似乎减少了点。训练看来还是成功的。

下面我们也列出一些可视化结果出来:

Image Question Idefics2 Idefics2+DPO
AMBER_2 Are there two ships in this image? Yes No
AMBER_111 Is the ground uneven in this image? No Yes
AMBER_7 Is there one shovel in this image? Yes No

你也可以自己找些例子来测试一下这个模型!

🤗 Space: HuggingFaceH4/compare_idefics-8b-dpo

微调 Llava 1.5 和 PaliGemma 等模型

截至本文完稿时,TRL 的 DPO 实现已支持 Idefics2、Llava 1.5 和 PaliGemma,同时 TRL 也在努力支持更多的模型。最简单的调用方法还是使用 TRL 提供的 示例脚本。例如,如果你想微调 PaliGemma,你可以这样:

accelerate launch examples/scripts/dpo_visual.py \--dataset_name HuggingFaceH4/rlaif-v_formatted \--model_name_or_path google/paligemma-3b-pt-224 \--per_device_train_batch_size 2 \--gradient_accumulation_steps 32 \--dataset_num_proc 32 \--output_dir dpo_paligemma_rlaif-v \--bf16 \--torch_dtype bfloat16 \--gradient_checkpointing \--use_peft \--lora_target_modules=all-linear

更多关于 PaliGemma 微调的信息可以在 smol-vision 这个项目里看到。

🚀🚀 好了!你现在已经会使用 DPO 微调 VLM 模型了!我们期待你在社区分享你的模型、数据和独特见解!


原文链接: https://hf.co/blog/dpo_vlm

原文作者: Quentin Gallouédec, Shengyi Costa Huang, Merve Noyan, Kashif Rasul

译者: hugging-hoi2022

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/744972.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

别小瞧它,提高效率可了解可拖拽的工作流引擎

提升效率,可以随时来了解低代码技术平台、可拖拽的工作流引擎更多特点。当前,社会发展程度越来越高,很多企业都希望寻求更优的平台产品实现提质增效的目的。低代码技术平台、可拖拽的工作流引擎具有可视化操作界面、更灵活、好操作等多个优势特点,在提升办公效率方面具有事…

fastqc和Trimmomatic的使用

1.FastQC分析检测报告 在先前的记录中,我们已经得到了我们的QC报告,现在要针对我们的报告对原始数据进行过滤 其中 和 都表明该数据需要去接头,并对序列进行处理 2.Trimmomatic的下载 首先,使用conda安装Trimmomatic conda install Trimmomatic 就可以安装完毕了,安装完使…

数据仓库建模工具之一——Hive学习第二天

Hive的概述 1、Hive基本概念 1.1 Hive简介Hive本质是将SQL转换为MapReduce的任务进行运算,底层由HDFS来提供数据存储,说白了hive可以理解为一个将SQL转换为MapReduce的任务的工具,甚至更近一步说hive就是一个MapReduce客户端。 为什么使用Hive?使用hadoop,成本太高,项目要…

三分钟了解自定义表单自定义工作流的多个优势

如果想了解自定义表单自定义工作流的优势特点,可以通过本文获取更多详情信息。降本、提高效率、解决信息孤岛是很多企业亟需要解决的问题。什么样的软件平台可以实现这一目标?可以随时来了解低代码技术平台。它当中的自定义表单自定义工作流拥有多个优势特点,可以为企业降低…

jenkins+allure常见问题汇总

1.生成allure报告时,报Can not find any allure commandline installation. 原因:jenkins下载安装allure插件后,没有配置allure路径 解决办法:Manage Jenkins>Tools>Allure Commandine安装2.执行pytest --alluredir=./allure-result --clean-alluredir时,报error: u…

SSM学习路线

Maven Spring Mybatis SpringMVC SSM整合 Spring Boot Mybatis Plus 项目学习

高通Perflock

高通的Perflock是Qualcomm公司开发的一项技术,用于优化设备性能和功耗管理。Perflock是一种锁定机制,允许操作系统或应用程序在需要时对处理器的性能状态进行控制,从而确保在关键任务或高性能需求的情况下,处理器能够维持在高性能状态。 主要功能和特点性能锁定:Perflock允…

vue3+TS从0到1手撸后台管理系统

1.路由配置 1.1路由组件的雏形 src\views\home\index.vue(以home组件为例)1.2路由配置 1.2.1路由index文件 src\router\index.ts //通过vue-router插件实现模板路由配置 import { createRouter, createWebHashHistory } from vue-router import { constantRoute } from ./rou…

黑盒测试用例设计方法三

一、正交实验法 1)正交法原理介绍1、日本人,统计学家提出的 2、使用的工具:正交表 3、统计和分析实验数据,从大量实验中找到合适的实验数据组合(原本用于工业生产的数据组合与实验室的数据挑选) 4、从大量的试验组合中,挑选出一部分具有代表性的点,进行实验,分析数据。…

需求流程之产品愿景和用户画像

1.产品愿景: 羽毛球比赛计分程序旨在为各类羽毛球赛事提供高效、准确且便捷的计分解决方案,提升比赛的组织效率和公正性,同时为运动员、裁判、观众和赛事组织者带来优质的体验。2.用户画像

荣耀折叠,太卷啦

又薄又强,只缺“杀手”……近日的Magic旗舰新品发布会上,荣耀一口气带来了两款新折叠屏旗舰新品——荣耀Magic V3和荣耀Magic Vs3。 荣耀总裁赵明一如既往地自信,向大家一一阐述在折叠屏领域近百亿元研发投入换来的种种“奇迹”:创行业纪录的轻薄,青海湖电池加持,航天特种…