Aller au contenu principal

Qualité des données : nettoyage et validation

Pourquoi le nettoyage est critique

Un fichier JSONL techniquement valide ne garantit pas un bon fine-tuning. Des données bruitées, incohérentes ou mal formatées vont dégrader les performances du modèle plutôt que les améliorer. Cette leçon couvre les techniques de nettoyage et de validation indispensables.

Validation technique du fichier

Avant toute chose, vérifiez que votre fichier JSONL est syntaxiquement correct :

import json

def valider_jsonl(chemin: str) -> dict:
    """Valide un fichier JSONL et retourne des statistiques."""
    erreurs = []
    stats = {"total": 0, "avec_system": 0, "multi_tour": 0}
    roles_valides = {"system", "user", "assistant"}

    with open(chemin, "r", encoding="utf-8") as f:
        for i, ligne in enumerate(f, 1):
            stats["total"] += 1
            try:
                obj = json.loads(ligne.strip())
            except json.JSONDecodeError as e:
                erreurs.append(f"Ligne {i} : JSON invalide — {e}")
                continue

            if "messages" not in obj:
                erreurs.append(f"Ligne {i} : clé 'messages' manquante")
                continue

            messages = obj["messages"]
            if not isinstance(messages, list) or len(messages) < 2:
                erreurs.append(f"Ligne {i} : minimum 2 messages requis")
                continue

            for j, msg in enumerate(messages):
                role = msg.get("role")
                if role not in roles_valides:
                    erreurs.append(f"Ligne {i}, msg {j} : rôle invalide")
                if not msg.get("content", "").strip():
                    erreurs.append(f"Ligne {i}, msg {j} : contenu vide")

            if not any(m["role"] == "assistant" for m in messages):
                erreurs.append(f"Ligne {i} : aucun message assistant")

            if messages[0]["role"] == "system":
                stats["avec_system"] += 1
            tours_user = sum(1 for m in messages if m["role"] == "user")
            if tours_user > 1:
                stats["multi_tour"] += 1

    return {"erreurs": erreurs, "stats": stats}

resultat = valider_jsonl("training_data.jsonl")
if resultat["erreurs"]:
    print(f"Erreurs : {len(resultat['erreurs'])}")
    for err in resultat["erreurs"][:10]:
        print(f"  - {err}")
else:
    print(f"Fichier valide — {resultat['stats']['total']} exemples")

Nettoyage du contenu

Supprimer les doublons

Les doublons exacts ou quasi-identiques biaisent l’entraînement en sur-représentant certains patterns :

import hashlib

def deduplication(exemples: list[dict]) -> list[dict]:
    """Supprime les doublons basés sur le contenu des messages."""
    vus = set()
    uniques = []

    for ex in exemples:
        contenu = "|".join(
            f"{m['role']}:{m['content']}" for m in ex["messages"]
        )
        h = hashlib.md5(contenu.encode()).hexdigest()

        if h not in vus:
            vus.add(h)
            uniques.append(ex)

    print(f"Doublons supprimés : {len(exemples) - len(uniques)}")
    return uniques

Normaliser le texte

  • Espaces : supprimez les espaces multiples et les retours à la ligne superflus
  • Encodage : normalisez en UTF-8, supprimez les caractères de contrôle
  • Ponctuation : assurez-vous que chaque réponse se termine correctement
  • Cohérence : si vous voulez du vouvoiement, vérifiez qu’aucun exemple ne tutoie
import re
import unicodedata

def normaliser_texte(texte: str) -> str:
    """Normalise un texte pour l'entraînement."""
    texte = unicodedata.normalize("NFC", texte)
    texte = re.sub(r"[\x00-\x09\x0b-\x0c\x0e-\x1f\x7f]", "", texte)
    texte = re.sub(r"[ \t]+", " ", texte)
    texte = re.sub(r"\n{3,}", "\n\n", texte)
    return texte.strip()

Vérification de la cohérence

Messages système identiques

Si vous utilisez un message système, il doit être identique dans tous vos exemples :

def verifier_system_messages(exemples: list[dict]):
    """Vérifie la cohérence des messages système."""
    system_msgs = set()
    for ex in exemples:
        for msg in ex["messages"]:
            if msg["role"] == "system":
                system_msgs.add(msg["content"].strip())

    if len(system_msgs) > 1:
        print(f"Attention : {len(system_msgs)} messages système différents")
        for sm in system_msgs:
            print(f"  - {sm[:80]}...")
    elif len(system_msgs) == 1:
        print("Message système cohérent")
    else:
        print("Aucun message système utilisé")

Longueur des réponses

Des réponses de longueurs très variables indiquent une incohérence :

def analyser_longueurs(exemples: list[dict]):
    """Analyse la distribution des longueurs de réponses."""
    longueurs = []
    for ex in exemples:
        for msg in ex["messages"]:
            if msg["role"] == "assistant":
                longueurs.append(len(msg["content"]))

    if longueurs:
        moy = sum(longueurs) / len(longueurs)
        mini, maxi = min(longueurs), max(longueurs)
        print(f"Longueur réponses — Moy: {moy:.0f} | Min: {mini} | Max: {maxi}")
        if maxi > moy * 5:
            print("Forte dispersion — vérifiez les outliers")

Utiliser l’API de validation OpenAI

Avant de lancer un fine-tuning, uploadez votre fichier et laissez OpenAI le valider :

from openai import OpenAI

client = OpenAI()

fichier = client.files.create(
    file=open("training_data.jsonl", "rb"),
    purpose="fine-tune"
)

print(f"Fichier uploadé : {fichier.id}")
print(f"Statut : {fichier.status}")

Si le fichier contient des erreurs, le statut passera à error avec un message détaillé. Corrigez les problèmes signalés avant de relancer.

Checklist avant fine-tuning

  • Fichier JSONL syntaxiquement valide
  • Aucun doublon
  • Texte normalisé (UTF-8, espaces, ponctuation)
  • Messages système cohérents
  • Au moins 50 exemples (idéalement 100+)
  • Split train/validation effectué
  • Distribution des longueurs raisonnable
  • Relecture humaine d’un échantillon aléatoire

Points clés à retenir

  • Validez toujours la syntaxe JSONL avant l’upload
  • Supprimez les doublons pour éviter le surapprentissage
  • La cohérence du message système est essentielle
  • Vérifiez la distribution des longueurs de réponses
  • Faites relire un échantillon par un humain — c’est irremplaçable