# Guide de fine-tuning Gemma 4

Vous pouvez désormais entraîner le modèle de Google [Gemma 4](https://unsloth.ai/docs/fr/modeles/qwen3.5) E2B, E4B, 26B-A4B et 31B avec [**Unsloth**](https://github.com/unslothai/unsloth). Unsloth prend en charge tous les fine-tunings vision, texte, audio et RL pour Gemma 4.

* Unsloth entraîne Gemma 4 **\~1,5x plus vite** avec **\~60 % de VRAM en moins** qu’avec les configurations FA2 (sans perte de précision)
* Nous avons corrigé de nombreux bugs universels [pour l’entraînement de Gemma 4](#bug-fixes--tips) (non dérivés d’Unsloth).
* L’entraînement de Gemma 4 E2B fonctionne sur **8 Go de VRAM**. E4B nécessite 10 Go de VRAM.

<a href="#quickstart" class="button primary" data-icon="bolt">Démarrage rapide</a><a href="#bug-fixes--tips" class="button secondary" data-icon="sparkle">Correctifs de bugs + conseils</a>

Fine-tune Gemma 4 via nos **notebooks Google Colab** **gratuits**:

| [**E4B + E2B** (Studio)](https://colab.research.google.com/github/unslothai/unsloth/blob/main/studio/Unsloth_Studio_Colab.ipynb) | [**31B** (Kaggle)](https://www.kaggle.com/code/danielhanchen/gemma4-31b-unsloth) | [E4B **(Vision + Texte)**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E4B\)-Vision.ipynb) | [E4B **(Audio)**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E4B\)-Audio.ipynb) |
| -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |

{% columns %}
{% column %}
Vous pouvez exécuter et entraîner Gemma 4 gratuitement avec une interface utilisateur dans notre [Unsloth Studio](https://unsloth.ai/docs/fr/nouveau/studio)✨ notebook :

Vous pouvez voir plus de [notebooks ici](#unsloth-core-code-based-guide).
{% endcolumn %}

{% column %}
{% embed url="<https://colab.research.google.com/github/unslothai/unsloth/blob/main/studio/Unsloth_Studio_Colab.ipynb>" %}
{% endcolumn %}
{% endcolumns %}

* Gemma 4 E2B LoRA fonctionne sur 8 à 10 Go de VRAM. E4B LoRA nécessite 17 Go de VRAM.
* **31B QLoRA fonctionne avec 22 Go** et 26B-A4B LoRA nécessite >40 Go
* **Exportation**/sauvegarde des modèles vers GGUF, etc. et Le fine-tuning complet **(FFT)** fonctionne aussi.

### :bug: Correctifs de bugs + conseils

{% hint style="success" %}
Si vous voyez **Gemma-4 E2B et E4B afficher une perte de 13 à 15, c’est tout à fait normal** - c’est une particularité courante des modèles multimodaux. Cela s’est également produit sur Gemma-3N, les modèles vision Llama Vision, Mistral et d’autres.

**Gemma 26B et 31B ont une perte plus faible, à 1-3 ou moins. La vision sera 2x plus élevée, donc 3-5**
{% endhint %}

#### :grapes:L’accumulation de gradients peut gonfler vos pertes

{% columns %}
{% column %}

<div data-with-frame="true"><figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FET1GgLeZanVHDpPXLkM9%2FTransformers%20%2B%20TRL%20%2B%20Gemma-4.png?alt=media&#x26;token=0149149f-4d34-4bcb-a545-42e12d5127eb" alt=""><figcaption></figcaption></figure></div>
{% endcolumn %}

{% column %}

<div data-with-frame="true"><figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FZZola3h7ujfqz87VdnQm%2FUnsloth%20%2B%20Gemma-4.png?alt=media&#x26;token=37fc2b61-ae5b-4203-b9a7-388439aefae5" alt=""><figcaption></figcaption></figure></div>
{% endcolumn %}
{% endcolumns %}

Si vous voyez des pertes supérieures à 13-15 (comme 100 ou 300), il est très probable que l’accumulation de gradients ne soit pas prise en compte correctement - nous avons **corrigé cela dans Unsloth et Unsloth Studio.**

Pour en savoir plus sur l’accumulation de gradients, consultez notre article de correction de bug sur l’accumulation de gradients : <https://unsloth.ai/blog/gradient>

#### :interrobang:IndexError sur l’inférence de Gemma-4 31B et 26B-A4B

Vous pourriez voir cette erreur lors de l’inférence avec 31B et 26B :

```python
File "/.../cache_utils.py", line 937, in update
    keys, values = self.layers[layer_idx].update(...)
IndexError: list index out of range
```

Le coupable est ci-dessous :

```python
if hasattr(decoder_config, "num_kv_shared_layers"):
    layer_types = layer_types[: -decoder_config.num_kv_shared_layers]
```

Or Gemma-4 31B et 26B-A4B sont livrés avec `num_kv_shared_layers = 0`. En Python, `-0 == 0`, donc `layer_types[:-0]` se réduit à `layer_types[:0] == []`. Le cache est construit avec zéro emplacement de couche et le tout premier forward d’attention plante dans `Cache.update`.

#### :no\_entry: `use_cache = True` la génération était du charabia pour E2B, E4B

[Voir le problème](https://github.com/huggingface/transformers/issues/45242) "\[Gemma 4] `use_cache=False` corrompt le calcul de l’attention, produisant des logits absurdes #45242"

Gemma-4 E2B et E4B partagent l’état KV entre les couches (`num_kv_shared_layers = 20` et `18`). Le cache est le seul endroit où les premières couches stockent les KV pour que les couches suivantes les réutilisent. Lorsque `use_cache=False` (comme tout tutoriel QLoRA le définit, et comme `gradient_checkpointing=True` l’impose), `Gemma4TextModel.forward` saute la construction du cache, donc les couches à KV partagé retombent à recalculer localement K et V à partir des états cachés actuels. Les logits deviennent incohérents et la perte d’entraînement diverge.

**Avant (`unsloth/gemma-4-E2B-it`, prompt « What is 1+1? ») :**

```
use_cache=True  -> '1 + 1 = **2**'
use_cache=False -> 'BROAD\肯. Specificallyboard K supposed\_n통  \'
max_abs_logit_diff: 48.937500
```

**Après notre correctif :**

```
use_cache=True  -> '1 + 1 = **2**'
use_cache=False -> '1 + 1 = **2**'
max_abs_logit_diff: 0.000000     (parité bit à bit, les 9 jetons identiques)
```

#### :radio:Dépassement de capacité float16 pour l’audio

`Gemma4AudioAttention` utilise `config.attention_invalid_logits_value = -1e9` dans un appel `masked_fill` Sur fp16 (Tesla T4), -1e9 dépasse le maximum fp16 de 65504, provoquant :

```python
RuntimeError: value cannot be converted to type c10::Half without overflow
```

Cela était dû à `self.config.attention_invalid_logits_value` :

```python
attn_weights = attn_weights.masked_fill(
    attention_mask.logical_not(), self.config.attention_invalid_logits_value
)
```

#### 💡Conseils pour Gemma-4

1. Si vous voulez **préserver la capacité de raisonnement** vous pouvez mélanger des exemples de style raisonnement avec des réponses directes (conservez un minimum de 75 % de raisonnement). Sinon, vous pouvez l’émettre entièrement.\
   \
   Utilisez `gemma-4` pour le modèle de chat sans réflexion et `gemma-4-thinking` pour la variante avec réflexion.\
   Utilisez la version avec réflexion pour les plus grands modèles 26B et 31B, et la version sans réflexion pour les petits modèles.<br>

   ```python
   from unsloth.chat_templates import get_chat_template
   tokenizer = get_chat_template(
       tokenizer,
       chat_template = "gemma-4-thinking", # Ou "gemma-4"
   )
   ```
2. Pour activer le mode réflexion, utilisez `enable_thinking = True / False` dans `tokenizer.apply_chat_template`<br>

   Réflexion activée :

   <pre class="language-python" data-overflow="wrap"><code class="lang-python">processor.tokenizer.apply_chat_template([
       {"role" : "user", "content" : "What is 2+2?"},
   ], tokenize = False, enable_thinking = True, add_generation_prompt = True)
   </code></pre>

   Affichera `<bos><|turn>system\n<|think|><turn|>\n<|turn>user\nWhat is 2+2?<turn|>\n<|turn>model\n`<br>

   Réflexion désactivée :

   ```python
   processor.tokenizer.apply_chat_template([
       {"role" : "user", "content" : "What is 2+2?"},
   ], tokenize = False, enable_thinking = False, add_generation_prompt = True)
   ```

   Affichera `<bos><|turn>user\nWhat is 2+2?<turn|>\n<|turn>model\n<|channel>thought\n<channel|>`
3. Gemma 4 est puissante pour le fine-tuning multilingue car elle prend en charge 140 langues.
4. Il est recommandé d’entraîner **E4B QLoRA** plutôt que **E2B LoRA** car E4B est plus grand et la différence de précision due à la quantisation est minime. Gemma 4 E4B LoRA est encore meilleure.
5. Après le fine-tuning, vous pouvez exporter vers [GGUF](#saving-export-your-fine-tuned-model) (pour llama.cpp/Unsloth/Ollama/etc.)

### ⚡Démarrage rapide

#### 🦥 Guide Unsloth Studio

{% columns %}
{% column %}
Gemma 4 peut être exécuté et fine-tuné dans [Unsloth Studio](https://unsloth.ai/docs/fr/nouveau/studio), notre nouvelle interface web open source pour l’IA locale.

Avec Unsloth Studio, vous pouvez exécuter des modèles localement sur **MacOS, Windows**, Linux et entraîner des GPU NVIDIA. La prise en charge de l’entraînement Intel, MLX et AMD arrive ce mois-ci.
{% endcolumn %}

{% column %}

<div data-with-frame="true"><figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FpZlhqoILYOzznGpbudUk%2Funsloth%20studio%20gemma%20graphic.png?alt=media&#x26;token=75e41585-e363-45cf-a87e-4d02960766ed" alt=""><figcaption></figcaption></figure></div>
{% endcolumn %}
{% endcolumns %}

{% stepper %}
{% step %}

#### Installer Unsloth

Exécutez dans votre terminal :

**MacOS, Linux, WSL :**

```bash
curl -fsSL https://unsloth.ai/install.sh | sh
```

**Windows PowerShell :**

```bash
irm https://unsloth.ai/install.ps1 | iex
```

{% hint style="success" %}
**L’installation sera rapide et prendra environ 1 à 2 min.**
{% endhint %}
{% endstep %}

{% step %}

#### Lancer Unsloth

**MacOS, Linux, WSL et Windows :**

```bash
unsloth studio -H 0.0.0.0 -p 8888
```

**Puis ouvrez `http://localhost:8888` dans votre navigateur.**
{% endstep %}

{% step %}

#### Entraîner Gemma 4

Lors du premier lancement, vous devrez créer un mot de passe pour sécuriser votre compte et vous reconnecter plus tard. Vous verrez ensuite un assistant d’intégration rapide pour choisir un modèle, un jeu de données et des paramètres de base. Vous pouvez le passer à tout moment.

Recherchez Gemma 4 dans la barre de recherche et sélectionnez le modèle et le jeu de données souhaités. Ensuite, ajustez vos hyperparamètres et la longueur de contexte comme vous le souhaitez.

<div data-with-frame="true"><figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FpZlhqoILYOzznGpbudUk%2Funsloth%20studio%20gemma%20graphic.png?alt=media&#x26;token=75e41585-e363-45cf-a87e-4d02960766ed" alt="" width="563"><figcaption></figcaption></figure></div>
{% endstep %}

{% step %}

#### Surveiller la progression de l’entraînement

Après avoir cliqué sur démarrer l’entraînement, vous pourrez surveiller et observer la progression de l’entraînement du modèle. La perte d’entraînement devrait diminuer régulièrement.\
Une fois terminé, le modèle sera automatiquement sauvegardé.

<div data-with-frame="true"><figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FeBrnu9zxARIkhOHzd0pq%2FScreenshot%202026-04-07%20at%205.53.32%E2%80%AFAM.png?alt=media&#x26;token=dae77231-5020-4e8c-b2b8-cc49a98a9edf" alt="" width="563"><figcaption></figcaption></figure></div>
{% endstep %}

{% step %}

#### Exporter votre modèle fine-tuné

Une fois terminé, Unsloth Studio vous permet d’exporter le modèle vers GGUF, safetensor, etc.

<div data-with-frame="true"><figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FBtpx58zCdrOD4zB4DPSC%2FScreenshot%202026-04-07%20at%206.12.41%E2%80%AFAM.png?alt=media&#x26;token=05f05af2-5f7f-4b91-9c99-21d6a9b04935" alt="" width="563"><figcaption></figcaption></figure></div>
{% endstep %}

{% step %}

#### Comparer le modèle fine-tuné au modèle d’origine

Cliquez sur `Mode Comparaison` pour comparer l’adaptateur LoRA et le modèle d’origine.

<div data-with-frame="true"><figure><img src="https://550366147-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fvm2CBSg7QBkKwTMKutyr%2FScreenshot%202026-04-07%20at%206.14.50%E2%80%AFAM.png?alt=media&#x26;token=8c9c159f-9d5b-4468-8984-681d19ebc427" alt="" width="563"><figcaption></figcaption></figure></div>
{% endstep %}
{% endstepper %}

#### 🦥 Guide Unsloth Core (basé sur le code)

Nous avons créé des notebooks gratuits pour Gemma 4 :

| [E4B **(Inférence + Texte)**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E4B\)-Text.ipynb) | [E4B **(Vision + Texte)**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E4B\)-Vision.ipynb) | [E4B **(Audio)**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E4B\)-Audio.ipynb) |
| ---------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| [**31B** (Kaggle)](https://www.kaggle.com/code/danielhanchen/gemma4-31b-unsloth)                                                   | [E2B **(Vision + Texte)**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E2B\)-Vision.ipynb) | [E2B **(Audio)**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E2B\)-Audio.ipynb) |

Nous avons aussi créé des notebooks pour les modèles Gemma 4 plus grands, mais ils nécessitent un A100 :

| [Gemma-4-26B-A4B](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(26B_A4B\)-Vision.ipynb) - GPU A100 | [Gemma-4-31B](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(31B\)-Vision.ipynb) - GPU A100 |
| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |

{% hint style="info" %}
**Si vous souhaitez faire** [**GRPO**](https://unsloth.ai/docs/fr/commencer/reinforcement-learning-rl-guide)**, cela fonctionne dans Unsloth si vous désactivez l’inférence rapide vLLM et utilisez à la place l’inférence Unsloth. Suivez nos exemples de notebooks** [**RL de vision**](https://unsloth.ai/docs/fr/commencer/reinforcement-learning-rl-guide/vision-reinforcement-learning-vlm-rl) **.**
{% endhint %}

Ci-dessous se trouve une recette SFT texte autonome pour Gemma-4-26B-A4B-it. Ceci est uniquement du texte - voir aussi notre [fine-tuning vision](https://unsloth.ai/docs/fr/notions-de-base/vision-fine-tuning) section pour plus de détails.

{% code expandable="true" %}

````python
from unsloth import FastModel
import torch

model, tokenizer = FastModel.from_pretrained(
    model_name = "unsloth/gemma-4-26B-A4B-it", # Remplacez ceci par unsloth/gemma-4-E2B-it etc
    dtype = None, # None pour la détection automatique
    max_seq_length = 8192, # Choisissez n’importe quelle valeur pour un long contexte !
    load_in_4bit = True,  # Quantification 4 bits pour réduire la mémoire
    full_finetuning = False, # [NOUVEAU !] Nous avons maintenant le fine-tuning complet !
    # token = "VOTRE_HF_TOKEN", # Jeton HF pour les modèles à accès restreint
)

"""# Gemma 4 peut traiter le texte, la vision et l’audio !

Voyons d’abord comment Gemma 4 peut gérer des entrées multimodales. Nous utilisons les réglages recommandés par Gemma 4 : `temperature = 1.0, top_p = 0.95, top_k = 64`
"""

from transformers import TextStreamer
# Fonction utilitaire pour l’inférence
def do_gemma_4_inference(messages, max_new_tokens = 128):
    _ = model.generate(
        **tokenizer.apply_chat_template(
            messages,
            add_generation_prompt = True, # Doit être ajouté pour la génération
            tokenize = True,
            return_dict = True,
            return_tensors = "pt",
        ).to("cuda"),
        max_new_tokens = max_new_tokens,
        use_cache=True,
        temperature = 1.0, top_p = 0.95, top_k = 64,
        streamer = TextStreamer(tokenizer, skip_prompt = True),
    )

"""# Gemma 4 peut voir des images !

<img src="https://files.worldwildlife.org/wwfcmsprod/images/Sloth_Sitting_iStock_3_12_2014/story_full_width/8l7pbjmj29_iStock_000011145477Large_mini__1_.jpg" alt="Texte alternatif" height="256">
"""

sloth_link = "https://files.worldwildlife.org/wwfcmsprod/images/Sloth_Sitting_iStock_3_12_2014/story_full_width/8l7pbjmj29_iStock_000011145477Large_mini__1_.jpg"

messages = [{
    "role" : "user",
    "content": [
        { "type": "image", "image" : sloth_link },
        { "type": "text",  "text" : "Dans quels films cet animal apparaît-il ?" }
    ]
}]
Vous devrez peut-être attendre 1 minute pour le compilateur automatique d’Unsloth
do_gemma_4_inference(messages, max_new_tokens = 256)

"""Écrivons un poème sur les paresseux !"""

messages = [{
    "role": "user",
    "content": [{ "type" : "text",
                  "text" : "Écris un poème sur les paresseux." }]
}]
do_gemma_4_inference(messages)

"""# Fine-tunons Gemma 4 !

Vous pouvez fine-tuner les parties vision et texte pour le moment via la sélection - la partie audio peut aussi être fine-tunée - nous travaillons à la rendre sélectionnable également !

Nous ajoutons maintenant des adaptateurs LoRA afin de n’avoir à mettre à jour qu’une petite quantité de paramètres !
"""

model = FastModel.get_peft_model(
    model,
    finetune_vision_layers     = False, # Désactivez pour uniquement le texte !
    finetune_language_layers   = True,  # À laisser activé !
    finetune_attention_modules = True,  # L’attention est bonne pour GRPO
    finetune_mlp_modules       = True,  # À laisser toujours activé !

    r = 8,           # Plus grand = meilleure précision, mais peut surajuster
    lora_alpha = 8,  # Alpha recommandé == r au moins
    lora_dropout = 0,
    bias = "none",
    random_state = 3407,
)

"""<a name="Data"></a>
### Préparation des données
Nous utilisons maintenant le format `Gemma-4` pour les fine-tunings de type conversation. Nous utilisons le jeu de données [FineTome-100k de Maxime Labonne](https://huggingface.co/datasets/mlabonne/FineTome-100k) au style ShareGPT. Gemma-4 affiche les conversations multi-tours comme ci-dessous :

```
<bos><|turn>user
Bonjour<turn|>
<|turn>model
Salut !<turn|>
```
Nous utilisons notre fonction `get_chat_template` pour obtenir le bon modèle de chat. Nous prenons en charge `zephyr, chatml, mistral, llama, alpaca, vicuna, vicuna_old, phi3, llama3, phi4, qwen2.5, gemma3, gemma-4` et plus encore.
"""

from unsloth.chat_templates import get_chat_template
tokenizer = get_chat_template(
    tokenizer,
    chat_template = "gemma-4-thinking",
)

"""Nous récupérons les 3000 premières lignes du jeu de données"""

from datasets import load_dataset
dataset = load_dataset("mlabonne/FineTome-100k", split = "train[:3000]")

"""Nous utilisons maintenant `standardize_data_formats` pour essayer de convertir les jeux de données au bon format à des fins de fine-tuning !"""

from unsloth.chat_templates import standardize_data_formats
dataset = standardize_data_formats(dataset)

"""Voyons à quoi ressemble la ligne 100 !"""

dataset[100]

"""Nous devons maintenant appliquer le modèle de chat `Gemma-3` aux conversations et l’enregistrer dans `text`. Nous supprimons le jeton `<bos>` en utilisant removeprefix(`'<bos>'`) puisque nous faisons du fine-tuning. Le Processor ajoutera ce jeton avant l’entraînement et le modèle n’en attend qu’un seul."""

def formatting_prompts_func(examples):
   convos = examples["conversations"]
   texts = [tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False).removeprefix('<bos>') for convo in convos]
   return { "text" : texts, }

dataset = dataset.map(formatting_prompts_func, batched = True)

"""Voyons comment le modèle de chat s’en est sorti ! Remarquez qu’il n’y a pas de jeton `<bos>` car le tokenizer du processeur en ajoutera un."""

dataset[100]["text"]

"""<a name="Train"></a>
### Entraîner le modèle
Maintenant, entraînons notre modèle. Nous faisons 60 étapes pour aller plus vite, mais vous pouvez définir `num_train_epochs=1` pour une exécution complète, et désactiver `max_steps=None`.
"""

from trl import SFTTrainer, SFTConfig
trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = dataset,
    eval_dataset = None, # On peut configurer l’évaluation !
    args = SFTConfig(
        dataset_text_field = "text",
        per_device_train_batch_size = 1,
        gradient_accumulation_steps = 4, # Utilisez GA pour simuler la taille de lot !
        warmup_steps = 5,
        # num_train_epochs = 1, # Définissez ceci pour un entraînement complet.
        max_steps = 60,
        learning_rate = 2e-4, # Réduisez à 2e-5 pour les longs entraînements
        logging_steps = 1,
        optim = "adamw_8bit",
        weight_decay = 0.001,
        lr_scheduler_type = "linear",
        seed = 3407,
        report_to = "none", # Utilisez TrackIO/WandB etc
    ),
)

"""Nous utilisons aussi la méthode `train_on_completions` d’Unsloth pour n’entraîner que sur les sorties de l’assistant et ignorer la perte sur les entrées de l’utilisateur. Cela aide à améliorer la précision des fine-tunings !"""

from unsloth.chat_templates import train_on_responses_only
trainer = train_on_responses_only(
    trainer,
    instruction_part = "<|turn>user\n",
    response_part = "<|turn>model\n",
)

"""Vérifions que le masquage de la partie instruction est effectué ! Réimprimons la 100e ligne. Remarquez que l’exemple n’a qu’un seul `<bos>` comme prévu !"""

tokenizer.decode(trainer.train_dataset[100]["input_ids"])

"""Imprimons maintenant l’exemple masqué - vous devriez voir que seule la réponse est présente :"""

tokenizer.decode([tokenizer.pad_token_id if x == -100 else x for x in trainer.train_dataset[100]["labels"]]).replace(tokenizer.pad_token, " ")

"""# Entraînons le modèle !

Pour reprendre un entraînement, définissez `trainer.train(resume_from_checkpoint = True)`
"""

trainer_stats = trainer.train()
````

{% endcode %}

{% hint style="info" %}
Si vous avez un OOM :

* Réduisez `per_device_train_batch_size` à **1** et/ou réduisez `max_seq_length`.&#x20;
* Conservez `use_`[`gradient_checkpointing`](https://unsloth.ai/docs/fr/blog/500k-context-length-fine-tuning#unsloth-gradient-checkpointing-enhancements)`="unsloth"` activé (il est conçu pour réduire l’utilisation de VRAM et augmenter la longueur de contexte).
  {% endhint %}

**Exemple de chargeur pour MoE (LoRA bf16) :**

```python
import os
import torch
from unsloth import FastModel

model, tokenizer = FastModel.from_pretrained(
    model_name = "unsloth/Gemma-4-26B-A4B-it",
    max_seq_length = 2048,
    load_in_4bit = False,     # La MoE QLoRA n’est pas recommandée, le 31B dense va bien
    load_in_16bit = True,     # LoRA bf16/16 bits
    full_finetuning = False,
)
```

Une fois chargé, vous attacherez des adaptateurs LoRA et entraînerez de manière similaire à l’exemple SFT ci-dessus.

### Fine-tuning MoE (26B-A4B)

Le **26B-A4B** modèle est le compromis entre vitesse et qualité dans la gamme Gemma 4. Comme c’est un modèle **MoE** avec seulement un sous-ensemble de paramètres actif par jeton, une approche prudente de fine-tuning est :

* utiliser **LoRA** plutôt que le fine-tuning complet
* préférer **LoRA 16 bits / bf16** si la mémoire le permet
* commencer d’abord avec des contextes plus courts et des rangs plus petits
* augmenter l’échelle uniquement une fois le pipeline stabilisé

Si votre objectif est la plus haute qualité et que vous avez plus de mémoire, utilisez **31B** à la place.

### Fine-tuning multimodal (E2B / E4B)

Parce que **E2B** et **E4B** prend en charge **l’image** et **l’audio**, ce sont les principales variantes de Gemma 4 pour le fine-tuning multimodal.

* chargez le modèle multimodal avec `FastVisionModel`
* gardez `finetune_vision_layers = False` d’abord
* fine-tunez uniquement les couches langage, attention et MLP
* activez ensuite les couches vision ou audio si votre tâche en a besoin

#### Exemple de LoRA multimodal Gemma 4 :

{% code expandable="true" %}

````python
from unsloth import FastVisionModel # FastLanguageModel pour les LLMs
import torch

model, processor = FastVisionModel.from_pretrained(
    "unsloth/gemma-4-26B-A4B-it",
    load_in_4bit = True, # Utilisez le 4 bits pour réduire l’utilisation de mémoire. False pour une LoRA 16 bits.
    use_gradient_checkpointing = "unsloth", # True ou "unsloth" pour un long contexte
)

"""Nous ajoutons maintenant des adaptateurs LoRA pour un fine-tuning efficace en paramètres, ce qui nous permet d’entraîner efficacement seulement 1 % de tous les paramètres du modèle.

**[NOUVEAU]** Nous prenons aussi en charge le fine-tuning uniquement du composant vision, uniquement du composant langage, ou des deux. De plus, vous pouvez choisir de fine-tuner les modules d’attention, les couches MLP, ou les deux !
"""

model = FastVisionModel.get_peft_model(
    model,
    finetune_vision_layers     = True, # False si vous ne fine-tunez pas les couches vision
    finetune_language_layers   = True, # False si vous ne fine-tunez pas les couches langage
    finetune_attention_modules = True, # False si vous ne fine-tunez pas les couches d’attention
    finetune_mlp_modules       = True, # False si vous ne fine-tunez pas les couches MLP

    r = 32,                           # Plus grand = meilleure précision, mais peut surajuster
    lora_alpha = 32,                  # Alpha recommandé == r au moins
    lora_dropout = 0,
    bias = "none",
    random_state = 3407,
    use_rslora = False,               # Nous prenons en charge LoRA avec stabilisation du rang
    loftq_config = None,               # Et LoftQ
    target_modules = "all-linear",    # Optionnel maintenant ! Peut spécifier une liste si nécessaire
)

"""<a name="Data"></a>
### Préparation des données
Nous utiliserons un jeu de données échantillonné de formules mathématiques manuscrites. L’objectif est de convertir ces images en un format lisible par ordinateur — plus précisément en LaTeX — afin qu’elles puissent être rendues. C’est particulièrement utile pour les expressions complexes.

Vous pouvez accéder au jeu de données [ici](https://huggingface.co/datasets/unsloth/LaTeX_OCR). Le jeu de données complet est [ici](https://huggingface.co/datasets/linxy/LaTeX_OCR).
"""

from datasets import load_dataset
dataset = load_dataset("unsloth/LaTeX_OCR", split = "train")

"""Prenons un aperçu du jeu de données. Nous examinerons la deuxième image et sa légende correspondante."""

dataset

dataset[2]["image"]

dataset[2]["text"]

"""Nous pouvons aussi rendre du LaTeX directement dans le navigateur !"""

from IPython.display import display, Math, Latex

latex = dataset[3]["text"]
display(Math(latex))

"""Pour formater le jeu de données, toutes les tâches de fine-tuning vision doivent suivre ce format :

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

instruction = "Écrivez la représentation LaTeX de 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["text"]}]},
    ]
    return {"messages": conversation}
pass

"""Convertissons maintenant le jeu de données dans le format « correct » pour le fine-tuning :"""

converted_dataset = [convert_to_conversation(sample) for sample in dataset]

"""Le premier exemple est maintenant structuré comme ci-dessous :"""

converted_dataset[0]

"""Prenons le modèle de chat d’instruction Gemma 4 et utilisons-le dans notre modèle de base"""

from unsloth import get_chat_template

processor = get_chat_template(
    processor,
    "gemma-4-thinking"
)

"""Avant le fine-tuning, évaluons les performances du modèle de base. Nous ne nous attendons pas à de bons résultats, car il n’a encore jamais rencontré ce modèle de chat."""

image = dataset[2]["image"]
instruction = "Écrivez la représentation LaTeX de cette image."

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

from transformers import TextStreamer

text_streamer = TextStreamer(processor, skip_prompt = True)
result = model.generate(**inputs, streamer = text_streamer, max_new_tokens = 128,
                        use_cache = True, temperature = 1.0, top_p = 0.95, top_k = 64)

"""Vous voyez que c’est absolument terrible ! Il ne suit pas du tout les instructions

<a name="Train"></a>
### Entraîner le modèle
Maintenant, entraînons notre modèle. Nous faisons 60 étapes pour accélérer les choses, mais vous pouvez définir `num_train_epochs=1` pour un exécution complète, et désactiver `max_steps=None`. Nous prenons également en charge `DPOTrainer` et `GRPOTrainer` pour l’apprentissage par renforcement !!

Nous utilisons notre nouveau `UnslothVisionDataCollator` qui nous aidera dans notre configuration de fine-tuning vision.
"""

from unsloth.trainer import UnslothVisionDataCollator
from trl import SFTTrainer, SFTConfig

trainer = SFTTrainer(
    model = model,
    train_dataset = converted_dataset,
    processing_class = processor.tokenizer,
    data_collator = UnslothVisionDataCollator(model, processor),
    args = SFTConfig(
        per_device_train_batch_size = 1,
        gradient_accumulation_steps = 4,
        max_grad_norm = 0.3,
        warmup_ratio = 0.03,
        max_steps = 60,
        # num_train_epochs = 2, # Définissez ceci à la place de max_steps pour les exécutions complètes d'entraînement
        learning_rate = 2e-4,
        logging_steps = 1,
        save_strategy = "steps",
        optim = "adamw_8bit",
        weight_decay = 0.001,
        lr_scheduler_type = "cosine",
        seed = 3407,
        output_dir = "outputs",
        report_to = "none", # Pour Weights and Biases ou autres

        # Vous DEVEZ mettre les éléments ci-dessous pour le fine-tuning vision :
        remove_unused_columns = False,
        dataset_text_field = "",
        dataset_kwargs = {"skip_prepare_dataset": True},
        max_length = 2048,
    )
)

trainer_stats = trainer.train()
````

{% endcode %}

#### Format d'exemple d'image

Rappelez-vous : pour les prompts multimodaux Gemma 4, placez l'image **avant** l'instruction textuelle.

{% code expandable="true" %}

```json
{
  "messages": [
    {
      "role": "user",
      "content": [
        {"type": "image", "image": "/path/to/image OR object"},
        {"type": "text", "text": "Extrayez tout le texte de ce reçu. Retournez les lignes, le total, le commerçant et la date sous forme de JSON."}
      ]
    },
    {
      "role": "assistant",
      "content": [
        {"type": "text", "text": "{\"merchant\": \"Example Store\", \"total\": \"19.99\"}"}
      ]
    }
  ]
}
```

{% endcode %}

#### Format d'exemple audio

L'audio est pour **E2B / E4B** uniquement. Gardez les clips courts et spécifiques à la tâche.

{% code expandable="true" %}

```json
{
  "messages": [
    {
      "role": "user",
      "content": [
        {"type": "audio", "audio": "/path/to/audio OR object"},
        {"type": "text", "text": "Transcrivez le segment de discours suivant en anglais en texte anglais. N'affichez que la transcription."}
      ]
    },
    {
      "role": "assistant",
      "content": [
        {"type": "text", "text": "Bonjour à tous et bon retour."}
      ]
    }
  ]
}
```

{% endcode %}

### Enregistrement / exportation du modèle fine-tuné

Vous pouvez consulter nos guides spécifiques d'inférence / déploiement pour [Unsloth Studio](https://unsloth.ai/docs/fr/nouveau/studio/export), [llama.cpp](https://unsloth.ai/docs/fr/notions-de-base/inference-and-deployment/saving-to-gguf), [vLLM](https://unsloth.ai/docs/fr/notions-de-base/inference-and-deployment/vllm-guide), [llama-server](https://unsloth.ai/docs/fr/notions-de-base/inference-and-deployment/llama-server-and-openai-endpoint), [Ollama](https://unsloth.ai/docs/fr/notions-de-base/inference-and-deployment/saving-to-ollama) ou [SGLang](https://unsloth.ai/docs/fr/notions-de-base/inference-and-deployment/sglang-guide).

#### Enregistrer au format GGUF

Unsloth prend en charge l'enregistrement direct au format GGUF :

```python
model.save_pretrained_gguf("directory", tokenizer, quantization_method = "q4_k_m")
model.save_pretrained_gguf("directory", tokenizer, quantization_method = "q8_0")
model.save_pretrained_gguf("directory", tokenizer, quantization_method = "f16")
```

Ou envoyer les GGUF sur Hugging Face :

```python
model.push_to_hub_gguf("hf_username/directory", tokenizer, quantization_method = "q4_k_m")
model.push_to_hub_gguf("hf_username/directory", tokenizer, quantization_method = "q8_0")
```

Si le modèle exporté se comporte moins bien dans un autre environnement d'exécution, Unsloth identifie la cause la plus courante : **mauvais modèle de chat / jeton EOS au moment de l'inférence** (vous devez utiliser le même modèle de chat que celui avec lequel vous avez entraîné).

Pour plus de détails, lisez nos guides d'inférence :

{% columns %}
{% column width="50%" %}
{% content-ref url="../../notions-de-base/inference-and-deployment" %}
[inference-and-deployment](https://unsloth.ai/docs/fr/notions-de-base/inference-and-deployment)
{% endcontent-ref %}

{% content-ref url="../../notions-de-base/inference-and-deployment/saving-to-gguf" %}
[saving-to-gguf](https://unsloth.ai/docs/fr/notions-de-base/inference-and-deployment/saving-to-gguf)
{% endcontent-ref %}
{% endcolumn %}

{% column width="50%" %}
{% content-ref url="../../nouveau/studio/export" %}
[export](https://unsloth.ai/docs/fr/nouveau/studio/export)
{% endcontent-ref %}

{% content-ref url="../../notions-de-base/inference-and-deployment/vllm-guide" %}
[vllm-guide](https://unsloth.ai/docs/fr/notions-de-base/inference-and-deployment/vllm-guide)
{% endcontent-ref %}
{% endcolumn %}
{% endcolumns %}

### Bonnes pratiques de données pour Gemma 4

Gemma 4 comporte quelques détails de formatage que vous devez garder à l'esprit.

#### 1. Utilisez les rôles de chat standard

Gemma 4 utilise les standards suivants :

* `system`
* `user`
* `assistant`

Cela signifie que votre ensemble de données SFT doit être rédigé au format de chat standard plutôt qu'avec d'anciens formats de rôles spécifiques à Gemma.

#### 2. Le mode réflexion est explicite

Si vous souhaitez conserver un comportement de type réflexion pendant le SFT :

* gardez le format cohérent
* décidez si vous voulez entraîner sur **des blocs de réflexion visibles** ou sur **les réponses finales uniquement**
* faites **ne pas** mélanger plusieurs formats de réflexion incompatibles dans le même ensemble de données

Pour la plupart des assistants de production, la configuration la plus simple consiste à fine-tuner sur la **réponse finale visible uniquement**.

#### 3. Règle des conversations à plusieurs tours

Pour les conversations à plusieurs tours, ne conservez que la **réponse finale visible** dans l'historique de la conversation. Ne **ne pas** réinjectez pas les blocs de réflexion précédents dans les tours suivants.
