Python ローカルPCにて、視覚言語モデルVLMを操作する「transformers, huggingface」

 本記事では、画像とプロンプト(命令文)を入力データとして、その回答を文章で生成するサンプルコードを掲載しました。この場合、視覚言語モデルVision Language Model, VLM)を用います。

 使用したモデルは、Hugging Faceの「llava-calm2-siglip」です。詳細については、https://huggingface.co/cyberagent/llava-calm2-siglip を参照ください。他のVLMモデルでも、Hugging Faceを活用して同様の手順で利用可能です。

 使用したPCスペックは以下の通りです。

 実行中のビデオメモリの使用量は常に上限の12GBでした。一方、GPUを利用せずにCPUでの実行時には処理が途中で落ちました。以上の結果より、VLMモデルを用いる場合は、ビデオカードは必須と思われます。

1. ライブラリとモデルの準備

 まず、必要なライブラリであるPILPython Imaging Library)とtransformersライブラリをインポートします。PILは画像処理に、transformersは視覚と言語モデルのロードに使用します。

from PIL import Image
import requests
from transformers import AutoProcessor, LlavaForConditionalGeneration
import torch
  • PIL.Image: 画像のロードと変換に使用。
  • requests: インターネットから画像を取得するためのライブラリ。
  • AutoProcessorLlavaForConditionalGeneration: 画像とテキストを扱うLlavaモデル用のユーティリティ。

2. モデルのダウンロード

 初回はインターネットから、モデルをダウンロード(DL)する必要があります。DLは下記コードで可能です。

# モデルをダウンロードする場合(ユーザーディレクトリのキャッシュに保存される。消さない限り残る)
processor = AutoProcessor.from_pretrained("cyberagent/llava-calm2-siglip")
processor

上記にてDLしたモデルは、ユーザーディレクトリのキャッシュフォルダ内に複数のファイルで構成されます(下図)。

このモデルを他のPCなどで使用する場合など、任意の場所へ移動やコピーして使用することも可能です。下記はその移動先の例です。

# (必要なら)上記にてDLしたモデルをローカルPCの任意の場所へ移動する。
# その場合、実行プログラムからみて相対パスもしくは絶対パスを指定
model_name = "./vlm_models/local_llava_calm2_siglip"

3. モデルのロード

# モデルとトークナイザーのロード
model = LlavaForConditionalGeneration.from_pretrained(
    #"cyberagent/llava-calm2-siglip",
    model_name,
    torch_dtype=torch.bfloat16,
).to(0)

processor = AutoProcessor.from_pretrained(model_name)
processor
  • torch_dtype=torch.bfloat16: 省メモリで高精度な計算を行うための設定。
  • to(0): モデルをGPU上に移動して高速化します。

4. 画像のロード

URLから直接画像をロードする場合や、ローカルに保存された画像を使用する場合があります。今回は、Wikipediaから昆虫の画像を取得しています。

# 画像ファイルを読み込む(URL指定の画像を指定する場合)
url = "https://upload.wikimedia.org/wikipedia/commons/9/9d/Shoryobatta_06z1482sv.jpg"
image = Image.open(requests.get(url, stream=True).raw).convert("RGB")
image

下図は、上記のリンク先の画像で、ショウリョウバッタです(Cory, CC 表示-継承 3.0, https://commons.wikimedia.org/w/index.php?curid=1047627による)。

ローカルPCにある画像ファイルを読み込む際のコードの例は次の通りです。

# 画像ファイルを読み込む(任意のローカルPC内にある画像を指定する場合)
image_path = "./pictures/Shoryobatta_06z1482sv.jpg"
image = Image.open(image_path).convert("RGB")
image

5. プロンプトの設定と推論実行

プロンプトを定義し、画像をモデルに入力して推論を行います。ここでは昆虫の名前を尋ねる形式の命令文を作成しています。

# プロンプト(命令文)の指定と実行
prompt = """USER: <image>
この昆虫の名称は、何かわかりますか?
ASSISTANT: """

inputs = processor(text=prompt, images=image, return_tensors="pt").to(0, torch.bfloat16)
  • processor: テキストと画像をモデルに適した形式に変換します。
  • return_tensors="pt": 入力をPyTorchテンソルに変換。
  • to(0): GPU上で計算を行う設定。

5. 出力の取得

モデルから生成された出力をテキストとして取得し、不要な部分を取り除いて最終的な回答を得ます。

generate_ids = model.generate(**inputs,
                              max_length=500,
                              do_sample=True,
                              temperature=0.2,
                    )
output = processor.tokenizer.decode(generate_ids[0][:-1], clean_up_tokenization_spaces=False)
# "ASSISTANT: " 以降のテキストを取得
answer = output.split("ASSISTANT: ", 1)[-1]
answer
  • temperature=0.2: モデルの出力に多様性を持たせるためのパラメータ。
  • split("ASSISTANT: ", 1)[-1]: 最終的な回答部分のみを抽出。

上記の一連のコードを実行して得られた結果は次の通りです。回答の精度はモデルによります。

'画像に写っている昆虫は、緑色の体と長い脚が特徴的な「バッタ」のようです。'

 

▼最後に、上記の一連のpythonコードを下記に記します。

from PIL import Image
import requests
from transformers import AutoProcessor, LlavaForConditionalGeneration
import torch

# モデルをダウンロードする場合(ユーザーディレクトリのキャッシュに保存される。消さない限り残る)
#processor = AutoProcessor.from_pretrained("cyberagent/llava-calm2-siglip")
#processor

# (必要なら)上記にてDLしたモデルをローカルPCの任意の場所へ移動する。
# その場合、実行プログラムからみて相対パスもしくは絶対パスを指定
model_name = "./vlm_models/local_llava_calm2_siglip"

# モデルとトークナイザーのロード
model = LlavaForConditionalGeneration.from_pretrained(
    #"cyberagent/llava-calm2-siglip",
    model_name,
    torch_dtype=torch.bfloat16,
).to(0)

processor = AutoProcessor.from_pretrained(model_name)
processor

# 画像ファイルを読み込む(URL指定の画像を指定する場合)
url = "https://upload.wikimedia.org/wikipedia/commons/9/9d/Shoryobatta_06z1482sv.jpg"
image = Image.open(requests.get(url, stream=True).raw).convert("RGB")
image

# 画像ファイルを読み込む(任意のローカルPC内にある画像を指定する場合)
# image_path = "./pictures/Shoryobatta_06z1482sv.jpg"  # 実際のパスに置き換えてください
# image = Image.open(image_path).convert("RGB")
# image

# プロンプト(命令文)の指定と実行
prompt = """USER: <image>
この昆虫の名称は、何かわかりますか?
ASSISTANT: """

inputs = processor(text=prompt, images=image, return_tensors="pt").to(0, torch.bfloat16)
generate_ids = model.generate(**inputs,
                              max_length=500,
                              do_sample=True,
                              temperature=0.2,
                    )
output = processor.tokenizer.decode(generate_ids[0][:-1], clean_up_tokenization_spaces=False)
# "ASSISTANT: " 以降のテキストを取得
answer = output.split("ASSISTANT: ", 1)[-1]
answer

以上

<広告>