Comment comparer deux versions d’IA équitablement
Un test A/B donne la version A à un groupe d’utilisateurs et la version B à un autre, puis compare la fréquence de réussite. Un test fantôme lance la nouvelle version sur de vraies requêtes, mais personne ne voit ses réponses.
Les deux peuvent tromper. Un écart de neuf points peut n’être que du hasard.
À la fin de cette leçon
Remarque
Je peux comparer deux versions sans me laisser tromper par le hasard.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Test A/B | deux groupes d’utilisateurs, une version chacun |
| Test fantôme | une nouvelle version lancée là où personne ne la voit |
| Taux de réussite | la part des requêtes qui ont marché |
| Écart | un taux de réussite moins l’autre, en points |
| Fluctuation | de combien un écart bouge par hasard |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un écart doit dépasser la fluctuation
La version B réussit 138 fois sur 200 et A réussit 120 fois sur 200. L’écart est de 9 points. Mais deux groupes de 200 utilisateurs diffèrent aussi par hasard.
La fluctuation mesure de combien un écart bouge par hasard. Une règle simple : ne faites confiance à un écart que s’il vaut au moins deux fois la fluctuation.
Exemple détaillé : deux tests
| Test | Utilisateurs par groupe | Écart | Fluctuation | Deux fois la fluctuation | Verdict |
|---|---|---|---|---|---|
| A contre B | 200 | 9,0 | 4,8 | 9,6 | Non prouvé |
| A contre C | 1 000 | 5,0 | 2,2 | 4,3 | Fiable |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une erreur courante
Attention
Beaucoup pensent que le meilleur score gagne. Un meilleur score sur un petit test peut venir du hasard. Ne nommez un gagnant que si l’écart vaut au moins deux fois la fluctuation. Sinon, dites non prouvé et récoltez plus de données.
Un test fantôme trouve les nouveaux échecs
Lancez la nouvelle version sur les mêmes vraies requêtes et gardez ses réponses cachées. Pour chaque requête, vous avez une paire : le résultat ancien et le résultat nouveau.
Des totaux égaux peuvent cacher un changement. Deux cas peuvent devenir pires pendant que deux autres deviennent meilleurs. Comptez les cas pires et lisez chacun.
Exemple détaillé : douze paires
| Paire (ancien, nouveau) | Sens | Nombre |
|---|---|---|
| réussi, réussi | Identique | 7 |
| échoué, échoué | Identique | 1 |
| échoué, réussi | Nouvelle meilleure | 2 |
| réussi, échoué | Nouvelle pire, à lire | 2 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Écrire l’assistant du modèle, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// Les jetons d’une réponse, ou null si elle n’en donne pas.' 'function compter(donnees, nom) {' ' return donnees.usage && typeof donnees.usage[nom] === \"number\" ? donnees.usage[nom] : null;' '}' > model.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire l’assistant du modèle, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function demander(messages, echantillon, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"statut \" + res.status);' ' const d = await res.json();' ' return { texte: d.choices[0].message.content, entree: compter(d, \"prompt_tokens\"), sortie: compter(d, \"completion_tokens\"), enregistre: false };' ' } catch (erreur) {' ' console.log(\"Échantillon enregistré : aucun modèle n’a répondu (\" + erreur.message + \").\");' ' return { texte: echantillon, entree: null, sortie: null, enregistre: true };' ' }' '}' 'module.exports = { demander };' >> model.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script de comparaison, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'function fluctuation(a, an, b, bn) {' ' const p = (a + b) / (an + bn);' ' return Math.sqrt(p * (1 - p) * (1 / an + 1 / bn)) * 100;' '}' 'function ecart(a, an, b, bn) { return (b / bn - a / an) * 100; }' 'function verdict(a, an, b, bn) {' ' const sur = Math.abs(ecart(a, an, b, bn)) >= 2 * fluctuation(a, an, b, bn);' ' return sur ? \"fiable\" : \"non prouvé\";' '}' 'function virgule(x) { return x.toFixed(1).replace(\".\", \",\"); }' > ab.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script de comparaison, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const tests = [' ' { nom: \"A contre B\", a: 120, an: 200, b: 138, bn: 200 },' ' { nom: \"A contre C\", a: 600, an: 1000, b: 650, bn: 1000 },' '];' 'const paires = \"rr rr re rr er rr ee rr re rr rr er\".split(\" \");' 'function compter(sorte) { return paires.filter(function (p) { return p === sorte; }).length; }' >> ab.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script de comparaison, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' tests.forEach(function (t) {' ' console.log(t.nom + \" : écart \" + virgule(ecart(t.a, t.an, t.b, t.bn)) + \" points, fluctuation \" + virgule(fluctuation(t.a, t.an, t.b, t.bn)) + \" -> \" + verdict(t.a, t.an, t.b, t.bn));' ' });' ' console.log(\"fantôme : identiques \" + (compter(\"rr\") + compter(\"ee\")) + \", nouvelle meilleure \" + compter(\"er\") + \", nouvelle pire \" + compter(\"re\"));' ' const q = \"En une phrase courte, pourquoi un écart de 9 points peut-il n’être que du hasard ?\";' ' const echantillon = \"Avec 200 utilisateurs seulement par groupe, le hasard peut à lui seul déplacer un score de cette taille.\";' ' const reponse = await demander([{ role: \"user\", content: q }], echantillon, 60);' ' console.log(\"le modèle dit : \" + reponse.texte);' '}' 'main();' >> ab.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Lancer la comparaison
docker exec -w /home/node/project agent node ab.jsAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Ce que le Run affiche
| Ligne | Ce qu’elle montre |
|---|---|
| A contre B : écart 9,0 points, fluctuation 4,8 -> non prouvé | Un grand écart, un petit test |
| A contre C : écart 5,0 points, fluctuation 2,2 -> fiable | Un écart plus petit, un test plus grand |
| fantôme : identiques 8, nouvelle meilleure 2, nouvelle pire 2 | Compté sur douze paires enregistrées |
| le modèle dit : ... | Une phrase du labo. Un échantillon enregistré si aucun modèle ne répond |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.