Choisir des cas représentatifs
- Leçon 4
- 45 min
- Module 2 : Créer un jeu de données d'évaluation
Leçon 4 sur 19 · Évaluer et tester les systèmes d'IAChoisir des cas représentatifs
1. Définir ce qui est « bon »
2. Créer un jeu de données d'évaluation
3. Choisir les métriques par composant
4. Utiliser des juges humains et des juges modèles
5. Comparer les systèmes de façon responsable
6. Automatiser l'évaluation et la rétroaction
Ce que vous saurez faire
Choisissez les cas de test pour que l’évaluation couvre le travail que l’assistant reçoit vraiment. Incluez les rares billets de soutien où une erreur coûte le plus cher. Comptez-les ensuite par groupe pour repérer les lacunes du jeu de données avant qu’un groupe absent cache un échec.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Ce que Grace attend de l’assistant et les critères que vous avez rédigés, dans Relier les objectifs aux preuves.
- L’exécution de référence, son identifiant et les échecs non négociables, dans Établir une référence de base.
Ce qu’il vous faut sur votre ordinateur
- Le projet
eval-harnessdans son état à la fin du module 1, avec son environnement virtuel actif.python -m pytest -qse termine par8 passed(8 tests réussis). - Si votre projet manque ou ne fonctionne plus, copiez l’instantané
C11/snapshots/end-of-m01du dépôt des laboratoires, comme dans Réinitialiser le projet. Recréez ensuite l’environnement virtuel.
Aucun compte ni aucune clé. Cette leçon lit seulement des fichiers enregistrés.
Testé avec Python 3.12.13 et pytest 9.1.1 sur macOS (Apple silicon). Sur Windows, les commandes Python sont les mêmes; elles n’y ont pas été testées.
Une cuillerée de soupe
Section intitulée « Une cuillerée de soupe »Une personne prépare une grande marmite de soupe et la goûte avant de la servir aux invités. Une cuillerée prise à la surface lui apprend peu de choses. Le sel peut rester au fond, là où la soupe peut aussi brûler. Elle remue donc la soupe, puis goûte au milieu, sur le côté et au fond. Chaque cuillerée est petite, mais ensemble, elles donnent une idée de toute la marmite.
Les données de test fonctionnent de la même façon. Chaque cas de test est une cuillerée. Le jeu de données renseigne sur l’assistant seulement si les cuillerées viennent de chaque partie de la marmite. La partie où la soupe brûle compte le plus.
Le problème : des données de test trop faciles
Section intitulée « Le problème : des données de test trop faciles »Dans Créer des applications fiables avec les API de LLM, Amira a testé l’assistant sur 69 billets. Priya compte ce qu’ils contiennent. Un seul des 69 billets est en français, alors que l’équipe de Camille à Montréal répond à des billets en français chaque jour. Seulement 9 billets exigent l’intervention d’une personne. Et les requêtes de ce cours approchaient déjà le score maximal. Le test ne pouvait montrer qu’une différence d’un à trois billets, ce qui ne prouvait rien.
Tomás répond vite : « Ajoutons d’autres billets comme ceux-là. » Mais d’autres cuillerées semblables donnent la même information. Voici une faiblesse que les 69 billets ne pouvaient pas montrer. La référence de base enregistrée au module 1 est moins bonne sur les questions auxquelles la politique ne répond pas. Par exemple : « Quelle est l’adresse de votre entrepôt? » Sur 27 questions de ce genre, elle prend la bonne décision seulement 19 fois. Elle les transmet souvent à une personne, même si aucune règle ne le demande. Un jeu de données sans ces questions ne montre jamais cette faiblesse.
L’idée : couvrir le travail, puis ajouter les cas dangereux
Section intitulée « L’idée : couvrir le travail, puis ajouter les cas dangereux »Le jeu de données d’évaluation, défini dans Relier les objectifs aux preuves, est une liste fixe de cas de test. Chaque cas contient la réponse qu’un bon système donne. Vous exécutez chaque version du système sur le même jeu de données et comparez les résultats. Ce jeu de données fournit les preuves de chaque affirmation sur l’assistant. Son contenu détermine donc ce que vous pouvez affirmer.
Un bon jeu de données d’évaluation remplit deux fonctions en même temps :
- Il ressemble au vrai travail. Il contient les types de billets que les clients envoient, dans leurs langues et leurs styles réels. Certains sont courts, d’autres longs, et certains contiennent des fautes de frappe ou deux demandes. Cette partie est représentative : comme les cuillerées, elle forme un échantillon fidèle de la marmite.
- Il couvre volontairement les cas rares et coûteux. Dans la réalité, un billet sur 100 peut concerner la sécurité. Un échantillon aléatoire de 200 billets ne contient alors qu’environ 2 billets de sécurité, trop peu pour mesurer quoi que ce soit. Vous en ajoutez donc plus que dans la réalité. Cette partie n’est pas représentative, et c’est voulu.
Pour vérifier ces deux fonctions, vous placez chaque cas dans un groupe, appelé sous-groupe, puis comptez les cas de chaque sous-groupe. Un sous-groupe est une partie du jeu de données que vous mesurez séparément, comme les « billets en français ». Le tableau des nombres de cas par sous-groupe est le tableau de couverture. Il montre immédiatement quels sous-groupes sont assez grands pour les mesures et lesquels manquent.
Un petit exemple : six billets, six sous-groupes
Section intitulée « Un petit exemple : six billets, six sous-groupes »Le jeu de données de Larkfield contient six sous-groupes. Chaque cas appartient à un seul sous-groupe : le premier qui lui correspond dans cet ordre. Le français vient en premier, puis « exige une personne », « la politique ne répond pas », « cas limite », « normal » et « difficile ». Voici six billets tirés du jeu de données, un par sous-groupe :
| Billet | Ce que le client écrit (début) | Sous-groupe | Pourquoi ce sous-groupe |
|---|---|---|---|
| T-81018 | « Je n’arrive plus à me connecter à mon compte… » | french (français) |
Il est en français. Le français vient en premier, donc la question du mot de passe ne détermine pas le sous-groupe. |
| T-81035 | « Veuillez réinitialiser le mot de passe du compte de mon mari… » | needs_person (exige une personne) |
Le compte d’un autre client : la règle H4 exige qu’une personne s’en occupe. |
| T-81058 | « Quelle est l’adresse de votre entrepôt?… » | unanswerable (sans réponse possible) |
La politique ne donne pas d’adresse. |
| T-65796 | « J’ai retourné ma poêle en fonte il y a quelques semaines, mais toujours aucun remboursement… » | boundary (cas limite) |
Il se situe à la limite définie par la politique entre l’équipe des retours et celle des paiements. |
| T-65781 | « Le bac potager surélevé LK-730158 s’est mis à fuir dans un coin… » | normal |
Une question claire sur la garantie, rédigée en anglais simple. |
| T-65114 | « Peut-on retourner des sécateurs dont l’emballage a été ouvert? » | difficult (difficile) |
Très court : le client ne donne presque aucun détail. |
Comptez-les : chaque sous-groupe contient 1 cas. Voilà le tableau de couverture de ces six billets. Avec un cas par sous-groupe, une erreur fait passer le score d’un sous-groupe de 100 % à 0 %. Six billets illustrent l’idée, mais c’est beaucoup trop peu pour mesurer quoi que ce soit.
Les 271 cas de Larkfield
Section intitulée « Les 271 cas de Larkfield »Priya a construit le jeu de données d’évaluation du cours en tenant compte des deux fonctions. Les billets sont inventés pour ce cours; aucun vrai client ne les a écrits. Ils proviennent de quatre sources. Lisez le schéma de gauche à droite.
Description textuelle du diagramme
Quatre sources alimentent le jeu de données d’évaluation. La première comprend 140 billets du cours sur l’apprentissage profond, choisis par équipe et style d’écriture. La deuxième comprend 60 billets écrits pour ce cours : 20 en français, 25 qui exigent une personne et 15 auxquels la politique ne répond pas. Ces 200 cas sont mélangés dans chaque sous-groupe, puis distribués à tour de rôle entre deux parties. La partie dev contient 102 cas, et la partie holdout en contient 98. La troisième source comprend les 69 billets du cours sur les applications de LLM, utilisés pour rédiger et comparer la requête v2. La quatrième comprend les 2 exemples écrits dans la requête v2. Ces 71 cas forment une troisième partie, contaminated, dessinée en pointillés parce que la requête les a déjà vus.
Les 140 billets réutilisés ont été choisis selon des quotas fixes : un nombre fixe de billets par équipe et style d’écriture. Les styles sont : simple, cas limite, deux sujets, négation, fautes de frappe, détail distrayant, demande à la fin, longue histoire et très peu de mots. Un quota empêche qu’un style remplisse le jeu de données par hasard. Les 60 nouveaux billets comblent les lacunes des anciens. Ils ajoutent du français, les règles H1 à H5 et des questions auxquelles la politique ne répond pas.
Les trois parties ont trois fonctions. Vous utilisez les données dev (développement) pendant que vous modifiez une requête. Les données holdout (réservées) restent figées pour décider de la mise en production. Les données contaminated (contaminées) contiennent les billets utilisés pour construire la requête de la référence de base. Éviter la contamination explique ces trois parties. Cette leçon examine les 200 cas de dev et holdout. Le graphique les compare aux 69 anciens billets et aux 2 exemples de la requête.
Description textuelle du graphique
Trois panneaux comparent l’ancien jeu de données aux nouveaux cas de dev et holdout. L’ancien jeu de données contient 71 cas : les 69 billets du cours sur les applications de LLM et les 2 exemples de la requête. Billets en français : 1 sur 71 (1.4 %) contre 20 sur 200 (10 %). Billets qui exigent une personne : 10 (14.1 %) contre 47 (23.5 %). Cas critiques, où une mauvaise réponse bloque une mise en production : 8 (11.3 %) contre 37 (18.5 %).
La proportion de billets en français est environ sept fois celle de l’ancien jeu de données. La proportion de cas critiques est aussi plus élevée. Un cas critique est un cas où une seule mauvaise réponse bloque une mise en production. Par exemple : un billet de sécurité, les données d’un autre client ou des instructions cachées dans un billet. Les règles de mise en production d’Établir une référence de base nomment ces échecs. Le jeu de données contient maintenant assez de cas pour les repérer.
Compter les cas : le tableau de couverture
Section intitulée « Compter les cas : le tableau de couverture »Le projet compte déjà les cas par partition (python -m harness cases). Vous ajoutez maintenant un décompte par sous-groupe et par partition : le tableau de couverture. Il s’agit d’une petite fonction dans harness/dataset.py et d’une nouvelle commande.
-
Ouvrez
harness/dataset.pydans VS Code. En haut, trouvez les lignes d’importation. Ajoutez la lignefrom collections import Counteraprèsimport json. Les importations commencent alors ainsi :harness/dataset.py import hashlibimport jsonfrom collections import Counterfrom pathlib import Pathfrom typing import Literal -
Trouvez la fonction
by_id. En dessous, laissez deux lignes vides et ajoutez cette fonction :harness/dataset.py def coverage(cases: list[Case]) -> dict[str, dict[str, int]]:"""How many cases each slice has, per split: the table that shows what the set is missing."""table: dict[str, dict[str, int]] = {}for c in cases:table.setdefault(c.slice, Counter())[c.split] += 1return {s: dict(table[s]) for s in sorted(table)}Un
Counterest un dictionnaire qui commence chaque décompte à 0.setdefaultcrée un compteur pour un sous-groupe la première fois qu’il apparaît. Le résultat associe chaque sous-groupe à son nombre de cas par partition. -
Ouvrez
harness/__main__.py. Trouvezdef cmd_runs(a). Au-dessus, ajoutez la fonction de la nouvelle commande et deux lignes vides :harness/__main__.py def cmd_coverage(a) -> None:table = dataset.coverage(load_cases(split="all"))print(f"{'slice':14}{'dev':>6}{'holdout':>9}{'contaminated':>14}")for name, row in table.items():print(f"{name:14}{row.get('dev', 0):>6}{row.get('holdout', 0):>9}{row.get('contaminated', 0):>14}") -
Dans le même fichier, trouvez la ligne
sub.add_parser("runs", help="the saved runs")dansmain(). Au-dessus, ajoutez cette ligne et une ligne vide :harness/__main__.py sub.add_parser("coverage", help="cases per slice and split").set_defaults(fn=cmd_coverage) -
Enregistrez les deux fichiers. Dans le terminal de VS Code, exécutez la nouvelle commande :
Fenêtre de terminal python -m harness coverageLa sortie :
slice dev holdout contaminatedboundary 14 14 0contaminated 0 0 71difficult 30 29 0french 10 10 0needs_person 22 21 0normal 12 11 0unanswerable 14 13 0 -
Exécutez les tests de nouveau :
Fenêtre de terminal python -m pytest -qLa dernière ligne doit encore indiquer
8 passed.
Le tableau montre l’effet du mélange. Dans chaque sous-groupe, dev et holdout contiennent le même nombre de cas, ou un cas de plus d’un côté. Les billets contaminés forment leur propre sous-groupe, parce que leur fonction est différente. Le graphique montre les mêmes nombres pour dev et holdout réunis.
Description textuelle du graphique
Un point par sous-groupe, pour les 200 cas de dev et holdout : normal 23, cas limite 28, difficile 59, français 20, exige une personne 43, la politique ne répond pas 27. Les deux derniers sous-groupes ont une deuxième couleur : ils ont été agrandis volontairement.
Ce qui manque au jeu de données
Section intitulée « Ce qui manque au jeu de données »Un tableau de couverture montre ce que contient le jeu de données. Priya note aussi ce qu’il ne contient pas. Un sous-groupe absent n’a aucun score, plutôt qu’un mauvais score. Voici sa liste pour Larkfield :
- La répartition réelle est inconnue. Personne n’a compté combien de billets de Larkfield sont en français ou exigent une personne. Le score global du jeu de données n’est donc pas celui que les clients verront. Interprétez-le comme « sur des billets comme ceux-ci », et examinez chaque sous-groupe séparément.
- L’écriture réelle. Des modèles de langage et l’auteur du cours ont rédigé les billets. Ils sont plus propres que de vrais billets : peu de fautes de frappe, une demande chacun et un style produit par les modèles. De vrais billets seraient plus difficiles.
- Les conversations. Chaque cas est un premier message. Le jeu de données ne contient pas de client qui écrit de nouveau, en colère, après une mauvaise réponse.
- Les autres langues et les pièces jointes. Dans dev et holdout, un billet en espagnol, un en allemand et deux contenant seulement une pièce jointe testent la règle H5. Cela vérifie que la règle fonctionne au moins une fois. Cela ne mesure pas à quel point elle fonctionne bien.
- Les petits sous-groupes. Le score d’un sous-groupe de 10 billets en français varie de 10 points quand un billet change. Le module 5 ajoute un intervalle au résultat de chaque sous-groupe pour montrer l’effet d’un seul billet.
Cette liste va dans la fiche du jeu de données, que vous rédigerez à la fin de ce module. Les modèles changent vite, tout comme les billets des clients. Les nombres et les scores de ce cours ne resteront donc pas valides longtemps. La méthode reste utile : comptez par sous-groupe et notez les lacunes.
Quand l’utiliser et ce qu’elle coûte
Section intitulée « Quand l’utiliser et ce qu’elle coûte »Créez un tableau de couverture chaque fois que vous construisez ou modifiez un jeu de données d’évaluation. Faites-le aussi avant de vous fier à un score global. Cela coûte quelques lignes de code et quelques minutes. Le vrai travail consiste à choisir les sous-groupes : ils doivent correspondre aux décisions importantes. Grace se soucie des clients francophones et des billets qui exigent une personne, donc ce sont des sous-groupes. Un sous-groupe sur lequel personne n’agit ajoute seulement du bruit.
Ajouter volontairement des cas rares a aussi un coût. Le jeu de données n’a plus la répartition réelle, donc sa moyenne globale n’est pas la moyenne réelle. Présentez les sous-groupes, pas seulement le total. Chaque sous-groupe doit aussi contenir assez de cas : environ 20 à 30 pour un score approximatif, beaucoup plus pour un score précis. Ce nombre augmente vite, ce qui explique pourquoi un jeu de données de 200 cas contient seulement six sous-groupes.
Exercice guidé : lire la couverture
Section intitulée « Exercice guidé : lire la couverture »-
Exécutez de nouveau
python -m harness coverage. Trouvez le sous-groupe qui contient le moins de cas dans dev. -
Examinez un cas de ce sous-groupe :
Fenêtre de terminal python -m harness show T-81019La sortie :
T-81019 | split dev | slice french | source courseText: Je reçois trop de courriels promotionnels. Merci de me retirer de votre liste.Expected: team account | needs_human false | next step noneReply must: reply_in_french | must not: claims_action, asks_secretLabelled by: course author, 2026-10-09; Grace's policy (teams, H1-H5) applied by reading; written with the case by the course author -
Exécutez
python -m harness cases. Comparez le nombre de billets en français dans la partition contaminated à celui de dev. -
Écrivez deux phrases pour Grace : quel sous-groupe agrandiriez-vous en premier, et pourquoi?
Afficher un exemple de réponse
Le plus petit sous-groupe de dev est french, avec 10 cas. La partition contaminated contient seulement 1 billet en français. Les anciennes données de test ne disaient donc presque rien sur les réponses en français.
« J’agrandirais le sous-groupe français en premier. L’équipe de Camille répond à des billets en français chaque jour. Avec 10 cas dans dev, une mauvaise réponse fait varier le score français de 10 points. »
À vous de jouer : compter par règle
Section intitulée « À vous de jouer : compter par règle »Le sous-groupe needs_person combine cinq règles : H1 sécurité, H2 juridique, H3 hors politique, H4 accès et H5 demande illisible. Un billet de sécurité et une menace juridique échouent de façons différentes. Écrivez une courte commande Python qui compte les cas de développement et les cas réservés par règle, de H1 à H5. Quelle règle a le moins de cas? Est-ce suffisant pour la mesurer?
Afficher une solution
python -c "from collections import Counter; from harness.dataset import load_cases; print(sorted(Counter(c.expected.rule for c in load_cases(split='all') if c.split != 'contaminated' and c.expected.rule).items()))"La sortie :
[('H1', 23), ('H2', 5), ('H3', 6), ('H4', 9), ('H5', 4)]H5 (demande illisible) compte seulement 4 cas, et H2 (juridique) en compte 5. Quatre cas peuvent montrer que la règle fonctionne, mais pas à quelle fréquence elle échoue. Une seule erreur fait varier le score de 25 points. Si les menaces juridiques comptent pour Grace, le jeu de données doit en contenir davantage.
Erreur courante
invalid choice : 'coverage' (choix invalide : « coverage »)
Symptôme : la nouvelle commande s’arrête immédiatement :
usage: python -m harness [-h] {cases,show,runs,score} ...python -m harness: error: argument command: invalid choice: 'coverage' (choose from cases, show, runs, score)Cause : la ligne de commande ne connaît pas encore cette commande. Soit harness/__main__.py n’est pas enregistré, soit la ligne sub.add_parser("coverage", …) de l’étape 4 manque. La liste dans le message montre les commandes que le programme connaît actuellement.
Solution :
- Dans VS Code, vérifiez l’onglet de
__main__.py. Un point sur l’onglet indique que le fichier n’est pas enregistré. Enregistrez-le. - Vérifiez que la nouvelle ligne se trouve dans
main(), avec quatre espaces avantsub.add_parser. - Exécutez de nouveau
python -m harness coverage.
Si le message est plutôt NameError: name 'Counter' is not defined (le nom « Counter » n’est pas défini), l’importation de l’étape 1 manque dans harness/dataset.py.
Vérifiez votre compréhension
Tomás veut agrandir le jeu de données en copiant deux fois de plus les 69 billets du cours Créer des applications fiables avec les API de LLM, pour obtenir 207 billets. Pourquoi cela n'aide-t-il pas, et qu'ajouteriez-vous à la place?
Afficher la réponse
Les copies vous donnent encore la même information, comme davantage de cuillerées prises à la surface de la marmite. Elles n’ajoutent aucun nouveau type de billet : toujours seulement 1 billet en français sur 69 et peu de billets nécessitant une personne. Elles donnent aussi l’impression que chaque résultat est trois fois plus certain qu’il ne l’est. Ajoutez plutôt les types de billets manquants : billets en français, règles H1 à H5 et questions sans réponse dans la politique. Ajoutez assez de cas de chaque type pour le mesurer comme sous-groupe.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Le jeu de données contient beaucoup plus de billets de sécurité que le trafic réel. Son score global est-il celui que les clients verront? Que devriez-vous présenter?
Afficher la réponse
Non. Les cas rares et coûteux ont été ajoutés volontairement, dans une proportion supérieure à leur proportion réelle. La moyenne globale ne reflète donc pas la répartition réelle. Si l’assistant traite mal les billets de sécurité, la moyenne du jeu est inférieure à la moyenne réelle; sinon, elle est supérieure. Présentez le score de chaque sous-groupe avec son nombre de cas. Précisez que le score global porte sur « des billets comme ceux-ci ».
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Vérification pratique : exécutez python -m harness coverage. Combien de cas sans réponse y a-t-il dans les données de développement et les données réservées? Pourquoi la partition contaminée contient-elle 0 cas dans ce sous-groupe?
Afficher la réponse
14 dans les données de développement et 13 dans les données réservées. La partition contaminée contient 0 cas dans le sous-groupe unanswerable (sans réponse), car ses 71 cas appartiennent au sous-groupe contaminated (contaminé). Leur rôle est différent : ils ne servent pas à noter l’assistant, mais seulement à mesurer un écart dans la dernière leçon de ce module.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Un jeu de données d’évaluation est une liste fixe de cas accompagnés des réponses qu’un bon système fournit. Il doit ressembler au travail réel et couvrir volontairement les cas rares et coûteux.
- Un sous-groupe est un groupe de cas que vous mesurez séparément. Le tableau de couverture compte les cas par sous-groupe et par partition. Il montre ce qui est trop petit ou absent.
- Le jeu de données de Larkfield contient 271 cas : 102 de développement, 98 réservés et 71 contaminés. Les billets sont fictifs et créés pour ce cours.
- Les nouveaux cas de développement et réservés comprennent beaucoup plus de billets en français, de billets nécessitant une personne et de cas critiques que les 69 anciens billets.
- Notez ce que le jeu de données ne contient pas : répartition réelle, textes réels, conversations et autres langues. Un sous-groupe absent n’a pas de score, plutôt qu’un mauvais score.
- Votre projet dispose maintenant de
python -m harness coverage, et les tests réussissent toujours :8 passed(8 tests réussis).
Ensuite, vous examinerez ce qu’un cas contient en plus de son texte : les étiquettes, leurs auteurs et la version du jeu complet.
Leçon suivante : Étiqueter et versionner