Specialisez un LLM open source avec le fine-tuning LoRA et QLoRA : dataset, entrainement sur GPU grand public, inference, export GGUF pour Ollama et bonnes pratiques.
Pourquoi specialiser un modele
Les LLM generalistes sont impressionnants, mais parfois inadaptes : ils ne respectent pas votre format de sortie, ignorent votre jargon metier, ou adoptent un ton qui ne colle pas a votre marque. Le fine-tuning reentraine le modele sur vos donnees pour lui enseigner un comportement specifique.
Historiquement, fine-tuner un LLM exigeait des moyens colossaux : plusieurs GPU professionnels, des jours d'entrainement, des teraoctets de stockage. LoRA a tout change en rendant le fine-tuning accessible sur un seul GPU grand public, voire gratuitement sur Colab.
LoRA et QLoRA : l'idee cle
Un fine-tuning complet modifie les milliards de poids du modele — couteux et gourmand en memoire. LoRA (Low-Rank Adaptation) gele les poids d'origine et n'entraine que de petites matrices additionnelles, de rang faible, injectees dans certaines couches.
Le resultat est un adaptateur de quelques megaoctets seulement, qui se branche sur le modele de base. QLoRA va plus loin : il quantifie le modele de base en 4 bits, divisant encore la VRAM necessaire, ce qui permet de fine-tuner un modele 7B sur un GPU de 16 Go.
| Methode | Poids entraines | VRAM (modele 7B) | Sortie |
|---|---|---|---|
| Full fine-tuning | 100% | Plusieurs GPU pro | Modele complet (Go) |
| LoRA | ~1% | ~24 Go | Adaptateur (Mo) |
| QLoRA (4 bits) | ~1% | ~12-16 Go | Adaptateur (Mo) |
Fine-tuning ou RAG ?
La question revient sans cesse. La reponse courte : ils repondent a des besoins differents et se combinent souvent. Notre article dedie fine-tuning vs RAG approfondit ; voici la synthese decisionnelle.
| Besoin | Solution |
|---|---|
| Connaissances factuelles a jour | RAG |
| Style, ton, voix de marque | Fine-tuning |
| Format de sortie strict | Fine-tuning |
| Vocabulaire de domaine | Fine-tuning |
| Donnees privees volumineuses | RAG |
| Reduire la taille du prompt systeme | Fine-tuning |
Construire un dataset de qualite
La qualite du dataset determine 90% du resultat. Le format standard est une liste d'exemples instruction/reponse, souvent en JSONL (un objet JSON par ligne).
// dataset.jsonl — un exemple par ligne (format chat)
{"messages": [{"role": "system", "content": "Tu es un assistant SAV poli et concis."}, {"role": "user", "content": "Ma commande est en retard."}, {"role": "assistant", "content": "Je comprends votre inquietude. Pouvez-vous me communiquer votre numero de commande ? Je verifie immediatement le suivi."}]}
{"messages": [{"role": "system", "content": "Tu es un assistant SAV poli et concis."}, {"role": "user", "content": "Je veux un remboursement."}, {"role": "assistant", "content": "Bien sur. Le remboursement est possible sous 14 jours. Indiquez-moi le numero de commande et je lance la procedure."}]}
- Exemples coherents avec le comportement vise (meme ton partout)
- Diversite des cas couverts (eviter le sur-apprentissage)
- Donnees propres : pas de fautes, pas de contradictions
- System prompt identique a celui de la production
- Separer un jeu de validation pour mesurer la generalisation
Environnement et dependances
L'ecosysteme Python Hugging Face fournit tout le necessaire : transformers, peft (Parameter-Efficient Fine-Tuning, qui implemente LoRA), trl et bitsandbytes pour la quantification.
# Installer la stack de fine-tuning LoRA
pip install transformers peft trl bitsandbytes datasets accelerate
# Verifier l'acces au GPU
python -c "import torch; print('GPU dispo :', torch.cuda.is_available())"
Entrainer un adaptateur LoRA
Voici un script QLoRA complet : chargement du modele en 4 bits, configuration LoRA, et entrainement avec SFTTrainer de la librairie trl.
# train_lora.py — fine-tuning QLoRA d'un modele open source
import torch
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig
from trl import SFTTrainer, SFTConfig
MODEL = "meta-llama/Llama-3.1-8B-Instruct"
# 1. Quantification 4 bits (QLoRA) pour tenir en VRAM reduite
bnb = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
)
tokenizer = AutoTokenizer.from_pretrained(MODEL)
model = AutoModelForCausalLM.from_pretrained(
MODEL, quantization_config=bnb, device_map="auto"
)
# 2. Configuration LoRA : quels modules adapter et a quel rang
lora = LoraConfig(
r=16, # rang des matrices LoRA (compromis taille/qualite)
lora_alpha=32, # facteur d'echelle
lora_dropout=0.05,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], # couches d'attention
task_type="CAUSAL_LM",
)
# 3. Charger le dataset au format chat
dataset = load_dataset("json", data_files="dataset.jsonl", split="train")
# 4. Configurer et lancer l'entrainement
trainer = SFTTrainer(
model=model,
train_dataset=dataset,
peft_config=lora,
args=SFTConfig(
output_dir="./adapter-sav",
num_train_epochs=3, # 2-3 epochs suffisent souvent
per_device_train_batch_size=2,
gradient_accumulation_steps=4, # simule un batch plus grand
learning_rate=2e-4,
bf16=True,
logging_steps=10,
),
)
trainer.train()
trainer.save_model("./adapter-sav") # sauvegarde l'adaptateur (quelques Mo)
print("Adaptateur LoRA sauvegarde dans ./adapter-sav")
r controle la capacite de l'adaptateur : plus il est eleve, plus l'adaptateur apprend (et grossit). Pour la plupart des taches de style/format, r=8 a r=16 est un bon point de depart.
Inference avec l'adaptateur
Une fois entraine, l'adaptateur se charge par-dessus le modele de base pour generer des reponses specialisees.
# infer_lora.py — charger le modele de base + l'adaptateur LoRA
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
base = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B-Instruct", device_map="auto"
)
# Brancher l'adaptateur entraine sur le modele de base
model = PeftModel.from_pretrained(base, "./adapter-sav")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B-Instruct")
messages = [
{"role": "system", "content": "Tu es un assistant SAV poli et concis."},
{"role": "user", "content": "Mon colis est arrive casse."},
]
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
# Generer une reponse dans le style appris
output = model.generate(**inputs, max_new_tokens=200, temperature=0.7)
print(tokenizer.decode(output[0], skip_special_tokens=True))
# Fusionner l'adaptateur dans le modele pour le deploiement (optionnel)
merged = model.merge_and_unload() # integre LoRA dans les poids
merged.save_pretrained("./modele-sav-fusionne")
tokenizer.save_pretrained("./modele-sav-fusionne")
Export GGUF pour Ollama
Pour servir votre modele specialise en local via Ollama, convertissez-le au format GGUF quantifie. Cela donne un fichier unique, leger, executable sur CPU ou GPU modeste.
# Convertir le modele fusionne en GGUF (via llama.cpp)
python llama.cpp/convert_hf_to_gguf.py ./modele-sav-fusionne \
--outfile modele-sav.gguf --outtype q4_k_m # quantification 4 bits
# Creer un Modelfile Ollama pointant vers le GGUF
cat > Modelfile <<'EOF'
FROM ./modele-sav.gguf
SYSTEM "Tu es un assistant SAV poli et concis."
PARAMETER temperature 0.7
EOF
# Enregistrer et lancer le modele dans Ollama
ollama create sav-assistant -f Modelfile
ollama run sav-assistant "Mon colis est en retard."
Evaluer l'adaptateur avant de deployer
Un adaptateur qui « a l'air bien » sur deux exemples peut s'effondrer en production. Avant tout deploiement, evaluez-le sur un jeu de test jamais vu a l'entrainement, avec des metriques objectives adaptees a votre tache (respect du format, exactitude, conformite de ton).
# eval_lora.py — comparer modele de base et adaptateur sur un jeu de test
import json
def taux_format_json(modele, tokenizer, cas_test):
"""Mesure la proportion de reponses en JSON valide (tache 'format strict')."""
ok = 0
for cas in cas_test:
sortie = generer(modele, tokenizer, cas["prompt"])
try:
json.loads(sortie) # la reponse doit etre du JSON parsable
ok += 1
except ValueError:
pass
return ok / len(cas_test)
cas_test = [json.loads(l) for l in open("test.jsonl")]
base_score = taux_format_json(base, tokenizer, cas_test)
ft_score = taux_format_json(model, tokenizer, cas_test)
print(f"Format valide — base: {base_score:.0%} | fine-tune: {ft_score:.0%}")
| Type de tache | Metrique pertinente | Seuil de mise en prod |
|---|---|---|
| Format de sortie (JSON, balises) | % de sorties valides | > 98 % |
| Classification | Exactitude / F1 | > modele de base |
| Ton / style | Note humaine ou juge LLM | Validation manuelle |
| Capacites generales | Non-regression (eval rapide) | Pas de chute notable |
Servir le modele dans une app web
Une fois le modele specialise tournant dans Ollama, votre application web l'interroge via une simple requete HTTP — exactement comme une API LLM cloud, mais en local et sans cout par token. On encapsule l'appel cote serveur Node.
// sav-service.js — appeler le modele fine-tune servi par Ollama
export async function repondreSav(question) {
const res = await fetch('http://localhost:11434/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'sav-assistant', // le modele cree via ollama create
messages: [{ role: 'user', content: question }],
stream: false,
}),
});
if (!res.ok) throw new Error('Modele local indisponible');
const data = await res.json();
return data.message.content; // reponse dans le style appris
}
// endpoint Express : exposer l'assistant specialise au front
app.post('/api/sav', async (req, res) => {
const { question } = req.body;
if (!question || question.length > 1000) {
return res.status(400).json({ error: 'Question invalide' });
}
try {
res.json({ reponse: await repondreSav(question) });
} catch (e) {
res.status(503).json({ error: 'Service IA momentanement indisponible' });
}
});
Alternative : fine-tuning manage sans GPU
Pas de GPU, pas d'envie de gerer l'infrastructure ? Les fournisseurs proposent un fine-tuning manage : vous televersez votre JSONL, ils entrainent, et vous obtenez un identifiant de modele a appeler comme n'importe quel autre. Plus cher au token, mais zero ops — souvent le bon choix pour demarrer.
// managed-finetune.js — lancer un fine-tuning via l'API OpenAI (Node)
import OpenAI from 'openai';
import { createReadStream } from 'node:fs';
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// 1. Televerser le dataset JSONL
const file = await client.files.create({
file: createReadStream('dataset.jsonl'),
purpose: 'fine-tune',
});
// 2. Creer le job de fine-tuning
const job = await client.fineTuning.jobs.create({
training_file: file.id,
model: 'gpt-4o-mini-2024-07-18', // modele de base supporte
});
console.log('Job lance :', job.id);
// 3. Une fois le job termine, appeler le modele specialise
const completion = await client.chat.completions.create({
model: 'ft:gpt-4o-mini:org::abc123', // id renvoye par le job termine
messages: [{ role: 'user', content: 'Mon colis est en retard.' }],
});
console.log(completion.choices[0].message.content);
Bonnes pratiques et pieges
- Commencer petit : valider sur un dataset reduit avant de scaler
- Garder un jeu de validation pour detecter le sur-apprentissage
- Limiter les epochs (2-3) pour eviter de memoriser le dataset
- Versionner les adaptateurs (ils sont legers)
- Comparer au modele de base pour mesurer le gain reel
- Fine-tuner pour injecter des faits (le RAG est meilleur)
- Trop d'epochs = oubli catastrophique des capacites generales
- Dataset bruite ou incoherent = comportement erratique
- Negliger le respect des licences du modele de base
Erreurs frequentes
L'erreur la plus repandue est de fine-tuner pour injecter des connaissances factuelles. Le fine-tuning enseigne un comportement, pas une base de faits ; vouloir lui faire memoriser un catalogue produit ou une documentation aboutit a des hallucinations confiantes. Pour des faits, le RAG reste la bonne reponse. Reservez l'adaptateur au style, au format et au ton.
Deuxieme piege technique : un format de chat incoherent entre l'entrainement et l'inference. Si votre dataset utilise un certain chat_template et que la production en applique un autre (ou un system prompt different), le modele ne reconnait plus le contexte appris et ses performances chutent. Le system prompt et le gabarit de conversation doivent etre identiques aux deux etapes.
Troisieme erreur, cote deploiement web : negliger la latence du premier appel. Charger un modele 8B et son adaptateur en VRAM prend plusieurs secondes ; si votre service le charge a froid a la premiere requete, l'utilisateur attend. Preferez un modele deja charge en memoire (serveur Ollama persistant ou worker dedie) et un endpoint qui se contente de transmettre la requete. Enfin, surveillez les licences : tous les modeles open source n'autorisent pas l'usage commercial du modele fine-tune.
Cote entrainement, le signe le plus revelateur d'un probleme est un ecart croissant entre la perte d'entrainement et celle de validation : la premiere baisse, la seconde stagne ou remonte. C'est le sur-apprentissage — le modele memorise vos exemples au lieu de generaliser. Reduisez le nombre d'epochs, augmentez la diversite du dataset ou baissez le rang r. Surveillez aussi une fuite de donnees : si des exemples quasi identiques se retrouvent a la fois dans l'entrainement et la validation, vos metriques sont trompeusement bonnes et s'effondreront en production sur des cas reellement nouveaux.
Conclusion
Le fine-tuning LoRA a democratise la specialisation des LLM : avec QLoRA, un modele 7-8B se fine-tune sur un GPU grand public, produisant un adaptateur de quelques megaoctets. La cle du succes n'est pas la puissance de calcul mais la qualite du dataset — quelques centaines d'exemples propres et coherents valent mieux qu'un gros volume bruite.
Reservez le fine-tuning a ce qu'il fait bien — style, format, ton, vocabulaire — et confiez les faits au RAG. La chaine complete dataset → QLoRA → GGUF → Ollama vous donne un modele specialise, prive et sans cout d'API. C'est le chainon manquant entre les modeles generalistes et vos besoins specifiques.
- LoRA entraine ~1% des poids ; QLoRA ajoute la quantification 4 bits
- Un adaptateur fait quelques Mo et se branche sur le modele de base
- Fine-tuning pour le comportement, RAG pour les faits
- La qualite du dataset prime sur sa taille
- Export GGUF pour servir en local via Ollama