Premiere commit - Omika
This commit is contained in:
+212
@@ -0,0 +1,212 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user