213 lines
10 KiB
Markdown
213 lines
10 KiB
Markdown
# 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.
|
|
|
|
```json
|
|
{
|
|
"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 `.spm` dé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
|
|
son `normalizer_spec` — c'est la première chose à comparer quand un découpage
|
|
diverge ;
|
|
- `cas` : `ids` vient de `EncodeAsIds(texte)`, `decode` de `DecodeIds(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.
|
|
|
|
```powershell
|
|
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 :
|
|
|
|
```powershell
|
|
# 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 :
|
|
|
|
1. le récupérer depuis le poste où le pack a été téléchargé (dossier de données
|
|
de l'application, sous `langues/`) ;
|
|
2. 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` :
|
|
|
|
1. dossier des fixtures : la variable d'environnement `OMIKA_SPM_FIXTURES` si
|
|
elle est définie et existe, sinon `tests/fixtures/sentencepiece` **résolu
|
|
depuis la racine du dépôt** (racine trouvée en remontant depuis l'assembly de
|
|
test jusqu'au dossier contenant `Omika.slnx` — aucun chemin absolu codé en
|
|
dur, aucun recours au dossier courant) ;
|
|
2. pour chaque `*.fixtures.json`, le `.spm` annoncé par `modele` est cherché dans
|
|
ce même dossier (nom simple, ou sous-dossier annoncé par la fixture) ;
|
|
3. si le `.spm` est **absent**, la fixture est ignorée — jamais un échec ;
|
|
4. 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 :
|
|
|
|
```powershell
|
|
$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** : `12` contre `2537` dit *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 avec `ProfilNormalisation` et `Avertissements` du 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èces `USER_DEFINED` sont
|
|
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é `Avertissements` du
|
|
tokeniseur le dit déjà quand c'est le cas.
|
|
En revanche `add_dummy_prefix`, `escape_whitespaces`,
|
|
`remove_extra_whitespaces` et 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
|
|
`Decode` du 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 `sentencepieceVersion` et 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 |
|