# Vision ファインチューニング

視覚モデルのファインチューニングにより、物体検出や動き検出など通常のLLMが得意でない特定のタスクでモデルを優れた性能にできます。 **また訓練することもできます** [**強化学習(RL)を用いたVLM**](https://unsloth.ai/docs/jp/meru/reinforcement-learning-rl-guide/vision-reinforcement-learning-vlm-rl)**.** 視覚ファインチューニング用の無料ノートブックを多数用意しています：

* [**Qwen3-VL**](https://unsloth.ai/docs/jp/moderu/tutorials/qwen3-how-to-run-and-fine-tune/qwen3-vl-how-to-run-and-fine-tune) **(8B) ビジョン：** [**ノートブック**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen3_VL_\(8B\)-Vision.ipynb)
* [**Ministral 3**](https://unsloth.ai/docs/jp/moderu/tutorials/ministral-3)：一般的なQ\&A向けのビジョンファインチューニング： [ノートブック](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Pixtral_\(12B\)-Vision.ipynb)\
  一般的なQ\&Aデータセットとよりニッチなデータセットを連結することで、ファインチューニング中にベースモデルのスキルを忘れさせないようにできます。
* **Gemma 3 (4B) ビジョン：** [ノートブック](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma3_\(4B\)-Vision.ipynb)
* **Llama 3.2 ビジョン** 放射線画像向けのファインチューニング： [ノートブック](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Llama3.2_\(11B\)-Vision.ipynb)\
  X線、CTスキャン、超音波検査の分析を医療専門家がより速く行えるように支援するにはどうすればよいか。
* **Qwen2.5 VL** 手書き文字をLaTeXに変換するためのファインチューニング： [ノートブック](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen2.5_VL_\(7B\)-Vision.ipynb)\
  これにより、複雑な数式を手作業で書かずに簡単にLaTeXとして転写できます。

{% hint style="info" %}
データセットのすべての画像が同じサイズ/寸法であることを確認するのが最適です。トレーニングが長引かないように、またはリソースを使いすぎないように、300–1000pxの寸法を使用してください。
{% endhint %}

### ビジョン／テキストのみのファインチューニングの無効化

ビジョンモデルをファインチューニングするために、どの部分をファインチューニングするかを選択できるようにしました。ビジョン層のみ、言語層のみ、またはアテンション／MLP層のみをファインチューニングするよう選べます！デフォルトではすべてオンにしています！

```python
model = FastVisionModel.get_peft_model(
    model,
    finetune_vision_layers     = True, # ビジョン層をファインチューニングしない場合はFalse
    finetune_language_layers   = True, # 言語層をファインチューニングしない場合はFalse
    finetune_attention_modules = True, # アテンション層をファインチューニングしない場合はFalse
    finetune_mlp_modules       = True, # MLP層をファインチューニングしない場合はFalse

    r = 16,                           # 値が大きいほど精度は高くなりますが、過学習する可能性があります
    lora_alpha = 16,                  # 推奨：alpha == r以上
    lora_dropout = 0,
    bias = "none",
    random_state = 3407,
    use_rslora = False,               # ランク安定化LoRAをサポートしています
    loftq_config = None,               # そしてLoftQ
    target_modules = "all-linear",    # これでオプションです！必要ならリストで指定できます
    modules_to_save=[
        "lm_head",
        "embed_tokens",
    ],
)
```

### ビジョン用データコレータ

ビジョンデータセット専用の特別なデータコレータがあります：

{% code overflow="wrap" %}

```python
from unsloth.trainer import UnslothVisionDataCollator
from trl import SFTTrainer, SFTConfig
trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    data_collator = UnslothVisionDataCollator(model, tokenizer),
    train_dataset = dataset,
    args = SFTConfig(...),
)
```

{% endcode %}

そしてデータコレータの引数は次のとおりです：

{% code expandable="true" %}

```python
class UnslothVisionDataCollator:
def __init__(
self,
model,
processor,
max_seq_length  = None, # [オプション] これは `FastVisionModel.from_pretrained(max_seq_length = ...)` から自動取得されます
formatting_func = None, # テキストを変換するための関数
resize = "min", # (10, 10) のように指定するか、モデルのデフォルト image_size に合わせてリサイズするために "min" または "max" を使えます
                # リサイズを行わず画像をそのままにする場合
ignore_index = -100, # [オプション] デフォルトは -100

# from unsloth.chat_templates import train_on_responses_only
# trainer = train_on_responses_only(
#     trainer,
#     instruction_part = "<|start_header_id|>user<|end_header_id|>\n\n",
#     response_part = "<|start_header_id|>assistant<|end_header_id|>\n\n",
# )
train_on_responses_only = False, # LLMでのtrain_on_responses_onlyと同等
instruction_part = None, # train_on_responses_only(instruction_part = ...) と同等
response_part    = None, # train_on_responses_only(response_part = ...) と同等
force_match      = True, # 改行も一致させる！

num_proc         = None, # [オプション] GPUの数を自動選択します
completion_only_loss = True, # [オプション] ビジョントークンのパディングを無視します — 常にTrueにすべきです！
pad_to_multiple_of = None, # [オプション] データコレータのパディング用
resize_dimension = 0, # 0, 1, 'max' または 'min' が指定可能
                      # (max は高さと幅の最大値に基づいてリサイズ、min は最小値に基づいてリサイズ、0 は最初の次元、など)
snap_to_patch_size = False, # [オプション] 画像をパッチサイズの倍数に強制する
)
```

{% endcode %}

### マルチ画像トレーニング

マルチ画像でモデルをファインチューニングまたは訓練するための最も簡単な変更は、次を入れ替えることです：

```python
ds_converted = ds.map(
    convert_to_conversation,
)
```

次のように：

```python
ds_converted = [convert_to_converation(sample) for sample in dataset]
```

map を使うとデータセットの標準化や Arrow の処理ルールが働きますが、これらは厳格で定義が複雑になることがあります。

### ビジョンファインチューニング用データセット

ビジョンまたはマルチモーダルモデルをファインチューニングするためのデータセットは、標準的な質問＆回答ペアに似ていますが、 [データセット ](https://unsloth.ai/docs/jp/meru/fine-tuning-llms-guide/datasets-guide)今回は画像入力も含まれる点が異なります。例えば、 [Llama 3.2 ビジョン ノートブック](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Llama3.2_\(11B\)-Vision.ipynb#scrollTo=vITh0KVJ10qX) は、放射線画像の事例を用いて、AIがX線、CTスキャン、超音波検査を医療専門家がより効率的に分析する手助けをする方法を示しています。

今回はROCO放射線データセットのサンプル版を使用します。データセットには次からアクセスできます [こちら](https://www.google.com/url?q=https%3A%2F%2Fhuggingface.co%2Fdatasets%2Funsloth%2FRadiology_mini)。データセットには医療的状態や疾患を示すX線、CT、超音波が含まれています。各画像には専門家が書いたキャプションが付いています。目的はVLMをファインチューニングして医療専門家にとって有用な分析ツールにすることです。

データセットを見て、最初の例が何を示しているかを確認しましょう：

```
Dataset({
    features: ['image', 'image_id', 'caption', 'cui'],
    num_rows: 1978
})
```

| 画像                                                                                                                                                                                                                                                                                | キャプション                                          |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| <div><figure><img src="https://735611837-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fgit-blob-97d4489827403bd4795494f33d01a10979788c30%2Fxray.png?alt=media" alt="" width="164"><figcaption></figcaption></figure></div> | パノラミック放射線写真は右後方上顎に骨融解性病変を示し、上顎洞底の吸収（矢印）を伴っています。 |

データセットをフォーマットするために、すべてのビジョンファインチューニングタスクは次のようにフォーマットする必要があります：

```python
[
{ "role": "user",
  "content": [{"type": "text",  "text": instruction}, {"type": "image", "image": image} ]
},
{ "role": "assistant",
  "content": [{"type": "text",  "text": answer} ]
},
]
```

VLMに放射線画像の専門家になるよう指示するカスタムインストラクションを作成します。1つの指示だけでなく、複数のターンを追加して動的な会話にすることもできます。

{% code expandable="true" %}

```notebook-python
instruction = "あなたは放射線撮影の専門家です。この画像で見えるものを正確に説明してください。"

def convert_to_conversation(sample):
    conversation = [
        { "role": "user",
          "content" : [
            {"type" : "text",  "text"  : instruction},
            {"type" : "image", "image" : sample["image"]} ]
        },
        { "role" : "assistant",
          "content" : [
            {"type" : "text",  "text"  : sample["caption"]} ]
        },
    ]
    return { "messages" : conversation }
pass
```

{% endcode %}

データセットをファインチューニング用の「正しい」形式に変換しましょう：

```notebook-python
converted_dataset = [convert_to_conversation(sample) for sample in dataset]
```

最初の例は現在次のように構造化されています：

```notebook-python
converted_dataset[0]
```

{% code overflow="wrap" %}

```
{'messages': [{'role': 'user',
   'content': [{'type': 'text',
     'text': 'あなたは放射線撮影の専門家です。この画像で見えるものを正確に説明してください。'},
    {'type': 'image',
     'image': <PIL.PngImagePlugin.PngImageFile image mode=L size=657x442>}]},
  {'role': 'assistant',
   'content': [{'type': 'text',
     'text': 'パノラミック放射線写真は右後方上顎に骨融解性病変を示し、上顎洞底の吸収（矢印）を伴っています。'}]}]}
```

{% endcode %}

ファインチューニングを行う前に、ビジョンモデルがすでに画像を解析できるかもしれません。これが当てはまるか確認してみましょう！

{% code expandable="true" %}

```notebook-python
FastVisionModel.for_inference(model) # 推論用に有効化！

image = dataset[0]["image"]
instruction = "あなたは放射線撮影の専門家です。この画像で見えるものを正確に説明してください。"

messages = [
    {"role": "user", "content": [
        {"type": "image"},
        {"type": "text", "text": instruction}
    ]}
]
input_text = tokenizer.apply_chat_template(messages, add_generation_prompt = True)
inputs = tokenizer(
    image,
    input_text,
    add_special_tokens = False,
    return_tensors = "pt",
).to("cuda")

from transformers import TextStreamer
text_streamer = TextStreamer(tokenizer, skip_prompt = True)
_ = model.generate(**inputs, streamer = text_streamer, max_new_tokens = 128,
                   use_cache = True, temperature = 1.5, min_p = 0.1)
```

{% endcode %}

そして結果：

```
このレントゲン写真は上顎と下顎の歯列のパノラマビュー、具体的にはオーソパントモグラム（OPG）であるようです。

* パノラマ放射線写真は正常な歯の構造を示しています。
* 右上部に放射線透過性の骨領域として表現される異常領域があり、それは上顎洞に対応します。

**重要な観察点**

* 左上歯の間の骨は比較的放射線不透過性（濃い）です。
* 画像の上部に2つの大きな矢印があり、この領域をより詳細に調べる必要があることを示唆しています。矢印の一つは左側に、もう一つは右側に位置しています。ただし、唯一
```

詳細については、当社のデータセットセクションを [こちらのノートブックでご覧ください](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Llama3.2_\(11B\)-Vision.ipynb#scrollTo=vITh0KVJ10qX).

### &#x20;:mag\_right:ビジョンモデル（VLM）におけるアシスタント応答のみでのトレーニング

言語モデルの場合、以下を使用できます `from unsloth.chat_templates import train_on_responses_only` 前述のように。ビジョンモデルの場合は、追加の引数を `UnslothVisionDataCollator` の一部として使用してください、以前と同様です！詳細な使用方法は [#vision-data-collator](#vision-data-collator "mention") ビジョンデータコレータの使い方を参照してください。

{% code overflow="wrap" %}

```python
class UnslothVisionDataCollator:
def __init__(
    self,
    ...
    # from unsloth.chat_templates import train_on_responses_only
    # trainer = train_on_responses_only(
    #     trainer,
    #     instruction_part = "<|start_header_id|>user<|end_header_id|>\n\n",
    #     response_part = "<|start_header_id|>assistant<|end_header_id|>\n\n",
    # )
    train_on_responses_only = False, # LLMでのtrain_on_responses_onlyと同等
    instruction_part = None, # train_on_responses_only(instruction_part = ...) と同等
    response_part    = None, # train_on_responses_only(response_part = ...) と同等
    force_match      = True, # 改行も一致させる！
)
```

{% endcode %}

例えば Llama 3.2 ビジョンの場合：

```python
UnslothVisionDataCollator(
    model, tokenizer,
    ...
    train_on_responses_only = True,
    instruction_part = "<|start_header_id|>user<|end_header_id|>\n\n",
    response_part = "<|start_header_id|>assistant<|end_header_id|>\n\n",
    ...
)
```
