> For the complete documentation index, see [llms.txt](https://unsloth.ai/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://unsloth.ai/docs/fr/modeles/gpt-oss-how-to-run-and-fine-tune/gpt-oss-reinforcement-learning/tutorial-how-to-train-gpt-oss-with-rl.md).

# Tutoriel : comment entraîner gpt-oss avec RL

Les LLM ont souvent du mal avec des tâches impliquant des environnements complexes. Cependant, en appliquant [l’apprentissage par renforcement](/docs/fr/commencer/reinforcement-learning-rl-guide.md) (RL) et en concevant une [fonction de récompense](/docs/fr/commencer/reinforcement-learning-rl-guide.md#reward-functions-verifiers)personnalisée, ces défis peuvent être surmontés.

Le RL peut être adapté à des tâches telles que la génération automatique de noyaux ou la création de stratégies. Ce tutoriel montre comment entraîner **gpt-oss** avec [**GRPO**](/docs/fr/commencer/reinforcement-learning-rl-guide.md#from-rlhf-ppo-to-grpo-and-rlvr) et Unsloth pour battre 2048 de manière autonome.

| [Notebook 2048](https://colab.research.google.com/github/openai/gpt-oss/blob/main/examples/reinforcement-fine-tuning.ipynb) (Exemple officiel d’OpenAI) | [Notebook de génération de noyaux](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/gpt-oss-\(20B\)-GRPO.ipynb) |
| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |

**Ce que vous allez construire :**

* Entraîner gpt-oss-20b pour que le modèle puisse gagner automatiquement à 2048
* Créer un environnement 2048 minimal avec lequel le modèle peut interagir
* Définir **des fonctions de récompense** qui :
  1. Vérifient que la stratégie générée compile et s’exécute,
  2. Empêchent le reward hacking (interdisent les imports externes), et
  3. Récompensent la réussite effective du jeu
* Exécuter l’inférence et exporter le modèle (MXFP4 4 bits ou FP16 fusionné)

{% hint style="info" %}
**Matériel :** L’exemple 2048 fonctionne sur un Colab T4 gratuit, mais l’entraînement sera lent. Un A100/H100 est bien plus rapide. Le chargement en 4 bits + LoRA permet de faire tenir un modèle de 20B dans une VRAM modeste.
{% endhint %}

{% stepper %}
{% step %}

#### Installer Unsloth

Exécutez cette cellule en haut d’un notebook (fonctionne sur Colab).

```bash
!pip install --upgrade -qqq uv
try: import numpy; get_numpy = f"numpy=={numpy.__version__}"
except: get_numpy = "numpy"
!uv pip install -qqq \\
    "torch>=2.8.0" "triton>=3.4.0" {get_numpy} torchvision bitsandbytes "transformers==4.56.2" \\
    "unsloth_zoo[base] @ git+https://github.com/unslothai/unsloth-zoo" \\
    "unsloth[base] @ git+https://github.com/unslothai/unsloth" \\
    git+https://github.com/triton-lang/triton.git@05b2c186c1b6c9a08375389d5efe9cb4c401c075#subdirectory=python/triton_kernels
!uv pip install --upgrade --no-deps transformers==4.56.2 tokenizers
!uv pip install --no-deps trl==0.22.2
```

{% endstep %}

{% step %}

#### Charger gpt-oss avec Unsloth

Chargez le modèle 20B en 4 bits QLoRA pour l’efficacité mémoire, puis enveloppez-le avec un adaptateur LoRA. Vous pouvez aussi l’entraîner en LoRA 16 bits, mais cela utilisera 4 fois plus de mémoire. Pour plus de paramètres, consultez notre [guide de configuration](https://unsloth.ai/docs/fr/modeles/gpt-oss-how-to-run-and-fine-tune/gpt-oss-reinforcement-learning/pages/9bbe6f156adffaddead7109d8475ab4a8547be46#id-2.-choose-the-right-model--method).

```python
from unsloth import FastLanguageModel
import torch

max_seq_length = 768        # Augmentez si votre tâche nécessite des sorties plus longues
lora_rank      = 4          # Rang plus élevé → meilleur, mais plus de VRAM/compute

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name        = "unsloth/gpt-oss-20b",  # ou unsloth/gpt-oss-20b-BF16 sur H100
    max_seq_length    = max_seq_length,
    load_in_4bit      = True,                    # False pour 16 bits
    offload_embedding = True,                    # économise ~1 Go de VRAM
)

model = FastLanguageModel.get_peft_model(
    model,
    r = lora_rank,
    target_modules = [
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj",
    ],
    lora_alpha = lora_rank * 2,
    use_gradient_checkpointing = "unsloth",     # gros gain de mémoire
    random_state = 3407,
)
```

{% hint style="info" %}
Si vous obtenez un OOM, essayez de réduire `max_seq_length`, `lora_rank`, ou `num_generations` (plus tard), et gardez `load_in_4bit=True`.
{% endhint %}
{% endstep %}

{% step %}

#### Environnement de jeu 2048 (minimal)

* Une `classe GameBoard` supportant **les mouvements** W/A/S/D
* La logique de fusion/score
* `execute_with_time_limit` wrapper pour empêcher les stratégies mal écrites de bloquer le noyau

Vous pouvez faire un test rapide avec une politique triviale :

```python
def always_move_left(board):
    return "W"

steps, outcome = execute_strategy(always_move_left, GameBoard(size=8, seed=42, target=2048, probability_fours=0.10))
```

{% endstep %}

{% step %}

#### Exécution de code sûre et vérifications anti-triche

Les stratégies générées sont **des fonctions Python**. Pour garder l’exécution sûre et empêcher le reward hacking :

* **Vérification de la liste blanche des modules** — n’autoriser que les symboles de la bibliothèque standard Python :

  ```python
  from unsloth import check_python_modules
  ok, info = check_python_modules("""
  def strategy(board):
      import math
      from typing import Callable
      return "W"
  """)
  # ok == True signifie que seuls des imports au niveau Python ont été utilisés
  ```
* **Bloquer les imports interdits** (par ex. NumPy) :

  ```python
  sample = """
  def strategy(board):
      from numpy import matmul
      return "W"
  """
  ok, info = check_python_modules(sample)  # ok => False
  ```
* **Verrouiller l’exécution** dans une fonction isolée :

  ```python
  from unsloth import create_locked_down_function
  function = """
  def add(a, b):
      def adder(a):
          return a + b
      return adder(b) + b
  """
  f = create_locked_down_function(function)  # erreurs si des globals / imports sont utilisés
  ```
* **Imposer une limite stricte de temps d’exécution** sur les exécutions de stratégies :

  ```python
  from unsloth import execute_with_time_limit
  @execute_with_time_limit(2)
  def execute_strategy(strategy, game):
      # boucle jusqu’à la fin du jeu ou jusqu’au timeout
      ...
  ```

{% endstep %}

{% step %}
\### Prompt et jeu de données

Nous demandons au modèle de **produire une courte fonction de stratégie** entre triples backticks :

````
Créez une nouvelle courte stratégie 2048 en utilisant uniquement du code Python natif.
On vous donne une liste de listes de nombres pour l’état actuel du plateau.
Produisez une action parmi "W", "A", "S", "D" selon le prochain meilleur coup.
Produisez votre nouvelle courte fonction entre des backticks en utilisant le format ci-dessous :
```python
def strategy(board):
    return "W"  # Exemple
````

Toutes les fonctions auxiliaires doivent être à l’intérieur de def strategy. N’affichez que la courte fonction `strategy`.

````

Créez un minuscule jeu de données synthétique (en réutilisant le même prompt) et calculez la longueur du prompt afin que GRPO sache combien de jetons de complétion échantillonner :

```python
from datasets import Dataset

prompt = ...  # comme ci-dessus

maximum_length = len(tokenizer.apply_chat_template(
    [{"role": "user", "content": prompt}], add_generation_prompt=True
))

dataset = Dataset.from_list([
    {"prompt": [{"role": "user", "content": prompt}], "answer": 0, "reasoning_effort": "low"}
] * 1000)
````

{% hint style="info" %} Vous pouvez remplacer ce jeu de données par de vrais prompts pour votre propre tâche RL. {% endhint %} {% endstep %}

{% step %}

#### C’est l’heure de la fonction de récompense !

1. **Extraire le bloc de code** de la réponse du modèle :

   ````python
   def extract_function(text):
       if text.count("```") >= 2:
           first = text.find("```") + 3
           second = text.find("```", first)
           fx = text[first:second].strip()
           fx = fx.removeprefix("python\n")
           fx = fx[fx.find("def"):]
           if fx.startswith("def strategy(board):"):
               return fx
       return None
   ````
2. **`function_works`** - Est-ce qu’elle compile et crée un callable ?

   ```python
   from unsloth import create_locked_down_function, check_python_modules

   def function_works(completions, **kwargs):
       scores = []
       for completion in completions:
           response = completion[0]["content"]
           function = extract_function(response)
           if function is None:
               scores.append(-2.0)
               continue
           ok, info = check_python_modules(function)
           if "error" in info:
               scores.append(-2.0)
               continue
           try:
               _ = create_locked_down_function(function)
               scores.append(1.0)
           except Exception:
               scores.append(-0.5)
       return scores
   ```
3. **`no_cheating`** - Aucun import non standard n’est autorisé :

   ```python
   def no_cheating(completions, **kwargs):
       scores = []
       for completion in completions:
           response = completion[0]["content"]
           function = extract_function(response)
           if function is None:
               scores.append(-1.0)
               continue
           ok, _ = check_python_modules(function)
           scores.append(1.0 if ok else -20.0)  # forte pénalité en cas de triche
       return scores
   ```
4. **`strategy_succeeds`** - Jouez sur un plateau aléatoire ; récompensez la réussite :

   ```python
   import numpy as np

   PRINTER = 0  # afficher occasionnellement pour le débogage

   def strategy_succeeds(completions, **kwargs):
       global PRINTER
       scores = []
       seed = np.random.randint(10000)
       for completion in completions:
           response = completion[0]["content"]
           function = extract_function(response)
           if function is None:
               scores.append(-2.0)
               continue
           try:
               new_strategy = create_locked_down_function(function)
           except Exception:
               scores.append(0.0)
               continue
           try:
               game = GameBoard(size=6, seed=seed, target=2048, probability_fours=0.10)
               steps, state = execute_strategy(new_strategy, game)
               if PRINTER % 5 == 0:
                   print(function)
                   print(f"Steps={steps} State={state}")
                   print(game.board().pretty())
               PRINTER += 1
               if state == "success":
                   scores.append(20.0)
               else:
                   scores.append(2.0)   # a fonctionné mais n’a pas atteint 2048
           except TimeoutError:
               scores.append(-1.0)      # délai dépassé
           except Exception:
               scores.append(-3.0)      # a planté
       return scores
   ```

{% endstep %}

{% step %}

#### Configurer GRPO

Nous utiliserons le **GRPOTrainer**. Définissez les longueurs de prompt/de complétion, puis construisez un `GRPOConfig`. Gardez à l’esprit que vous pouvez aussi définir le type d’algorithme RL sur d’autres, comme [GSPO](/docs/fr/commencer/reinforcement-learning-rl-guide/advanced-rl-documentation/gspo-reinforcement-learning.md) ou Dr. GRPO.

```python
from trl import GRPOConfig, GRPOTrainer

max_prompt_length     = maximum_length + 1
max_completion_length = max_seq_length - max_prompt_length

training_args = GRPOConfig(
    temperature=1.0,
    learning_rate=5e-5,
    weight_decay=0.01,
    warmup_ratio=0.1,
    lr_scheduler_type="linear",
    optim="adamw_8bit",
    logging_steps=1,
    per_device_train_batch_size=1,
    gradient_accumulation_steps=1,    # augmentez à 4 pour des signaux de récompense plus lisses
    num_generations=2,                # diminuez si vous obtenez un OOM
    max_prompt_length=max_prompt_length,
    max_completion_length=max_completion_length,
    max_steps=1000,                   # ou définissez num_train_epochs=1
    save_steps=100,
    report_to="none",
    output_dir="outputs",
)

trainer = GRPOTrainer(
    model=model,
    processing_class=tokenizer,
    reward_funcs=[function_works, no_cheating, strategy_succeeds],
    args=training_args,
    train_dataset=dataset,
    # Split d’évaluation optionnel :
    # train_dataset=new_dataset["train"],
    # eval_dataset=new_dataset["test"],
)
```

{% hint style="info" %} **Lecture des journaux :** Regardez `reward` et `reward_std`. Il est normal de voir des récompenses faibles/nulles au début (environ les 100–200 premiers pas sur de petites GPU). {% endhint %} {% endstep %}

{% step %}

#### Entraînez votre modèle

```python
trainer.train()
```

Cela lance la boucle RL complète : échantillonner des complétions → les noter avec vos récompenses → optimiser la politique (LoRA). {% endstep %}

{% step %}

#### Inférence (après l’entraînement)

Générez une nouvelle stratégie avec l’adaptateur entraîné :

```python
from transformers import TextStreamer

text = tokenizer.apply_chat_template(
    [{"role": "user", "content": prompt}],
    tokenize=False,
    add_generation_prompt=True,
    reasoning_effort="low",
)

_ = model.generate(
    **tokenizer(text, return_tensors="pt").to("cuda"),
    temperature=1.0,
    max_new_tokens=1024,
    streamer=TextStreamer(tokenizer, skip_prompt=False)
```

{% endstep %}

{% step %}

#### Enregistrer / exporter votre modèle affiné

* **Fusionner et enregistrer en 4 bits (MXFP4)**

  ```
  ```

python model.save\_pretrained\_merged("finetuned\_model", tokenizer, save\_method="mxfp4") # ou push model.push\_to\_hub\_merged("\<org\_or\_user>/", tokenizer, token="\<hf\_token>", save\_method="mxfp4") \`\`\`

* **Fusionner et enregistrer en 16 bits**

  ```python
  model.save_pretrained_merged("finetuned_model", tokenizer, save_method="merged_16bit")
  # ou pousser
  model.push_to_hub_merged("<org_or_user>/<repo>", tokenizer, token="<hf_token>", save_method="merged_16bit")
  ```

{% endstep %}

{% step %}

#### Dépannage et conseils

* **OOM / lent**: réduisez `max_seq_length`, `num_generations`, `lora_rank`; gardez le 4 bits ; essayez un A100 si disponible.
* **Aucune amélioration de la récompense**: augmentez le nombre de pas d’entraînement, adoucissez les pénalités ou ajoutez un curriculum (commencez avec des plateaux plus petits / des objectifs plus faibles).
* **Reward hacking**: gardez `check_python_modules` strict ; validez le comportement de la stratégie sur plusieurs graines aléatoires.
* **Entraînement instable**: augmentez `gradient_accumulation_steps` pour lisser les mises à jour ; baissez `learning_rate` (par ex. 2e-5).
* **Blocages longs**: assurez-vous que `execute_with_time_limit` encadre toute exécution de stratégie.
  {% endstep %}

{% step %}

#### Adapter à votre propre tâche RL

* Remplacez l’environnement 2048 par votre propre environnement et **trois récompenses**: (a) syntaxe/compilation, (b) anti-triche/sécurité, (c) réussite de la tâche.
* Mettez à jour le **prompt** pour demander le type de fonction ou de sortie dont vous avez besoin.
* Conservez la même structure Unsloth + GRPO ; remplacez seulement l’environnement et les récompenses.
  {% endstep %}
  {% endstepper %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://unsloth.ai/docs/fr/modeles/gpt-oss-how-to-run-and-fine-tune/gpt-oss-reinforcement-learning/tutorial-how-to-train-gpt-oss-with-rl.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
