# Entraînement des LLM 3x plus rapide avec les kernels Unsloth + le packing

Unsloth prend désormais en charge jusqu'à **5× plus rapide** (typiquement 3x) l'entraînement avec nos nouveaux **noyaux RoPE et MLP Triton**, plus notre nouveau empaquetage automatique intelligent. Les nouveaux noyaux et fonctionnalités d'Unsloth augmentent non seulement la vitesse d'entraînement, mais **réduisent également l'utilisation de la VRAM (30% - 90%)** sans perte de précision. [Unsloth GitHub](https://github.com/unslothai/unsloth)\
\
Cela signifie que vous pouvez désormais entraîner des LLMs comme [Qwen3](/docs/fr/modeles/tutorials/qwen3-how-to-run-and-fine-tune.md)-4B non seulement sur seulement **3 Go de VRAM**, mais aussi 3x plus rapidement.

Notre auto [**sans remplissage**](#padding-free-by-default) l'empaquetage non contaminé est activé intelligemment pour toutes les sessions d'entraînement sans aucun changement, et pour tous les backends d'attention rapide (FlashAttention 3, xFormers, SDPA). [Benchmarks](#analysis-and-benchmarks) montrent que les pertes d'entraînement correspondent aux exécutions sans empaquetage **exactement**.

* **Noyau Triton RoTation QK 2,3x plus rapide** noyau Triton fusionné avec prise en charge de l'empaquetage
* Noyaux SwiGLU, GeGLU mis à jour avec **indexation int64 pour un contexte long**
* **Empaquetage non contaminé 2,5x à 5x plus rapide** avec les backends xformers, SDPA, FA3
* **2,1x plus rapide sans remplissage, 50% de VRAM en moins**, 0% de changement de précision
* Unsloth bénéficie désormais également d'une meilleure stabilité de la perte SFT et d'une utilisation GPU plus prévisible.
* Cette nouvelle mise à niveau fonctionne **pour toutes les méthodes d'entraînement** par ex. fine-tuning complet, préentraînement, etc.

### :drum:Noyau Triton RoPE QK fusionné avec empaquetage

En décembre 2023, nous avons présenté un noyau RoPE codé en Triton dans le cadre du lancement d'Unsloth. En mars 2024, un membre de la communauté a rendu l'entraînement de bout en bout 1-2% plus rapide en optimisant le noyau RoPE pour permettre de lancer un bloc pour un groupe de têtes. Voir [PR 238](https://github.com/unslothai/unsloth/pull/238).

<figure><img src="/files/c4250004c93a4769db63e804c49ca6d1d5388cc3" alt="" width="563"><figcaption></figcaption></figure>

Un problème était que pour chaque Q et K, il y avait 2 noyaux Triton. Nous les avons maintenant fusionnés en 1 noyau Triton, et activé la RoPE de longueur variable, ce qui était impératif pour le sans remplissage et la prise en charge de l'empaquetage. Cela rend le noyau RoPE dans les micro-benchmarks **2,3x plus rapide sur des longueurs de contexte plus longues**, et 1,9x plus rapide sur des longueurs de contexte plus courtes.

Nous avons également éliminé toutes les copies et opérations de transposition contiguës, et donc **RoPE est désormais entièrement en place**, réduisant encore la mémoire GPU. Notez que pour la passe arrière, nous observons que `sin1 = -sin1` puisque :

```
Q * cos + rotate_half(Q) * sin
est équivalent à
Q * cos + Q @ R * sin
où R est une matrice de rotation [ 0,  I]
                             [-I,  0]
dC/dY = dY * cos + dY @ R.T * sin
où R.T est encore la même  [ 0, -I]
mais le signe moins est transposé. [ I,  0]
```

### :railway\_car:Indexation Int64 pour les noyaux Triton

Lors d'un entraînement sur contexte long de 500K que nous avons introduit dans [500K Context Training](/docs/fr/blog/500k-context-length-fine-tuning.md), nous avions des erreurs CUDA hors limites. C'était parce que les noyaux MLP pour SwiGLU, GeGLU utilisaient une indexation int32 qui est par défaut dans Triton et CUDA.

Nous ne pouvons pas simplement faire `tl.program_id(0).to(tl.int64)` car l'entraînement sera légèrement plus lent en raison de l'indexation int64. Nous en faisons plutôt une `LONG_INDEXING: tl.constexpr` variable afin que le compilateur Triton puisse la spécialiser. Cela permet aux exécutions de contexte courts et longs de bien fonctionner !

{% code overflow="wrap" %}

```python
block_idx = tl.program_id(0)
if LONG_INDEXING:
    offsets = block_idx.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE).to(tl.int64)
    n_elements = tl.cast(n_elements, tl.int64)
else:
    offsets = block_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
```

{% endcode %}

### :abacus:Pourquoi le remplissage est-il nécessaire & accélération mathématique

Les ordinateurs et GPU ne peuvent pas traiter des ensembles de données de longueurs différentes, nous devons donc les remplir avec des 0. Cela provoque du gaspillage. Supposons que nous ayons un ensemble de données composé à 50% de courtes séquences S et à 50% de longues séquences L, alors dans le pire des cas, le remplissage fera que l'utilisation des tokens sera $$\text{batchsize} \times L$$ puisque la longueur de la séquence la plus longue domine.

En empaquetant plusieurs exemples dans un seul tenseur unidimensionnel long, nous pouvons éliminer une quantité significative de remplissage. En fait, nous obtenons l'utilisation de tokens ci-dessous :

$$
\text{Token Usage} = \frac{\text{batchsize}}{2}L+\frac{\text{batchsize}}{2}S
$$

Par quelques calculs et algèbre, nous pouvons déterminer l'accélération via :

$$
\text{Speedup} = \frac{\text{batchsize} \times L}{\frac{\text{batchsize}}{2}L+\frac{\text{batchsize}}{2}S} = 2 \frac{L}{L + S}
$$

En supposant $$S\rightarrow0$$ alors nous obtenons un gain théorique de 2x car $$2 \frac{L}{L + 0} = 2$$

En changeant le ratio de 50% de courtes séquences, et en supposant que nous ayons PLUS de courtes séquences, par exemple 20% de longues séquences et 80% de courtes séquences, nous obtenons $$\frac{L}{0.2L + 0.8S}\rightarrow\frac{L}{0.2L}=5$$ donc un entraînement 5x plus rapide ! Cela signifie que l'accélération de l'empaquetage dépend de la proportion de lignes courtes dans votre ensemble de données (plus il y a de courtes, plus c'est rapide).

### :clapper:Sans remplissage par défaut

En plus des gains de débit importants disponibles lors du réglage de `packing = True` dans votre `SFTConfig` , nous allons **utilise automatiquement le batching sans remplissage** afin de réduire le gaspillage dû au remplissage, d'améliorer le débit et d'augmenter les tokens/s de débit, tout en obtenant ***exactement la même perte*** que dans la version précédente d'Unsloth.

Par exemple, pour Qwen3-8B et Qwen3-32B, nous constatons une diminution de l'utilisation mémoire de 60%, une vitesse 2x supérieure et des courbes de perte et de norme de gradient identiques !

<div><figure><img src="/files/9ef8b506a42c25ee3ab659bd916e465ea5edbd8a" alt=""><figcaption></figcaption></figure> <figure><img src="/files/42b0ee0112dba6fc1119d48a7d413923c5dfabef" alt=""><figcaption></figcaption></figure></div>

<div><figure><img src="/files/0d1d5503c56e5661a3502e90f15838dde6267681" alt="" width="563"><figcaption></figcaption></figure> <figure><img src="/files/40ed1b399e607b34db1a3719da031d1679ecc904" alt="" width="563"><figcaption></figcaption></figure> <figure><img src="/files/152362ff98d5d5eaf9f3cea1f64cdc016f445532" alt="" width="563"><figcaption></figcaption></figure> <figure><img src="/files/67b2fa762c378d58a348d8ef03bb36042f5ca707" alt="" width="563"><figcaption></figcaption></figure></div>

### :spades:Empaquetage non contaminé : entraînement 2-5x plus rapide

Les jeux de données réels peuvent contenir des longueurs de séquence différentes, donc augmenter la taille de lot à 32 par exemple entraînera du remplissage, rendant l'entraînement plus lent et utilisant plus de VRAM.

{% hint style="success" %}
Par le passé, augmenter `: Assurez-vous que les paramètres` à de grands nombres (>32) rendait l'entraînement PLUS LENT, pas plus rapide. Cela était dû au remplissage - nous pouvons désormais éliminer ce problème via `packing = True`, et donc l'entraînement est PLUS RAPIDE !
{% endhint %}

Lorsque nous empaquetons plusieurs échantillons dans un seul tenseur unidimensionnel, nous conservons les métadonnées de longueur de séquence afin de bien masquer les échantillons, sans fuir l'attention entre eux. Nous avons également besoin du noyau RoPE décrit dans [#fused-qk-rope-triton-kernel-with-packing](#fused-qk-rope-triton-kernel-with-packing "mention") pour permettre la réinitialisation des identifiants de position.

{% columns %}
{% column width="41.66666666666667%" %}

<div align="center" data-full-width="false"><figure><img src="/files/1d8566ec3b0a4f271934c2848a68f642f78f143b" alt="" width="563"><figcaption><p>4 exemples sans empaquetage gaspillent de l'espace</p></figcaption></figure></div>

{% endcolumn %}

{% column width="58.33333333333333%" %}

<figure><img src="/files/17057ef836fd43a1972f7beb6d8e063f02b64a75" alt=""><figcaption><p>L'empaquetage non contaminé crée un schéma d'attention correct</p></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

En changeant le ratio de 50% de courtes séquences, et en supposant que nous ayons PLUS de courtes séquences, par ex. 20% de longues séquences et 80% de longues séquences, nous obtenons $$\frac{L}{0.2L + 0.8S}\rightarrow\frac{L}{0.2L}=5$$ donc un entraînement 5x plus rapide ! Cela signifie que l'accélération de l'empaquetage dépend de la proportion de lignes courtes dans votre ensemble de données (plus il y a de courtes, plus c'est rapide).

### :beach:Analyse et benchmarks

Pour démontrer les diverses améliorations lors de l'entraînement avec nos nouveaux noyaux et données empaquetées, nous avons exécuté des runs de fine-tuning avec [Qwen3-32B](/docs/fr/modeles/tutorials/qwen3-how-to-run-and-fine-tune.md), Qwen3-8B, Llama 3 8B sur le `yahma/alpaca-cleaned` jeu de données et mesuré diverses [pertes d'entraînement](#padding-free-by-default) débit et métriques d'efficacité. Nous avons comparé nos nouvelles exécutions avec un run d'entraînement optimisé standard avec nos propres noyaux/optimisations activés et des noyaux comme Flash Attention 3 (FA3) activés. Nous avons fixé `max_length = 1024` et fait varier la taille de lot dans {1, 2, 4, 8, 16, 32}. Cela permet au nombre maximal de tokens par lot de varier dans {1024, 2048, 4096, 8192, 16K, 32K}.

<figure><img src="/files/b34ac591bef397516acd6c78707e902af8a6e36f" alt="" width="563"><figcaption></figcaption></figure>

Ce qui précède montre comment le débit d'entraînement en tokens par seconde (tokens/s) varie pour le nouvel Unsloth en fonction de la taille de lot. Cela se traduit par l'entraînement de votre modèle sur une époque de votre jeu de données **1,7-3x plus rapide (parfois même 5x ou plus)**! Ces gains seront plus prononcés s'il y a beaucoup de courtes séquences dans vos données et si vous avez des runs d'entraînement plus longs, comme décrit dans [#why-is-padding-needed-and-mathematical-speedup](#why-is-padding-needed-and-mathematical-speedup "mention")

<figure><img src="/files/354dd3edafe5a8b4fa295618b6764d1f2ae247b7" alt="" width="563"><figcaption></figcaption></figure>

Ce qui précède montre le pourcentage moyen de tokens par lot qui sont valides (c.-à-d. non remplissage). À mesure que la longueur de la taille de lot augmente, beaucoup plus de tokens de remplissage apparaissent dans le cas non empaqueté, tandis que nous obtenons une grande efficacité d'empaquetage dans le cas empaqueté quel que soit la longueur de séquence maximale.

Notez que, puisque la logique de batching ajuste les lots à la longueur de séquence maximale vue dans le lot, lorsque la taille de lot est 1, les données non empaquetées sont toutes des tokens valides (c.-à-d. pas de remplissage). Cependant, à mesure que davantage d'exemples sont ajoutés au lot, le remplissage augmente en moyenne, atteignant presque 50% de remplissage avec une taille de lot de 8 ! Notre implémentation d'empaquetage d'échantillons élimine ce gaspillage.

<div><figure><img src="/files/552352d5ac1da198bf5914324af090fcd31e6513" alt="" width="563"><figcaption></figcaption></figure> <figure><img src="/files/2146712a055c10a794189c7f9bd13ea89bb215e2" alt="" width="563"><figcaption></figcaption></figure></div>

Le premier graphique (ci-dessus) trace la progression sur `yahma/alpaca-cleaned` avec `max_length = 2048`, Unsloth nouveau avec empaquetage + noyaux (bordeaux) vs. Unsloth ancien (gris). Les deux sont entraînés avec `max_steps = 500`, mais nous affichons l'axe des x en temps réel. Remarquez que nous entraînons près de 40% d'une époque dans le cas empaqueté avec le même nombre d'étapes (et seulement un peu plus de temps réel) qu'il n'en faut pour entraîner moins de 5% d'une époque dans le cas non empaqueté.

De même, le 2ème graphique (ci-dessus) trace la perte des mêmes runs, cette fois tracée avec les étapes d'entraînement sur l'axe des x. Remarquez que les pertes correspondent en échelle et en tendance, mais la perte dans le cas empaqueté est moins variable puisque le modèle voit plus de tokens par étape d'entraînement.

### :sparkles:Comment activer l'empaquetage ?

**Mettez d'abord Unsloth à jour et le sans remplissage est activé par défaut** ! Ainsi, tout l'entraînement est immédiatement 1,1 à 2x plus rapide avec au moins 30% de mémoire en moins et aucune modification de la courbe de perte !

{% code overflow="wrap" %}

```bash
pip install --upgrade --force-reinstall --no-cache-dir --no-deps unsloth
pip install --upgrade --force-reinstall --no-cache-dir --no-deps unsloth_zoo
```

{% endcode %}

Nous prenons également en charge Flash Attention 3 via Xformers, le support SDPA, Flash Attention 2, et cela fonctionne sur les anciennes GPU (Tesla T4, RTX 2080) et les nouvelles comme les H100, B200, etc. ! L'empaquetage d'échantillons fonctionne *indépendamment du choix du backend d'attention ou de la famille de modèles*, profitez donc des mêmes accélérations qu'auparavant avec ces implémentations d'attention rapides !

Si vous souhaitez activer l'empaquetage explicite, ajoutez simplement `packing = True` pour permettre jusqu'à 5x d'entraînement plus rapide !

{% hint style="warning" %}
Remarque `packing=True` modifiera la perte d'entraînement et fera que le nombre de lignes du jeu de données soit tronqué, puisque plusieurs courtes séquences sont empaquetées en 1 séquence. Vous pourriez voir le nombre d'exemples dans le jeu de données diminuer.

Pour ne pas obtenir des nombres de perte d'entraînement différents, réglez simplement `packing=False` et nous activerons l'auto sans remplissage, ce qui rend déjà l'entraînement plus rapide !
{% endhint %}

```python
from unsloth import FastLanguageModel
from trl import SFTTrainer, SFTConfig

model, tokenizer = FastLanguageModel.from_pretrained(
    "unsloth/Qwen3-14B",
)

trainer = SFTTrainer(
    trainer = Trainer(
    processing_class = tokenizer,
    model = model,
    args = SFTConfig(
        args = TrainingArguments(
        max_length = 4096,
        …,
        packing = True, # requis pour activer l'empaquetage d'échantillons !
    ),
)
trainer.train()
```

Tous nos notebooks sont automatiquement plus rapides (aucune action requise). Voir [Carnets Unsloth](/docs/fr/commencer/unsloth-notebooks.md)

{% columns %}
{% column %}
Qwen3 14B plus rapide :

{% embed url="<https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen3_(14B)-Reasoning-Conversational.ipynb>" %}
{% endcolumn %}

{% column %}
Llama 3.1 Conversationnel plus rapide :

{% embed url="<https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Llama3.2_(1B_and_3B)-Conversational.ipynb>" %}
{% endcolumn %}
{% endcolumns %}

Merci ! Si cela vous intéresse, consultez notre [500K Context Training](/docs/fr/blog/500k-context-length-fine-tuning.md) blog, [RL économe en mémoire](/docs/fr/commencer/reinforcement-learning-rl-guide/memory-efficient-rl.md) blog et [Long Context gpt-oss](/docs/fr/modeles/gpt-oss-how-to-run-and-fine-tune/long-context-gpt-oss-training.md) blog pour plus de sujets sur les noyaux et les gains de performance !


---

# 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/blog/3x-faster-training-packing.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.
