Évaluer votre modèle fine-tuné
Pourquoi l’évaluation est indispensable
Un fine-tuning réussi ne se mesure pas uniquement à la loss. La vraie question est : votre modèle fine-tuné est-il réellement meilleur que le modèle de base pour votre cas d’usage ? Cette leçon vous montre comment construire un protocole d’évaluation rigoureux.
Construire un jeu de test
Votre jeu de test doit être séparé de vos données d’entraînement ET de validation. Il représente les situations réelles que votre modèle rencontrera en production.
import json
import random
def preparer_jeux(fichier: str, ratios: tuple = (0.8, 0.1, 0.1)):
"""Sépare les données en train/validation/test."""
with open(fichier, "r") as f:
exemples = [json.loads(l) for l in f]
random.shuffle(exemples)
n = len(exemples)
i1 = int(n * ratios[0])
i2 = int(n * (ratios[0] + ratios[1]))
jeux = {
"train": exemples[:i1],
"validation": exemples[i1:i2],
"test": exemples[i2:]
}
for nom, data in jeux.items():
with open(f"{nom}.jsonl", "w") as f:
for ex in data:
f.write(json.dumps(ex, ensure_ascii=False) + "\n")
print(f"{nom} : {len(data)} exemples")
return jeux
preparer_jeux("toutes_les_donnees.jsonl")
Évaluation automatique
Évaluation par métriques
from openai import OpenAI
client = OpenAI()
def evaluer_modele(modele: str, exemples_test: list[dict]) -> dict:
"""Évalue un modèle sur un jeu de test."""
resultats = []
for ex in exemples_test:
# Extraire la question (sans la réponse attendue)
messages_input = [
m for m in ex["messages"] if m["role"] != "assistant"
]
reponse_attendue = next(
m["content"] for m in ex["messages"] if m["role"] == "assistant"
)
# Obtenir la réponse du modèle
response = client.responses.create(
model=modele,
input=messages_input
)
reponse_modele = response.output_text
resultats.append({
"input": messages_input[-1]["content"],
"attendu": reponse_attendue,
"obtenu": reponse_modele
})
return resultats
Évaluation par LLM juge
Une technique puissante consiste à utiliser un modèle performant pour juger la qualité des réponses :
def evaluer_avec_juge(resultats: list[dict]) -> list[dict]:
"""Utilise GPT-5.4 comme juge pour évaluer les réponses."""
evaluations = []
for r in resultats:
prompt_juge = f"""Évaluez la qualité de la réponse obtenue par rapport à la réponse attendue.
Question : {r['input']}
Réponse attendue : {r['attendu']}
Réponse obtenue : {r['obtenu']}
Critères (notez chacun de 1 à 5) :
1. Exactitude : les informations sont-elles correctes ?
2. Complétude : tous les points importants sont-ils couverts ?
3. Style : le ton et le format sont-ils appropriés ?
4. Concision : la réponse est-elle de longueur appropriée ?
Répondez en JSON : {{"exactitude": N, "completude": N, "style": N, "concision": N, "commentaire": "..."}}"""
response = client.responses.create(
model="gpt-5.4",
input=prompt_juge
)
try:
evaluation = json.loads(response.output_text)
evaluations.append(evaluation)
except json.JSONDecodeError:
evaluations.append({"erreur": "Réponse non parseable"})
return evaluations
Métriques spécialisées
Pour la classification
def metriques_classification(resultats: list[dict]) -> dict:
"""Calcule précision, rappel et F1 pour une tâche de classification."""
correct = sum(
1 for r in resultats
if r["attendu"].strip().lower() == r["obtenu"].strip().lower()
)
total = len(resultats)
precision = correct / total if total > 0 else 0
print(f"Précision : {precision:.2%} ({correct}/{total})")
return {"precision": precision, "correct": correct, "total": total}
Pour le function calling
def metriques_function_calling(resultats: list[dict]) -> dict:
"""Évalue la qualité des appels de fonctions."""
stats = {"bonne_fonction": 0, "bons_params": 0, "total": 0}
for r in resultats:
stats["total"] += 1
if r.get("fonction_attendue") == r.get("fonction_obtenue"):
stats["bonne_fonction"] += 1
if r.get("params_attendus") == r.get("params_obtenus"):
stats["bons_params"] += 1
print(f"Bonne fonction : {stats['bonne_fonction']}/{stats['total']}")
print(f"Bons paramètres : {stats['bons_params']}/{stats['total']}")
return stats
Évaluation humaine
L’évaluation automatique ne capture pas tout. Préparez un protocole d’évaluation humaine :
- Échantillonnez 20 à 50 réponses aléatoires
- Définissez des critères clairs et mesurables
- Utilisez une grille de notation standardisée
- Faites évaluer par plusieurs personnes pour réduire le biais
- Calculez l’accord inter-évaluateurs
Grille d’évaluation type
| Critère | 1 (Mauvais) | 3 (Acceptable) | 5 (Excellent) |
|---|---|---|---|
| Pertinence | Hors sujet | Partiellement pertinent | Parfaitement ciblé |
| Ton | Inapproprié | Correct sans plus | Parfaitement aligné |
| Format | Non respecté | Partiellement respecté | Parfaitement respecté |
Rapport d’évaluation
def rapport_evaluation(resultats_auto: dict, resultats_humains: dict):
"""Génère un rapport d'évaluation combiné."""
print("=" * 60)
print("RAPPORT D'ÉVALUATION DU MODÈLE FINE-TUNÉ")
print("=" * 60)
print(f"\nMétriques automatiques :")
for k, v in resultats_auto.items():
print(f" {k} : {v}")
print(f"\nÉvaluation humaine :")
for k, v in resultats_humains.items():
print(f" {k} : {v}")
print(f"\nRecommandation :")
score_moyen = sum(resultats_humains.values()) / len(resultats_humains)
if score_moyen >= 4:
print(" Le modèle est prêt pour la production")
elif score_moyen >= 3:
print(" Améliorations nécessaires — itérez sur les données")
else:
print(" Résultats insuffisants — revoyez votre approche")
Points clés à retenir
- Séparez toujours un jeu de test indépendant (ni train, ni validation)
- Combinez évaluation automatique et évaluation humaine
- L’évaluation par LLM juge est un bon compromis entre les deux
- Définissez des critères mesurables avant de commencer
- Un score de loss bas ne garantit pas la qualité en production