> For the complete documentation index, see [llms.txt](https://docs.clore.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.clore.ai/guides/guides_v2-fr/entrainement/finetune-llm.md).

# Ajustement fin de LLM

Affinez des LLM personnalisés avec des techniques efficaces sur les GPU Clore.ai

Entraînez votre propre LLM personnalisé en utilisant des techniques de fine-tuning efficaces sur les GPU CLORE.AI.

{% hint style="success" %}
Tous les exemples peuvent être exécutés sur des serveurs GPU loués via [la place de marché CLORE.AI](https://clore.ai/marketplace).
{% endhint %}

## Louer sur CLORE.AI

1. Visitez [la place de marché CLORE.AI](https://clore.ai/marketplace)
2. Filtrez par type de GPU, VRAM et prix
3. Choisissez **À la demande** (tarif fixe) ou **Spot** (prix d'enchère)
4. Configurez votre commande :
   * Sélectionnez l'image Docker
   * Définissez les ports (TCP pour SSH, HTTP pour les interfaces web)
   * Ajoutez des variables d'environnement si nécessaire
   * Entrez la commande de démarrage
5. Sélectionnez le paiement : **CLORE**, **BTC**, ou **USDT/USDC**
6. Créez la commande et attendez le déploiement

### Accédez à votre serveur

* Trouvez les détails de connexion dans **Mes commandes**
* Interfaces web : utilisez l'URL du port HTTP
* SSH : `ssh -p <port> root@<proxy-address>`

## Qu'est-ce que LoRA/QLoRA ?

* **LoRA** (Adaptation de bas rang) - Entraînez de petites couches d'adaptateur au lieu du modèle complet
* **QLoRA** - LoRA avec quantification pour encore moins de VRAM
* Entraînez un modèle 7B sur une seule RTX 3090
* Entraînez un modèle 70B sur une seule A100

## Exigences

| Modèle | Méthode      | VRAM minimale | Recommandé                                                                                               |
| ------ | ------------ | ------------- | -------------------------------------------------------------------------------------------------------- |
| 7B     | QLoRA        | 12 Go         | [RTX 3090](https://clore.ai/rent-3090.html?utm_source=docs\&utm_medium=guide\&utm_campaign=finetune-llm) |
| 13B    | QLoRA        | 20 Go         | [RTX 4090](https://clore.ai/rent-4090.html?utm_source=docs\&utm_medium=guide\&utm_campaign=finetune-llm) |
| 70B    | QLoRA        | 48GB          | A100 80 Go                                                                                               |
| 7B     | LoRA complet | 24GB          | RTX 4090                                                                                                 |

## Déploiement rapide

**Image Docker :**

```
pytorch/pytorch:2.11.0-cuda12.8-cudnn9-devel
```

**Ports :**

```
22/tcp
8888/http
6006/http
```

**Commande :**

```bash
pip install "transformers>=4.45" "datasets>=2.20" accelerate "peft>=0.14" \
    bitsandbytes "trl>=0.12" wandb jupyterlab && \
jupyter lab --ip=0.0.0.0 --port=8888 --allow-root
```

## Accéder à votre service

Après le déploiement, trouvez votre `http_pub` URL dans **Mes commandes**:

1. Accédez à **Mes commandes** la page
2. Cliquez sur votre commande
3. Trouvez le `http_pub` URL (par ex., `abc123.clorecloud.net`)

Utilisez `https://YOUR_HTTP_PUB_URL` au lieu de `localhost` dans les exemples ci-dessous.

## Préparation du jeu de données

### Format de discussion (recommandé)

```json
[
  {
    "messages": [
      {"role": "system", "content": "Vous êtes un assistant utile."},
      {"role": "user", "content": "Qu'est-ce que Python ?"},
      {"role": "assistant", "content": "Python est un langage de programmation..."}
    ]
  }
]
```

### Format d'instruction

```json
[
  {
    "instruction": "Traduisez en français",
    "input": "Bonjour, comment allez-vous ?",
    "output": "Bonjour, comment allez-vous ?"
  }
]
```

### Format Alpaca

```json
[
  {
    "instruction": "Donnez trois conseils pour rester en bonne santé.",
    "input": "",
    "output": "1. Mangez des repas équilibrés..."
  }
]
```

## Modèles modernes pris en charge (2025)

| Modèle                      | ID HF                                     | VRAM minimale (QLoRA) |
| --------------------------- | ----------------------------------------- | --------------------- |
| Llama 3.1 / 3.3 8B          | `meta-llama/Llama-3.1-8B-Instruct`        | 12 Go                 |
| Qwen 2.5 7B / 14B           | `Qwen/Qwen2.5-7B-Instruct`                | 12 Go / 20 Go         |
| DeepSeek-R1-Distill (7B/8B) | `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` | 12 Go                 |
| Mistral 7B v0.3             | `mistralai/Mistral-7B-Instruct-v0.3`      | 12 Go                 |
| Gemma 2 9B                  | `google/gemma-2-9b-it`                    | 14 Go                 |
| Phi-4 14B                   | `microsoft/phi-4`                         | 20 Go                 |

## Script de fine-tuning QLoRA

Exemple moderne avec PEFT 0.14+, Flash Attention 2, prise en charge de DoRA et compatibilité Qwen2.5 / DeepSeek-R1 :

```python
import torch
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    TrainingArguments,
)
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from datasets import load_dataset
from trl import SFTTrainer, SFTConfig

# === Configuration ===
# Choisissez l'une des options : Qwen2.5, DeepSeek-R1-Distill, Llama 3.1, Mistral, etc.
MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
# MODEL_NAME = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"
# MODEL_NAME = "meta-llama/Llama-3.1-8B-Instruct"

DATASET = "your_dataset.json"  # ou nom de dataset HuggingFace
OUTPUT_DIR = "./output"
MAX_SEQ_LENGTH = 4096           # Qwen2.5 prend en charge jusqu'à 32K de contexte
USE_DORA = True                 # DoRA améliore la qualité par rapport au LoRA standard
USE_FLASH_ATTN = True           # Flash Attention 2 économise de la VRAM et accélère

# === Chargement du modèle avec quantification 4 bits ===
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
)

model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True,  # Requis pour Qwen2.5 et DeepSeek
    # Flash Attention 2 : nécessite un GPU Ampere+ (RTX 30/40, A100)
    attn_implementation="flash_attention_2" if USE_FLASH_ATTN else "eager",
)

tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"

# === Configuration de LoRA avec DoRA optionnel ===
# DoRA (Adaptation de bas rang décomposée par poids) — PEFT >= 0.14 requis
# use_dora=True décompose les poids en magnitude + direction pour une meilleure qualité
lora_config = LoraConfig(
    r=64,                    # Rang (plus élevé = plus de capacité, plus de VRAM)
    lora_alpha=16,           # Facteur d'échelle (conserver égal à ou à la moitié de r)
    target_modules=[
        "q_proj", "k_proj", "v_proj", "o_proj",  # Couches d'attention
        "gate_proj", "up_proj", "down_proj",      # Couches MLP
    ],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
    use_dora=USE_DORA,        # DoRA : qualité améliorée (PEFT 0.14+)
    # use_rslora=True,        # LoRA à rang stabilisé en option
)

# Préparer le modèle pour l'entraînement QLoRA
model = prepare_model_for_kbit_training(
    model,
    use_gradient_checkpointing=True,
    gradient_checkpointing_kwargs={"use_reentrant": False},
)
model = get_peft_model(model, lora_config)

# Afficher le résumé des paramètres entraînables
model.print_trainable_parameters()
# Exemple de sortie : trainable params: 42,991,616 || all params: 7,284,891,648 || trainable%: 0.59

# === Charger le jeu de données ===
dataset = load_dataset("json", data_files=DATASET)
# Ou utilisez un jeu de données public :
# dataset = load_dataset("HuggingFaceH4/ultrachat_200k")

# === Formater le jeu de données au format Qwen2.5 / ChatML ===
def format_chat_qwen(example):
    """Format pour Qwen2.5 à l'aide du modèle ChatML."""
    messages = example.get("messages", [])
    if not messages:
        # Gérer les données au style Alpaca
        text = f"<|im_start|>system\nVous êtes un assistant utile.<|im_end|>\n"
        text += f"<|im_start|>user\n{example['instruction']}"
        if example.get("input"):
            text += f"\n{example['input']}"
        text += f"<|im_end|>\n<|im_start|>assistant\n{example['output']}<|im_end|>"
    else:
        # Gérer le format messages (ChatML)
        text = tokenizer.apply_chat_template(
            messages,
            tokenize=False,
            add_generation_prompt=False,
        )
    return {"text": text}

dataset = dataset.map(format_chat_qwen, remove_columns=dataset["train"].column_names)

# === Paramètres d'entraînement (PEFT 0.14+ / TRL 0.12+) ===
training_args = SFTConfig(
    output_dir=OUTPUT_DIR,
    num_train_epochs=3,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=8,         # Batch effectif = 2 * 8 = 16
    learning_rate=2e-4,
    weight_decay=0.001,
    warmup_ratio=0.03,
    lr_scheduler_type="cosine",
    logging_steps=10,
    save_steps=100,
    save_total_limit=3,
    bf16=True,                             # Utiliser bf16 pour les GPU modernes (A100, RTX 30/40)
    # fp16=True,                           # Utiliser fp16 pour les GPU plus anciens
    optim="paged_adamw_8bit",
    max_grad_norm=0.3,
    group_by_length=True,
    report_to="wandb",                     # ou "tensorboard"
    # Spécifique à SFTConfig :
    max_seq_length=MAX_SEQ_LENGTH,
    dataset_text_field="text",
    packing=True,                          # Regrouper plusieurs exemples pour plus d'efficacité
)

# === Entraîner ===
trainer = SFTTrainer(
    model=model,
    train_dataset=dataset["train"],
    tokenizer=tokenizer,
    args=training_args,
)

trainer.train()

# === Enregistrer l'adaptateur LoRA ===
trainer.save_model(f"{OUTPUT_DIR}/final")
tokenizer.save_pretrained(f"{OUTPUT_DIR}/final")
print(f"Modèle enregistré dans {OUTPUT_DIR}/final")
```

## Flash Attention 2

Flash Attention 2 réduit l'utilisation de la VRAM et accélère considérablement l'entraînement. Nécessite un GPU Ampere+ (RTX 3090, RTX 4090, A100).

```bash
# Installer Flash Attention 2
pip install flash-attn --no-build-isolation
```

```python
# L'activer lors du chargement du modèle :
model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    attn_implementation="flash_attention_2",  # <-- ajoutez ceci
    torch_dtype=torch.bfloat16,               # FA2 nécessite bf16 ou fp16
    device_map="auto",
)
```

| Paramètre                 | VRAM (7B) | Vitesse   |
| ------------------------- | --------- | --------- |
| Attention standard (fp16) | \~22 Go   | référence |
| Flash Attention 2 (bf16)  | \~16 Go   | +30%      |
| Flash Attention 2 + QLoRA | \~12 Go   | +30%      |

## DoRA (LoRA décomposée par poids)

DoRA (PEFT >= 0.14) décompose les poids pré-entraînés en composantes de magnitude et de direction. Cela améliore la qualité du fine-tuning, en particulier pour les rangs plus faibles.

```python
from peft import LoraConfig

# LoRA standard
lora_config = LoraConfig(r=64, lora_alpha=16, use_dora=False, ...)

# DoRA — mêmes paramètres, meilleure qualité
lora_config = LoraConfig(r=64, lora_alpha=16, use_dora=True, ...)
# Remarque : DoRA ajoute environ 5 à 10 % de surcharge VRAM par rapport au LoRA standard
# Remarque : pas compatible dans tous les cas avec les modèles quantifiés (4 bits/8 bits)
```

## Exemples Qwen2.5 et DeepSeek-R1-Distill

### Fine-tuning Qwen2.5

```python
MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
# Pour 14B : "Qwen/Qwen2.5-14B-Instruct" (nécessite plus de 20 Go de VRAM avec QLoRA)

model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True,          # Requis pour Qwen2.5
    attn_implementation="flash_attention_2",
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)

# Qwen2.5 utilise le format ChatML — utilisez apply_chat_template
messages = [
    {"role": "system", "content": "Vous êtes un assistant utile."},
    {"role": "user", "content": "Bonjour !"},
    {"role": "assistant", "content": "Salut ! Comment puis-je aider ?"},
]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)
```

### Fine-tuning DeepSeek-R1-Distill

Les modèles DeepSeek-R1-Distill (Qwen-7B, Qwen-14B, Llama-8B, Llama-70B) sont axés sur le raisonnement. Fine-tunez-les pour adapter leur style de chaîne de pensée à votre domaine.

```python
# Variantes DeepSeek-R1-Distill
MODEL_NAME = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"   # 7B sur base Qwen2.5
# MODEL_NAME = "deepseek-ai/DeepSeek-R1-Distill-Llama-8B" # 8B sur base Llama3
# MODEL_NAME = "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B" # 14B (nécessite un A100)

model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True,
    attn_implementation="flash_attention_2",
)

# DeepSeek-R1 utilise les balises <think>...</think> pour le raisonnement
# Conservez cela dans les données d'entraînement pour préserver la capacité de chaîne de pensée
example_format = """<|im_start|>user
Résoudre : Quel est 15 * 23?<|im_end|>
<|im_start|>assistant
<think>
15 * 23 = 15 * 20 + 15 * 3 = 300 + 45 = 345
</think>
La réponse est 345.<|im_end|>"""

# Modules cibles LoRA pour DeepSeek-R1-Distill (base Qwen2.5)
lora_config = LoraConfig(
    r=32,
    lora_alpha=16,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    use_dora=True,
    task_type="CAUSAL_LM",
)
```

## Utilisation d'Axolotl (plus simple)

Axolotl simplifie le fine-tuning avec des configurations YAML :

```bash
pip install axolotl

# Créer la configuration
cat > config.yml << 'EOF'
base_model: mistralai/Mistral-7B-v0.1
model_type: MistralForCausalLM
tokenizer_type: LlamaTokenizer

load_in_4bit: true
adapter: qlora
lora_r: 32
lora_alpha: 16

datasets:
  - path: your_data.json
    type: alpaca

sequence_len: 4096
sample_packing: true

gradient_accumulation_steps: 4
micro_batch_size: 2
num_epochs: 3
learning_rate: 2e-4

output_dir: ./output
EOF

# Entraîner
accelerate launch -m axolotl.cli.train config.yml
```

## Exemples de configuration Axolotl

### Modèle de discussion

```yaml
base_model: mistralai/Mistral-7B-Instruct-v0.2
load_in_4bit: true
adapter: qlora

datasets:
  - path: data.json
    type: sharegpt

chat_template: mistral
```

### Modèle de code

```yaml
base_model: codellama/CodeLlama-7b-hf
load_in_4bit: true
adapter: qlora

datasets:
  - path: code_data.json
    type: alpaca

sequence_len: 8192  # Contexte plus long pour le code
```

## Fusion des poids LoRA

Après l'entraînement, fusionnez LoRA dans le modèle de base :

```python
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer

# Charger le modèle de base
base_model = AutoModelForCausalLM.from_pretrained(
    "mistralai/Mistral-7B-v0.1",
    torch_dtype=torch.float16,
    device_map="auto",
)

# Charger LoRA
model = PeftModel.from_pretrained(base_model, "./output/final")

# Fusionner
merged_model = model.merge_and_unload()

# Enregistrer le modèle fusionné
merged_model.save_pretrained("./merged_model")
tokenizer.save_pretrained("./merged_model")
```

## Conversion en GGUF

À utiliser avec llama.cpp/Ollama :

```bash

# Cloner llama.cpp
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp

# Convertir
python convert.py ../merged_model --outtype f16 --outfile model-f16.gguf

# Quantifier
./quantize model-f16.gguf model-q4_k_m.gguf q4_k_m
```

## Surveillance de l'entraînement

### Weights & Biases

```python
import wandb
wandb.init(project="llm-finetune", name="mistral-7b-lora")
```

### TensorBoard

```python

# Dans les arguments d'entraînement
report_to="tensorboard"
logging_dir="./logs"

# Afficher
tensorboard --logdir ./logs --port 6006 --bind_all
```

## Bonnes pratiques

### Hyperparamètres

| Paramètre   | Modèle 7B | Modèle 13B | Modèle 70B |
| ----------- | --------- | ---------- | ---------- |
| batch\_size | 4         | 2          | 1          |
| grad\_accum | 4         | 8          | 16         |
| lr          | 2e-4      | 1e-4       | 5e-5       |
| lora\_r     | 64        | 32         | 16         |
| epochs      | 3         | 2-3        | 1-2        |

### Taille du jeu de données

* Minimum : 1 000 exemples
* Bien : plus de 10 000 exemples
* La qualité > la quantité

### Éviter le surapprentissage

```python
training_args = TrainingArguments(
    ...
    weight_decay=0.01,
    warmup_ratio=0.03,
    save_total_limit=3,
    load_best_model_at_end=True,
    evaluation_strategy="steps",
    eval_steps=100,
)
```

## Entraînement multi-GPU

```bash

# Avec accelerate
accelerate launch --multi_gpu --num_processes 4 train.py

# Avec DeepSpeed
accelerate launch --use_deepspeed --num_processes 4 train.py
```

Configuration DeepSpeed :

```json
{
  "bf16": {"enabled": true},
  "zero_optimization": {
    "stage": 2,
    "offload_optimizer": {"device": "cpu"}
  }
}
```

## Sauvegarde et exportation

```bash

# Enregistrer l'adaptateur LoRA
trainer.save_model("./lora_adapter")

# Enregistrer le modèle fusionné
merged_model.save_pretrained("./full_model")

# Téléverser vers HuggingFace
huggingface-cli login
merged_model.push_to_hub("username/my-model")
```

## Dépannage

### Erreurs OOM

* Réduire la taille du lot
* Augmenter l'accumulation de gradients
* Utilisez `gradient_checkpointing=True`
* Réduire lora\_r

### La perte d'entraînement ne diminue pas

* Vérifier le format des données
* Augmenter le taux d'apprentissage
* Vérifier les problèmes de données

### Perte NaN

* Réduire le taux d'apprentissage
* Utiliser fp32 au lieu de fp16
* Vérifier les données corrompues

## Estimation des coûts

Tarifs habituels du marketplace CLORE.AI (en 2026) :

| GPU        | Tarif horaire | Tarif journalier | Session de 4 heures |
| ---------- | ------------- | ---------------- | ------------------- |
| RTX 3060   | \~$0.03       | \~$0.70          | \~$0.12             |
| RTX 3090   | \~$0.06       | \~$1.50          | \~$0.25             |
| RTX 4090   | \~$0.10       | \~$2.30          | \~$0.40             |
| A100 40 Go | \~$0.17       | \~$4.00          | \~$0.70             |
| A100 80 Go | \~$0.25       | \~$6.00          | \~$1.00             |

*Les prix varient selon le fournisseur et la demande. Vérifiez* [*la place de marché CLORE.AI*](https://clore.ai/marketplace) *les tarifs actuels.*

> 📚 Voir aussi : [Comment fine-tuner LLaMA 3 sur un GPU cloud — Guide étape par étape](https://blog.clore.ai/how-to-fine-tune-llama-3-cloud-gpu/)

**Économisez de l'argent :**

* Utilisez le **Spot** marché pour les travaux interrompables — environ un tiers des serveurs ont un prix spot inférieur au tarif à la demande (médiane \~13 % de remise), les autres s'alignent dessus
* Payez avec **CLORE** jetons
* Comparez les prix entre différents fournisseurs


---

# 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://docs.clore.ai/guides/guides_v2-fr/entrainement/finetune-llm.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.
