見出し画像

Llama3.2でAIで作成した画像のキャプションを付けてみる

 オープンソースの生成AI、Llama3.2は、デフォルトの状態で日本語が使え、画像を読み込んで解釈できるので試して見た。結論だけ書くとあまり上手くいかない。プロンプトとモデルのファインチューニングがいるかな。

テストに使うモデル

 今回は必要なVRAMが少なくて済むSeanScripts/Llama-3.2-11B-Vision-Instruct-nf4を利用する。nf4についてはzennに書いた。

 さらにサンプルスクリプトもそのまま転用する。

注意事項:

  • あらかじめhuggingface-cliでログインしないとモデルがダウンロード出来ない。Loginにはhuggingfaceでログイン用Tokenを発行しておく必要がある。

  • metaの公式モデルはダウンロード前にライセンスを了承しないと行けないはず

  • マニュアル通りやっても不足するライブラリがあるのでエラーをチェックしながらインストールしないといけない。

    • transformers

    • huggingface-hub

    • bitsandbytes

    • tourchはOSとGPUやドライバでインストールするものが変わる

――と

 huggingface-cli login

テスト用サンプル

from transformers import MllamaForConditionalGeneration, AutoProcessor, BitsAndBytesConfig
from PIL import Image
import time
import os

# Load model
model_id = "SeanScripts/Llama-3.2-11B-Vision-Instruct-nf4"
model = MllamaForConditionalGeneration.from_pretrained(
    model_id,
    use_safetensors=True,
    device_map="cuda:0"
)
# Load tokenizer
processor = AutoProcessor.from_pretrained(model_id)

base_folder = "<path>/<to>" #ここに検索するフォルダを指定

for filename in os.listdir(base_folder):
# Caption a local image (could use a more specific prompt)
  if filename.endswith(".jpg"):
    IMAGE = Image.open(os.path.join(base_folder, filename)).convert("RGB")
  else:
    continue
  PROMPT = """<|begin_of_text|><|start_header_id|>user<|end_header_id|>
  画像の中の少女が話しそうなセリフを15文字以内の日本語で書いてください(出力は {"text":"キャプション"} ):
  <|image|><|eot_id|><|start_header_id|>assistant<|end_header_id|>
  """

  inputs = processor(IMAGE, PROMPT, return_tensors="pt").to(model.device)
  prompt_tokens = len(inputs['input_ids'][0])
  print(f"Prompt tokens: {prompt_tokens}")

  retry = True
  while retry:
    t0 = time.time()
    generate_ids = model.generate(**inputs, max_new_tokens=64)
    t1 = time.time()
    total_time = t1 - t0
    generated_tokens = len(generate_ids[0]) - prompt_tokens
    time_per_token = generated_tokens/total_time
    print(f"Generated {generated_tokens} tokens in {total_time:.3f} s ({time_per_token:.3f} tok/s)")

    output = processor.decode(generate_ids[0][prompt_tokens:]).replace('<|eot_id|>', '')
    try:
      import json
      text = json.loads(output)['text']
      if "おい" in text or "おお" in text or "おや" in text or len(text) > 15:
        continue
      retry = False
    except:
      print(f"Error: {output}")
      pass

  print(filename, text)
  1. 出力形式を明示的にないと出力が安定しない。

    1. 入力プロンプトを「画像の中の少女が話しそうなセリフを15文字以内の日本語で書いてください(出力は {"text":"キャプション"})」 として JSON形式で出力させる。

  2. JSONでparse出来ない場合はリトライする

    1. {"text":"おおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお みたいなバグがあるので。

  3. 変な日本語が出てくる場合もリトライさせる

    1. おやおや、 っておかしくないか……

  4. 概ねTokenに比例して遅くなるのでTokenは小さめに設定する(今回は、15文字程度としているので20tokenで十分)Tokenを増やしすぎるとメモリが圧迫され急激に遅くなる。

実行結果

実行環境

  • AMD Ryzen 7 7700

  • RAM 64GB

  • Windows11(23H2)

  • NVIDIA GeForce RTX 4070Ti Super TDP limit 285W

  • Python 3.10.14/torch==2.4.1+cu124

結果

  • modelのロード 10.093s(ストレージの速度に依存)

  • 1回の生成にかかる時間 1.2sec(9token) ~ 4.5sec(64token)

  • リトライ回数 0~5回

 直訳っぽい言い回しがきになる……。

今日は良い日ですね。
この部屋にいらっしゃいましたか
お仕事、済みましたね。
今日はいい天気ですね
花は咲いていますね
これは私たちの飲料です。
月夜に星を願って


いいなと思ったら応援しよう!

この記事が参加している募集