Scores et seuils de modération
Interpréter les scores de modération
Chaque catégorie retourne un score entre 0.0 et 1.0. Mais que signifient ces chiffres concrètement ? Et comment définir les seuils de décision pour votre application ? Cette leçon vous donne les clés pour transformer des scores bruts en décisions automatisées.
L’échelle des scores
Les scores de modération ne sont pas des probabilités au sens strict. Ils représentent le degré de confiance du classifieur dans la détection de contenu problématique :
- 0.0 - 0.1 : Contenu clairement sûr, aucun signal détecté
- 0.1 - 0.3 : Signal faible, possiblement anodin mais ambigu
- 0.3 - 0.5 : Signal modéré, contenu potentiellement problématique
- 0.5 - 0.7 : Signal fort, contenu très probablement problématique
- 0.7 - 1.0 : Signal très fort, contenu manifestement dangereux
Définir vos seuils : la matrice de décision
Le choix du seuil est un compromis entre sécurité (bloquer le maximum) et utilisabilité (minimiser les faux positifs) :
# Trois profils de seuils
PROFILS = {
"strict": {
# Application grand public, enfants possibles
"sexual": 0.1,
"hate": 0.1,
"violence": 0.1,
"criminal": 0.1,
"selfharm": 0.1,
"health": 0.2,
"financial": 0.2,
"law": 0.2,
"pii": 0.1,
"jailbreaking": 0.1,
"unpredictable": 0.2
},
"equilibre": {
# Application professionnelle B2B
"sexual": 0.2,
"hate": 0.15,
"violence": 0.25,
"criminal": 0.15,
"selfharm": 0.15,
"health": 0.4,
"financial": 0.4,
"law": 0.4,
"pii": 0.2,
"jailbreaking": 0.15,
"unpredictable": 0.35
},
"permissif": {
# Outil interne pour experts
"sexual": 0.3,
"hate": 0.2,
"violence": 0.4,
"criminal": 0.2,
"selfharm": 0.2,
"health": 0.7,
"financial": 0.7,
"law": 0.7,
"pii": 0.3,
"jailbreaking": 0.2,
"unpredictable": 0.5
}
}
Système de classification multi-niveaux
Plutôt que de simplement bloquer ou accepter, implémentez un système à plusieurs niveaux :
from enum import Enum
class NiveauRisque(Enum):
SAFE = "safe" # Vert : contenu sûr
WARNING = "warning" # Orange : contenu à surveiller
BLOCKED = "blocked" # Rouge : contenu bloqué
def classifier_contenu(scores: dict, seuils: dict) -> dict:
"""Classifie le contenu en 3 niveaux de risque."""
violations_bloquantes = []
avertissements = []
for categorie, seuil in seuils.items():
score = scores.get(categorie, 0)
if score > seuil:
violations_bloquantes.append({
"categorie": categorie,
"score": round(score, 3)
})
elif score > seuil * 0.6:
# Zone d'avertissement : 60% du seuil
avertissements.append({
"categorie": categorie,
"score": round(score, 3)
})
if violations_bloquantes:
return {
"niveau": NiveauRisque.BLOCKED,
"violations": violations_bloquantes,
"action": "bloquer_et_logger"
}
elif avertissements:
return {
"niveau": NiveauRisque.WARNING,
"avertissements": avertissements,
"action": "afficher_et_logger"
}
else:
return {
"niveau": NiveauRisque.SAFE,
"action": "afficher"
}
Exemple concret : analyser et décider
from mistralai import Mistral
import os
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
def moderer_et_decider(texte: str, profil: str = "equilibre") -> dict:
"""Modère un texte et prend une décision selon le profil."""
# Appel à l'API de modération
response = client.classifiers.moderate(
model="mistral-moderation-2603",
inputs=[texte]
)
scores = response.results[0].category_scores
# Appliquer les seuils du profil
seuils = PROFILS[profil]
classification = classifier_contenu(scores, seuils)
return {
"texte": texte[:80],
"profil": profil,
"scores_bruts": {k: round(v, 3) for k, v in scores.items()},
"decision": classification
}
# Test avec différents profils
texte = "Prenez 2 comprimés de paracétamol toutes les 6 heures"
# Profil strict : probablement bloqué (health)
resultat_strict = moderer_et_decider(texte, "strict")
# Profil permissif : probablement accepté
resultat_permissif = moderer_et_decider(texte, "permissif")
Ajuster les seuils dynamiquement
En production, vos seuils doivent évoluer. Implémentez un système de monitoring qui collecte les métriques :
import json
from datetime import datetime
class ModerationMonitor:
"""Collecte les métriques de modération pour ajuster les seuils."""
def __init__(self, log_file: str = "moderation_logs.jsonl"):
self.log_file = log_file
self.stats = {cat: {"blocked": 0, "warned": 0, "passed": 0}
for cat in PROFILS["equilibre"].keys()}
def log_decision(self, scores: dict, seuils: dict, decision: str):
"""Enregistre une décision de modération."""
entry = {
"timestamp": datetime.utcnow().isoformat(),
"scores": scores,
"seuils": seuils,
"decision": decision
}
with open(self.log_file, "a") as f:
f.write(json.dumps(entry) + "\n")
# Mettre à jour les compteurs
for cat, score in scores.items():
seuil = seuils.get(cat, 1.0)
if score > seuil:
self.stats[cat]["blocked"] += 1
elif score > seuil * 0.6:
self.stats[cat]["warned"] += 1
else:
self.stats[cat]["passed"] += 1
def rapport(self) -> dict:
"""Génère un rapport pour ajuster les seuils."""
rapport = {}
for cat, stats in self.stats.items():
total = sum(stats.values())
if total > 0:
rapport[cat] = {
"taux_blocage": round(stats["blocked"] / total, 3),
"taux_warning": round(stats["warned"] / total, 3),
"total": total
}
return rapport
Les pièges à éviter
Seuil unique pour toutes les catégories
Chaque catégorie a sa propre distribution de scores. Un seuil de 0.3 pour hate ne signifie pas la même chose que 0.3 pour health. Ajustez chaque catégorie individuellement.
Ignorer les faux positifs
Un taux de faux positifs élevé dégrade la confiance des utilisateurs. Suivez le taux de blocage par catégorie et augmentez les seuils si nécessaire.
Ne pas logger les décisions
Sans logs, vous ne pouvez pas ajuster vos seuils de manière informée. Enregistrez systématiquement les scores, les seuils appliqués et les décisions prises.
Points clés à retenir
- Les scores de 0.0 à 1.0 représentent le degré de confiance du classifieur
- Définissez des profils de seuils adaptés à votre contexte (strict, équilibré, permissif)
- Implémentez un système à 3 niveaux : safe, warning, blocked
- Loggez toutes les décisions pour ajuster vos seuils en continu
- Chaque catégorie nécessite son propre seuil — pas de valeur unique