PaliGemma – 谷歌尖端开放视觉语言模型

发布于 2024 年 5 月 14 日
在 GitHub 上更新

更新于 2024 年 5 月 23 日:我们对 Transformers PaliGemma 的微调实现进行了一些更改,您可以在此 notebook 中找到。

PaliGemma 是谷歌新推出的视觉语言模型系列。PaliGemma 可以接收图像和文本并输出文本。

谷歌团队发布了三种类型的模型:预训练(pt)模型、混合模型和微调(ft)模型,每种模型都具有不同的分辨率,并提供多种精度以方便使用。

所有模型都发布在 Hugging Face Hub 模型仓库中,并附带模型卡和许可证,并集成了 Transformers。

什么是 PaliGemma?

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

Architecture

PaliGemma 发布了三种类型的模型

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

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

正如后面详细解释的,高分辨率模型需要更多的内存才能运行,因为输入序列更长。它们可能有助于 OCR 等精细任务,但对于大多数任务而言,质量提升很小。224 版本对于大多数用途来说已经足够了。

您可以在此 集合 中找到所有模型和空间。

模型能力

PaliGemma 是一个单轮视觉语言模型,不适用于对话式使用,并且在微调到特定用例时效果最佳。

您可以通过使用任务前缀(如“detect”或“segment”)对模型进行条件化来配置模型将解决的任务。预训练模型就是以这种方式训练的,以使其具备丰富的R能力集(问答、字幕、分割等)。然而,它们并非设计用于直接使用,而是通过(微调)转移到使用类似提示结构特定任务。对于交互式测试,您可以使用已在混合任务上进行微调的“mix”模型家族。

以下示例使用混合检查点来演示一些功能。

图像字幕

PaliGemma 可以在被提示时为图像添加字幕。您可以使用混合检查点尝试各种字幕提示,以查看它们如何响应。

Captioning

视觉问答

PaliGemma 可以回答关于图像的问题,只需将您的问题和图像一起传递即可。

VQA

检测

PaliGemma 可以使用 `detect [entity]` 提示来检测图像中的实体。它将以特殊 `` 标记的形式输出边界框坐标的位置,其中 `value` 是表示归一化坐标的数字。每次检测都由四个位置坐标表示,顺序为 *y_min, x_min, y_max, x_max*,后面是被检测到的标签。要将值转换为坐标,首先需要将数字除以 1024,然后将 `y` 乘以图像高度,将 `x` 乘以图像宽度。这将为您提供边界框相对于原始图像大小的坐标。

Detection

指称表达式分割

PaliGemma 混合检查点在给定 `segment [entity]` 提示时,也可以分割图像中的实体。这被称为指称表达式分割,因为我们使用自然语言描述来指代感兴趣的实体。输出是位置和分割标记的序列。位置标记表示如上所述的边界框。分割标记可以进一步处理以生成分割掩码。

Segmentation

文档理解

PaliGemma 混合检查点具有出色的文档理解和推理能力。

ocrqa

混合基准

您可以在下方找到混合检查点的分数。

模型 MMVP 准确率 POPE 准确率 (随机/流行/对抗)
mix-224 46.00 88.00 86.63 85.67
mix-448 45.33 89.37 88.40 87.47

微调检查点

除了预训练模型和混合模型,谷歌还发布了已经转移到各种任务的模型。它们对应于学术基准,研究界可以使用它们来比较它们的性能。下面,您可以找到一些精选的模型。这些模型也提供不同的分辨率。您可以查看任何模型的模型卡以获取所有指标。

模型名称 数据集/任务 在转移任务中的得分
paligemma-3b-ft-vqav2-448 图表理解 VQAV2 上的准确率 85.64%
paligemma-3b-ft-cococap-448 COCO 图像标题 144.6 CIDEr
paligemma-3b-ft-science-qa-448 科学问答 ScienceQA Img 子集(无 CoT)准确率 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%

演示

作为此次发布的一部分,我们提供了一个 演示,它封装了 big_vision 仓库中的参考实现,并提供了一种简单的方式来试用混合模型。

我们还提供了 兼容 Transformers 的演示版本,以展示如何使用 PaliGemma Transformers API。

如何运行推理

要访问 PaliGemma 模型,您需要接受 Gemma 的许可条款和条件。如果您已经拥有 Hugging Face 中其他 Gemma 模型的访问权限,则无需额外操作。否则,请访问任何 PaliGemma 模型,如果您同意,请接受许可证。获得访问权限后,您需要通过 notebook_loginhuggingface-cli login 进行身份验证。登录后,即可开始使用!

您也可以立即 在此笔记本中 尝试推理。

使用 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://huggingface.co/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 集成还允许您利用 Hugging Face 生态系统中的其他工具,例如

详细推理过程

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

输入文本照常进行分词。在开头添加一个 `` 标记,并附加一个换行符 (`\n`)。这个换行符是模型训练时输入提示的重要组成部分,因此明确添加它可以确保它始终存在。分词后的文本还会加上固定数量的 `` 标记。有多少个?这取决于输入图像分辨率和 SigLIP 模型使用的补丁大小。PaliGemma 模型预训练时使用三种方形大小之一(224x224、448x448 或 896x896),并且始终使用 14 的补丁大小。因此,对于 224 模型,要预先添加的 `` 标记数量为 256 (`224/14 * 224/14`),对于 448 模型为 1024,对于 896 模型为 4096。

请注意,更大的图像会导致更长的输入序列,因此需要更多的内存来处理模型的语言部分。在考虑使用哪种模型时请牢记这一点。对于 OCR 等更精细的任务,更大的图像可能有助于获得更好的结果,但对于绝大多数任务而言,质量增益很小。在决定使用更大分辨率之前,请务必在您的任务上进行测试!

这个完整的“提示”通过语言模型的文本嵌入层,并生成每个标记 2048 维的标记嵌入。

与此同时,输入图像使用双三次插值法调整大小到所需的输入尺寸(对于分辨率最小的模型是 224x224)。然后它通过 SigLIP 图像编码器生成每个补丁 1152 维的图像嵌入。线性投影器在此发挥作用:图像嵌入被投影以获得每个补丁 2048 维的表示,与从文本标记获得的表示相同。最终的图像嵌入与 `` 文本嵌入合并,这是用于自回归文本生成的最终输入。生成以自回归模式正常工作。它对完整输入(`image + bos + prompt + \n`)使用全块注意力,并对生成的文本使用因果注意力掩码。

所有这些细节都在处理器和模型类中自动处理,因此可以使用前面示例中所示的熟悉的高级 Transformers API 执行推理。

微调

使用 big_vision

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

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

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

使用 Transformers

PaliGemma 的微调非常容易,这得益于 Transformers。还可以进行 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"]

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

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

我们将创建一个提示模板,以 PliGemma 条件化以回答视觉问题。由于分词器会填充输入,我们需要将标签中的填充设置为分词器中填充标记之外的其他内容,以及图像标记。

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 位加载模型用于 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 位加载模型进行 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 SansevieroLucas BeyerXiaohua ZhaiMatthias Minderer 对本博客文章的详细审阅。我们感谢 Peter Robicheaux 在 Transformers 微调更改方面的帮助。

社区

image_23.jpg

注册登录 发表评论