# マルチモーダルチャットテンプレート

マルチモーダルチャットモデルは、テキストに加えて、画像、音声、動画などの入力を受け付けます。マルチモーダルチャット履歴の`content`キーは、異なるタイプの複数のアイテムを含むリストです。これに対し、テキスト専用のチャットモデルの`content`キーは単一の文字列です。

テキスト専用モデルのチャットテンプレートとトークン化を処理する [Tokenizer](./fast_tokenizers) クラスと同様に、マルチモーダルモデルの前処理、トークン化、チャットテンプレートを処理するのが [Processor](./processors) クラスです。それぞれの [apply_chat_template()](/docs/transformers/main/ja/main_classes/processors#transformers.ProcessorMixin.apply_chat_template) メソッドはほぼ同一です。

このガイドでは、高レベルの[ImageTextToTextPipeline](/docs/transformers/main/ja/main_classes/pipelines#transformers.ImageTextToTextPipeline)と、低レベルの[apply_chat_template()](/docs/transformers/main/ja/main_classes/processors#transformers.ProcessorMixin.apply_chat_template)および[generate()](/docs/transformers/main/ja/main_classes/text_generation#transformers.GenerationMixin.generate)メソッドを使用して、マルチモーダルモデルとチャットする方法を示します。

## ImageTextToTextPipeline

[ImageTextToTextPipeline](/docs/transformers/main/ja/main_classes/pipelines#transformers.ImageTextToTextPipeline) は、チャットモードを備えた高レベルな画像・テキスト生成クラスです。対話型モデルが検出され、チャットプロンプトが[適切にフォーマット](./llm_tutorial#prompt-format)されている場合、チャットモードが有効になります。

チャット履歴の`content`キーに、画像ブロックとテキストブロックを追加します。

```py
messages = [
    {
        "role": "system",
        "content": [{"type": "text", "text": "You are a friendly chatbot who always responds in the style of a pirate"}],
    },
    {
      "role": "user",
      "content": [
            {"type": "image", "url": "http://images.cocodataset.org/val2017/000000039769.jpg"},
            {"type": "text", "text": "What are these?"},
        ],
    },
]
```

[ImageTextToTextPipeline](/docs/transformers/main/ja/main_classes/pipelines#transformers.ImageTextToTextPipeline) を作成し、チャットを渡します。大規模モデルの場合、[device_map="auto"](./models#big-model-inference) を設定すると、モデルの読み込みが速くなり、利用可能な最速のデバイスに自動的に配置されます。データ型を [auto](./models#model-data-type) に設定すると、メモリを節約し、速度を向上させるのにも役立ちます。

```python
import torch
from transformers import pipeline

pipe = pipeline("image-text-to-text", model="Qwen/Qwen2.5-VL-3B-Instruct", device_map="auto", dtype="auto")
out = pipe(text=messages, max_new_tokens=128)
print(out[0]['generated_text'][-1]['content'])
```

```text
Ahoy, me hearty! These be two feline friends, likely some tabby cats, taking a siesta on a cozy pink blanket. They're resting near remote controls, perhaps after watching some TV or just enjoying some quiet time together. Cats sure know how to find comfort and relaxation, don't they?
```

海賊言葉から現代のアメリカ英語への段階的な移行（毕竟、**3B**モデルに過ぎないため）を除けば、これは正しいです！

## `apply_chat_template`の使用

[テキスト専用モデル](./chat_templating)と同様に、マルチモーダルモデルのチャットメッセージを準備するには、[apply_chat_template()](/docs/transformers/main/ja/main_classes/processors#transformers.ProcessorMixin.apply_chat_template)メソッドを使用します。このメソッドは、画像やその他のメディアタイプを含むチャットメッセージのトークン化とフォーマットを処理します。生成のために、得られた入力がモデルに渡されます。

```python
from transformers import AutoProcessor, AutoModelForImageTextToText

model = AutoModelForImageTextToText.from_pretrained("Qwen/Qwen2.5-VL-3B-Instruct", device_map="auto", torch_dtype="auto")
processor = AutoProcessor.from_pretrained("Qwen/Qwen2.5-VL-3B-Instruct")

messages = [
    {
      "role": "system",
      "content": [{"type": "text", "text": "You are a friendly chatbot who always responds in the style of a pirate"}],
    },
    {
      "role": "user",
      "content": [
            {"type": "image", "url": "http://images.cocodataset.org/val2017/000000039769.jpg"},
            {"type": "text", "text": "What are these?"},
        ],
    },
]
```

`messages` を [apply_chat_template()](/docs/transformers/main/ja/main_classes/processors#transformers.ProcessorMixin.apply_chat_template) に渡して、入力内容をトークン化します。テキストモデルとは異なり、`apply_chat_template` の出力には、トークン化されたテキストに加えて、前処理済みの画像データを含む `pixel_values` キーが含まれます。

```py
processed_chat = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt")
print(list(processed_chat.keys()))
```

```text
['input_ids', 'attention_mask', 'pixel_values', 'image_grid_thw']
```

これらの入力を[generate()](/docs/transformers/main/ja/main_classes/text_generation#transformers.GenerationMixin.generate)に渡します。

```python
out = model.generate(**processed_chat.to(model.device), max_new_tokens=128)
print(processor.decode(out[0]))
```

デコードされた出力には、ユーザーメッセージや画像情報を含むプレースホルダートークンなど、これまでの会話全体が含まれています。ユーザーに表示する前に、出力から以前の会話を切り捨てる必要がある場合があります。

## 動画入力

一部のビジョンモデルは、動画入力にも対応しています。メッセージの形式は、前述のセクションで説明した画像入力の形式と非常に似ています。

- コンテンツの `"type"` は、コンテンツが動画であることを示すために `"video"` である必要があります。
- 動画の場合、リンク (`"url"`) またはファイルパス (`"path"`) を指定できます。動画は [torchcodec](https://meta-pytorch.org/torchcodec/stable/index.html) でデコードされます。torchcodec が利用できず、古いバージョンの torchvision を使用している場合、デコードは torchvision にフォールバックします。
- URLやファイルパスから動画を読み込むことに加えて、デコード済みの動画データを直接渡すこともできます。これは、メモリ内の他の場所で動画フレームを事前処理またはデコード済みの場合に便利です。ファイルに保存したり、URLに格納したりする必要はありません。

> [!TIP]
> [PyAV](https://pyav.basswood-io.com/docs/stable/) と [Decord](https://github.com/dmlc/decord) は利用可能ですが、`load_video(backend=...)` でバックエンドを明示的に指定して、自分で動画をデコードする場合に限ります。

```python
from transformers import AutoProcessor, LlavaOnevisionForConditionalGeneration

model_id = "llava-hf/llava-onevision-qwen2-0.5b-ov-hf"
model = LlavaOnevisionForConditionalGeneration.from_pretrained(model_id)
processor = AutoProcessor.from_pretrained(model_id)

messages = [
    {
      "role": "system",
      "content": [{"type": "text", "text": "You are a friendly chatbot who always responds in the style of a pirate"}],
    },
    {
      "role": "user",
      "content": [
            {"type": "video", "url": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/720/Big_Buck_Bunny_720_10s_10MB.mp4"},
            {"type": "text", "text": "What do you see in this video?"},
        ],
    },
]
```

### 例:デコード済み動画オブジェクトの渡し方

```python
import numpy as np

video_object1 = np.random.randint(0, 255, size=(16, 224, 224, 3), dtype=np.uint8),

messages = [
    {
        "role": "system",
        "content": [{"type": "text", "text": "You are a friendly chatbot who always responds in the style of a pirate"}],
    },
    {
        "role": "user",
        "content": [
            {"type": "video", "video": video_object1},
            {"type": "text", "text": "What do you see in this video?"}
        ],
    },
]
```

既存の`"load_video()"`関数を使用して動画を読み込み、メモリ上で動画を編集してから、メッセージに渡すこともできます。

```python

# Make sure a video backend library (torchcodec, pyav, or decord) is available.
from transformers.video_utils import load_video

# load a video file in memory for testing
video_object2, _ = load_video(
    "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/720/Big_Buck_Bunny_720_10s_10MB.mp4"
)

messages = [
    {
        "role": "system",
        "content": [{"type": "text", "text": "You are a friendly chatbot who always responds in the style of a pirate"}],
    },
    {
        "role": "user",
        "content": [
            {"type": "video", "video": video_object2},
            {"type": "text", "text": "What do you see in this video?"}
        ],
    },
]
```

`messages` を [apply_chat_template()](/docs/transformers/main/ja/main_classes/processors#transformers.ProcessorMixin.apply_chat_template) に渡して、入力内容をトークン化します。[apply_chat_template()](/docs/transformers/main/ja/main_classes/processors#transformers.ProcessorMixin.apply_chat_template) には、サンプリングプロセスを制御するいくつかの追加パラメータを含める必要があります。

`num_frames` パラメータは、動画から均等にサンプリングするフレーム数を制御します。各チェックポイントには、事前学習で使用された最大フレーム数が存在し、この数を超えると生成品質が大幅に低下する可能性があります。モデルの容量とハードウェアのリソースの両方に適したフレーム数を選択することが重要です。`num_frames` が指定されていない場合、フレームサンプリングなしで動画全体が読み込まれます。

```python
processed_chat = processor.apply_chat_template(
    messages,
    add_generation_prompt=True,
    tokenize=True,
    return_dict=True,
    return_tensors="pt",
    num_frames=32,
)
print(processed_chat.keys())
```

これらの入力値は、[generate()](/docs/transformers/main/ja/main_classes/text_generation#transformers.GenerationMixin.generate)で使用できるようになりました。

長い動画の場合、`fps` パラメータを使用してより多くのフレームをサンプリングすることで、より良い表現が得られる場合があります。このパラメータは、1秒あたり何フレーム抽出するかを決定します。例えば、動画の長さが10秒で`fps=2`の場合、モデルは20フレームをサンプリングします。言い換えれば、10秒間隔で2フレームが均等にサンプリングされるということです。

```py
processed_chat = processor.apply_chat_template(
    messages,
    add_generation_prompt=True,
    tokenize=True,
    return_dict=True,
    fps=16,
)
print(processed_chat.keys())
```

動画は、完全な動画ファイルではなく、画像として保存されたサンプリングされたフレームのセットとして存在する場合もあります。

この場合、画像ファイルのパスのリストを渡すと、プロセッサが自動的にそれらを連結して動画に変換します。同じ動画からのフレームであると想定されるため、すべての画像が同じサイズであることを確認してください。

```py
frames_paths = ["/path/to/frame0.png", "/path/to/frame5.png", "/path/to/frame10.png"]
messages = [
    {
        "role": "system",
        "content": [{"type": "text", "text": "You are a friendly chatbot who always responds in the style of a pirate"}],
    },
    {
      "role": "user",
      "content": [
            {"type": "video", "path": frames_paths},
            {"type": "text", "text": "What do you see in this video?"},
        ],
    },
]

processed_chat = processor.apply_chat_template(
    messages,
    add_generation_prompt=True,
    tokenize=True,
    return_dict=True,
)
print(processed_chat.keys())
```

