跳到正文
原文
Hugging Face Blog·· 2024-05-14精选AI 评分70

Google 发布开源视觉语言模型 PaliGemma

PaliGemma – Google's Cutting-Edge Open Vision Language Model

AI 导读

Google 发布视觉语言模型系列 PaliGemma,由 SigLIP-So400m 图像编码器和 Gemma-2B 文本解码器组成,可输入图像和文本并输出文本。

推荐理由

梳理了 PaliGemma 的架构、三种权重类型与分辨率选择建议,方便判断是否适合自身微调任务。

正文 · AI 翻译

2024年5月23日更新:我们对 transformers PaliGemma 实现中的微调部分做了一些更改,详情可见此notebook。

PaliGemma 是 Google 推出的新一族视觉语言模型。PaliGemma 可以接收图像和文本,并输出文本。

Google 团队发布了三种类型的模型:预训练(pt)模型、混合(mix)模型和微调(ft)模型,每种都有不同的分辨率,并为了方便提供了多种精度版本。

所有模型都发布在 HF Mirror Hub 模型仓库中,附带模型卡和许可证,并已集成 transformers。

什么是 PaliGemma?

PaliGemma(Github)是一族视觉语言模型,其架构由作为图像编码器的 SigLIP-So400m 和作为文本解码器的 Gemma-2B 组成。SigLIP 是一个能同时理解图像和文本的最先进模型。与 CLIP 类似,它由一个联合训练的图像编码器和文本编码器组成。与 PaLI-3 类似,组合后的 PaliGemma 模型在图像-文本数据上预训练,之后可以轻松地微调用于下游任务,例如图像描述或指代分割。Gemma 是一个仅解码器模型,用于文本生成。通过线性适配器将 SigLIP 的图像编码器与 Gemma 结合,使 PaliGemma 成为一个强大的视觉语言模型。

Architecture

PaliGemma 发布包含三种类型的模型:

  • PT 检查点:预训练模型,可以微调用于下游任务。
  • Mix 检查点:微调至混合任务的 PT 模型。它们适用于使用自由文本提示的通用推理,并且只能用于研究目的。
  • FT 检查点:一组微调模型,每个模型专门针对不同的学术基准。它们提供多种分辨率,并且只能用于研究目的。

这些模型有三种不同的分辨率(224x224、448x448、896x896)和三种不同的精度(bfloat16、float16 和 float32)。每个仓库包含给定分辨率和任务的检查点,每种可用精度都有三个修订版本。每个仓库的 main 分支包含 float32 检查点,而 bfloat16 和 float16 修订版本包含相应的精度。有单独的仓库用于与 🤗 transformers 兼容的模型,以及与原始 JAX 实现兼容的模型。

如下文详细解释,高分辨率模型运行需要更多内存,因为输入序列长得多。它们可能有助于 OCR 等细粒度任务,但对大多数任务来说质量提升很小。对于大多数用途,224 版本完全足够。

你可以在此合集中找到所有模型和 Space。

模型能力

PaliGemma 是一个单轮视觉语言模型,不适用于对话用途,它在微调至特定用例时效果最佳。

你可以通过任务前缀(例如“检测”或“分割”)来配置模型将解决的任务。预训练模型就是以这种方式训练的,以赋予它们丰富的功能(问答、图像描述、分割等)。然而,它们并非设计为直接使用,而是通过使用类似的提示结构,将其(通过微调)迁移到特定任务。对于交互式测试,你可以使用“mix”系列模型,它们已在混合任务上进行了微调。

以下示例使用 mix checkpoints 来演示部分功能。

图像描述

PaliGemma 在收到提示时可以生成图像描述。你可以尝试用 mix checkpoints 测试各种描述提示,看看它们如何回应。

Captioning

视觉问答

PaliGemma 可以回答关于图像的问题,只需将你的问题与图像一起传入即可。

VQA

检测

PaliGemma 可以使用 detect [entity] prompt 检测图像中的实体。它会以特殊的 <loc[value]> token 形式输出边界框坐标的位置,其中 value 是表示归一化坐标的数字。每个检测结果由四个位置坐标表示,顺序为 y_min、x_min、y_max、x_max,后跟在该框中检测到的标签。要将值转换为坐标,首先需要将数字除以 1024,然后将 y 乘以图像高度,将 x 乘以图像宽度。这将给出相对于原始图像尺寸的边界框坐标。

Detection

指称表达分割

当给定 segment [entity] prompt 时,PaliGemma mix checkpoints 还可以分割图像中的实体。这被称为指称表达分割,因为我们使用自然语言描述来指代感兴趣的实体。输出是位置和分割 token 的序列。位置 token 表示如上所述的边界框。分割 token 可以进一步处理以生成分割掩码。

Segmentation

文档理解

PaliGemma mix checkpoints 具有出色的文档理解和推理能力。

ocrqa

Mix 基准测试

下面你可以找到 mix checkpoints 的分数。

模型 MMVP 准确率 POPE 准确率(random/popular/adversarial)
mix-224 46.00 88.00 86.63 85.67
mix-448 45.33 89.37 88.40 87.47

微调 Checkpoints

除了预训练模型和 mix 模型之外,Google 还发布了已经迁移到各种任务的模型。它们对应于学术基准测试,研究社区可以使用这些基准来比较它们的表现。下面你可以找到精选的几个。这些模型也有不同的分辨率。你可以查看任何模型的 model card 以了解所有指标。

模型名称 数据集/任务 迁移任务中的分数
paligemma-3b-ft-vqav2-448 图表理解 VQAV2 上 85.64 准确率
paligemma-3b-ft-cococap-448 COCO Captions 144.6 CIDEr
paligemma-3b-ft-science-qa-448 科学问答 在无 CoT 的 ScienceQA Img 子集上 95.93 准确率
paligemma-3b-ft-refcoco-seg-896 理解图像中特定对象的引用 refcoco 平均 IoU 76.94 refcoco+ 平均 IoU 72.18 refcocog 平均 IoU 72.22
paligemma-3b-ft-rsvqa-hr-224 遥感视觉问答 测试集上 92.61 准确率 测试集2上 90.58 准确率

演示

作为本次发布的一部分,我们提供了一个 demo,它封装了 big_vision repository 中的参考实现,并提供了一种简单的方法来试用 mix 模型。

我们还提供了一个与 Transformers 兼容的 demo 版本,以展示如何使用 PaliGemma transformers API。

如何运行推理

要获取 PaliGemma 模型的访问权限,你需要接受 Gemma 许可条款和条件。如果你已经在 HF Mirror 中拥有其他 Gemma 模型的访问权限,那就没问题了。否则,请访问任意 PaliGemma 模型,如果你同意许可条款,请接受。获得访问权限后,你需要通过 notebook_login 或 huggingface-cli login 进行身份验证。登录后,你就可以开始了!

你也可以立即在此 notebook 中尝试推理。

使用 Transformers

你可以使用 PaliGemmaForConditionalGeneration 类来对任何已发布的模型进行推理。只需使用内置处理器对提示词和图像进行预处理,然后将预处理后的输入传递给生成过程即可。

from transformers import AutoProcessor, PaliGemmaForConditionalGeneration

model_id = "google/paligemma-3b-mix-224"
model = PaliGemmaForConditionalGeneration.from_pretrained(model_id)
processor = AutoProcessor.from_pretrained(model_id)

prompt = "What is on the flower?"
image_file = "https://hf-mirror.com/datasets/huggingface/documentation-images/resolve/main/bee.jpg?download=true"
raw_image = Image.open(requests.get(image_file, stream=True).raw)
inputs = processor(prompt, raw_image, return_tensors="pt")
output = model.generate(**inputs, max_new_tokens=20)

print(processor.decode(output[0], skip_special_tokens=True)[len(prompt):])
# bee

你也可以按如下方式以 4 位加载模型。

from transformers import BitsAndBytesConfig

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)
model = PaliGemmaForConditionalGeneration.from_pretrained(
    model_id,
    quantization_config=bnb_config,
    device_map={"":0}
)

除了 4 位(或 8 位)加载外,transformers 集成还允许你利用 HF Mirror 生态系统中的其他工具,例如:

详细推理过程

如果你想编写自己的预处理或训练代码,或者想更详细地了解 PaliGemma 的工作原理,以下是输入图像和文本所经历的步骤。

输入文本会按常规方式进行分词。开头会添加一个 <bos> token,并追加一个额外的换行 token(\n)。这个换行 token 是模型训练时所用输入提示词的重要组成部分,因此显式添加它可以确保它始终存在。分词后的文本还会以一个固定数量的 <image> token 作为前缀。多少呢?这取决于输入图像的分辨率以及 SigLIP 模型使用的 patch 大小。PaliGemma 模型在三种方形尺寸(224x224、448x448 或 896x896)之一上进行预训练,并且始终使用 14 的 patch 大小。因此,需要前置的 <image> token 数量为:224 模型(224/14 * 224/14)为 256,448 模型为 1024,896 模型为 4096。

请注意,更大的图像会导致输入序列长得多,因此通过模型的语言部分需要多得多的内存。在考虑使用哪个模型时,请记住这一点。对于更细粒度的任务,例如 OCR,更大的图像可能有助于获得更好的结果,但对于绝大多数任务而言,质量提升很小。在决定迁移到更大分辨率之前,请务必在你的任务上进行测试!

这个完整的“提示词”会经过语言模型的文本嵌入层,并生成每个 token 2048 维的 token 嵌入。

与此同时,输入图像会使用双三次重采样调整到所需的输入尺寸(最小分辨率模型为 224x224)。然后它经过 SigLIP 图像编码器,生成每个 patch 1152 维的图像嵌入。线性投影器就在这里发挥作用:图像嵌入会被投影以获得每个 patch 2048 维的表示,与从文本 token 获得的表示相同。最终的图像嵌入随后会与 <image> 文本嵌入合并,这就是用于自回归文本生成的最终输入。生成以常规的自回归模式进行。它对完整输入(image + bos + prompt + \n)使用全块注意力,对生成的文本使用因果注意力掩码。

所有这些细节都在 processor 和 model 类中自动处理,因此可以使用前几个示例中展示的常见高层 transformers API 进行推理。

微调

使用 big_vision

PaliGemma 是在 big_vision 代码库中训练的。该代码库此前已用于开发 BiT、原始 ViT、LiT、CapPa、SigLIP 等众多模型。

项目配置文件夹 configs/proj/paligemma/ 包含一个 README.md。可以通过运行 transfers/ 子文件夹中的配置文件来迁移预训练模型,我们所有的迁移结果都是通过运行其中提供的配置获得的。如果你想迁移自己的模型,请 fork 示例配置 transfers/forkme.py,并按照注释中的说明将其调整为你的用例。

还有一个 Colab finetune_paligemma.ipynb,它运行一个简化版微调,可在免费 T4 GPU 运行时上工作。为了适应有限的主机和 GPU 内存,Colab 中的代码仅更新注意力层中的权重(170M 参数),并使用 SGD(而不是 Adam)。

使用 transformers

得益于 transformers,微调 PaliGemma 非常容易。也可以进行 QLoRA 或 LoRA 微调。在这个示例中,我们将简要微调解码器,然后展示如何切换到 QLoRA 微调。 我们将安装最新版本的 transformers 库。

pip install transformers

与推理部分一样,我们将进行身份验证以使用 notebook_login() 访问模型。

from huggingface_hub import notebook_login
notebook_login()

对于这个示例,我们将使用 VQAv2 数据集,并微调模型以回答有关图像的问题。让我们加载数据集。我们将只使用 question、multiple_choice_answer 和 image 列,因此也删除其余列。我们还将拆分数据集。

from datasets import load_dataset 
ds = load_dataset('HuggingFaceM4/VQAv2', split="train") 
cols_remove = ["question_type", "answers", "answer_type", "image_id", "question_id"] 
ds = ds.remove_columns(cols_remove)
ds = ds.train_test_split(test_size=0.1)
train_ds = ds["train"]
val_ds = ds["test"]

我们现在将加载 processor,它包含图像处理和分词部分,并预处理我们的数据集。

from transformers import PaliGemmaProcessor 
model_id = "google/paligemma-3b-pt-224"
processor = PaliGemmaProcessor.from_pretrained(model_id)

我们将创建一个提示模板,让 PaliGemma 以回答视觉问题为条件。由于分词器会填充输入,我们需要将标签中的填充设置为不同于分词器中的填充 token,以及图像 token。

import torch
device = "cuda"

image_token = processor.tokenizer.convert_tokens_to_ids("<image>")
def collate_fn(examples):
  texts = ["answer " + example["question"] for example in examples]
  labels= [example['multiple_choice_answer'] for example in examples]
  images = [example["image"].convert("RGB") for example in examples]
  tokens = processor(text=texts, images=images, suffix=labels,
                    return_tensors="pt", padding="longest")

  tokens = tokens.to(torch.bfloat16).to(device)
  return tokens

你可以直接加载模型,或以 4-bit 加载模型以进行 QLoRA。下面你可以看到如何直接加载模型。我们将加载模型,并冻结图像编码器和投影器,只微调解码器。如果你的图像属于特定领域,而该领域可能不在模型预训练所用的数据集中,你可能想跳过冻结图像编码器。

model = PaliGemmaForConditionalGeneration.from_pretrained(model_id, torch_dtype=torch.bfloat16).to(device)

for param in model.vision_tower.parameters():
    param.requires_grad = False

for param in model.multi_modal_projector.parameters():
    param.requires_grad = True

如果你想以 4-bit 加载模型以进行 QLoRA,可以在下方添加以下更改。

from transformers import BitsAndBytesConfig
from peft import get_peft_model, LoraConfig

bnb_config = BitsAndBytesConfig(
        load_in_4bit=True,
        bnb_4bit_quant_type="nf4",
        bnb_4bit_compute_type=torch.bfloat16
)

lora_config = LoraConfig(
    r=8, 
    target_modules=["q_proj", "o_proj", "k_proj", "v_proj", "gate_proj", "up_proj", "down_proj"],
    task_type="CAUSAL_LM",
)
model = PaliGemmaForConditionalGeneration.from_pretrained(model_id, quantization_config=bnb_config, device_map={"":0})
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
#trainable params: 11,298,816 || all params: 2,934,634,224 || trainable%: 0.38501616002417344

我们现在将初始化 Trainer 和 TrainingArguments。如果你要进行 QLoRA 微调,请将优化器改为 paged_adamw_8bit。

from transformers import TrainingArguments
args=TrainingArguments(
            num_train_epochs=2,
            remove_unused_columns=False,
            per_device_train_batch_size=16,
            gradient_accumulation_steps=4,
            warmup_steps=2,
            learning_rate=2e-5,
            weight_decay=1e-6,
            adam_beta2=0.999,
            logging_steps=100,
            optim="adamw_hf",
            save_strategy="steps",
            save_steps=1000,
            push_to_hub=True,
            save_total_limit=1,
            bf16=True,
            report_to=["tensorboard"],
            dataloader_pin_memory=False
        )

初始化 Trainer,传入数据集、数据整理函数和训练参数,并调用 train() 开始训练。

trainer = Trainer(
        model=model,
        train_dataset=train_ds,
        eval_dataset=val_ds,
        data_collator=collate_fn,
        args=args
        )
trainer.train()

其他资源

我们要感谢 Omar Sanseviero、Lucas Beyer、Xiaohua Zhai 和 Matthias Minderer 对这篇博客文章的细致审阅。我们还要感谢 Peter Robicheaux 在 transformers 中微调修改方面提供的帮助。

来源:Hugging Face Blog · hf-mirror.com