記事一覧に戻る

この記事は中国語版 简体中文 でもご覧いただけます。

2024-05-23 更新: transformers の PaliGemma 実装にファインチューニングに関するいくつかの変更を加えました。詳細はこちらの ノートブック をご覧ください。

PaliGemma は Google が開発した新しいファミリーの視覚言語モデルです。PaliGemma は画像とテキストを受け取り、テキストを出力できます。

Google のチームは、事前学習済み (pt) モデル、mix モデル、ファインチューニング済み (ft) モデルの 3 種類を、異なる解像度と複数の精度で公開しています。

すべてのモデルは、モデルカードとライセンスとともに Hugging Face Hub のモデルリポジトリで公開されており、transformers との統合が提供されています。

PaliGemma とは

PaliGemma (Github) は、画像エンコーダに SigLIP-So400m、テキストデコーダに Gemma-2B を用いたアーキテクチャを持つ視覚言語モデルのファミリーです。SigLIP は画像とテキストの両方を理解できる最先端のモデルです。CLIP と同様に、画像エンコーダとテキストエンコーダが共同で学習されています。PaLI-3 と同様に、統合された PaliGemma モデルは画像-テキストデータで事前学習されており、キャプション生成や参照セグメンテーションなどの下流タスクに簡単にファインチューニングできます。Gemma はテキスト生成のためのデコーダのみのモデルです。SigLIP の画像エンコーダと Gemma を線形アダプタで結合することで、PaliGemma は強力な視覚言語モデルになります。

Architecture

PaliGemma のリリースには 3 種類のモデルが含まれています:

  • PT チェックポイント: 下流タスクにファインチューニング可能な事前学習済みモデル。
  • Mix チェックポイント: 複数のタスクにファインチューニングされた PT モデル。自由形式のテキストプロンプトによる汎用推論に適しており、研究目的のみに使用できます。
  • FT チェックポイント: さまざまな学術ベンチマークに特化した一連のファインチューニング済みモデル。さまざまな解像度で提供されており、研究目的のみに使用できます。

モデルは 3 つの異なる解像度 (224x224448x448896x896) と 3 つの異なる精度 (bfloat16float16float32) で提供されます。各リポジトリには、特定の解像度とタスクのチェックポイントが含まれており、利用可能な精度ごとに 3 つのリビジョンがあります。各リポジトリの main ブランチには float32 チェックポイントが含まれており、bfloat16 および float16 のリビジョンにはそれぞれの精度のチェックポイントが含まれています。🤗 transformers および元の JAX 実装と互換性のあるモデル用に、別々のリポジトリが用意されています。

後述するように、高解像度モデルは入力シーケンスが非常に長くなるため、実行に多くのメモリを必要とします。OCR などの細かなタスクには役立つ場合がありますが、ほとんどのタスクでは品質の向上はわずかです。224 バージョンはほとんどの用途に完全に適しています。

すべてのモデルと Space はこちらの コレクション でご覧いただけます。

モデルの能力

PaliGemma は会話用途を想定していない単一ターンの視覚言語モデルであり、特定のユースケースにファインチューニングした際に最も効果を発揮します。

「detect」や「segment」などのタスクプレフィックスでモデルを条件付けすることで、モデルが解決するタスクを設定できます。事前学習済みモデルはこの方法で学習されており、質問応答、キャプション生成、セグメンテーションなどの豊富な能力を備えています。ただし、これらは直接使用することを想定しておらず、同様のプロンプト構造を用いて特定のタスクに転移 (ファインチューニング) することを目的としています。インタラクティブなテストには、複数のタスクにファインチューニングされた「mix」ファミリーのモデルを使用できます。

以下の例では、mix チェックポイントを使用して一部の能力を示します。

画像キャプション生成

PaliGemma はプロンプトを与えると画像にキャプションを付けることができます。mix チェックポイントでさまざまなキャプション生成プロンプトを試して、応答を確認できます。

Captioning

視覚的質問応答

PaliGemma は画像に関する質問に答えることができます。質問と画像を渡すだけで回答します。

VQA

検出

PaliGemma は detect [entity] プロンプトを使用して画像内のエンティティを検出できます。出力は特殊な <loc[value]> トークンの形式でバウンディングボックスの座標位置を示します。ここで value は正規化された座標を表す数値です。各検出は y_min, x_min, y_max, x_max の順に 4 つの位置座標で表され、その後に検出されたラベルが続きます。値を座標に変換するには、まず数値を 1024 で割り、その後 y に画像の高さを、x に幅を掛けてください。これにより、元の画像サイズに対するバウンディングボックスの座標が得られます。

Detection

参照表現セグメンテーション

PaliGemma の mix チェックポイントは segment [entity] プロンプトを与えると画像内のエンティティをセグメント化することもできます。これは、対象のエンティティを自然言語の記述で参照するため、参照表現セグメンテーションと呼ばれます。出力は位置トークンとセグメンテーショントークンのシーケンスです。位置トークンは前述のバウンディングボックスを表します。セグメンテーショントークンはさらに処理してセグメンテーションマスクを生成できます。

Segmentation

文書理解

PaliGemma の mix チェックポイントは優れた文書理解および推論能力を備えています。

ocrqa

Mix ベンチマーク

以下に mix チェックポイントのスコアを示します。

モデル 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

ファインチューニング済みチェックポイント

事前学習済みモデルと mix モデルに加えて、Google はさまざまなタスクにすでに転移されたモデルも公開しています。これらは研究コミュニティがパフォーマンスを比較するために使用できる学術ベンチマークに対応しています。以下に一部を紹介します。これらのモデルもさまざまな解像度で提供されています。すべてのメトリクスについては、各モデルのモデルカードをご確認ください。

デモ

このリリースの一環として、big_vision リポジトリ のリファレンス実装をラップし、mix モデルを簡単に試せる デモ を提供しています。

また、Transformers と互換性のある デモ もあり、PaliGemma の transformers API の使用方法を示しています。

推論の実行方法

PaliGemma モデルにアクセスするには、Gemma のライセンス条項と条件に同意する必要があります。すでに Hugging Face で他の Gemma モデルにアクセスできる場合は、そのままご利用いただけます。そうでない場合は、PaliGemma モデルのいずれかにアクセスし、同意する場合はライセンスに同意してください。アクセス権を取得したら、notebook_login または huggingface-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 の動作の詳細を理解したい場合は、入力画像とテキストが経る手順を以下に示します。

入力テキストは通常どおりトークン化されます。<bos> トークンが先頭に追加され、改行トークン (\n) が追加で付加されます。この改行トークンは、モデルが学習した入力プロンプトの必須部分であるため、明示的に追加することで常に存在することが保証されます。トークン化されたテキストには、固定数の <image> トークンが前置されます。その数は? 入力画像の解像度と SigLIP モデルで使用されるパッチサイズによって異なります。PaliGemma モデルは 3 つの正方形サイズ (224x224、448x448、または 896x896) のいずれかで事前学習されており、パッチサイズは常に 14 を使用します。したがって、前置する <image> トークンの数は、224 モデルでは 256 (224/14 * 224/14)、448 モデルでは 1024、896 モデルでは 4096 になります。

大きな画像は入力シーケンスがはるかに長くなり、モデルの言語部分を処理するために多くのメモリを必要とすることに注意してください。使用するモデルを検討する際には、この点を念頭に置いてください。OCR などの細かなタスクでは大きな画像がより良い結果をもたらす場合がありますが、ほとんどのタスクでは品質の向上はわずかです。より大きな解像度に移行する前に、必ずタスクでテストしてください!

この完全な「プロンプト」は言語モデルのテキスト埋め込み層を通過し、トークンごとに 2048 次元のトークン埋め込みを生成します。

これと並行して、入力画像はバイキュービックリサンプリングを使用して必要な入力サイズ (最小解像度モデルの場合は 224x224) にリサイズされます。その後、SigLIP 画像エンコーダを通過して、パッチごとに 1152 次元の画像埋め込みを生成します。ここで線形プロジェクタが機能します: 画像埋め込みは、テキストトークンから得られるものと同じパッチごとに 2048 次元の表現を得るために射影されます。最終的な画像埋め込みは <image> テキスト埋め込みとマージされ、これが自己回帰テキスト生成に使用される最終入力になります。生成は自己回帰モードで通常どおり動作します。完全な入力 (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 をフォークし、コメントの指示に従ってユースケースに適応させてください。

また、無料の T4 GPU ランタイムで動作する 簡易ファインチューニング を実行する Colab finetune_paligemma.ipynb もあります。限られたホストおよび 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"]

画像処理とトークン化の部分を含むプロセッサを読み込み、データセットを前処理します。

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

視覚的質問に PaliGemma を条件付けするためのプロンプトテンプレートを作成します。トークナイザは入力をパディングするため、ラベル内のパディングをトークナイザのパッドトークン以外のものに設定し、画像トークンも設定する必要があります。

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

モデルを直接読み込むか、QLoRA 用に 4 ビットで読み込むことができます。以下にモデルを直接読み込む方法を示します。モデルを読み込み、画像エンコーダとプロジェクタを凍結し、デコーダのみをファインチューニングします。画像がデータセットの事前学習に使用されたものとは異なる特定のドメイン内にある場合は、画像エンコーダの凍結をスキップすることを検討してください。

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

QLoRA 用に 4 ビットでモデルを読み込みたい場合は、以下に示す変更を追加できます。

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 におけるファインチューニング変更に関する支援に感謝します。