Comparer avec le modèle de base
Mis à jour le 28 juillet 2026
Pourquoi comparer systématiquement
Le fine-tuning a un coût : temps de préparation des données, coût d’entraînement, complexité de maintenance. Vous devez prouver que votre modèle fine-tuné apporte une amélioration mesurable par rapport au modèle de base avec un bon prompt. Si la différence est marginale, le fine-tuning n’en vaut peut-être pas la peine.
La tentation, dans ce genre d’exercice, est de comparer votre modèle entraîné à un modèle de base mal instruit. La démonstration est alors flatteuse et sans valeur : vous avez battu un adversaire que vous aviez affaibli vous-même. La seule comparaison honnête oppose votre modèle fine-tuné au meilleur usage possible du modèle de base.
Définir la baseline
La baseline est le modèle de base avec votre meilleur prompt système. Optimisez ce prompt avant de comparer. Concrètement, cela signifie écrire les règles que vous aviez implicitement enseignées par l’exemple pendant le fine-tuning : le rôle, le ton, la longueur attendue, la manière de clore un échange. Le prompt ci-dessous fait exactement cela pour un assistant de support ; notez que le modèle fine-tuné, lui, s’appelle sans instruction, puisque ces règles font désormais partie de ses poids.
from openai import OpenAI
client = OpenAI()
PROMPT_SYSTEME_OPTIMISE = """Vous êtes un assistant support client pour TechCorp.
Règles :
- Répondez de manière professionnelle et empathique
- Proposez toujours une solution concrète
- Limitez vos réponses à 3 phrases maximum
- Terminez par une question de suivi"""
def reponse_baseline(question: str) -> str:
"""Obtient une réponse du modèle de base."""
response = client.responses.create(
model="gpt-5.6-luna",
instructions=PROMPT_SYSTEME_OPTIMISE,
input=question
)
return response.output_text
def reponse_fine_tune(question: str, modele_ft: str) -> str:
"""Obtient une réponse du modèle fine-tuné."""
response = client.responses.create(
model=modele_ft,
input=question
)
return response.output_text
Produire les réponses côte à côte
L’étape suivante consiste à interroger les deux modèles sur exactement les mêmes questions et à conserver les réponses appariées. Choisissez des questions représentatives de votre trafic réel plutôt que des cas d’école : une réclamation de livraison, une demande de procédure, un mécontentement, une demande de remboursement, une question factuelle. Ce sont ces cinq familles qui feront la différence en production, pas les formulations parfaites.
import json
def comparer_modeles(
questions: list[str],
modele_base: str,
modele_ft: str,
prompt_systeme: str
) -> list[dict]:
"""Compare les réponses de deux modèles sur les mêmes questions."""
comparaisons = []
for q in questions:
# Modèle de base avec prompt optimisé
r_base = client.responses.create(
model=modele_base,
instructions=prompt_systeme,
input=q
)
# Modèle fine-tuné (prompt minimal ou absent)
r_ft = client.responses.create(
model=modele_ft,
input=q
)
comparaisons.append({
"question": q,
"reponse_base": r_base.output_text,
"reponse_ft": r_ft.output_text
})
return comparaisons
# Questions de test
questions_test = [
"Mon colis n'est pas arrivé.",
"Comment réinitialiser mon mot de passe ?",
"Votre produit ne fonctionne pas comme prévu.",
"Je voudrais un remboursement.",
"Quels sont vos horaires d'ouverture ?",
]
resultats = comparer_modeles(
questions_test,
"gpt-5.6-luna",
"ft:gpt-5.6-luna:org::suffix:xxxxxxxx",
PROMPT_SYSTEME_OPTIMISE
)
Faire trancher un modèle tiers
Relire cent paires à la main est vite décourageant, et le jugement se relâche à mesure que la fatigue vient. Un modèle tiers, qui n’a produit aucune des deux réponses, arbitre chaque paire sur quatre critères et rend un verdict global. Neutralisez d’abord les étiquettes : le juge voit « Réponse A » et « Réponse B », jamais quel modèle les a produites, sans quoi son verdict porterait autant sur le nom du modèle que sur le texte. Prévoyez ensuite le cas où la sortie n’est pas parseable, faute de quoi une seule réponse malformée interrompt toute la campagne.
def juger_comparaison(comparaison: dict) -> dict:
"""Fait juger la comparaison par un modèle tiers."""
prompt = f"""Comparez ces deux réponses à la même question.
Question : {comparaison['question']}
Réponse A : {comparaison['reponse_base']}
Réponse B : {comparaison['reponse_ft']}
Pour chaque critère, indiquez quelle réponse est meilleure (A, B ou égalité) :
1. Pertinence de la réponse
2. Professionnalisme du ton
3. Respect du format attendu
4. Utilité pour l'utilisateur
Répondez en JSON : {{"pertinence": "A/B/egal", "ton": "A/B/egal", "format": "A/B/egal", "utilite": "A/B/egal", "gagnant_global": "A/B/egal"}}"""
response = client.responses.create(
model="gpt-5.6-sol",
input=prompt
)
try:
return json.loads(response.output_text)
except json.JSONDecodeError:
return {"erreur": "Réponse non parseable"}
# Juger toutes les comparaisons
jugements = [juger_comparaison(c) for c in resultats]
Reste à agréger ces verdicts en un chiffre unique. Le taux d’amélioration — la part des paires où le modèle fine-tuné l’emporte — se lit avec des seuils fixés à l’avance : au-delà de 60 %, le gain est net ; entre 40 et 60 %, il faut mettre ce gain en regard du surcoût ; en dessous, votre fine-tuning n’a pas fait ses preuves et le problème est presque toujours dans les données.
def score_comparaison(jugements: list[dict]) -> dict:
"""Calcule le score global de la comparaison."""
scores = {"A_gagne": 0, "B_gagne": 0, "egalite": 0}
for j in jugements:
gagnant = j.get("gagnant_global", "egal")
if gagnant == "A":
scores["A_gagne"] += 1
elif gagnant == "B":
scores["B_gagne"] += 1
else:
scores["egalite"] += 1
total = len(jugements)
print(f"Modèle de base gagne : {scores['A_gagne']}/{total}")
print(f"Modèle fine-tuné gagne : {scores['B_gagne']}/{total}")
print(f"Égalité : {scores['egalite']}/{total}")
taux_amelioration = scores["B_gagne"] / total * 100
print(f"\nTaux d'amélioration : {taux_amelioration:.1f}%")
if taux_amelioration >= 60:
print("Le fine-tuning apporte une amélioration significative.")
elif taux_amelioration >= 40:
print("Amélioration modeste — vérifiez si le coût est justifié.")
else:
print("Le fine-tuning n'apporte pas d'amélioration claire.")
return scores
score_comparaison(jugements)
Comparer les coûts
La qualité n’est qu’une moitié de la décision. Le fine-tuning réduit souvent les tokens en entrée, puisque le prompt long devient inutile, mais chaque token facturé sur un modèle fine-tuné coûte davantage. Les deux effets jouent en sens contraire et seul le calcul tranche. La fonction ci-dessous les met face à face sur un mois de trafic : un prompt qui tombe de 500 à 50 tokens compense largement un tarif d’entrée doublé, mais ce ne serait plus vrai avec un prompt de base déjà court.
def comparer_couts(
nb_requetes_jour: int,
tokens_prompt_base: int,
tokens_prompt_ft: int,
tokens_output_moyen: int,
prix_input_base: float,
prix_input_ft: float,
prix_output: float
):
"""Compare les coûts mensuels entre modèle de base et fine-tuné."""
jours = 30
cout_base = jours * nb_requetes_jour * (
tokens_prompt_base * prix_input_base / 1_000_000 +
tokens_output_moyen * prix_output / 1_000_000
)
cout_ft = jours * nb_requetes_jour * (
tokens_prompt_ft * prix_input_ft / 1_000_000 +
tokens_output_moyen * prix_output / 1_000_000
)
economie = cout_base - cout_ft
print(f"Coût mensuel base : {cout_base:.2f} $")
print(f"Coût mensuel fine-tuné : {cout_ft:.2f} $")
print(f"Économie mensuelle : {economie:.2f} $")
# Exemple
comparer_couts(
nb_requetes_jour=1000,
tokens_prompt_base=500, # prompt long avec instructions
tokens_prompt_ft=50, # prompt court, le comportement est intégré
tokens_output_moyen=200,
prix_input_base=0.15,
prix_input_ft=0.30, # le fine-tuné coûte plus par token
prix_output=0.60
)
Comparer la latence
Un prompt raccourci de plusieurs centaines de tokens se ressent aussi sur le temps de réponse, et c’est un critère que vos utilisateurs perçoivent bien avant la finesse du ton. Mesurez la latence sur les mêmes questions que la comparaison qualitative, en dehors des heures de pointe, et refaites la mesure plusieurs fois : une valeur isolée reflète surtout l’état du réseau au moment du test.
import time
def mesurer_latence(modele: str, questions: list[str], prompt: str = "") -> float:
"""Mesure la latence moyenne d'un modèle."""
latences = []
for q in questions:
debut = time.time()
kwargs = {"model": modele, "input": q}
if prompt:
kwargs["instructions"] = prompt
client.responses.create(**kwargs)
latences.append(time.time() - debut)
moy = sum(latences) / len(latences)
print(f"{modele} — Latence moyenne : {moy:.2f}s")
return moy
Décider en fonction du résultat
Croisez maintenant la qualité et le coût. Chaque combinaison appelle une décision différente, et la plus inconfortable reste celle du milieu : lorsque les deux modèles se valent, il faut renoncer à un travail déjà payé plutôt que de lui chercher une justification après coup.
| Résultat | Action |
|---|---|
| FT gagne sur qualité + coûts | Déployez le modèle fine-tuné |
| FT gagne sur qualité, perd sur coûts | Évaluez si la qualité justifie le surcoût |
| Résultats similaires | Restez sur le modèle de base + prompt |
| Base gagne | Améliorez vos données et relancez |
Points clés à retenir
- Comparez toujours avec un modèle de base + prompt optimisé, pas un prompt basique
- Utilisez un jugement LLM pour automatiser la comparaison qualitative
- Mesurez aussi les coûts et la latence, pas seulement la qualité
- Un taux d’amélioration de 60%+ justifie le fine-tuning
- Si le fine-tuning n’apporte pas d’amélioration claire, restez sur le prompt engineering