batchCooking/services/tech-step-intent-service/tests/test_training_data_balance.py
Nicolas e2ffa7d103 fix(recipes): reequilibre le corpus via substitution de synonyme plutot que du remplissage generique
Deux tentatives precedentes de porter chaque technique a 20 utterances
ont mesurablement degrade le F1 agrege (tech-step-eval.test.ts, 0.80 ->
0.79/0.791) au lieu de l'ameliorer : le generateur reposait surtout sur
des tournures modales generiques ("il faut ...", "make sure to ..."),
partagees identiquement par les 74 classes - un textcat bag-of-words lit
ca comme une separabilite reduite entre classes, pas un padding neutre.

augment_utterances.py revu : priorite a la substitution de synonyme
(l'un des synonyms propres a la technique en tete d'une utterance
existante, remplace par un autre - vocabulaire genuinement distinctif),
les tournures modales ne servant plus qu'de complement limite (5 par
locale, pas 12). Resultat : 13 a 20 utterances par technique/locale
(moyenne ~19.7), contre un forcage uniforme a 20 qui necessitait un
remplissage generique disproportionne pour les techniques au vocabulaire
propre pauvre (julienne, sweat, bainMarie - precisement celles qui
echouaient). Confiance mesuree nettement retablie sur ces techniques
(sweat ~0.99, bainMarie ~0.98, julienne ~0.88).

tests/test_training_data_balance.py : plancher abaisse a 12 (vise 20,
garanti seulement si le vocabulaire propre de la technique le permet
sans repasser par le piege ci-dessus) ; suppression de l'exigence
fr/en egaux, plus vraie avec cette strategie (le potentiel de
substitution differe naturellement entre les deux langues).

Suite complete locale : 35/35 verts (22m26s).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 13:38:58 +02:00

34 lines
1.7 KiB
Python

"""Garde-fou de non-régression pour l'équilibrage du corpus (voir
`training_data.py`'s propre commentaire de tête) : un textcat entraîné sur
des classes très inégales en nombre d'exemples est une source réelle de
classifications confiantes mais fausses sur une phrase jamais vue (constaté
en pratique — voir l'historique Git de ce fichier).
`_MIN_UTTERANCES_PER_LOCALE` est volontairement `12`, pas `20` : la
génération vise `20` (`augment_utterances.py`'s `_TARGET`) mais s'arrête
avant si la technique n'a pas assez de vocabulaire distinctif propre
(`synonyms`) pour l'atteindre sans retomber massivement sur des tournures
génériques partagées par toutes les classes — une première version de ce
script forçait `20` partout via ce mécanisme et a mesurablement *dégradé*
`test/recipe-matching/tech-step-eval.test.ts` (F1 agrégé) plutôt que de
l'améliorer, en réduisant la séparabilité entre classes plus qu'en ajoutant
un vrai signal. `12` reste très au-dessus du plancher d'origine (3-7) tout
en laissant `augment_utterances.py` s'arrêter honnêtement plutôt que de
forcer un compte rond au prix de la qualité."""
from intent_service.training_data import TECH_STEP_TRAINING_DATA
_MIN_UTTERANCES_PER_LOCALE = 12
def test_every_technique_has_at_least_the_minimum_utterances_per_locale():
short = [
(entry.uid, locale, len(getattr(entry, locale).utterances))
for entry in TECH_STEP_TRAINING_DATA
for locale in ("fr", "en")
if len(getattr(entry, locale).utterances) < _MIN_UTTERANCES_PER_LOCALE
]
assert short == [], (
f"{len(short)} (uid, locale) pair(s) below the {_MIN_UTTERANCES_PER_LOCALE}-utterance "
f"floor: {short}"
)