10 KiB
Fixtures SentencePiece — découpage de référence
Ce dossier contient les fixtures de référence du tokeniseur SentencePiece
maison (src/Omika.App.Local/Services/SentencePieceTokenizer.cs).
À quoi ça sert
Le tokeniseur est réécrit à la main : lecture protobuf minimale, Viterbi UNIGRAM, BPE, repli octet, normalisation. Son risque n'est pas de lever une exception — c'est de découper légèrement autrement que le modèle, et donc de dégrader les traductions sans aucun signal : pas de plantage, pas de message, juste des phrases un peu plus mauvaises.
Une fixture transforme ce risque en mesure : pour une série de textes, elle fige
ce que produit la bibliothèque sentencepiece de référence.
{
"modele": "vocab.spm",
"sentencepieceVersion": "0.2.0",
"vocabSize": 32000,
"addDummyPrefix": true,
"removeExtraWhitespaces": true,
"escapeWhitespaces": true,
"normalizerName": "nmt_nfkc",
"cas": [
{ "texte": "Bonjour le monde.", "ids": [1, 2, 3], "decode": "Bonjour le monde." }
]
}
modele: nom du fichier.spmdécrit (modèle cherché dans ce dossier) ;sentencepieceVersion: version de la référence — elle date la comparaison, pour qu'une divergence constatée dans six mois ne se confonde pas avec un changement de version de la bibliothèque ;vocabSize,addDummyPrefix,removeExtraWhitespaces,escapeWhitespaces,normalizerName: les réglages du modèle qui décident du découpage, lus dans sonnormalizer_spec— c'est la première chose à comparer quand un découpage diverge ;cas:idsvient deEncodeAsIds(texte),decodedeDecodeIds(ids)appliqué à ces identifiants. La fixture est donc auto-cohérente : le test compare le tokeniseur C# à la référence, jamais à une transcription manuelle qui figerait une erreur de recopie ;- un fichier par modèle, nommé
<nomDuModeleSansExtension>.fixtures.json, UTF-8 sans BOM, indenté.
Les textes couverts sont construits par le script et comprennent, au minimum :
français accentué, espagnol accentué avec ¿¡, espaces de tête, de queue et
doubles, chaîne vide, chaîne d'espaces seuls, chiffres et ponctuation, phrase
très longue, casse mêlée, et une écriture non latine (arabe et cyrillique).
Générer les fixtures
Prérequis : Python 3 et la bibliothèque de référence.
pip install sentencepiece
.\scripts\Generer-Fixtures-SentencePiece.ps1 -DossierModeles C:\Chemin\Vers\Les\Modeles
Le générateur cherche récursivement tous les *.spm sous -DossierModeles
et écrit un fixture par modèle dans tests\fixtures\sentencepiece (ou dans le
dossier passé à -Sortie). Relancer le script est sans danger : il réécrit ses
propres fixtures et ne supprime rien d'autre.
Options utiles :
# Copie chaque .spm à côté de son fixture : le test cherche le modèle ici.
.\scripts\Generer-Fixtures-SentencePiece.ps1 -DossierModeles C:\Modeles -CopierModeles
# Autre dossier de sortie (utile pour un pack non versionné).
.\scripts\Generer-Fixtures-SentencePiece.ps1 -DossierModeles C:\Modeles -Sortie C:\Temp\fixtures
Où trouver un .spm
Le vocabulaire est livré dans chaque pack de langue, un fichier par sens de traduction :
langues/{code}/{sens}/vocab.spm # {code} = es, pt, ar… {sens} = versFrancais, depuisFrancais
Ce chemin est celui du stockage local de l'application (voir
LanguagePaths dans src/Omika.App.Local/Infrastructure). Deux façons de se
procurer le fichier :
- le récupérer depuis le poste où le pack a été téléchargé (dossier de données
de l'application, sous
langues/) ; - le demander à l'API des langues, qui le sert avec le reste du pack.
Un même dossier langues/{code}/{sens} peut donc servir de -DossierModeles.
Les .spm ne sont pas versionnés
Les fichiers .spm (comme les encoder.onnx) restent hors du dépôt :
- ils pèsent plusieurs centaines de kilo-octets à plusieurs méga-octets chacun, multipliés par le nombre de langues et de sens ;
- ce sont des artefacts de modèle, produits par un entraînement et soumis à leurs propres conditions de licence : leur place est dans un pack de langue distribué, pas dans l'historique Git.
Le générateur ne versionne que les fixtures (quelques kilo-octets de texte), qui décrivent le comportement attendu sans embarquer le modèle.
Comment le test trouve tout ça
tests/Omika.Tests/Mobile/SentencePieceFixturesTests.cs :
- dossier des fixtures : la variable d'environnement
OMIKA_SPM_FIXTURESsi elle est définie et existe, sinontests/fixtures/sentencepiecerésolu depuis la racine du dépôt (racine trouvée en remontant depuis l'assembly de test jusqu'au dossier contenantOmika.slnx— aucun chemin absolu codé en dur, aucun recours au dossier courant) ; - pour chaque
*.fixtures.json, le.spmannoncé parmodeleest cherché dans ce même dossier (nom simple, ou sous-dossier annoncé par la fixture) ; - si le
.spmest absent, la fixture est ignorée — jamais un échec ; - si le dossier est absent ou vide, le test passe en expliquant pourquoi sur la sortie standard.
C'est délibéré : une suite rouge sur une machine fraîchement clonée, pour de simples fichiers absents, pousserait à désactiver le test — exactement l'inverse du but. Ces tests ne font échouer que sur une divergence réellement mesurée.
Pour vérifier un pack sans rien copier dans le dépôt :
$env:OMIKA_SPM_FIXTURES = 'C:\Chemin\Vers\fixtures-et-spm'
dotnet test tests\Omika.Tests\Omika.Tests.csproj --filter FullyQualifiedName~SentencePieceFixturesTests
Deux autres tests de ce fichier s'exécutent même sans fixture : l'idempotence de
l'aller-retour (Decode(Encode(x)) rejoué donne le même texte — jamais
Decode(Encode(x)) == x, qui dépend du vocabulaire), et le fait qu'un fichier
inexploitable lève une SentencePieceException en français plutôt qu'une
erreur système.
Lire un échec
Le message d'échec est conçu pour être exploitable seul :
Découpage divergent pour le cas « Bonjour le monde. » (fixture vocab.fixtures.json).
Premier écart à l'index 1 : référence 2537 « le », tokeniseur 12 « ▁le ».
Identifiants de référence (4) : 1, 2537, 232, 4
Identifiants obtenus (4) : 1, 12, 232, 4
Texte reconstruit par la référence : « Bonjour le monde. ».
Ce qu'il faut y lire :
- l'index du premier écart : les identifiants qui précèdent sont corrects, la divergence commence là. Un écart à l'index 0 est un problème de préfixe factice ou de normalisation ; un écart en milieu de phrase est un problème de segmentation (Viterbi, scores, longueur maximale de pièce) ;
- les deux identifiants et leur pièce :
12contre2537dit quelle pièce le tokeniseur a choisie à la place de celle du modèle. Une pièce très courte (▁, un caractère) à la place d'une pièce longue signale une table de pièces incomplète ou un score mal lu ; l'inverse signale un score trop favorable ; - les deux listes : des tailles différentes signalent une couverture
incomplète (caractère devenu
<unk>au lieu d'une pièce) ; decode: « Décodage divergent » n'a rien à voir avec le découpage — le décodage est testé sur les identifiants de la référence, donc l'erreur est dans la reconstruction (marqueur ▁, rognage des bords, reconstitution des octets) ;- les réglages en tête de fixture (
addDummyPrefix,normalizerName…) sont à comparer avecProfilNormalisationetAvertissementsdu tokeniseur chargé.
Vérifier aussi que la fixture et le modèle vont bien ensemble : le test échoue
explicitement si la taille de vocabulaire de la fixture ne correspond pas à celle
du .spm (mauvais appariement de fichiers, pas divergence de segmentation).
Que faire d'une divergence
La signaler. Ne jamais l'absorber.
- Ne pas desserrer la valeur attendue, ne pas régénérer la fixture « jusqu'à ce que ça passe », ne pas supprimer le cas gênant : une fixture réécrite pour coller au tokeniseur ne mesure plus rien, et la divergence repartira en production sous forme de traductions dégradées.
- Ouvrir une anomalie avec : le nom de la fixture, le texte du cas, l'index du
premier écart, les deux identifiants et les deux pièces, les deux listes, la
version
sentencepieceVersion, et la taille du vocabulaire. - Regarder d'abord les limites assumées documentées dans l'en-tête de
SentencePieceTokenizer.cs: la table de normalisation compilée (precompiled_charsmap) n'est pas appliquée, et les piècesUSER_DEFINEDsont traitées comme des pièces ordinaires. Une divergence sur un texte en pleine largeur (123), une ligature (fi), un exposant ou une forme de compatibilité vient très souvent de là — la propriétéAvertissementsdu tokeniseur le dit déjà quand c'est le cas. En revancheadd_dummy_prefix,escape_whitespaces,remove_extra_whitespaceset le repli octet sont lus dans le modèle et honorés : une divergence sur ces points n'est plus une limite connue, c'est un défaut à corriger. - Vérifier en priorité les cas « espaces seuls » et espaces de bord : le
Decodedu tokeniseur ne retire que le préfixe factice qu'il a lui-même ajouté, alors que la référence applique sa propre règle de rognage. C'est le premier écart susceptible d'apparaître sur un run réel. - Corriger le tokeniseur (avec un test qui reproduit le cas), jamais la
fixture. Si la référence elle-même change de comportement d'une version à
l'autre, mettre à jour
sentencepieceVersionet justifier l'écart dans le message de commit.
Fichiers de ce dossier
| Fichier | Contenu |
|---|---|
LISEZMOI.md |
ce document |
<modele>.fixtures.json |
fixtures de référence, régénérables, versionnées |
*.spm |
non versionnés — modèles, à copier sur le poste de test |