# 视觉微调

微调视觉模型可使模型在某些任务上表现更出色，而普通 LLM 在这些任务上不会那么擅长，例如目标/运动检测。 **你还可以训练** [**带有 RL 的 VLM**](/docs/zh/kai-shi-shi-yong/reinforcement-learning-rl-guide/vision-reinforcement-learning-vlm-rl.md)**.** 我们有许多用于视觉微调的免费笔记本：

* [**Qwen3-VL**](/docs/zh/mo-xing/tutorials/qwen3-how-to-run-and-fine-tune/qwen3-vl-how-to-run-and-fine-tune.md) **（8B）视觉：** [**笔记本**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen3_VL_\(8B\)-Vision.ipynb)
* [**Ministral 3**](/docs/zh/mo-xing/tutorials/ministral-3.md)：用于通用问答的视觉微调： [笔记本](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Pixtral_\(12B\)-Vision.ipynb)\
  可以将通用问答数据集与更细分的数据集拼接起来，使微调不会忘记基础模型的能力。
* **Gemma 3（4B）视觉：** [笔记本](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma3_\(4B\)-Vision.ipynb)
* **Llama 3.2 Vision** 用于放射影像的微调： [笔记本](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, # 如果不微调 attention 层则设为 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,               # 我们支持 rank stabilized 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) 或 "min"，将图像缩放以适配模型的默认 image_size，或使用 "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, # [可选] 忽略填充的视觉 token - 应始终为 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_conversation(sample) for sample in dataset]
```

使用 map 会触发数据集标准化和 arrow 处理规则，而这些规则可能比较严格且更复杂。

### 用于视觉微调的数据集

用于微调视觉或多模态模型的数据集，与标准问答对类似 [datasets ](/docs/zh/kai-shi-shi-yong/fine-tuning-llms-guide/datasets-guide.md)，但这次还包含图像输入。例如， [Llama 3.2 Vision 笔记本](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="/files/e86577dd0fc6ec21972a8e1e764eea71135b9fac" 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）。

* 全景放射影像显示正常的牙科结构。
* 右上方有一个异常区域，表现为一片透亮的骨质区域，对应于上颌窦。

**关键观察**

* 左上牙之间的骨质相对较为致密不透亮。
* 图像上方有两个大箭头，提示需要更仔细检查该区域。其中一个箭头位于左侧，另一个位于右侧。然而，只有
```

有关更多详细信息，请查看我们的数据集部分在 [此处的笔记本](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 Vision：

```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",
    ...
)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://unsloth.ai/docs/zh/ji-chu/vision-fine-tuning.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
