视觉语言模型详解

发布于 2024 年 4 月 11 日
在 GitHub 上更新

这篇博客文章写于 2024 年 4 月,对视觉语言模型的内部工作原理、现有视觉语言模型套件以及如何微调它们进行了很好的介绍。我们还撰写了 2025 年 4 月更新版,其中包含更多功能和更多模型。阅读完这篇后,请务必查看!

视觉语言模型是一种可以同时从图像和文本中学习的模型,用于处理从视觉问答到图像字幕生成的多种任务。在本文中,我们将深入探讨视觉语言模型的主要构建模块:了解其概况、掌握其工作原理、找出如何选择合适的模型、如何使用它们进行推理,以及如何利用今天发布的新版 trl 轻松地对其进行微调!

什么是视觉语言模型?

视觉语言模型被广义地定义为可以从图像和文本中学习的多模态模型。它们是一种生成模型,接收图像和文本输入,并生成文本输出。大型视觉语言模型具有良好的零样本能力、泛化性强,并且可以处理多种类型的图像,包括文档、网页等。其用例包括围绕图像聊天、通过指令进行图像识别、视觉问答、文档理解、图像字幕生成等。一些视觉语言模型还能捕捉图像中的空间属性。这些模型可以在被提示检测或分割特定主体时输出边界框或分割掩码,或者它们可以定位不同的实体或回答关于其相对或绝对位置的问题。现有的大型视觉语言模型在训练数据、图像编码方式以及因此具备的能力方面存在很大的多样性。

VLM Capabilities

开源视觉语言模型概览

Hugging Face Hub 上有许多开源视觉语言模型。下表展示了一些最著名的模型。

  • 有基础模型,也有为聊天微调的模型,后者可以在对话模式下使用。
  • 其中一些模型具有一种称为“接地”(grounding)的功能,可以减少模型的幻觉。
  • 除非另有说明,所有模型均在英语数据上训练。
模型 宽松许可证 模型大小 图像分辨率 附加功能
LLaVA 1.6 (Hermes 34B) 34B 672x672
deepseek-vl-7b-base 7B 384x384
DeepSeek-VL-Chat 7B 384x384 聊天
moondream2 ~2B 378x378
CogVLM-base 17B 490x490
CogVLM-Chat 17B 490x490 接地,聊天
Fuyu-8B 8B 300x300 图像内文本检测
KOSMOS-2 ~2B 224x224 接地,零样本对象检测
Qwen-VL 4B 448x448 零样本对象检测
Qwen-VL-Chat 4B 448x448 聊天
Yi-VL-34B 34B 448x448 双语(英语,中文)

寻找合适的视觉语言模型

有多种方法可以选择最适合您用例的模型。

视觉竞技场 (Vision Arena) 是一个完全基于对模型输出的匿名投票而建立的排行榜,并持续更新。在这个竞技场中,用户输入一张图片和一个提示,然后匿名抽样两个不同模型的输出,用户可以选择他们更喜欢的输出。通过这种方式,排行榜完全基于人类偏好构建。

Vision Arena 视觉竞技场

开放 VLM 排行榜 (Open VLM Leaderboard) 是另一个排行榜,其中各种视觉语言模型根据这些指标和平均分进行排名。您还可以根据模型大小、专有或开源许可证以及不同指标的排名来筛选模型。

VLM Capabilities 开放 VLM 排行榜

VLMEvalKit 是一个用于在视觉语言模型上运行基准测试的工具包,它为开放 VLM 排行榜提供支持。另一个评估套件是 LMMS-Eval,它提供了一个标准的命令行界面,用于评估您选择的 Hugging Face 模型在 Hugging Face Hub 上托管的数据集上的表现,如下所示:

accelerate launch --num_processes=8 -m lmms_eval --model llava --model_args pretrained="liuhaotian/llava-v1.5-7b" --tasks mme,mmbench_en --batch_size 1 --log_samples --log_samples_suffix llava_v1.5_mme_mmbenchen --output_path ./logs/ 

视觉竞技场和开放 VLM 排行榜都仅限于提交给它们的模型,并且需要更新才能添加新模型。如果您想查找其他模型,可以在 Hub 上浏览 `image-text-to-text` 任务下的模型

有不同的基准可以评估视觉语言模型,您可能会在排行榜中遇到。我们将介绍其中几个。

MMMU

面向专家级 AGI 的海量多学科多模态理解与推理基准 (MMMU) 是评估视觉语言模型最全面的基准。它包含 11,500 个多模态挑战,这些挑战需要跨越艺术和工程等不同学科的大学水平知识和推理能力。

MMBench

MMBench 是一个评估基准,包含 3000 个单选题,涵盖 20 种不同技能,包括 OCR、物体定位等。该论文还引入了一种名为 CircularEval 的评估策略,即问题的答案选项被以不同组合方式打乱,模型需要在每次都给出正确答案。还有其他更专业的跨领域基准,包括 MathVista(视觉数学推理)、AI2D(图表理解)、ScienceQA(科学问答)和 OCRBench(文档理解)。

技术细节

预训练视觉语言模型有多种方法。主要技巧是统一图像和文本的表示,并将其输入文本解码器进行生成。最常见和最著名的模型通常由一个图像编码器、一个用于对齐图像和文本表示的嵌入投影器(通常是密集神经网络)和一个文本解码器按此顺序堆叠而成。至于训练部分,不同的模型采用了不同的方法。

例如,LLaVA 由一个 CLIP 图像编码器、一个多模态投影器和一个 Vicuna 文本解码器组成。作者将一个包含图像和字幕的数据集输入到 GPT-4 中,生成了与字幕和图像相关的问题。作者冻结了图像编码器和文本解码器,仅训练多模态投影器,通过向模型输入图像和生成的问题,并将模型输出与真实字幕进行比较,来对齐图像和文本特征。在投影器预训练之后,他们保持图像编码器冻结,解冻文本解码器,并与解码器一起训练投影器。这种预训练和微调的方式是训练视觉语言模型最常见的方法。

VLM Structure
典型视觉语言模型的结构

VLM Structure
投影和文本嵌入被连接在一起

另一个例子是 KOSMOS-2,作者选择端到端地完全训练模型,这与 LLaVA 类的预训练相比计算成本更高。作者后来进行了纯语言指令微调来对齐模型。再举个例子,Fuyu-8B 甚至没有图像编码器。相反,图像块直接被送入一个投影层,然后序列经过一个自回归解码器。大多数时候,你不需要从头预训练一个视觉语言模型,因为你可以直接使用现有的模型,或者在自己的用例上对其进行微调。我们将介绍如何使用 transformers 来使用这些模型,以及如何使用 `SFTTrainer` 进行微调。

使用 transformers 运行视觉语言模型

你可以使用 `LlavaNext` 模型通过 Llava 进行推理,如下所示。

我们首先初始化模型和处理器。

from transformers import LlavaNextProcessor, LlavaNextForConditionalGeneration
import torch

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
processor = LlavaNextProcessor.from_pretrained("llava-hf/llava-v1.6-mistral-7b-hf")
model = LlavaNextForConditionalGeneration.from_pretrained(
    "llava-hf/llava-v1.6-mistral-7b-hf",
    torch_dtype=torch.float16,
    low_cpu_mem_usage=True
)
model.to(device)

我们现在将图像和文本提示传递给处理器,然后将处理后的输入传递给 `generate`。请注意,每个模型都使用自己的提示模板,务必使用正确的模板以避免性能下降。

from PIL import Image
import requests

url = "https://github.com/haotian-liu/LLaVA/blob/1a91fc274d7c35a9b50b3cb29c4247ae5837ce39/images/llava_v1_5_radar.jpg?raw=true"
image = Image.open(requests.get(url, stream=True).raw)
prompt = "[INST] <image>\nWhat is shown in this image? [/INST]"

inputs = processor(prompt, image, return_tensors="pt").to(device)
output = model.generate(**inputs, max_new_tokens=100)

调用 decode 来解码输出的 token。

print(processor.decode(output[0], skip_special_tokens=True))

使用 TRL 微调视觉语言模型

我们很高兴地宣布,TRL 的 `SFTTrainer` 现在已包含对视觉语言模型的实验性支持!我们在这里提供一个示例,展示如何在一个 Llava 1.5 VLM 上使用 llava-instruct 数据集进行监督微调(SFT),该数据集包含 26 万对图像-对话。数据集包含格式化为消息序列的用户-助手交互。例如,每次对话都与一张用户提问的图像配对。

要使用实验性的 VLM 训练支持,您必须安装最新版本的 TRL,使用 `pip install -U trl`。完整的示例脚本可以在这里找到。

from trl.commands.cli_utils import SftScriptArguments, TrlParser

parser = TrlParser((SftScriptArguments, TrainingArguments))
args, training_args = parser.parse_args_and_config()

为指令微调初始化聊天模板。

LLAVA_CHAT_TEMPLATE = """A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions. {% for message in messages %}{% if message['role'] == 'user' %}USER: {% else %}ASSISTANT: {% endif %}{% for item in message['content'] %}{% if item['type'] == 'text' %}{{ item['text'] }}{% elif item['type'] == 'image' %}<image>{% endif %}{% endfor %}{% if message['role'] == 'user' %} {% else %}{{eos_token}}{% endif %}{% endfor %}"""

我们现在将初始化我们的模型和分词器。

from transformers import AutoTokenizer, AutoProcessor, TrainingArguments, LlavaForConditionalGeneration
import torch

model_id = "llava-hf/llava-1.5-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.chat_template = LLAVA_CHAT_TEMPLATE
processor = AutoProcessor.from_pretrained(model_id)
processor.tokenizer = tokenizer

model = LlavaForConditionalGeneration.from_pretrained(model_id, torch_dtype=torch.float16)

我们来创建一个数据整理器 (data collator) 以组合文本和图像对。

class LLavaDataCollator:
    def __init__(self, processor):
        self.processor = processor

    def __call__(self, examples):
        texts = []
        images = []
        for example in examples:
            messages = example["messages"]
            text = self.processor.tokenizer.apply_chat_template(
                messages, tokenize=False, add_generation_prompt=False
            )
            texts.append(text)
            images.append(example["images"][0])

        batch = self.processor(texts, images, return_tensors="pt", padding=True)

        labels = batch["input_ids"].clone()
        if self.processor.tokenizer.pad_token_id is not None:
            labels[labels == self.processor.tokenizer.pad_token_id] = -100
        batch["labels"] = labels

        return batch

data_collator = LLavaDataCollator(processor)

加载我们的数据集。

from datasets import load_dataset

raw_datasets = load_dataset("HuggingFaceH4/llava-instruct-mix-vsft")
train_dataset = raw_datasets["train"]
eval_dataset = raw_datasets["test"]

初始化 SFTTrainer,传入模型、数据集拆分、PEFT 配置和数据整理器,然后调用 `train()`。要将我们最终的检查点推送到 Hub,请调用 `push_to_hub()`。

from trl import SFTTrainer

trainer = SFTTrainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
    dataset_text_field="text",  # need a dummy field
    tokenizer=tokenizer,
    data_collator=data_collator,
    dataset_kwargs={"skip_prepare_dataset": True},
)

trainer.train()

保存模型并推送到 Hugging Face Hub。

trainer.save_model(training_args.output_dir)
trainer.push_to_hub()

你可以在这里找到训练好的模型。

你可以在下面的 VLM 游乐场中直接尝试我们刚刚训练的模型 ⬇️

致谢

我们要感谢 Pedro Cuenca、Lewis Tunstall、Kashif Rasul 和 Omar Sanseviero 对这篇博文的审阅和建议。

社区

注册登录以发表评论