Le fine-tuning supervisé (SFT)
Spécialiser un modèle pré-entraîné sur vos données : jeux d'instructions, LoRA/QLoRA et la boucle d'entraînement.
Un modèle de base (base model) sait une seule chose : prédire le prochain token (next-token prediction). C'est utile, mais inexploitable tel quel : il ne sait ni suivre une consigne, ni adopter un format de réponse, ni s'arrêter au bon moment. Le fine-tuning supervisé (Supervised Fine-Tuning, SFT) comble ce fossé. En ré-entraînant le modèle sur un jeu soigneusement constitué de paires instruction → réponse, on lui apprend deux choses à la fois : à respecter un format de dialogue (chat format) — ce qui le transforme en assistant conversationnel — et à orienter sa connaissance générale vers une tâche ou un domaine précis.
Iusztin et Labonne insistent sur un point essentiel : le SFT ne crée pas de connaissance à partir de rien. Il réoriente les paramètres déjà présents dans les poids pré-entraînés. Cette nuance gouverne tout le reste du chapitre — quand fine-tuner, comment construire le jeu de données, et quelle technique (full fine-tuning, LoRA, QLoRA) choisir. Ce chapitre suit le fil rouge du livre, le LLM Twin (un agent qui imite votre style d'écriture), mais les techniques restent valables pour n'importe quel cas d'usage.
Quand fine-tuner (et quand s'en abstenir)
La première recommandation des auteurs est contre-intuitive : commencez par l'ingénierie de prompt (prompt engineering), pas par le fine-tuning. Avec des techniques comme le few-shot prompting ou la génération augmentée par récupération (retrieval-augmented generation, RAG), on résout déjà énormément de problèmes — sans entraîner quoi que ce soit, et en gardant la liberté d'utiliser un modèle propriétaire. Le prompt permet aussi de bâtir un pipeline d'évaluation mesurant exactitude, coût et latence. Ce n'est que si ces résultats ne suffisent pas, et si l'on dispose d'assez de données, que le fine-tuning devient une option.
Le SFT excelle pour quatre besoins distincts : spécialiser le style (la voix d'un auteur), le format (sorties structurées systématiques), le domaine (vocabulaire médical, juridique, financier) et pour internaliser un comportement au lieu de le réinjecter à chaque appel via le prompt. Il répond aussi à des besoins de contrôle (« connaître ses données ») et de personnalisation : le modèle obtenu est unique.
Attention
Le SFT a des limites réelles. Une connaissance trop éloignée de ce que le modèle a vu en pré-entraînement (une langue rare, par exemple) s'apprend mal. Pire : une étude citée par les auteurs montre que fine-tuner sur des connaissances nouvelles peut augmenter les hallucinations. Et selon la technique, on risque d'effacer des compétences existantes — c'est l'oubli catastrophique (catastrophic forgetting). Le fine-tuning réoriente des poids ; il n'ajoute pas magiquement du savoir.
Prompt engineering (few-shot, RAG) --> Évaluation
| |
| suffisant ? -- oui --> on s'arrête
|
| non, et assez de données ?
v
Créer un jeu d'instructions --> SFT --> Réévaluer Construire un jeu d'instructions de qualité
Dans la plupart des cas, constituer le jeu de données est la partie la plus difficile du fine-tuning. Le texte brut abonde, mais les paires instruction/réponse naturelles sont rares : il faut transformer le brut en ce format, puis vérifier la qualité — souvent à la main, échantillon par échantillon.
Les trois dimensions de la qualité
Une fois assez d'exemples collectés, on filtre pour ne garder que le meilleur. Les auteurs définissent la qualité selon trois axes :
- Exactitude (accuracy) : les réponses doivent être factuellement correctes et pertinentes par rapport à leur instruction.
- Diversité (diversity) : couvrir un large éventail de sujets, contextes, longueurs et styles, pour développer des capacités robustes de suivi d'instructions.
- Complexité (complexity) : les exemples triviaux n'apprennent rien. Il faut des problèmes à plusieurs étapes, du raisonnement, des tâches qui repoussent les limites du modèle.
Combien d'exemples ?
Le nombre idéal dépend de la qualité des données et de la taille du modèle. Le papier LIMA montre qu'un gros modèle (~70 milliards de paramètres) peut être aligné avec à peine 1 000 exemples de haute qualité. Un petit modèle (~7 milliards) en demande davantage, ne serait-ce que pour assimiler le chat template. Le tableau suivant résume les ordres de grandeur observés.
| Type de modèle | Objectif | Ordre de grandeur |
|---|---|---|
| Généraliste (general-purpose) | Reproduire un assistant type GPT | ~1 million (OpenHermes, Dolphin) ; jusqu'à 10 M pour Llama 3 (tout le post-training, alignement de préférences inclus) |
| Spécifique à une tâche (task-specific) | Traduction, résumé, sentiment | 100 à 100 000 |
| Spécifique à un domaine (domain-specific) | Médecine, droit, e-commerce | Très variable, du task-specific au general-purpose |
Pour un domaine, deux facteurs comptent : sa « taille » (l'étendue de son vocabulaire spécialisé) et sa représentation dans les données de pré-entraînement. Un domaine bien représenté demande moins de fine-tuning ; un domaine de niche en exige davantage.
Filtrer, dédupliquer, décontaminer
Une fois les datasets rassemblés, le livre détaille un pipeline de raffinement :
- Filtrage par règles (rule-based filtering) : seuils de longueur, exclusion de mots-clés, vérification de format. Rapide, scalable, transparent — mais binaire (passe/échoue), au risque d'éliminer des exemples valides mais atypiques.
- Déduplication (deduplication) : exacte (hachage MD5/SHA-256 après normalisation) pour les doublons stricts, floue (MinHash, comparaison par similarité de Jaccard) ou sémantique (embeddings + similarité cosinus) pour les quasi-doublons. Les doublons mènent au surapprentissage et gonflent artificiellement les métriques.
- Décontamination (decontamination) : retirer du jeu d'entraînement tout ce qui ressemble au jeu de test. Une astuce élégante : ajouter le jeu d'évaluation pendant l'étape de déduplication, en ne supprimant les doublons que côté entraînement.
Évaluer la qualité à grande échelle
L'annotation humaine est fiable mais coûteuse. Pour passer à l'échelle, les auteurs présentent trois approches automatiques. Le LLM-juge (LLM-as-a-judge) demande à un LLM de noter chaque échantillon ; flexible, mais entaché de biais (préférence pour la première réponse, pour les réponses longues, et favoritisme intra-famille). Les modèles de récompense (reward models) ajoutent une tête linéaire sur un décodeur et renvoient un score. Enfin, les classifieurs encodeurs (encoder-only), petits et rapides, filtrent des millions d'échantillons au prix d'une moindre finesse.
Astuce
Pour les notations comparatives (« A est-elle meilleure que B ? »), randomisez l'ordre des réponses afin de contrer le biais de position. Et plutôt qu'un seul juge, utilisez un jury de plusieurs LLM : cela réduit les biais, améliore la cohérence, et un jury de petits modèles peut même coûter moins cher tout en gagnant en précision.
Générer et augmenter des données
Quand les jeux existants ne suffisent pas, on génère des données synthétiques avec un LLM. On part d'un ensemble de prompts d'amorçage (seed prompts, parfois appelés taxonomie), comme dans le dataset Alpaca, puis on contrôle la complexité, la longueur, le ton et le format des sorties (la génération structurée via une librairie comme Outlines aide à respecter un schéma).
L'augmentation de données (data augmentation) part d'exemples existants pour en accroître diversité et complexité. La méthode Evol-Instruct fait évoluer des instructions simples via deux stratégies : in-depth (ajout de contraintes, approfondissement, concrétisation, étapes de raisonnement, formats complexes) et in-breadth (création d'instructions inédites, plus rares). La méthode UltraFeedback, elle, vise la qualité des réponses via le retour critique d'un LLM puissant.
En pratique : du texte brut au dataset LLM Twin
Pour le LLM Twin, les auteurs partent d'articles bruts (issus du scraping du chapitre 3) — non structurés et peu nombreux. La solution combine rétro-traduction (backtranslation : produire l'instruction à partir de la réponse attendue) et reformulation (rephrasing : nettoyer le texte tout en imitant le style de l'auteur). Chaque article est découpé en chunks de 1 000 à 2 000 caractères, et l'on génère cinq paires par chunk pour multiplier les échantillons.
from openai import OpenAI
def generate_instruction_answer_pairs(extract: str, client: OpenAI):
prompt = f"""Based on the following extract, generate five
instruction-answer pairs. Each instruction must ask to write
about a specific topic contained in the context. Each answer
must provide a relevant paragraph based on the context.
Instructions must be self-contained and general.
Answers must imitate the writing style of the context.
Provide your response in JSON format:
{{"instruction_answer_pairs": [
{{"instruction": "...", "answer": "..."}}, ...]}}
Extract:
{extract}"""
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "You generate "
"instruction-answer pairs. Respond in JSON."},
{"role": "user", "content": prompt},
],
response_format={"type": "json_object"}, # JSON mode
max_tokens=1200,
temperature=0.7, # encourage la diversité
)
# ...parsing JSON -> liste de paires Deux détails comptent. D'abord, la génération structurée : les LLM produisent mal du JSON fiable, d'où l'usage du JSON mode d'OpenAI pour garantir un objet valide. Ensuite, le coût : faire tourner ce pipeline sur tout le corpus avec GPT-4o mini revient à moins de 0,5 dollar, pour 3 335 paires au format Alpaca.
Formats et chat templates
Une fois les paires obtenues, deux notions à ne pas confondre.
Le format de stockage organise les données sur disque. Les trois standards sont Alpaca (instruction/input/output, mono-tour), ShareGPT et OpenAI (listes de messages, multi-tours). Le choix mono-tour vs multi-tours dépend du cas d'usage et impacte directement le stockage.
Le chat template structure ces paires pour le modèle, avec des tokens spéciaux marquant le début, la fin et l'auteur de chaque message. Un modèle de base n'a pas de template : on peut donc en choisir n'importe lequel. Un modèle déjà instruct impose en revanche de réutiliser le sien (fine-tuner un modèle instruct n'est d'ailleurs pas recommandé).
| Template | Tokens spéciaux clés | Remarque |
|---|---|---|
| Alpaca | ### Instruction: / ### Response: + <EOS> | Aucun token spécial dédié, peu sujet aux erreurs |
| ChatML | <\|im_start\|> / <\|im_end\|> | Popularisé par OpenAI, très répandu |
| Llama 3 | <\|start_header_id\|> / <\|eot_id\|> | Format natif des modèles Meta |
| Gemma | <start_of_turn> / <end_of_turn> | Rôle « model » au lieu de « assistant » |
Piège courant
Avec un chat template, chaque espace et chaque saut de ligne compte. Ajouter ou retirer un seul caractère change la tokenisation et dégrade les performances. Utilisez des templates fiables au format Jinja (intégrés à la librairie Transformers) : ils gèrent boucles et conditions, ce qui permet d'employer le même template à l'entraînement et à l'inférence via add_generation_prompt.
En inférence, on ne fournit que les parties système et utilisateur, puis on amorce la réponse en ajoutant l'en-tête de l'assistant (par exemple <|im_start|>assistant\n). Comme le modèle a été entraîné avec ce template, il comprend que les tokens suivants doivent être une réponse — c'est exactement ainsi qu'un modèle acquiert sa capacité à suivre les instructions.
Trois techniques de SFT : full, LoRA, QLoRA
Le SFT s'est concentré sur trois techniques, qui se distinguent par le nombre de paramètres entraînés et donc par la mémoire GPU consommée.
Full fine-tuning
Le full fine-tuning ré-entraîne tous les paramètres. C'est la méthode la plus directe, qui donne souvent les meilleurs résultats — mais elle est gourmande. La mémoire se décompose ainsi (en précision fp32) :
Paramètres : 4 octets/param (FP32) ou 2 (FP16/BF16)
Gradients : 4 octets/param
États optimiseur : 8 octets/param (Adam : moment + variance)
Activations : variable (souvent négligeable, petits batchs)
------------------------------------------------------------
Base ~ 16 octets/param
-> 7 Md params ~ 112 Go de VRAM
-> 70 Md params ~ 1 120 Go de VRAM Des techniques atténuent la facture (parallélisme de modèle, accumulation de gradient, optimiseurs 8-bit, checkpointing d'activations), abaissant le coût autour de 14-15 octets/param — mais les besoins restent considérables. Surtout, le full fine-tuning modifie directement les poids pré-entraînés : il est destructif par nature, et expose à l'oubli catastrophique.
LoRA
LoRA (Low-Rank Adaptation) résout le problème autrement. Plutôt que de modifier la matrice de poids W, on la gèle et on apprend deux petites matrices A et B de rang faible (low-rank). Le poids effectif devient W' = W + BA. Le produit BA a la forme de W, mais un rang bien inférieur, ce qui réduit drastiquement le nombre de paramètres entraînables.
L'effet est spectaculaire : cibler chaque module d'un Llama 3 8B avec un rang de 16 ne produit que 42 millions de paramètres entraînables sur 8 milliards — soit 0,52 %. On fine-tune ainsi un modèle 7B sur un seul GPU avec 14 à 18 Go de VRAM. Et comme les poids d'origine sont préservés, LoRA est non destructif et permet de changer de tâche en échangeant simplement les poids LoRA (servis dynamiquement par LoRAX, TGI ou NIM).
Deux hyperparamètres pilotent LoRA :
- Rang
r: la taille des matrices. Point de départ courant :r = 8, mais jusqu'à 256 selon les cas. Un rang élevé capte plus de diversité mais peut surapprendre. - Alpha
α: un facteur d'échelle appliqué à la mise à jour (proportionnel àα/r). Heuristique répandue : fixerαau double der.
On peut ajouter un dropout léger (0 à 0,1) en régularisation. Les modules cibles (target modules) étaient au départ les matrices Q et V de l'attention ; les expériences montrent un gain à étendre aux matrices K, aux projections de sortie O, aux blocs feed-forward (MLP) et aux couches linéaires de sortie — au prix de plus de paramètres entraînables.
QLoRA
QLoRA (Quantized LoRA, Dettmers et al.) pousse l'économie plus loin : on quantifie le modèle de base en 4 bits (type NormalFloat NF4) avant d'y appliquer LoRA. Seuls les adaptateurs sont entraînés ; les poids quantifiés restent figés. Deux raffinements : la double quantification (quantifier les constantes de quantification elles-mêmes) et les optimiseurs paginés (paged optimizers), qui amortissent les pics mémoire via la mémoire unifiée Nvidia.
Le gain est net : pour un modèle 7B, le pic mémoire passe de 15,6 Go (LoRA) à 9,3 Go en fine-tuning, soit ~40 %. La contrepartie : QLoRA est ~30 % plus lent que LoRA, pour des performances de modèle quasi identiques.
| Critère | Full fine-tuning | LoRA | QLoRA |
|---|---|---|---|
| Paramètres entraînés | 100 % | ~0,5 % | ~0,5 % |
| VRAM (modèle 7B) | ~112 Go+ | 14-18 Go | ~9 Go |
| Qualité | Référence (souvent la meilleure) | Comparable, parfois supérieure | Très proche de LoRA |
| Vitesse | Lente | Rapide | ~30 % plus lente que LoRA |
| Destructif ? | Oui (oubli catastrophique) | Non (poids gelés) | Non (poids gelés) |
| Quand l'utiliser | Ressources GPU abondantes, qualité maximale | Bon compromis qualité/mémoire | Mémoire = contrainte principale |
À retenir
Le choix QLoRA vs LoRA est un arbitrage. Si la mémoire est le facteur limitant (très gros modèle, GPU modeste), QLoRA. Si la vitesse prime et que la VRAM suffit, LoRA. Le full fine-tuning ne se justifie que lorsqu'on dispose de plusieurs GPU haut de gamme et qu'on vise la qualité maximale en acceptant le caractère destructif.
Les hyperparamètres d'entraînement
Plusieurs hyperparamètres gouvernent la convergence et la généralisation.
- Taux d'apprentissage (learning rate) : le plus important. Trop bas, l'entraînement stagne ; trop haut, il diverge. De
1e-6à1e-3, point de départ courant ~1e-5. Un scheduler (linéaire ou cosinus) le fait décroître au fil de l'entraînement, souvent après une phase de warmup (~5 % des étapes) qui stabilise le début. - Taille de batch (batch size) : typiquement 1 à 32. Plus grand = gradients plus stables, mais plus de mémoire. L'accumulation de gradient (gradient accumulation) simule un grand batch effectif sans le coût mémoire :
batch effectif = batch × GPU × étapes d'accumulation. - Longueur de séquence max (maximum sequence length) : 512 à 4 096 tokens, parfois plus. Au-delà, les séquences sont tronquées. Le packing combine plusieurs exemples courts dans un même slot pour maximiser l'utilisation du batch (avec des masques d'attention empêchant un exemple de porter de l'attention sur un autre).
- Nombre d'epochs : 1 à 10, souvent 2 à 5. Trop peu = sous-apprentissage ; trop = surapprentissage. Surveiller la perte de validation et utiliser l'arrêt anticipé (early stopping).
- Optimiseur : AdamW, idéalement sa version 8-bit (même qualité, moins de mémoire). AdaFactor pour les contraintes mémoire sévères.
- Weight decay : pénalise les poids élevés pour favoriser la généralisation. 0,01 à 0,1, départ à 0,01.
- Gradient checkpointing : recalcule certaines activations au backward pour économiser la mémoire — un compromis calcul contre mémoire.
La boucle d'entraînement en pratique
Pour le LLM Twin, les auteurs choisissent Llama 3.1 8B (licence commerciale permissive, taille raisonnable, bonnes performances). L'écosystème Hugging Face offre plusieurs outils : TRL (la librairie de référence, avec son SFTTrainer), Axolotl (configs YAML réutilisables) et Unsloth (kernels optimisés, 2-5× plus rapide, jusqu'à 80 % de mémoire en moins, mais mono-GPU). C'est Unsloth qui est retenu ici.
D'abord, charger le modèle et configurer LoRA. Le paramètre load_in_4bit bascule entre LoRA (False) et QLoRA (True) :
from unsloth import FastLanguageModel, is_bfloat16_supported
max_seq_length = 2048
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="meta-llama/Meta-Llama-3.1-8B",
max_seq_length=max_seq_length,
load_in_4bit=False, # False = LoRA, True = QLoRA
)
# Configuration LoRA : rang 32 pour imiter le style et
# absorber la connaissance des échantillons.
model = FastLanguageModel.get_peft_model(
model,
r=32,
lora_alpha=32,
lora_dropout=0,
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj", # toutes les linéaires
],
) Le dataset llmtwin (3 000 exemples) est trop petit pour que le modèle apprenne bien le chat template. Les auteurs le complètent avec 10 000 exemples d'un dataset généraliste de qualité (FineTome), puis formatent le tout avec le template Alpaca — en ajoutant manuellement le token de fin de phrase (EOS) à chaque réponse, faute de quoi le modèle générerait sans jamais s'arrêter.
from trl import SFTTrainer
from transformers import TrainingArguments
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset["train"],
eval_dataset=dataset["test"],
dataset_text_field="text",
max_seq_length=max_seq_length,
packing=True, # combine les exemples courts
args=TrainingArguments(
learning_rate=3e-4,
lr_scheduler_type="linear",
per_device_train_batch_size=2,
gradient_accumulation_steps=8, # batch effectif = 16
num_train_epochs=3,
fp16=not is_bfloat16_supported(),
bf16=is_bfloat16_supported(),
optim="adamw_8bit",
weight_decay=0.01,
warmup_steps=10,
output_dir="output",
report_to="comet_ml", # suivi d'expériences
seed=0,
),
)
trainer.train() # ~50 min sur un GPU A100 Une fois entraîné, un test rapide (avec FastLanguageModel.for_inference) vérifie qu'il n'y a pas d'erreur grossière de tokenizer ou de template — l'évaluation sérieuse viendra plus tard. On sauvegarde enfin le modèle en fusionnant les poids LoRA dans le modèle de base (save_pretrained_merged), prêt à être poussé sur le Hub.
Note
Pendant l'entraînement, surveillez trois métriques via un outil comme Comet ML. La perte d'entraînement (training loss) doit décroître puis plateauer ; des pics signalent un problème (données, tokenizer, learning rate). La perte de validation (validation loss) doit suivre la training loss avec un écart faible : si elle remonte alors que la training loss baisse, c'est du surapprentissage. La norme du gradient (gradient norm) doit rester stable ; de grandes valeurs trahissent une instabilité, qu'on peut brider par gradient clipping.
À retenir
- Le SFT transforme un modèle de base (qui ne fait que prédire le prochain token) en assistant capable de suivre des instructions, en l'entraînant sur des paires instruction → réponse ; il réoriente la connaissance existante mais n'en crée pas.
- Commencez par le prompt engineering (few-shot, RAG) ; ne fine-tunez que si l'évaluation l'exige et que vous avez assez de données. Attention à l'oubli catastrophique et aux hallucinations sur des connaissances nouvelles.
- La qualité du jeu d'instructions repose sur trois axes — exactitude, diversité, complexité — affinés par filtrage, déduplication, décontamination, génération synthétique et augmentation (Evol-Instruct, UltraFeedback).
- Ne confondez pas format de stockage (Alpaca, ShareGPT, OpenAI) et chat template (ChatML, Llama 3, Gemma) : ce dernier, où chaque espace compte, donne au modèle sa capacité à suivre les instructions.
- LoRA gèle les poids et n'entraîne que de petites matrices de rang faible (~0,5 % des paramètres) ; QLoRA y ajoute une quantification 4 bits pour minimiser la VRAM (~9 Go pour un 7B) au prix de ~30 % de lenteur. Le full fine-tuning reste le plus coûteux et le seul destructif.
- Les hyperparamètres clés — learning rate (le plus important), batch effectif via accumulation de gradient, epochs, rang et alpha de LoRA — se règlent en surveillant les pertes d'entraînement et de validation, idéalement via un outil de suivi d'expériences.