Coûts et optimisation
Comprendre la structure des coûts
Le fine-tuning OpenAI engendre deux types de coûts : le coût d’entraînement (ponctuel) et le coût d’inférence (récurrent). Comprendre et optimiser ces coûts est essentiel pour un déploiement rentable.
Coût d’entraînement
Le coût d’entraînement est facturé au nombre de tokens traités pendant l’entraînement :
Coût entraînement = tokens_totaux × nombre_epochs × prix_par_token_entrainement
Estimer le coût avant de lancer
import json
def estimer_cout_entrainement(
fichier_jsonl: str,
n_epochs: int,
prix_par_million_tokens: float
) -> dict:
"""Estime le coût d'entraînement avant de lancer le job."""
total_tokens = 0
with open(fichier_jsonl, "r") as f:
nb_exemples = 0
for ligne in f:
nb_exemples += 1
obj = json.loads(ligne)
for msg in obj["messages"]:
# Estimation grossière : 1 token ~ 4 caractères en français
total_tokens += len(msg["content"]) // 4
tokens_entrainement = total_tokens * n_epochs
cout = tokens_entrainement * prix_par_million_tokens / 1_000_000
return {
"exemples": nb_exemples,
"tokens_par_passage": total_tokens,
"epochs": n_epochs,
"tokens_total": tokens_entrainement,
"cout_estime": f"{cout:.2f} $"
}
resultat = estimer_cout_entrainement(
"training_data.jsonl",
n_epochs=3,
prix_par_million_tokens=8.0 # vérifiez le tarif actuel
)
for k, v in resultat.items():
print(f"{k} : {v}")
Coût d’inférence
Le coût d’inférence est facturé par appel au modèle, au nombre de tokens en entrée et en sortie. Les modèles fine-tunés ont généralement un tarif légèrement supérieur au modèle de base.
Comparer le coût total
def cout_mensuel(
requetes_par_jour: int,
tokens_input_moyen: int,
tokens_output_moyen: int,
prix_input_par_million: float,
prix_output_par_million: float
) -> float:
"""Calcule le coût mensuel d'inférence."""
requetes_mois = requetes_par_jour * 30
cout_input = requetes_mois * tokens_input_moyen * prix_input_par_million / 1_000_000
cout_output = requetes_mois * tokens_output_moyen * prix_output_par_million / 1_000_000
return cout_input + cout_output
# Scénario : modèle de base avec long prompt
cout_base = cout_mensuel(
requetes_par_jour=5000,
tokens_input_moyen=800, # long prompt système + question
tokens_output_moyen=200,
prix_input_par_million=0.15,
prix_output_par_million=0.60
)
# Scénario : modèle fine-tuné avec prompt minimal
cout_ft = cout_mensuel(
requetes_par_jour=5000,
tokens_input_moyen=100, # prompt court, comportement intégré
tokens_output_moyen=200,
prix_input_par_million=0.30, # tarif FT plus élevé
prix_output_par_million=1.20
)
print(f"Coût mensuel base : {cout_base:.2f} $")
print(f"Coût mensuel fine-tuné : {cout_ft:.2f} $")
print(f"Différence : {cout_ft - cout_base:+.2f} $")
Stratégies d’optimisation
Réduire les tokens d’entrée
Le principal levier d’économie avec un modèle fine-tuné est la réduction du prompt :
| Approche | Tokens input | Économie |
|---|---|---|
| Modèle de base + prompt détaillé | 500-1000 tokens | Référence |
| Fine-tuné + prompt minimal | 50-100 tokens | 80-90 % sur l'input |
| Fine-tuné + sans prompt système | 20-50 tokens | 95 % sur l'input |
Optimiser le nombre d’exemples
Plus de données = meilleur modèle, mais aussi coût d’entraînement plus élevé. Trouvez le point d’équilibre :
def trouver_optimal(resultats_par_taille: dict):
"""Identifie la taille de dataset optimale."""
print("Taille dataset | Score qualité | Coût entraînement")
print("-" * 55)
meilleur_ratio = 0
taille_optimale = 0
for taille, data in sorted(resultats_par_taille.items()):
score = data["score"]
cout = data["cout"]
ratio = score / cout if cout > 0 else 0
print(f"{taille:>14} | {score:>13.2f} | {cout:>17.2f} $")
if ratio > meilleur_ratio:
meilleur_ratio = ratio
taille_optimale = taille
print(f"\nTaille optimale : {taille_optimale} exemples")
Choisir le bon modèle de base
GPT-5.3-mini est moins cher que GPT-5.4-mini, tant en entraînement qu’en inférence. Commencez toujours par le modèle le moins cher et montez en gamme uniquement si la qualité est insuffisante.
Réduire les epochs
Chaque epoch supplémentaire multiplie le coût d’entraînement. Si 2 epochs donnent 95 % de la qualité de 4 epochs, le choix est vite fait.
Surveiller les coûts en production
class CostTracker:
"""Suivi des coûts en temps réel."""
def __init__(self, budget_mensuel: float):
self.budget = budget_mensuel
self.depenses = 0.0
self.appels = 0
def enregistrer(self, tokens_input: int, tokens_output: int,
prix_input: float, prix_output: float):
"""Enregistre le coût d'un appel."""
cout = (
tokens_input * prix_input / 1_000_000 +
tokens_output * prix_output / 1_000_000
)
self.depenses += cout
self.appels += 1
# Alerte si on approche du budget
utilisation = self.depenses / self.budget * 100
if utilisation > 80:
print(f"ALERTE : {utilisation:.1f}% du budget mensuel utilisé")
def rapport(self):
"""Rapport de consommation."""
print(f"Appels : {self.appels}")
print(f"Dépenses : {self.depenses:.2f} $")
print(f"Budget restant : {self.budget - self.depenses:.2f} $")
if self.appels > 0:
print(f"Coût moyen par appel : {self.depenses / self.appels:.4f} $")
Calcul du ROI
def calculer_roi(
cout_entrainement: float,
economie_mensuelle: float,
gain_qualite_valeur: float = 0
) -> dict:
"""Calcule le retour sur investissement du fine-tuning."""
benefice_mensuel = economie_mensuelle + gain_qualite_valeur
mois_amortissement = cout_entrainement / benefice_mensuel if benefice_mensuel > 0 else float("inf")
return {
"cout_entrainement": f"{cout_entrainement:.2f} $",
"benefice_mensuel": f"{benefice_mensuel:.2f} $",
"amortissement": f"{mois_amortissement:.1f} mois",
"roi_annuel": f"{(benefice_mensuel * 12 - cout_entrainement) / cout_entrainement * 100:.0f}%"
}
Points clés à retenir
- Estimez le coût d’entraînement avant de lancer le job
- Le principal levier d’économie est la réduction des tokens d’entrée
- Commencez par GPT-5.3-mini avant de considérer GPT-5.4-mini
- Surveillez les coûts en production avec des alertes de budget
- Calculez le ROI en incluant les gains de qualité et les économies de tokens