Guide étape par étape pour fine-tuner un LLM open source avec LoRA et QLoRA : préparation du dataset, configuration des hyperparamètres, entraînement, évaluation, et déploiement de l'adaptateur.
Le prompting a ses limites. Quand un LLM ne produit pas les résultats attendus malgré des prompts bien construits, le fine-tuning devient nécessaire. Grâce à LoRA (Low-Rank Adaptation) et QLoRA (Quantized LoRA), il est désormais possible d'adapter un modèle de 7B à 70B paramètres sur un seul GPU consumer (24 Go VRAM).
Cet article est un guide pratique issu de plusieurs fine-tunings réalisés en production - dont un 70B avec adaptateurs LoRA dynamiques servi sur 2×H100 chez Scaleway, avec bascule de personnalité à chaud - couvrant la préparation des données jusqu'au déploiement.
La qualité du dataset est le facteur n°1 de succès. Garbage in, garbage out.
Le format standard est une liste de conversations en JSON :
{
"conversations": [
{"role": "system", "content": "Tu es un assistant juridique spécialisé en droit du travail français."},
{"role": "user", "content": "Quels sont les délais de préavis pour un CDI de 3 ans ?"},
{"role": "assistant", "content": "Pour un salarié en CDI avec 3 ans d'ancienneté, le préavis légal est de 2 mois..."}
]
}
QLoRA charge le modèle de base en 4-bit (NF4) et n'entraîne que les adaptateurs LoRA en FP16/BF16. Cela divise la VRAM par 4 :
Unsloth optimise automatiquement les kernels CUDA pour LoRA/QLoRA. Il supporte Llama, Mistral, Phi, Qwen, Gemma.
Un cas d'usage de plus en plus courant : fine-tuner un LLM pour qu'il exploite mieux le contexte récupéré par un pipeline RAG.
Meme les meilleurs LLMs out-of-the-box ont des limites en RAG : - Ils peuvent ignorer le contexte fourni et répondre de mémoire (hallucination) - Ils ne savent pas toujours citer leurs sources de manière fiable - Le format de citation peut être inconstant (parfois en footnote, parfois inline) - Ils peuvent inventer des passages qui ne sont pas dans le contexte
Le dataset doit enseigner au modèle à :
1. Toujours baser sa réponse sur le contexte fourni (et le dire quand le contexte ne contient pas la réponse)
2. Citer les sources avec un format cohérent ([Source: nom, p.X])
3. Synthétiser plusieurs passages plutôt que copier-coller
Format type :
{
"conversations": [
{"role": "system", "content": "Réponds en te basant uniquement sur le contexte fourni. Cite tes sources."},
{"role": "user", "content": "Contexte:\n[Source: rapport.pdf, p.12] Le chiffre d'affaires a augmenté de 15%...\n[Source: bilan.pdf, p.3] La marge nette est de 8.2%...\n\nQuestion: Quelle est la santé financière de l'entreprise ?"},
{"role": "assistant", "content": "L'entreprise montre une bonne santé financière : le chiffre d'affaires a progressé de 15% [Source: rapport.pdf, p.12] avec une marge nette de 8.2% [Source: bilan.pdf, p.3]."}
]
}
Quand vous n'avez pas assez de paires question-réponse, utilisez un LLM plus puissant (GPT-4, Claude) pour en générer :
Cette approche (distillation) permet de passer de 100 exemples manuels à 2000+ exemples de qualité.
Ne jamais évaluer uniquement sur la loss d'entrainement. Métriques à suivre :
Le test ultime : déployer le modèle fine-tuné en parallèle du modèle de base et comparer les métriques utilisateur (thumbs up/down, reformulations, temps passé sur la réponse).
Apres le SFT (Supervised Fine-Tuning), vous pouvez aller plus loin avec DPO (Direct Preference Optimization) : fournir des paires (réponse préférée, réponse rejetée) pour que le modèle apprenne vos préférences de qualité. DPO est plus simple que RLHF (pas besoin d'un reward model séparé) et fonctionne bien avec TRL de HuggingFace.
Fusionner l'adaptateur LoRA dans le modèle de base pour éliminer l'overhead d'inférence :
merged_model = model.merge_and_unload()
merged_model.save_pretrained("./merged-model")
tokenizer.save_pretrained("./merged-model")
Convertir le modèle mergé en GGUF pour le déployer avec llama.cpp :
python convert_hf_to_gguf.py ./merged-model --outtype f16
./llama-quantize merged-model-f16.gguf merged-model-Q4_K_M.gguf Q4_K_M
Le modèle GGUF résultant est directement utilisable dans llama.cpp, llama-cpp-python, ou Ollama.
Un avantage majeur de LoRA : on peut maintenir plusieurs adaptateurs pour le meme modèle de base. Un adaptateur "juridique", un "médical", un "RAG-optimisé". Le routage se fait au niveau de l'application, le modèle de base n'est chargé qu'une fois en mémoire.
Le fine-tuning avec LoRA/QLoRA démocratise l'adaptation des LLM. Les clés du succes : un dataset de qualité (500+ exemples minimum), une évaluation rigoureuse (jamais juste la loss), et une approche itérative. Commencez avec un petit dataset et un rank faible, mesurez avec des métriques concrètes, puis augmentez progressivement. Pour le RAG, le fine-tuning améliore significativement la capacité du modèle à utiliser le contexte et à citer ses sources.
Dans MIRROR, le Llama 3.1 8B de base gère les citations RAG raisonnablement bien grâce au prompt engineering (voir l'article RAG). Le fine-tuning serait la prochaine étape si la qualité des citations devait être améliorée - le format de dataset RAG décrit ci-dessus est exactement l'approche que j'utiliserais.
Michail Berjaoui - Décembre 2024