Avancé 25 minFine-tuning

Fine-tuning LLM en local : exemple pas à pas LoRA / QLoRA

Le fine-tuning LLM en local avec LoRA et QLoRA permet d'adapter un modèle 7B à votre vocabulaire métier, votre style d'écriture ou un format de réponse précis — le tout sur une RTX 3090 d'occasion. Plus besoin de cluster A100. Ce guide vous emmène de l'absence d'expérience au modèle GGUF chargé dans Ollama, en passant par le format de dataset, le notebook Unsloth pas à pas, et l'export final.

Par Mohamed Meguedmi·Màj 2026-05-20·Testé sur Windows, macOS, Linux

#Pourquoi fine-tuner un LLM local ?

Un LLM de base est polyvalent mais imparfait pour votre usage spécifique. Le prompt engineering et le RAG résolvent 80% des cas — fine-tuner sert pour les 20% restants. Vous devriez envisager le fine-tuning quand : votre modèle doit répondre dans un format strict (JSON, balises, structure interne), respecter un style d'entreprise (ton, vocabulaire), maîtriser un domaine pointu (médical, juridique, technique métier), ou reproduire des comportements qu'aucun prompt ne stabilise vraiment.

À l'inverse, n'utilisez pas le fine-tuning pour injecter de la connaissance factuelle (le RAG le fait mieux et reste à jour), corriger une hallucination unique (un meilleur prompt suffit), ou rivaliser avec GPT-5 sur les benchmarks (vous ne gagnerez pas).

i
La règle empirique
Si vous pouvez exprimer ce que vous voulez en moins de 5 exemples dans un prompt, faites un system prompt. Si vous avez 50+ exemples et que le modèle dérive quand même, fine-tunez.

#LoRA vs full fine-tune : pourquoi LoRA gagne

Le full fine-tuning met à jour tous les paramètres du modèle. Pour un 7B en FP16, c'est 14 Go de poids + ~30 Go pour les gradients et l'optimiseur Adam. Total : 40 à 60 Go de VRAM minimum. Inaccessible sans A100 80GB ou H100.

LoRA (Low-Rank Adaptation) gèle les poids originaux et entraîne uniquement de petites matrices d'adaptation rang-r, insérées dans les couches d'attention. Concrètement, pour un 7B avec r=16, vous entraînez ~20 millions de paramètres au lieu de 7 milliards — soit 0,3% du modèle. La VRAM nécessaire pour les gradients et l'optimiseur s'effondre.

Full fine-tune 7B
~60 Go VRAM, 2-4× A100, plusieurs heures, fichier de sortie de 14 Go.
LoRA 7B
~16 Go VRAM, une RTX 4080 suffit, 30 min à 2h, adaptateur de 30-200 Mo seulement.
QLoRA 7B
~6 Go VRAM, RTX 3060 12 Go suffisante, qualité quasi-identique à LoRA classique.
Quasi-équivalent en pratique
Sur des datasets de taille modeste (< 10 000 exemples), LoRA atteint 95-99% de la qualité d'un full fine-tune. La différence devient visible seulement sur des tâches très éloignées du pré-entraînement. Pour votre premier fine-tune, ne vous posez même pas la question : LoRA.

#QLoRA : la révolution VRAM

QLoRA pousse l'idée plus loin : le modèle de base est chargé en 4-bit (NF4, NormalFloat 4-bit) au lieu de FP16. Pendant l'entraînement, les poids restent quantifiés ; seuls les adaptateurs LoRA en FP16 reçoivent les gradients. Résultat : un 7B tient dans 5-6 Go de VRAM, un 13B dans 10 Go, un 70B dans 48 Go.

La perte de qualité est négligeable grâce à deux techniques : la quantization NF4 (calibrée sur la distribution gaussienne des poids) et la double quantization (les constantes de quantization sont elles-mêmes quantifiées). En pratique, sur la majorité des benchmarks, QLoRA est à moins de 1% de LoRA FP16.

i
Le bon défaut
Pour un premier fine-tuning local, QLoRA est presque toujours le bon choix. Vous économisez 60-70% de VRAM pour une perte de qualité imperceptible. Passez à LoRA classique seulement si vous visez un usage production critique avec une RTX 4090 ou plus.

#VRAM nécessaire par taille de modèle

Les chiffres ci-dessous sont des planchers réalistes avec Unsloth, batch size 2, séquence 2048 tokens, gradient checkpointing activé. Ajoutez 20% de marge pour les pics et l'OS.

Modèle 3B (Llama 3.2, Qwen 2.5)
QLoRA : 4 Go VRAM · LoRA FP16 : 8 Go · GTX 1660 6 Go ou RTX 3050 suffisent en QLoRA.
Modèle 7B (Mistral, Llama 3.1 8B, Qwen 2.5 7B)
QLoRA : 6 Go VRAM · LoRA FP16 : 16 Go · RTX 3060 12 Go confortable, RTX 3090 idéale.
Modèle 13B-14B (Phi-4, Qwen 2.5 14B)
QLoRA : 10 Go VRAM · LoRA FP16 : 28 Go · RTX 3090/4090 24 Go en QLoRA, A100 40GB en LoRA.
Modèle 32B (Qwen 2.5 32B)
QLoRA : 22 Go VRAM · LoRA FP16 : impossible sur grand public · RTX 3090/4090 ou RTX 5090 en QLoRA strict.
Modèle 70B (Llama 3.3 70B)
QLoRA : 48 Go VRAM · 2× RTX 3090 ou 1× A100 80GB · multi-GPU obligatoire avec Unsloth Pro.

Pour ce guide, la cible est le 7B en QLoRA sur RTX 3090 24 Go. C'est la combinaison la plus universelle : RTX 3060 12 Go suffit aussi, mais une 3090 d'occasion (~600 €) ouvre toutes les tailles jusqu'au 14B en QLoRA confortable. Une RTX 4090 ou 5090 accélèrent par 1,5 à 2× mais ne sont pas indispensables.

#Prérequis matériels et logiciels

GPU NVIDIA avec CUDA 11.8+
RTX 3060 12 Go minimum, RTX 3090 recommandée. Les GPU AMD ROCm fonctionnent partiellement mais Unsloth est optimisé CUDA — restez NVIDIA pour ce premier guide.
Python 3.10 ou 3.11
Pas 3.12 (incompatibilités bitsandbytes au moment de la rédaction). Utilisez conda ou pyenv.
32 Go de RAM système
16 Go peuvent suffire mais le chargement initial du modèle et la préparation du dataset sont plus confortables avec 32 Go.
50 Go d'espace disque
Modèle de base + checkpoints + adaptateur fusionné + export GGUF Q4_K_M. Prévoyez large.
Connexion correcte
Le téléchargement initial du modèle 7B fait 4-8 Go. Une seule fois.

Côté logiciel, on utilise Unsloth (github.com/unslothai/unsloth), un framework qui réécrit les kernels Triton pour gagner 2× en vitesse et 60% en mémoire vs Hugging Face PEFT standard. Installation dans un environnement dédié :

Installation Unsloth
conda create -n unsloth python=3.11 -y
conda activate unsloth
pip install --upgrade pip
pip install "unsloth[cu121-torch240] @ git+https://github.com/unslothai/unsloth.git"
pip install --no-deps trl peft accelerate bitsandbytes
!
Versions CUDA
Adaptez cu121-torch240 à votre installation : cu118 pour CUDA 11.8, cu124 pour CUDA 12.4. Vérifiez avec nvidia-smi en haut à droite. Une mauvaise version provoquera des erreurs cryptiques au premier import.

#Préparer son dataset au format JSONL

Le format standard pour le fine-tuning d'instruction est JSONL (un objet JSON par ligne). Trois variantes sont courantes :

Format Alpaca (le plus simple)
{"instruction": "Traduis en français formel.", "input": "Hey, what's up?", "output": "Bonjour, comment allez-vous ?"}
{"instruction": "Résume en une phrase.", "input": "Le chat noir a sauté...", "output": "Un chat noir saute sur la table."}
Format ShareGPT (multi-tours)
{"conversations": [
  {"from": "system", "value": "Tu es un assistant juridique."},
  {"from": "human", "value": "Qu'est-ce qu'une clause léonine ?"},
  {"from": "gpt", "value": "Une clause léonine est une disposition contractuelle..."}
]}

Pour un premier fine-tune, choisissez Alpaca : un seul tour, format limpide, supporté nativement par Unsloth. Quelques règles cruciales pour la qualité :

Volume minimum
300 exemples pour observer un effet, 1000-5000 pour un fine-tune solide, 10 000+ pour du sérieux. Moins de 300, vous perdez votre temps.
Qualité > quantité
100 exemples impeccables battent 5000 exemples bruités. Relisez. Faites relire. Le modèle apprend littéralement votre dataset, défauts inclus.
Diversité des inputs
Si tous vos inputs commencent par "Traduis", le modèle ne saura plus rien faire d'autre. Variez les formulations.
Longueurs équilibrées
Si vos outputs font tous 2 phrases, le modèle ne saura plus produire de réponse longue. Mélangez.
Réservez 10% pour validation
Séparez aléatoirement train.jsonl et val.jsonl pour mesurer le sur-apprentissage.
Générer un dataset synthétique
Vous n'avez pas 1000 exemples sous la main ? Utilisez un gros modèle (Claude, GPT-5, ou DeepSeek V4 en local) pour générer des paires instruction/output synthétiques à partir de vos documents internes. C'est la technique standard, dite "self-instruct". Comptez 1 à 2 heures de génération pour 1000 exemples.

#Le notebook Unsloth commenté

Voici un script complet et minimal pour fine-tuner Mistral 7B en QLoRA sur votre dataset. À exécuter en .py ou dans un notebook Jupyter.

finetune.py — étape 1 : chargement
from unsloth import FastLanguageModel
import torch

MODEL = "unsloth/mistral-7b-instruct-v0.3-bnb-4bit"
MAX_SEQ = 2048

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = MODEL,
    max_seq_length = MAX_SEQ,
    dtype = None,           # auto : bf16 sur Ampere+, fp16 sinon
    load_in_4bit = True,    # QLoRA : modèle quantifié 4-bit
)

Unsloth héberge des versions pré-quantifiées en 4-bit de la plupart des modèles populaires sur Hugging Face (préfixe unsloth/). Téléchargement plus rapide, démarrage immédiat. La première exécution télécharge ~4 Go.

finetune.py — étape 2 : config LoRA
model = FastLanguageModel.get_peft_model(
    model,
    r = 16,                  # rang de la décomposition LoRA
    target_modules = [
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj",
    ],
    lora_alpha = 16,
    lora_dropout = 0,
    bias = "none",
    use_gradient_checkpointing = "unsloth",  # économie VRAM
    random_state = 42,
)

Le rang r=16 est un bon défaut. Augmentez à 32 ou 64 si vous avez beaucoup de données (>10k) et un domaine très éloigné du pré-entraînement. Un rang plus haut = plus de paramètres entraînables = plus de capacité mais plus de risque de sur-apprentissage.

finetune.py — étape 3 : dataset
from datasets import load_dataset

ALPACA_PROMPT = """### Instruction:
{}

### Input:
{}

### Réponse:
{}"""

EOS = tokenizer.eos_token

def format_prompt(ex):
    texts = [
        ALPACA_PROMPT.format(i, inp or "", out) + EOS
        for i, inp, out in zip(ex["instruction"], ex["input"], ex["output"])
    ]
    return {"text": texts}

ds = load_dataset("json", data_files="train.jsonl", split="train")
ds = ds.map(format_prompt, batched=True)
!
N'oubliez pas le token EOS
Sans EOS à la fin de chaque exemple, le modèle apprend à ne jamais s'arrêter. Symptôme classique : votre modèle fine-tuné génère sans fin, répète, ou enchaîne avec une nouvelle question inventée. Vérifiez toujours que tokenizer.eos_token est bien ajouté.
finetune.py — étape 4 : entraînement
from trl import SFTTrainer
from transformers import TrainingArguments

trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = ds,
    dataset_text_field = "text",
    max_seq_length = MAX_SEQ,
    args = TrainingArguments(
        per_device_train_batch_size = 2,
        gradient_accumulation_steps = 4,    # batch effectif = 8
        warmup_steps = 10,
        num_train_epochs = 3,
        learning_rate = 2e-4,
        fp16 = not torch.cuda.is_bf16_supported(),
        bf16 = torch.cuda.is_bf16_supported(),
        logging_steps = 10,
        optim = "adamw_8bit",
        weight_decay = 0.01,
        lr_scheduler_type = "linear",
        seed = 42,
        output_dir = "outputs",
    ),
)

trainer.train()

Sur RTX 3090 avec un dataset de 1000 exemples et 3 époques, comptez 20 à 40 minutes. Sur RTX 4090, 12 à 25 minutes. Sur RTX 5090, environ 8 à 15 minutes. Surveillez la loss : elle doit descendre régulièrement puis se stabiliser. Si elle remonte sur la validation, vous sur-apprenez.

Quand s'arrêter
3 époques est un bon défaut pour un dataset de 1k-5k exemples. Au-delà de 5 époques, le risque de sur-apprentissage devient sérieux : le modèle récite par cœur au lieu de généraliser. Si vous avez plus de 20k exemples, descendez à 1-2 époques.

#Export GGUF vers Ollama

Votre adaptateur LoRA est dans outputs/. C'est un fichier de quelques dizaines de Mo, séparé du modèle de base. Pour l'utiliser dans Ollama, deux étapes : fusionner l'adaptateur au modèle, puis convertir en GGUF quantifié.

finetune.py — étape 5 : export GGUF Q4_K_M
model.save_pretrained_gguf(
    "mon-modele-gguf",
    tokenizer,
    quantization_method = "q4_k_m",  # le défaut recommandé
)

Unsloth s'occupe de tout : fusion adaptateur + base, conversion via llama.cpp, quantization Q4_K_M. Le résultat : un fichier mon-modele-gguf/unsloth.Q4_K_M.gguf d'environ 4,1 Go pour un 7B. Q5_K_M et Q8_0 sont disponibles si vous voulez plus de précision (et plus de poids).

Pour le charger dans Ollama qui écoute sur localhost:11434, créez un Modelfile minimal puis enregistrez le modèle :

Modelfile
cat > Modelfile <<EOF
FROM ./mon-modele-gguf/unsloth.Q4_K_M.gguf

TEMPLATE """### Instruction:
{{ .Prompt }}

### Réponse:
"""

PARAMETER temperature 0.7
PARAMETER stop "### Instruction:"
EOF

ollama create mon-modele -f Modelfile
ollama run mon-modele
i
Le template doit matcher
Le template Ollama doit reproduire exactement le format utilisé à l'entraînement (ici Alpaca avec ### Instruction: et ### Réponse:). Sinon le modèle reçoit des prompts qu'il n'a jamais vus et hallucine. Si vous avez utilisé ShareGPT ou ChatML, adaptez en conséquence.

#Astuces et dépannage

OutOfMemoryError au démarrage
Réduisez per_device_train_batch_size à 1 et augmentez gradient_accumulation_steps en proportion. Réduisez max_seq_length à 1024 si vos exemples sont courts.
Loss qui ne descend pas
Learning rate trop bas (essayez 5e-4) ou format de prompt cassé. Imprimez 2-3 exemples de ds[0]["text"] et vérifiez visuellement qu'ils ressemblent à ce que vous voulez.
Loss qui explose (NaN)
Learning rate trop haut. Repassez à 1e-4. Ou activez bf16 si vous étiez en fp16 sur Ampere+ — fp16 a tendance à overflow.
Modèle qui répète à l'infini
Token EOS manquant à l'entraînement, ou paramètre stop absent dans Modelfile. Les deux problèmes se cumulent souvent.
Sur-apprentissage visible
Loss train descend, loss validation remonte. Arrêtez à l'époque où elles divergent. Réduisez epochs ou augmentez lora_dropout à 0.05.
Export GGUF qui crash
Unsloth télécharge llama.cpp à la volée la première fois — assurez-vous d'avoir git, cmake et build-essential installés sur Linux. Sur Mac, xcode-select --install.
Modèle qui ignore les nouvelles instructions
Dataset pas assez varié ou rang LoRA trop bas. Passez r=32 et lora_alpha=64, refaites tourner.
Garder l'adaptateur séparé
Pour itérer rapidement, gardez aussi une version non fusionnée de l'adaptateur (~100 Mo) avec model.save_pretrained("adapter"). Vous pouvez la réutiliser plus tard avec un autre modèle de base ou la combiner à d'autres adaptateurs sans tout retélécharger.

#Pour aller plus loin

Vous avez un premier modèle fine-tuné qui tourne dans Ollama. Trois directions naturelles pour creuser :

Choisir la bonne quantification
Vous avez exporté en Q4_K_M par défaut. Le guide choisir sa quantification compare Q4_K_M, Q5_K_M, Q8_0 et FP16 — utile pour décider si la qualité de votre fine-tune mérite Q5 ou Q8.
RAG plutôt que fine-tune pour la connaissance
Si vous voulez que le modèle connaisse vos documents (et pas juste un style), le guide RAG local introduction explique pourquoi le RAG est presque toujours préférable au fine-tune pour injecter des faits.
Hardware pour passer aux gros modèles
Pour fine-tuner du 32B ou 70B en QLoRA, il faut 24 Go puis 48 Go de VRAM. Le guide quel LLM pour 24 Go de VRAM cartographie ce qui est faisable sur RTX 3090, 4090 et RX 7900 XTX.
Ce guide vous a aidé ?

Un retour, une erreur, une précision ? Faites-nous signe, ça améliore le guide pour tout le monde.