# Apprentissage par renforcement pour la vision (VLM RL)

Unsloth prend désormais en charge le RL vision/multimodal avec [Qwen3-VL](https://unsloth.ai/docs/fr/modeles/tutorials/qwen3-how-to-run-and-fine-tune/qwen3-vl-how-to-run-and-fine-tune), [Gemma 3](https://unsloth.ai/docs/fr/modeles/tutorials/gemma-3-how-to-run-and-fine-tune) et plus encore. En raison du [partage de poids](https://unsloth.ai/docs/fr/commencer/reinforcement-learning-rl-guide/..#what-unsloth-offers-for-rl) et des noyaux personnalisés d'Unsloth, Unsloth rend le RL VLM **1,5–2× plus rapide,** utilise **90% moins de VRAM**, et permet **des contextes** 15× plus longs que les configurations FA2, sans perte de précision. Cette mise à jour introduit également l' [GSPO](#gspo-rl) algorithme.

Unsloth peut entraîner Qwen3-VL-8B avec GSPO/GRPO sur un GPU Colab T4 gratuit. D'autres VLM fonctionnent aussi, mais peuvent nécessiter des GPU plus grands. Gemma exige des GPU plus récents que le T4 parce que vLLM [se limite à Bfloat16](https://unsloth.ai/docs/fr/modeles/tutorials/gemma-3-how-to-run-and-fine-tune#unsloth-fine-tuning-fixes), nous recommandons donc NVIDIA L4 sur Colab. Nos notebooks résolvent des problèmes de mathématiques numériques impliquant des images et des schémas :

* **Qwen-3 VL-8B** (inférence vLLM)**:** [Colab](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen3_VL_\(8B\)-Vision-GRPO.ipynb)
* **Qwen-2.5 VL-7B** (inférence vLLM)**:** [Colab](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen2_5_7B_VL_GRPO.ipynb) •[ Kaggle](https://www.kaggle.com/notebooks/welcome?src=https://github.com/unslothai/notebooks/blob/main/nb/Kaggle-Qwen2_5_7B_VL_GRPO.ipynb\&accelerator=nvidiaTeslaT4)
* **Gemma-3-4B** (inférence Unsloth) : [Colab](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma3_\(4B\)-Vision-GRPO.ipynb)

Nous avons également intégré nativement vLLM VLM dans Unsloth, donc tout ce que vous avez à faire pour utiliser l'inférence vLLM est d'activer le `fast_inference=True` lors de l'initialisation du modèle. Remerciements particuliers à [Sinoué GAD](https://github.com/unslothai/unsloth/pull/2752) pour avoir fourni le [premier notebook](https://github.com/GAD-cell/vlm-grpo/blob/main/examples/VLM_GRPO_basic_example.ipynb) qui a rendu l'intégration du RL VLM plus facile !

Ce support VLM intègre également notre dernière mise à jour pour un RL encore plus économe en mémoire et plus rapide, incluant notre [fonctionnalité Standby](https://unsloth.ai/docs/fr/commencer/memory-efficient-rl#unsloth-standby), qui limite de manière unique la dégradation de la vitesse par rapport à d'autres implémentations.

{% hint style="info" %}
Vous ne pouvez utiliser que `fast_inference` pour les VLM pris en charge par vLLM. Certains modèles, comme Llama 3.2 Vision, ne peuvent donc fonctionner qu'en dehors de vLLM, mais ils fonctionnent toujours dans Unsloth.
{% endhint %}

```python
os.environ['UNSLOTH_VLLM_STANDBY'] = '1' # Pour activer GRPO économe en mémoire avec vLLM
model, tokenizer = FastVisionModel.from_pretrained(
    model_name = "Qwen/Qwen2.5-VL-7B-Instruct",
    max_seq_length = 16384, # Doit être aussi grand pour insérer l'image dans le contexte
    load_in_4bit = True, # False pour LoRA 16bit
    fast_inference = True, # Activer l'inférence rapide vLLM
    gpu_memory_utilization = 0.8, # Réduire si mémoire insuffisante
)
```

Il est également important de noter que vLLM ne prend pas en charge LoRA pour les couches vision/encodeur, définissez donc `finetune_vision_layers = False` lors du chargement d'un adaptateur LoRA.\
Cependant, vous POUVEZ entraîner également les couches vision si vous utilisez l'inférence via transformers/Unsloth.

```python
# Ajouter l'adaptateur LoRA au modèle pour un ajustement fin efficace en paramètres
model = FastVisionModel.get_peft_model(
    model,

    finetune_vision_layers     = False,# fast_inference ne prend pas encore en charge finetune_vision_layers :(
    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 = lora_rank, # Choisissez n'importe quel nombre > 0 ! Suggestions : 8, 16, 32, 64, 128
    lora_alpha = lora_rank*2, # *2 accélère l'entraînement
    use_gradient_checkpointing = "unsloth", # Réduit l'utilisation de la mémoire
    random_state = 3407,
)
```

## :butterfly:Problèmes et particularités du RL Vision Qwen 2.5 VL

Pendant le RL pour Qwen 2.5 VL, vous pourriez voir la sortie d'inférence suivante :

{% code overflow="wrap" %}

```
 addCriterion
 <tool_call>\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n\n addCriterion\n\n 自动生成\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n addCriterion\n\n\n addCriterion\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
```

{% endcode %}

Ceci a été [signalé](https://github.com/QwenLM/Qwen2.5-VL/issues/759) également dans Qwen2.5-VL-7B-Instruct comme résultat inattendu « addCriterion ». En fait nous voyons cela aussi ! Nous avons essayé des machines non Unsloth, bfloat16 et float16 et d'autres choses, mais cela semble persister. Par exemple l'élément 165 c.-à-d. `train_dataset[165]` du [AI4Math/MathVista](https://huggingface.co/datasets/AI4Math/MathVista) jeu de données est ci-dessous :

{% code overflow="wrap" %}

```
La figure est une vue aérienne du trajet emprunté par un pilote de course lorsque sa voiture heurte le mur de la piste. Juste avant la collision, il se déplace à la vitesse v_i = 70 \mathrm{~m} / \mathrm{s} le long d'une ligne droite à 30^{\circ} du mur. Juste après la collision, il se déplace à la vitesse v_f = 50 \mathrm{~m} / \mathrm{s} le long d'une ligne droite à 10^{\circ} du mur. Sa masse m est de 80 \mathrm{~kg}. La collision dure 14 \mathrm{~ms}. Quelle est la magnitude de la force moyenne sur le pilote pendant la collision ?
```

{% endcode %}

<figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fgit-blob-61a659529171fcc10ed6398a15912b21d6b1a076%2FUntitled.png?alt=media" alt="" width="128"><figcaption></figcaption></figure>

Et ensuite nous obtenons la sortie incompréhensible ci‑dessus. On pourrait ajouter une fonction de récompense pour pénaliser l'ajout de addCriterion, ou pénaliser les sorties incompréhensibles. Cependant, l'autre approche est de l'entraîner plus longtemps. Par exemple, seulement après environ 60 étapes nous voyons le modèle réellement apprendre via le RL :

<figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fgit-blob-5f34f66f0ac6508fd28343b16592c59b889ec5ca%2Fimage.webp?alt=media" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Forcer `<|assistant|>` pendant la génération réduira les occurrences de ces résultats incompréhensibles comme prévu puisque c'est un modèle Instruct, cependant il est toujours préférable d'ajouter une fonction de récompense pour pénaliser les mauvaises générations, comme décrit dans la section suivante.
{% endhint %}

## :medal:Fonctions de récompense pour réduire les sorties incompréhensibles

Pour pénaliser `addCriterion` et les sorties incompréhensibles, nous avons modifié la fonction de récompense pour pénaliser trop de `addCriterion` et de sauts de ligne.

```python
def formatting_reward_func(completions,**kwargs):
    import re
    thinking_pattern = f'{REASONING_START}(.*?){REASONING_END}'
    answer_pattern = f'{SOLUTION_START}(.*?){SOLUTION_END}'

    scores = []
    for completion in completions:
        score = 0
        thinking_matches = re.findall(thinking_pattern, completion, re.DOTALL)
        answer_matches = re.findall(answer_pattern, completion, re.DOTALL)
        if len(thinking_matches) == 1:
            score += 1.0
        if len(answer_matches) == 1:
            score += 1.0

        # Corriger les problèmes addCriterion
        # Voir https://docs.unsloth.ai/new/vision-reinforcement-learning-vlm-rl#qwen-2.5-vl-vision-rl-issues-and-quirks
        # Pénaliser l'excès de addCriterion et de sauts de ligne
        if len(completion) != 0:
            removal = completion.replace("addCriterion", "").replace("\n", "")
            if (len(completion)-len(removal))/len(completion) >= 0.5:
                score -= 2.0

        scores.append(score)
    return scores
```

## :checkered\_flag:Reinforcement Learning GSPO

Cette mise à jour ajoute en outre GSPO ([Group Sequence Policy Optimization](https://arxiv.org/abs/2507.18071)) qui est une variante de GRPO créée par l'équipe Qwen d'Alibaba. Ils ont remarqué que GRPO implique implicitement des poids d'importance pour chaque token, même si les avantages explicites ne se mettent pas à l'échelle ou ne changent pas avec chaque token.

Cela a conduit à la création de GSPO, qui assigne maintenant l'importance sur la vraisemblance de la séquence plutôt que sur les vraisemblances individuelles des tokens. La différence entre ces deux algorithmes peut être vue ci‑dessous, issue à la fois de l'article GSPO de Qwen et Alibaba :

<figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fgit-blob-45d743dd5dcd590626777ce09cfab61808aa8c24%2Fimage.png?alt=media" alt="" width="563"><figcaption><p>Algorithme GRPO, Source : <a href="https://arxiv.org/abs/2507.18071">Qwen</a></p></figcaption></figure>

<figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fgit-blob-ee755850cbe17482ce240dde227d55c62e9a3e64%2Fimage.png?alt=media" alt="" width="563"><figcaption><p>Algorithme GSPO, Source : <a href="https://arxiv.org/abs/2507.18071">Qwen</a></p></figcaption></figure>

Dans l'équation 1, on peut voir que les avantages mettent à l'échelle chacune des lignes dans les logprobs des tokens avant que ce tenseur ne soit sommée. Essentiellement, chaque token reçoit la même mise à l'échelle bien que cette mise à l'échelle ait été appliquée à l'ensemble de la séquence plutôt qu'à chaque token individuel. Un diagramme simple de ceci peut être vu ci‑dessous :

<figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fgit-blob-b3c944808a15dde0a7ff45782f9f074993304bf1%2FCopy%20of%20GSPO%20diagram%20(1).jpg?alt=media" alt="" width="286"><figcaption><p>Ratio de logprob GRPO mis à l'échelle ligne par ligne avec les avantages</p></figcaption></figure>

L'équation 2 montre que les ratios de logprob pour chaque séquence sont sommés et exponentiés après le calcul des ratios de logprob, et seuls les ratios de séquence résultants sont multipliés ligne par ligne par les avantages.

<figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fgit-blob-62fc5b50921e79cce155d2794201c9b96faf941e%2FGSPO%20diagram%20(1).jpg?alt=media" alt="" width="313"><figcaption><p>Ratio de séquence GSPO mis à l'échelle ligne par ligne avec les avantages</p></figcaption></figure>

Activer GSPO est simple, il vous suffit de définir le `importance_sampling_level = "sequence"` indicateur dans la configuration GRPO.

```python
training_args = GRPOConfig(
    output_dir = "vlm-grpo-unsloth",
    per_device_train_batch_size = 8,
    gradient_accumulation_steps = 4,
    learning_rate = 5e-6,
    adam_beta1 = 0.9,
    adam_beta2 = 0.99,
    weight_decay = 0.1,
    warmup_ratio = 0.1,
    lr_scheduler_type = "cosine",
    optim = "adamw_8bit",
    # beta = 0.00,
    epsilon = 3e-4,
    epsilon_high = 4e-4,
    num_generations = 8,    
    max_prompt_length = 1024,
    max_completion_length = 1024,
    log_completions = False,
    max_grad_norm = 0.1,
    temperature = 0.9,
    # report_to = "none", # Mettre à "wandb" si vous souhaitez enregistrer sur Weights & Biases
    num_train_epochs = 2, # Pour un test rapide, augmenter pour un entraînement complet
    report_to = "none"
    
    # GSPO est ci‑dessous :
    importance_sampling_level = "sequence",
    
    # Dr GRPO / GAPO etc
    loss_type = "dr_grpo",
)
```

Dans l'ensemble, Unsloth permet désormais avec l'inférence rapide VLM vLLM à la fois une réduction de 90% de l'utilisation de la mémoire mais aussi une vitesse 1,5–2x plus élevée avec GRPO et GSPO !

Si vous souhaitez en savoir plus sur le reinforcement learning, consultez notre guide RL :

[](https://unsloth.ai/docs/fr/commencer/reinforcement-learning-rl-guide "mention")

***Auteurs :** Un grand merci à* [*Keith*](https://www.linkedin.com/in/keith-truongcao-7bb84a23b/) *et* [*Datta*](https://www.linkedin.com/in/datta0/) *pour avoir contribué à cet article !*


---

# 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/fr/commencer/reinforcement-learning-rl-guide/vision-reinforcement-learning-vlm-rl.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.
