# Fine-tuning vision

L'affinage (fine-tuning) des modèles de vision permet au modèle d'exceller dans certaines tâches pour lesquelles les LLM classiques ne sont pas aussi performants, telles que la détection d'objets/mouvements. **Vous pouvez également entraîner** [**des VLMs avec RL**](https://unsloth.ai/docs/fr/commencer/reinforcement-learning-rl-guide/vision-reinforcement-learning-vlm-rl)**.** Nous avons de nombreux notebooks gratuits pour l'affinage en vision :

* [**Qwen3-VL**](https://unsloth.ai/docs/fr/modeles/tutorials/qwen3-how-to-run-and-fine-tune/qwen3-vl-how-to-run-and-fine-tune) **(8B) Vision :** [**Notebook**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen3_VL_\(8B\)-Vision.ipynb)
* [**Ministral 3**](https://unsloth.ai/docs/fr/modeles/tutorials/ministral-3) : affinage vision pour Q\&R général : [Notebook](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Pixtral_\(12B\)-Vision.ipynb)\
  On peut concaténer des ensembles de données de Q\&R général avec des ensembles de données plus spécialisés pour empêcher l'affinage d'oublier les compétences du modèle de base.
* **Gemma 3 (4B) Vision :** [Notebook](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma3_\(4B\)-Vision.ipynb)
* **Llama 3.2 Vision** affinage pour radiographie : [Notebook](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Llama3.2_\(11B\)-Vision.ipynb)\
  Comment pouvons-nous aider les professionnels de la santé à analyser plus rapidement les radiographies, les scanners et les échographies ?
* **Qwen2.5 VL** affinage pour convertir l'écriture manuscrite en LaTeX : [Notebook](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen2.5_VL_\(7B\)-Vision.ipynb)\
  Cela permet de transcrire facilement des formules mathématiques complexes en LaTeX sans les écrire manuellement.

{% hint style="info" %}
Il est préférable de s'assurer que votre jeu de données contient des images toutes de la même taille/dimensions. Utilisez des dimensions de 300 à 1000 px pour garantir que l'entraînement ne prenne pas trop de temps ni n'utilise trop de ressources.
{% endhint %}

### Désactivation de la vision / affinage texte uniquement

Pour affiner les modèles de vision, nous vous permettons désormais de sélectionner quelles parties du modèle affiner. Vous pouvez choisir d'affiner uniquement les couches de vision, ou les couches de langage, ou les couches d'attention / MLP ! Nous les activons toutes par défaut !

```python
model = FastVisionModel.get_peft_model(
    model,
    finetune_vision_layers     = True, # False si vous n’affinez pas les couches vision
    finetune_language_layers   = True, # False si vous n’affinez pas les couches de langage
    finetune_attention_modules = True, # False si vous n’affinez pas les couches d’attention
    finetune_mlp_modules       = True, # False si vous n’affinez pas les couches MLP

    r = 16,                           # Plus grand = meilleure précision, mais peut suradapter
    lora_alpha = 16,                  # Alpha recommandé == r au moins
    lora_dropout = 0,
    bias = "none",
    random_state = 3407,
    use_rslora = False,               # Nous supportons LoRA stabilisé en rang
    loftq_config = None,               # Et LoftQ
    target_modules = "all-linear",    # Optionnel maintenant ! Peut spécifier une liste si nécessaire
    modules_to_save=[
        "lm_head",
        "embed_tokens",
    ],
)
```

### Vision Data Collator

Nous avons un data collator spécial uniquement pour les jeux de données vision :

{% 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 %}

Et les arguments pour le data collator sont :

{% code expandable="true" %}

```python
class UnslothVisionDataCollator :
def __init__(
self,
model,
processor,
max_seq_length  = None, # [Optionnel] Nous récupérons automatiquement ceci depuis `FastVisionModel.from_pretrained(max_seq_length = ...)
formatting_func = None, # Fonction pour transformer le texte
resize = "min", # Peut être (10, 10) ou "min" pour redimensionner afin d'adapter la image_size par défaut du modèle ou "max"
                # pour ne pas redimensionner et laisser l'image intacte
ignore_index = -100, # [Optionnel] Par défaut -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, # ÉQUIVALENT à train_on_responses_only pour les LLMs
instruction_part = None, # ÉQUIVALENT à train_on_responses_only(instruction_part = ...)
response_part    = None, # ÉQUIVALENT à train_on_responses_only(response_part = ...)
force_match      = True, # Faire correspondre les sauts de ligne également !

num_proc         = None, # [Optionnel] Sélectionnera automatiquement le nombre de GPU
completion_only_loss = True, # [Optionnel] Ignore les tokens de vision de remplissage - devrait toujours être True !
pad_to_multiple_of = None, # [Optionnel] Pour le padding du data collator
resize_dimension = 0, # peut être 0, 1, 'max' ou 'min'
                      # (max redimensionne selon le maximum de la hauteur et largeur, min selon la taille minimale, 0 la première dimension, etc)
snap_to_patch_size = False, # [Optionnel] Forcer l'image à être un multiple de la taille du patch
)
```

{% endcode %}

### Entraînement multi-image

Pour affiner ou entraîner des modèles avec plusieurs images, le changement le plus simple est de remplacer :

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

par :

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

L'utilisation de map déclenche la standardisation du jeu de données et les règles de traitement arrow qui peuvent être strictes et plus compliquées à définir.

### Jeu de données pour l'affinage en vision

Le jeu de données pour affiner un modèle de vision ou multimodal est similaire aux paires standard de question & réponse [jeux de données ](https://unsloth.ai/docs/fr/commencer/fine-tuning-llms-guide/datasets-guide), mais cette fois, ils incluent également des entrées d'images. Par exemple, le [Notebook Llama 3.2 Vision](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Llama3.2_\(11B\)-Vision.ipynb#scrollTo=vITh0KVJ10qX) utilise un cas de radiographie pour montrer comment l'IA peut aider les professionnels médicaux à analyser plus efficacement les radiographies, les scanners et les échographies.

Nous utiliserons une version échantillonnée du jeu de données de radiographie ROCO. Vous pouvez accéder au jeu de données [ici](https://www.google.com/url?q=https%3A%2F%2Fhuggingface.co%2Fdatasets%2Funsloth%2FRadiology_mini). Le jeu de données inclut des radiographies, des scanners et des échographies présentant des conditions et des maladies médicales. Chaque image a une légende rédigée par des experts la décrivant. L'objectif est d'affiner un VLM pour en faire un outil d'analyse utile pour les professionnels médicaux.

Examinons le jeu de données et vérifions ce que montre le 1er exemple :

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

| Image                                                                                                                                                                                                                                                                             | Légende                                                                                                                                                     |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <div><figure><img src="https://550366147-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> | La radiographie panoramique montre une lésion ostéolytique dans la maxillaire postérieure droite avec résorption du plancher du sinus maxillaire (flèches). |

Pour formater le jeu de données, toutes les tâches d'affinage vision doivent être formatées comme suit :

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

Nous allons créer une instruction personnalisée demandant au VLM d'être un radiographe expert. Notez aussi qu'au lieu d'une seule instruction, vous pouvez ajouter plusieurs tours pour en faire une conversation dynamique.

{% code expandable="true" %}

```notebook-python
instruction = "Vous êtes un radiographe expert. Décrivez avec précision ce que vous voyez sur cette image."

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 %}

Convertissons le jeu de données au format "correct" pour l'affinage :

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

Le premier exemple est maintenant structuré comme ci‑dessous :

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

{% code overflow="wrap" %}

```
{'messages': [{'role': 'user',
   'content': [{'type': 'text',
     'text': 'Vous êtes un radiographe expert. Décrivez avec précision ce que vous voyez sur cette image.'},
    {'type': 'image',
     'image': <PIL.PngImagePlugin.PngImageFile image mode=L size=657x442>}]},
  {'role': 'assistant',
   'content': [{'type': 'text',
     'text': 'La radiographie panoramique montre une lésion ostéolytique dans la maxillaire postérieure droite avec résorption du plancher du sinus maxillaire (flèches).'}]}]}
```

{% endcode %}

Avant de faire tout affinage, peut-être que le modèle de vision sait déjà comment analyser les images ? Vérifions si c'est le cas !

{% code expandable="true" %}

```notebook-python
FastVisionModel.for_inference(model) # Activer pour l'inférence !

image = dataset[0]["image"]
instruction = "Vous êtes un radiographe expert. Décrivez avec précision ce que vous voyez sur cette image."

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 %}

Et le résultat :

```
Cette radiographie semble être une vue panoramique de la dentition supérieure et inférieure, spécifiquement un orthopantomogramme (OPG).

* La radiographie panoramique démontre des structures dentaires normales.
* Il existe une zone anormale en haut à droite, représentée par une zone d'os radiotranslucide, correspondant à l'antre.

**Observations clés**

* L'os entre les dents supérieures gauches est relativement radio-opaque.
* Il y a deux grandes flèches au-dessus de l'image, suggérant la nécessité d'un examen plus approfondi de cette zone. L'une des flèches est positionnée à gauche, et l'autre à droite. Cependant, seulement
```

Pour plus de détails, consultez notre section jeu de données dans le [notebook ici](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Llama3.2_\(11B\)-Vision.ipynb#scrollTo=vITh0KVJ10qX).

### &#x20;:mag\_right:Entraîner uniquement sur les réponses de l'assistant pour les modèles de vision, VLMs

Pour les modèles de langage, nous pouvons utiliser `from unsloth.chat_templates import train_on_responses_only` comme décrit précédemment. Pour les modèles de vision, utilisez les arguments supplémentaires dans `UnslothVisionDataCollator` comme précédemment ! Voir [#vision-data-collator](#vision-data-collator "mention") pour plus de détails sur la façon d'utiliser le vision data collator.

{% 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, # ÉQUIVALENT à train_on_responses_only pour les LLMs
    instruction_part = None, # ÉQUIVALENT à train_on_responses_only(instruction_part = ...)
    response_part    = None, # ÉQUIVALENT à train_on_responses_only(response_part = ...)
    force_match      = True, # Faire correspondre les sauts de ligne également !
)
```

{% endcode %}

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