Token Classification
Transformers
Safetensors
Malagasy
xlm-roberta
punctuation-restoration
truecasing
malagasy
asr-postprocessing
Instructions to use Flo976/malagasy-punctuation with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Flo976/malagasy-punctuation with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="Flo976/malagasy-punctuation")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForTokenClassification tokenizer = AutoTokenizer.from_pretrained("Flo976/malagasy-punctuation") model = AutoModelForTokenClassification.from_pretrained("Flo976/malagasy-punctuation", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Premiere publication
Browse files- .gitattributes +1 -0
- README.md +90 -0
- common.py +170 -0
- config.json +87 -0
- labels.json +22 -0
- model.safetensors +3 -0
- punctuate.py +197 -0
- sentencepiece.bpe.model +3 -0
- special_tokens_map.json +51 -0
- tables.json +0 -0
- tokenizer.json +3 -0
- tokenizer_config.json +55 -0
- valid_metrics.json +50 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
language:
|
| 3 |
+
- mg
|
| 4 |
+
license: cc-by-sa-4.0
|
| 5 |
+
library_name: transformers
|
| 6 |
+
pipeline_tag: token-classification
|
| 7 |
+
base_model: Davlan/xlm-roberta-base-finetuned-malagasy
|
| 8 |
+
datasets:
|
| 9 |
+
- badrex/malagasy-speech-full
|
| 10 |
+
tags:
|
| 11 |
+
- punctuation-restoration
|
| 12 |
+
- truecasing
|
| 13 |
+
- malagasy
|
| 14 |
+
- asr-postprocessing
|
| 15 |
+
- xlm-roberta
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
<p align="center"><a href="#results"><img src="https://img.shields.io/badge/formatted%20WER%20on%20ASR%20output-18.5%20%25%20vs%2021.7%20%25-2F7D55?style=flat-square&labelColor=1E2A5E" alt="formatted WER on ASR output: 18.5 % against 21.7 %"></a> <a href="#results"><img src="https://img.shields.io/badge/period%20F1-79-2F7D55?style=flat-square&labelColor=1E2A5E" alt="period F1: 79"></a> <a href="#what-it-does"><img src="https://img.shields.io/badge/words-never%20changed-E9C46A?style=flat-square&labelColor=1E2A5E" alt="words: never changed"></a> <a href="#license"><img src="https://img.shields.io/badge/license-CC--BY--SA--4.0-8A93A6?style=flat-square&labelColor=1E2A5E" alt="license: CC-BY-SA-4.0"></a> <a href="https://huggingface.co/spaces/Flo976/malagasy-asr-demo"><img src="https://img.shields.io/badge/demo-try%20it%20in%20the%20browser-E9C46A?style=flat-square&labelColor=1E2A5E" alt="demo: try it in the browser"></a></p>
|
| 19 |
+
|
| 20 |
+
# Malagasy casing and punctuation restoration for speech recognition
|
| 21 |
+
|
| 22 |
+
Speech recognition models such as [Flo976/w2v-bert-malagasy](https://huggingface.co/Flo976/w2v-bert-malagasy) output lowercase text without punctuation. This model puts back **capital letters, commas, periods, question and exclamation marks and colons, without ever changing a word**, and restores accented letters that ASR normalization strips (`tan na` → `tanàna`).
|
| 23 |
+
|
| 24 |
+
```
|
| 25 |
+
in: vehivavy fohy volo iray ary miloko mainty manamboatra volo manoloana ny fitaratra
|
| 26 |
+
out: Vehivavy fohy volo iray ary miloko mainty, manamboatra volo manoloana ny fitaratra.
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
**Résumé en français.** Remet les majuscules et la ponctuation dans la sortie d'un modèle de reconnaissance vocale malgache, sans jamais changer les mots. Sur la vraie sortie de l'ASR, le texte final mis en forme compte 18,5 % d'erreurs contre 21,7 % pour une mise en forme minimale (majuscule initiale et point final). Les points sont bien placés (F1 79), les virgules une fois sur deux environ (F1 52).
|
| 30 |
+
|
| 31 |
+
## What it does
|
| 32 |
+
|
| 33 |
+
For every word it predicts a case (lower, Capitalized, UPPER, mixed) and the mark that follows it: none, `,` `.` `?` `!` `:` or `;`. Since it classifies words instead of generating text, it cannot invent, drop or rewrite words: every call asserts that `normalize(output) == normalize(input)`, where `normalize` is the lowercase, punctuation-free form used to score the ASR. This assertion held on every test output and in a fuzz test.
|
| 34 |
+
|
| 35 |
+
## Results
|
| 36 |
+
|
| 37 |
+
Test split of [badrex/malagasy-speech-full](https://huggingface.co/datasets/badrex/malagasy-speech-full), 3,101 clips, speakers never seen in training. *Formatted WER* compares the final text to the original transcript **with** its casing and punctuation, without any normalization: it measures what a reader actually sees.
|
| 38 |
+
|
| 39 |
+
| Input | System | Formatted WER | F1 macro `, . ? ! :` | F1 comma | F1 period | Case accuracy |
|
| 40 |
+
| --- | --- | --- | --- | --- | --- | --- |
|
| 41 |
+
| real ASR output (10.6 % WER) | capitalize first word + final period | 21.73 % | 13.1 | 0 | 65.6 | 96.0 % |
|
| 42 |
+
| real ASR output (10.6 % WER) | **this model** | **18.47 %** | **45.2** | 51.9 | 79.0 | 97.1 % |
|
| 43 |
+
| reference words | capitalize first word + final period | 13.09 % | 13.3 | 0 | 66.3 | 96.1 % |
|
| 44 |
+
| reference words | **this model** | **9.18 %** | **51.2** | 52.7 | 79.8 | 97.3 % |
|
| 45 |
+
|
| 46 |
+
On real ASR output the gain over the trivial baseline is **−3.26 points** of formatted WER (paired bootstrap, 95 % CI [−3.51, −3.02]). With perfect labels, the floor would be 2.4 %: quotes, brackets and some accents are out of scope.
|
| 47 |
+
|
| 48 |
+
## Usage
|
| 49 |
+
|
| 50 |
+
```python
|
| 51 |
+
import os, sys
|
| 52 |
+
from huggingface_hub import snapshot_download
|
| 53 |
+
|
| 54 |
+
path = snapshot_download("Flo976/malagasy-punctuation")
|
| 55 |
+
sys.path.insert(0, path)
|
| 56 |
+
from punctuate import Restorer
|
| 57 |
+
|
| 58 |
+
restorer = Restorer(path) # CPU by default; Restorer(path, device="cuda") on GPU
|
| 59 |
+
print(restorer.restore("tena mamy tokoa ity voankazo ity ary tsy mifidy"))
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
Requirements: `torch`, `transformers`, `sentencepiece`, `numpy`. Long texts are processed in sliding windows of 150 words. On two CPU threads, a 32-word utterance takes about 80 ms.
|
| 63 |
+
|
| 64 |
+
The [demo](https://huggingface.co/spaces/Flo976/malagasy-asr-demo) also capitalizes the word after `.`, `?` and `!`, as 99.6 % of sentence starts are in the validation transcripts. That rule is not included in the measurements above.
|
| 65 |
+
|
| 66 |
+
## Training
|
| 67 |
+
|
| 68 |
+
| | |
|
| 69 |
+
| --- | --- |
|
| 70 |
+
| Base model | [Davlan/xlm-roberta-base-finetuned-malagasy](https://huggingface.co/Davlan/xlm-roberta-base-finetuned-malagasy), XLM-R base adapted to Malagasy |
|
| 71 |
+
| Head | one token-classification head, 28 classes (4 cases × 7 marks), read on the first sub-token of each word |
|
| 72 |
+
| Data | 0.93 M words of raw train-split transcripts of badrex/malagasy-speech-full + 1.5 M words of filtered Malagasy Wikipedia sentences |
|
| 73 |
+
| Robustness | half of the training inputs receive synthetic CTC-style errors (substituted letters, merged or split words) |
|
| 74 |
+
| Accent table | built from the training text, restores letters that normalization strips; same no-word-change guarantee |
|
| 75 |
+
| Selection | on the validation split only; the test was used once |
|
| 76 |
+
| Hardware | one RTX 5070 Ti, 13 minutes |
|
| 77 |
+
|
| 78 |
+
## Limitations
|
| 79 |
+
|
| 80 |
+
- **Commas remain hard** (F1 about 52): transcribers place them inconsistently.
|
| 81 |
+
- **Question and exclamation marks are rare** in the test (20 and 35 cases): their scores are very uncertain. Semicolons are never predicted.
|
| 82 |
+
- **Domain**: trained on read image descriptions and Wikipedia. Dialogue and questions, as in a voice assistant, are less well punctuated.
|
| 83 |
+
- **Size**: 1.1 GB in fp32. Dynamic int8 quantization breaks it (comma F1 from 54 to 19): do not quantize.
|
| 84 |
+
- Pauses in the audio are not used. Adding them helped a separate classifier, but they are not integrated in this model.
|
| 85 |
+
|
| 86 |
+
## License
|
| 87 |
+
|
| 88 |
+
**CC-BY-SA-4.0**, because the model is trained on Malagasy Wikipedia (CC BY-SA). The base model, Davlan/xlm-roberta-base-finetuned-malagasy, declares no license on its card; XLM-R itself is MIT. The speech dataset, badrex/malagasy-speech-full, declares no license either. Please credit Davlan (David Adelani) for the base model.
|
| 89 |
+
|
| 90 |
+
Made for [Milo Voice](https://github.com/Flo976/milo) by [Flo976](https://huggingface.co/Flo976) — Florent Didelot / [Sooatek](https://sooatek.com).
|
common.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Briques communes du chantier ponctuation/majuscules.
|
| 2 |
+
|
| 3 |
+
Schema d'etiquettes, par mot NORMALISE (normalize() d'evaluate_asr.py) :
|
| 4 |
+
- casse : L (minuscule), C (Capitale initiale), U (TOUT EN MAJUSCULES, >= 2 lettres),
|
| 5 |
+
M (mixte, ex. "McDonald", "iPhone" : forme rendue via une table apprise)
|
| 6 |
+
- ponct. : signe qui suit le mot : O (aucun) , . ? ! : ;
|
| 7 |
+
("..." est compte comme "." ; guillemets, parentheses, tirets
|
| 8 |
+
d'incise ne sont pas predits : voir README).
|
| 9 |
+
|
| 10 |
+
Alignement texte brut -> mots normalises : normalize() agit caractere par
|
| 11 |
+
caractere a l'interieur de chaque jeton separe par des blancs (minuscules,
|
| 12 |
+
puis tout caractere hors [a-z0-9'-] devient un blanc, puis les jetons "-" et
|
| 13 |
+
"'" isoles sont supprimes). On rejoue exactement cette operation en gardant la
|
| 14 |
+
position de chaque caractere dans le texte brut : chaque mot normalise est
|
| 15 |
+
alors une plage du texte brut, d'ou on lit la casse ; le "trou" entre deux mots
|
| 16 |
+
(blancs, ponctuation, lettres accentuees supprimees) donne la ponctuation.
|
| 17 |
+
Si la reconstruction ne redonne pas normalize(texte).split(), l'exemple est
|
| 18 |
+
ignore et compte.
|
| 19 |
+
"""
|
| 20 |
+
import os
|
| 21 |
+
import re
|
| 22 |
+
import sys
|
| 23 |
+
import unicodedata
|
| 24 |
+
|
| 25 |
+
try:
|
| 26 |
+
from evaluate_asr import APOS, DASH, normalize # noqa: E402
|
| 27 |
+
except ImportError: # hors du depot (Space, paquet du modele) : copie conforme
|
| 28 |
+
APOS = dict.fromkeys(map(ord, "’ʼʻ`´"), "'")
|
| 29 |
+
DASH = dict.fromkeys(map(ord, "‐‑‒–—―"), "-")
|
| 30 |
+
_KEEP_RE = re.compile(r"[^a-z0-9'\- ]+")
|
| 31 |
+
|
| 32 |
+
def normalize(text):
|
| 33 |
+
if not text:
|
| 34 |
+
return ""
|
| 35 |
+
text = unicodedata.normalize("NFC", text).translate(APOS).translate(DASH).lower()
|
| 36 |
+
text = re.sub(r"\s+", " ", _KEEP_RE.sub(" ", text)).strip()
|
| 37 |
+
return " ".join(w for w in text.split() if w not in {"-", "'"})
|
| 38 |
+
|
| 39 |
+
CASES = ["L", "C", "U", "M"]
|
| 40 |
+
PUNCTS = ["O", ",", ".", "?", "!", ":", ";"]
|
| 41 |
+
CASE2ID = {c: i for i, c in enumerate(CASES)}
|
| 42 |
+
P2ID = {p: i for i, p in enumerate(PUNCTS)}
|
| 43 |
+
ALLOWED = set("abcdefghijklmnopqrstuvwxyz0123456789'- ")
|
| 44 |
+
# Priorite quand un trou contient plusieurs signes (ex. '".' ou ').').
|
| 45 |
+
PRIORITY = ["?", "!", ".", ":", ";", ","]
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def gap_punct(gap):
|
| 49 |
+
for p in PRIORITY:
|
| 50 |
+
if p in gap:
|
| 51 |
+
return p
|
| 52 |
+
return "O"
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def case_of(s):
|
| 56 |
+
letters = [c for c in s if c.isalpha()]
|
| 57 |
+
if not letters:
|
| 58 |
+
return "L"
|
| 59 |
+
if all(c.islower() for c in letters):
|
| 60 |
+
return "L"
|
| 61 |
+
if letters[0].isupper() and all(c.islower() for c in letters[1:]):
|
| 62 |
+
return "C"
|
| 63 |
+
if len(letters) >= 2 and all(c.isupper() for c in letters):
|
| 64 |
+
return "U"
|
| 65 |
+
if len(letters) == 1:
|
| 66 |
+
return "C"
|
| 67 |
+
return "M"
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def segments(raw):
|
| 71 |
+
"""Rend [(debut, fin)] des mots normalises dans le texte brut (NFC), et le texte NFC.
|
| 72 |
+
|
| 73 |
+
Les positions renvoient au texte NFC + apostrophes/tirets unifies (meme
|
| 74 |
+
longueur que le NFC, la traduction etant caractere pour caractere)."""
|
| 75 |
+
t = unicodedata.normalize("NFC", raw).translate(APOS).translate(DASH)
|
| 76 |
+
segs, start = [], None
|
| 77 |
+
for i, ch in enumerate(t):
|
| 78 |
+
low = ch.lower()
|
| 79 |
+
keep = len(low) == 1 and low in ALLOWED and low != " "
|
| 80 |
+
if ch.isspace():
|
| 81 |
+
keep = False
|
| 82 |
+
if keep:
|
| 83 |
+
if start is None:
|
| 84 |
+
start = i
|
| 85 |
+
else:
|
| 86 |
+
if start is not None:
|
| 87 |
+
segs.append((start, i))
|
| 88 |
+
start = None
|
| 89 |
+
if start is not None:
|
| 90 |
+
segs.append((start, len(t)))
|
| 91 |
+
segs = [(a, b) for a, b in segs if t[a:b] not in ("-", "'")]
|
| 92 |
+
return segs, t
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def label_text(raw):
|
| 96 |
+
"""Texte brut -> (mots normalises, casses, ponctuations, formes brutes) ou None si desaccord."""
|
| 97 |
+
segs, t = segments(raw)
|
| 98 |
+
words = [t[a:b].lower() for a, b in segs]
|
| 99 |
+
if words != normalize(raw).split():
|
| 100 |
+
return None
|
| 101 |
+
cases, puncts, forms = [], [], []
|
| 102 |
+
for k, (a, b) in enumerate(segs):
|
| 103 |
+
nxt = segs[k + 1][0] if k + 1 < len(segs) else len(t)
|
| 104 |
+
cases.append(case_of(t[a:b]))
|
| 105 |
+
puncts.append(gap_punct(t[b:nxt]))
|
| 106 |
+
forms.append(t[a:b])
|
| 107 |
+
return words, cases, puncts, forms
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def apply_case(word, c, mixed_table=None):
|
| 111 |
+
if c == "L":
|
| 112 |
+
return word
|
| 113 |
+
if c == "U":
|
| 114 |
+
return word.upper()
|
| 115 |
+
if c == "M" and mixed_table and word in mixed_table:
|
| 116 |
+
return mixed_table[word]
|
| 117 |
+
start = 0
|
| 118 |
+
if c == "M":
|
| 119 |
+
# M hors table : le cas typique est un nom propre apres elision ou
|
| 120 |
+
# trait d'union ("an'Antananarivo", "maha-Malagasy").
|
| 121 |
+
cut = max(word.rfind("'"), word.rfind("-"))
|
| 122 |
+
if 0 <= cut < len(word) - 1:
|
| 123 |
+
start = cut + 1
|
| 124 |
+
# C : premiere lettre en majuscule.
|
| 125 |
+
for i, ch in enumerate(word):
|
| 126 |
+
if i < start:
|
| 127 |
+
continue
|
| 128 |
+
if ch.isalpha():
|
| 129 |
+
return word[:i] + ch.upper() + word[i + 1 :]
|
| 130 |
+
return word
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def render(words, cases, puncts, mixed_table=None, lex_table=None):
|
| 134 |
+
"""Assemble la sortie. lex_table (optionnelle) : n-gramme normalise -> forme brute
|
| 135 |
+
minuscule accentuee (ex. ("tan","na") -> "tanàna"), appliquee avant la casse.
|
| 136 |
+
La sortie verifie toujours normalize(sortie).split() == words."""
|
| 137 |
+
out, i, n = [], 0, len(words)
|
| 138 |
+
while i < n:
|
| 139 |
+
span = 1
|
| 140 |
+
form = None
|
| 141 |
+
if lex_table:
|
| 142 |
+
for k in (3, 2, 1):
|
| 143 |
+
if i + k <= n:
|
| 144 |
+
key = " ".join(words[i : i + k])
|
| 145 |
+
if key in lex_table:
|
| 146 |
+
form, span = lex_table[key], k
|
| 147 |
+
break
|
| 148 |
+
if form is None:
|
| 149 |
+
tok = apply_case(words[i], cases[i], mixed_table)
|
| 150 |
+
else:
|
| 151 |
+
tok = apply_case(form, cases[i], None)
|
| 152 |
+
p = puncts[i + span - 1]
|
| 153 |
+
out.append(tok + ("" if p == "O" else p))
|
| 154 |
+
i += span
|
| 155 |
+
return " ".join(out)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def check_words(inp_words, text):
|
| 159 |
+
return normalize(text).split() == inp_words
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def trivial_baseline(norm_text):
|
| 163 |
+
"""Baseline triviale : majuscule en debut + point final."""
|
| 164 |
+
w = norm_text.split()
|
| 165 |
+
if not w:
|
| 166 |
+
return ""
|
| 167 |
+
return render(w, ["C"] + ["L"] * (len(w) - 1), ["O"] * (len(w) - 1) + ["."])
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
WORK = os.path.dirname(os.path.abspath(__file__))
|
config.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"XLMRobertaForTokenClassification"
|
| 4 |
+
],
|
| 5 |
+
"attention_probs_dropout_prob": 0.1,
|
| 6 |
+
"bos_token_id": 0,
|
| 7 |
+
"classifier_dropout": null,
|
| 8 |
+
"dtype": "float32",
|
| 9 |
+
"eos_token_id": 2,
|
| 10 |
+
"hidden_act": "gelu",
|
| 11 |
+
"hidden_dropout_prob": 0.1,
|
| 12 |
+
"hidden_size": 768,
|
| 13 |
+
"id2label": {
|
| 14 |
+
"0": "L|O",
|
| 15 |
+
"1": "L|,",
|
| 16 |
+
"2": "L|.",
|
| 17 |
+
"3": "L|?",
|
| 18 |
+
"4": "L|!",
|
| 19 |
+
"5": "L|:",
|
| 20 |
+
"6": "L|;",
|
| 21 |
+
"7": "C|O",
|
| 22 |
+
"8": "C|,",
|
| 23 |
+
"9": "C|.",
|
| 24 |
+
"10": "C|?",
|
| 25 |
+
"11": "C|!",
|
| 26 |
+
"12": "C|:",
|
| 27 |
+
"13": "C|;",
|
| 28 |
+
"14": "U|O",
|
| 29 |
+
"15": "U|,",
|
| 30 |
+
"16": "U|.",
|
| 31 |
+
"17": "U|?",
|
| 32 |
+
"18": "U|!",
|
| 33 |
+
"19": "U|:",
|
| 34 |
+
"20": "U|;",
|
| 35 |
+
"21": "M|O",
|
| 36 |
+
"22": "M|,",
|
| 37 |
+
"23": "M|.",
|
| 38 |
+
"24": "M|?",
|
| 39 |
+
"25": "M|!",
|
| 40 |
+
"26": "M|:",
|
| 41 |
+
"27": "M|;"
|
| 42 |
+
},
|
| 43 |
+
"initializer_range": 0.02,
|
| 44 |
+
"intermediate_size": 3072,
|
| 45 |
+
"label2id": {
|
| 46 |
+
"C|!": 11,
|
| 47 |
+
"C|,": 8,
|
| 48 |
+
"C|.": 9,
|
| 49 |
+
"C|:": 12,
|
| 50 |
+
"C|;": 13,
|
| 51 |
+
"C|?": 10,
|
| 52 |
+
"C|O": 7,
|
| 53 |
+
"L|!": 4,
|
| 54 |
+
"L|,": 1,
|
| 55 |
+
"L|.": 2,
|
| 56 |
+
"L|:": 5,
|
| 57 |
+
"L|;": 6,
|
| 58 |
+
"L|?": 3,
|
| 59 |
+
"L|O": 0,
|
| 60 |
+
"M|!": 25,
|
| 61 |
+
"M|,": 22,
|
| 62 |
+
"M|.": 23,
|
| 63 |
+
"M|:": 26,
|
| 64 |
+
"M|;": 27,
|
| 65 |
+
"M|?": 24,
|
| 66 |
+
"M|O": 21,
|
| 67 |
+
"U|!": 18,
|
| 68 |
+
"U|,": 15,
|
| 69 |
+
"U|.": 16,
|
| 70 |
+
"U|:": 19,
|
| 71 |
+
"U|;": 20,
|
| 72 |
+
"U|?": 17,
|
| 73 |
+
"U|O": 14
|
| 74 |
+
},
|
| 75 |
+
"layer_norm_eps": 1e-05,
|
| 76 |
+
"max_position_embeddings": 514,
|
| 77 |
+
"model_type": "xlm-roberta",
|
| 78 |
+
"num_attention_heads": 12,
|
| 79 |
+
"num_hidden_layers": 12,
|
| 80 |
+
"output_past": true,
|
| 81 |
+
"pad_token_id": 1,
|
| 82 |
+
"position_embedding_type": "absolute",
|
| 83 |
+
"transformers_version": "4.57.1",
|
| 84 |
+
"type_vocab_size": 1,
|
| 85 |
+
"use_cache": true,
|
| 86 |
+
"vocab_size": 250002
|
| 87 |
+
}
|
labels.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema": "etiquette combinee = indice_casse * 7 + indice_ponct, lue sur le premier sous-jeton de chaque mot normalise",
|
| 3 |
+
"cases": {
|
| 4 |
+
"L": "minuscule",
|
| 5 |
+
"C": "Capitale initiale",
|
| 6 |
+
"U": "TOUT EN MAJUSCULES",
|
| 7 |
+
"M": "mixte (forme lue dans tables.json['mixed'], sinon capitale apres la derniere apostrophe/trait d'union)"
|
| 8 |
+
},
|
| 9 |
+
"puncts": [
|
| 10 |
+
"O",
|
| 11 |
+
",",
|
| 12 |
+
".",
|
| 13 |
+
"?",
|
| 14 |
+
"!",
|
| 15 |
+
":",
|
| 16 |
+
";"
|
| 17 |
+
],
|
| 18 |
+
"normalisation_entree": "evaluate_asr.normalize (minuscules, [a-z0-9'-], apostrophes/tirets unifies)",
|
| 19 |
+
"tables": "tables.json : 'mixed' = formes a casse mixte ; 'lex' = n-gramme normalise -> forme accentuee (ex. 'tan na' -> 'tanàna'), construite sur train + Wikipedia",
|
| 20 |
+
"base": "Davlan/xlm-roberta-base-finetuned-malagasy",
|
| 21 |
+
"entrainement": "work/punct/train.py --noise 0.5 --epochs 3 (checkpoint epoque 2.5)"
|
| 22 |
+
}
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:13887a16c47f554d329ccfa6e88c081db0a1139bfa2b5c7096a79d57165eb415
|
| 3 |
+
size 1109922400
|
punctuate.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
"""Restauration des majuscules et de la ponctuation pour la sortie ASR malgache.
|
| 3 |
+
|
| 4 |
+
from punctuate import restore
|
| 5 |
+
restore("lanonana ara-panambadiana iray ahitana karazan'olona mandihy")
|
| 6 |
+
-> "Lanonana ara-panambadiana iray ahitana karazan'olona mandihy."
|
| 7 |
+
|
| 8 |
+
Garantie : normalize(sortie).split() == normalize(entree).split() (assert).
|
| 9 |
+
Les mots ne sont jamais changes ; seules la casse et la ponctuation qui suit
|
| 10 |
+
chaque mot sont predites (plus, en option, la remise des lettres accentuees
|
| 11 |
+
qu'efface normalize(), ex. "tan na" -> "tanàna", qui respecte la meme garantie).
|
| 12 |
+
|
| 13 |
+
Modele : XLM-R base (Davlan/xlm-roberta-base-finetuned-malagasy) + une tete de
|
| 14 |
+
classification de jetons combinee (4 casses x 7 ponctuations), etiquette lue
|
| 15 |
+
sur le premier sous-jeton de chaque mot. Textes longs : fenetres glissantes.
|
| 16 |
+
|
| 17 |
+
CLI :
|
| 18 |
+
python punctuate.py "texte en minuscules" # restaure
|
| 19 |
+
python punctuate.py --bench 100 --threads 2 # latence CPU
|
| 20 |
+
"""
|
| 21 |
+
import argparse
|
| 22 |
+
import json
|
| 23 |
+
import os
|
| 24 |
+
import sys
|
| 25 |
+
import time
|
| 26 |
+
|
| 27 |
+
import numpy as np
|
| 28 |
+
import torch
|
| 29 |
+
|
| 30 |
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
| 31 |
+
sys.path.insert(0, HERE)
|
| 32 |
+
from common import CASES, PUNCTS, normalize, render # noqa: E402
|
| 33 |
+
|
| 34 |
+
DEFAULT_MODEL = os.environ.get("MG_PUNCT_DIR", HERE)
|
| 35 |
+
NP = len(PUNCTS)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def windows(n, size, step):
|
| 39 |
+
"""Fenetres [a, b) couvrant n mots ; chaque mot sera lu dans la fenetre ou il est le plus central."""
|
| 40 |
+
if n <= size:
|
| 41 |
+
return [(0, n)]
|
| 42 |
+
out, a = [], 0
|
| 43 |
+
while True:
|
| 44 |
+
b = min(a + size, n)
|
| 45 |
+
out.append((a, b))
|
| 46 |
+
if b == n:
|
| 47 |
+
return out
|
| 48 |
+
a += step
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class Restorer:
|
| 52 |
+
def __init__(self, model_dir=DEFAULT_MODEL, device="cpu", model=None, tokenizer=None, use_lex=True, use_bias=True, quantize=False,
|
| 53 |
+
win_words=150, win_step=100, max_tokens=512):
|
| 54 |
+
from transformers import AutoModelForTokenClassification, AutoTokenizer
|
| 55 |
+
|
| 56 |
+
self.device = device
|
| 57 |
+
self.tok = tokenizer or AutoTokenizer.from_pretrained(model_dir)
|
| 58 |
+
if model is None:
|
| 59 |
+
model = AutoModelForTokenClassification.from_pretrained(model_dir)
|
| 60 |
+
model.to(device).eval()
|
| 61 |
+
if quantize and device == "cpu":
|
| 62 |
+
# int8 dynamique sur les couches lineaires (option, CPU seulement).
|
| 63 |
+
model = torch.ao.quantization.quantize_dynamic(model, {torch.nn.Linear}, dtype=torch.qint8)
|
| 64 |
+
self.model = model
|
| 65 |
+
tables = {"mixed": {}, "lex": {}}
|
| 66 |
+
tpath = os.path.join(model_dir, "tables.json") if model_dir else ""
|
| 67 |
+
if tpath and os.path.exists(tpath):
|
| 68 |
+
tables = json.load(open(tpath))
|
| 69 |
+
self.mixed = tables.get("mixed", {})
|
| 70 |
+
self.lex = tables.get("lex", {}) if use_lex else {}
|
| 71 |
+
self.win_words, self.win_step, self.max_tokens = win_words, win_step, max_tokens
|
| 72 |
+
# Biais additif par signe sur log P(ponct.), regle sur la validation (tune_bias.py).
|
| 73 |
+
self.bias = np.zeros(NP, dtype=np.float32)
|
| 74 |
+
bpath = os.path.join(model_dir, "punct_bias.json") if model_dir else ""
|
| 75 |
+
if use_bias and bpath and os.path.exists(bpath):
|
| 76 |
+
b = json.load(open(bpath))
|
| 77 |
+
self.bias = np.array([b.get(p, 0.0) for p in PUNCTS], dtype=np.float32)
|
| 78 |
+
|
| 79 |
+
def predict_words(self, batch_words, batch_size=32):
|
| 80 |
+
"""Liste de listes de mots -> liste de (casses, ponctuations). Argmax des marginales."""
|
| 81 |
+
out = []
|
| 82 |
+
for w, (pc, pp, seen) in zip(batch_words, self.predict_probs(batch_words, batch_size)):
|
| 83 |
+
cases = [CASES[i] for i in pc.argmax(1)] if len(w) else []
|
| 84 |
+
puncts = [PUNCTS[i] for i in (np.log(pp + 1e-9) + self.bias).argmax(1)] if len(w) else []
|
| 85 |
+
# Mot jamais vu (tronque) : pas de prediction -> minuscule, sans signe.
|
| 86 |
+
for g in range(len(w)):
|
| 87 |
+
if not seen[g]:
|
| 88 |
+
cases[g], puncts[g] = "L", "O"
|
| 89 |
+
out.append((cases, puncts))
|
| 90 |
+
return out
|
| 91 |
+
|
| 92 |
+
@torch.no_grad()
|
| 93 |
+
def predict_probs(self, batch_words, batch_size=32):
|
| 94 |
+
"""Liste de listes de mots -> [(P(casse) [n,4], P(ponct.) [n,7], vu [n])] par exemple."""
|
| 95 |
+
jobs = [] # (index exemple, a, b)
|
| 96 |
+
for k, w in enumerate(batch_words):
|
| 97 |
+
for a, b in windows(len(w), self.win_words, self.win_step):
|
| 98 |
+
jobs.append((k, a, b))
|
| 99 |
+
probs = [np.zeros((len(w), len(CASES) * NP), dtype=np.float32) for w in batch_words]
|
| 100 |
+
best = [np.full(len(w), -1.0) for w in batch_words]
|
| 101 |
+
jobs.sort(key=lambda j: j[2] - j[1])
|
| 102 |
+
for s in range(0, len(jobs), batch_size):
|
| 103 |
+
chunk = jobs[s : s + batch_size]
|
| 104 |
+
enc = self.tok([batch_words[k][a:b] for k, a, b in chunk], is_split_into_words=True,
|
| 105 |
+
truncation=True, max_length=self.max_tokens, padding=True, return_tensors="pt")
|
| 106 |
+
logits = self.model(input_ids=enc["input_ids"].to(self.device),
|
| 107 |
+
attention_mask=enc["attention_mask"].to(self.device)).logits.float()
|
| 108 |
+
p = torch.softmax(logits, -1).cpu().numpy()
|
| 109 |
+
for j, (k, a, b) in enumerate(chunk):
|
| 110 |
+
prev = None
|
| 111 |
+
for t, wid in enumerate(enc.word_ids(j)):
|
| 112 |
+
if wid is None or wid == prev:
|
| 113 |
+
prev = wid
|
| 114 |
+
continue
|
| 115 |
+
prev = wid
|
| 116 |
+
g = a + wid
|
| 117 |
+
# centralite : distance au bord de fenetre (les bords de texte ne comptent pas)
|
| 118 |
+
left = wid if a > 0 else 10**6
|
| 119 |
+
right = (b - a - 1 - wid) if b < len(batch_words[k]) else 10**6
|
| 120 |
+
c = min(left, right)
|
| 121 |
+
if c > best[k][g]:
|
| 122 |
+
best[k][g] = c
|
| 123 |
+
probs[k][g] = p[j, t]
|
| 124 |
+
out = []
|
| 125 |
+
for k, w in enumerate(batch_words):
|
| 126 |
+
pr = probs[k].reshape(len(w), len(CASES), NP)
|
| 127 |
+
out.append((pr.sum(2), pr.sum(1), best[k] >= 0))
|
| 128 |
+
return out
|
| 129 |
+
|
| 130 |
+
def restore_many(self, texts, batch_size=32):
|
| 131 |
+
words = [normalize(t).split() for t in texts]
|
| 132 |
+
preds = self.predict_words(words, batch_size=batch_size)
|
| 133 |
+
outs = []
|
| 134 |
+
for w, (c, p) in zip(words, preds):
|
| 135 |
+
o = render(w, c, p, self.mixed, self.lex) if w else ""
|
| 136 |
+
assert normalize(o).split() == w, (w, o)
|
| 137 |
+
outs.append(o)
|
| 138 |
+
return outs
|
| 139 |
+
|
| 140 |
+
def restore(self, text):
|
| 141 |
+
return self.restore_many([text])[0]
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
_DEFAULT = None
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def restore(text: str) -> str:
|
| 148 |
+
"""Restaure majuscules et ponctuation (CPU). Le modele est charge au premier appel."""
|
| 149 |
+
global _DEFAULT
|
| 150 |
+
if _DEFAULT is None:
|
| 151 |
+
_DEFAULT = Restorer()
|
| 152 |
+
return _DEFAULT.restore(text)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def bench(n, threads, model_dir, quantize=False):
|
| 156 |
+
"""Latence CPU, une phrase a la fois, sur n hypotheses ASR de la VALIDATION."""
|
| 157 |
+
torch.set_num_threads(threads)
|
| 158 |
+
r = Restorer(model_dir, quantize=quantize)
|
| 159 |
+
path = os.path.join(HERE, "asr_valid", "valid_lm_transcripts.jsonl")
|
| 160 |
+
if os.path.exists(path):
|
| 161 |
+
texts = [json.loads(l)["hyp"] for l in open(path)]
|
| 162 |
+
import random
|
| 163 |
+
texts = random.Random(0).sample(texts, min(n, len(texts))) # le fichier est trie par duree
|
| 164 |
+
else:
|
| 165 |
+
texts = [" ".join(json.loads(l)["words"]) for l in open(os.path.join(HERE, "data", "valid.jsonl"))][:n]
|
| 166 |
+
for t in texts[:5]:
|
| 167 |
+
r.restore(t)
|
| 168 |
+
lat = []
|
| 169 |
+
for t in texts:
|
| 170 |
+
t0 = time.perf_counter()
|
| 171 |
+
r.restore(t)
|
| 172 |
+
lat.append(1000 * (time.perf_counter() - t0))
|
| 173 |
+
lat = np.array(lat)
|
| 174 |
+
words = np.mean([len(t.split()) for t in texts])
|
| 175 |
+
res = {"n": len(texts), "threads": threads, "int8": quantize, "mots_moyens": round(float(words), 1),
|
| 176 |
+
"median_ms": round(float(np.median(lat)), 1), "p90_ms": round(float(np.percentile(lat, 90)), 1),
|
| 177 |
+
"max_ms": round(float(lat.max()), 1), "moyenne_ms": round(float(lat.mean()), 1)}
|
| 178 |
+
print(json.dumps(res, ensure_ascii=False))
|
| 179 |
+
return res
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
if __name__ == "__main__":
|
| 183 |
+
ap = argparse.ArgumentParser()
|
| 184 |
+
ap.add_argument("text", nargs="?")
|
| 185 |
+
ap.add_argument("--model", default=DEFAULT_MODEL)
|
| 186 |
+
ap.add_argument("--bench", type=int, default=0)
|
| 187 |
+
ap.add_argument("--threads", type=int, default=2)
|
| 188 |
+
ap.add_argument("--out", default=None)
|
| 189 |
+
ap.add_argument("--int8", action="store_true", help="quantification dynamique int8 (CPU)")
|
| 190 |
+
a = ap.parse_args()
|
| 191 |
+
if a.bench:
|
| 192 |
+
res = bench(a.bench, a.threads, a.model, a.int8)
|
| 193 |
+
if a.out:
|
| 194 |
+
json.dump(res, open(a.out, "w"), ensure_ascii=False, indent=2)
|
| 195 |
+
else:
|
| 196 |
+
torch.set_num_threads(a.threads)
|
| 197 |
+
print(Restorer(a.model, quantize=a.int8).restore(a.text or sys.stdin.read()))
|
sentencepiece.bpe.model
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cfc8146abe2a0488e9e2a0c56de7952f7c11ab059eca145a0a727afce0db2865
|
| 3 |
+
size 5069051
|
special_tokens_map.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bos_token": {
|
| 3 |
+
"content": "<s>",
|
| 4 |
+
"lstrip": false,
|
| 5 |
+
"normalized": false,
|
| 6 |
+
"rstrip": false,
|
| 7 |
+
"single_word": false
|
| 8 |
+
},
|
| 9 |
+
"cls_token": {
|
| 10 |
+
"content": "<s>",
|
| 11 |
+
"lstrip": false,
|
| 12 |
+
"normalized": false,
|
| 13 |
+
"rstrip": false,
|
| 14 |
+
"single_word": false
|
| 15 |
+
},
|
| 16 |
+
"eos_token": {
|
| 17 |
+
"content": "</s>",
|
| 18 |
+
"lstrip": false,
|
| 19 |
+
"normalized": false,
|
| 20 |
+
"rstrip": false,
|
| 21 |
+
"single_word": false
|
| 22 |
+
},
|
| 23 |
+
"mask_token": {
|
| 24 |
+
"content": "<mask>",
|
| 25 |
+
"lstrip": true,
|
| 26 |
+
"normalized": false,
|
| 27 |
+
"rstrip": false,
|
| 28 |
+
"single_word": false
|
| 29 |
+
},
|
| 30 |
+
"pad_token": {
|
| 31 |
+
"content": "<pad>",
|
| 32 |
+
"lstrip": false,
|
| 33 |
+
"normalized": false,
|
| 34 |
+
"rstrip": false,
|
| 35 |
+
"single_word": false
|
| 36 |
+
},
|
| 37 |
+
"sep_token": {
|
| 38 |
+
"content": "</s>",
|
| 39 |
+
"lstrip": false,
|
| 40 |
+
"normalized": false,
|
| 41 |
+
"rstrip": false,
|
| 42 |
+
"single_word": false
|
| 43 |
+
},
|
| 44 |
+
"unk_token": {
|
| 45 |
+
"content": "<unk>",
|
| 46 |
+
"lstrip": false,
|
| 47 |
+
"normalized": false,
|
| 48 |
+
"rstrip": false,
|
| 49 |
+
"single_word": false
|
| 50 |
+
}
|
| 51 |
+
}
|
tables.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:883b037111086fd4dfebbbc9b7cee11e1517b5e0c0514879478661440f137085
|
| 3 |
+
size 17082987
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"added_tokens_decoder": {
|
| 3 |
+
"0": {
|
| 4 |
+
"content": "<s>",
|
| 5 |
+
"lstrip": false,
|
| 6 |
+
"normalized": false,
|
| 7 |
+
"rstrip": false,
|
| 8 |
+
"single_word": false,
|
| 9 |
+
"special": true
|
| 10 |
+
},
|
| 11 |
+
"1": {
|
| 12 |
+
"content": "<pad>",
|
| 13 |
+
"lstrip": false,
|
| 14 |
+
"normalized": false,
|
| 15 |
+
"rstrip": false,
|
| 16 |
+
"single_word": false,
|
| 17 |
+
"special": true
|
| 18 |
+
},
|
| 19 |
+
"2": {
|
| 20 |
+
"content": "</s>",
|
| 21 |
+
"lstrip": false,
|
| 22 |
+
"normalized": false,
|
| 23 |
+
"rstrip": false,
|
| 24 |
+
"single_word": false,
|
| 25 |
+
"special": true
|
| 26 |
+
},
|
| 27 |
+
"3": {
|
| 28 |
+
"content": "<unk>",
|
| 29 |
+
"lstrip": false,
|
| 30 |
+
"normalized": false,
|
| 31 |
+
"rstrip": false,
|
| 32 |
+
"single_word": false,
|
| 33 |
+
"special": true
|
| 34 |
+
},
|
| 35 |
+
"250001": {
|
| 36 |
+
"content": "<mask>",
|
| 37 |
+
"lstrip": true,
|
| 38 |
+
"normalized": false,
|
| 39 |
+
"rstrip": false,
|
| 40 |
+
"single_word": false,
|
| 41 |
+
"special": true
|
| 42 |
+
}
|
| 43 |
+
},
|
| 44 |
+
"bos_token": "<s>",
|
| 45 |
+
"clean_up_tokenization_spaces": false,
|
| 46 |
+
"cls_token": "<s>",
|
| 47 |
+
"eos_token": "</s>",
|
| 48 |
+
"extra_special_tokens": {},
|
| 49 |
+
"mask_token": "<mask>",
|
| 50 |
+
"model_max_length": 512,
|
| 51 |
+
"pad_token": "<pad>",
|
| 52 |
+
"sep_token": "</s>",
|
| 53 |
+
"tokenizer_class": "XLMRobertaTokenizer",
|
| 54 |
+
"unk_token": "<unk>"
|
| 55 |
+
}
|
valid_metrics.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"step": 8070,
|
| 3 |
+
"epoch": 2.5,
|
| 4 |
+
"min": 10.9,
|
| 5 |
+
"valid": {
|
| 6 |
+
"par_signe": {
|
| 7 |
+
",": {
|
| 8 |
+
"P": 60.66,
|
| 9 |
+
"R": 49.17,
|
| 10 |
+
"F1": 54.31,
|
| 11 |
+
"n_ref": 4444
|
| 12 |
+
},
|
| 13 |
+
".": {
|
| 14 |
+
"P": 75.29,
|
| 15 |
+
"R": 84.74,
|
| 16 |
+
"F1": 79.73,
|
| 17 |
+
"n_ref": 5885
|
| 18 |
+
},
|
| 19 |
+
"?": {
|
| 20 |
+
"P": 52.17,
|
| 21 |
+
"R": 42.86,
|
| 22 |
+
"F1": 47.06,
|
| 23 |
+
"n_ref": 28
|
| 24 |
+
},
|
| 25 |
+
"!": {
|
| 26 |
+
"P": 76.92,
|
| 27 |
+
"R": 47.62,
|
| 28 |
+
"F1": 58.82,
|
| 29 |
+
"n_ref": 42
|
| 30 |
+
},
|
| 31 |
+
":": {
|
| 32 |
+
"P": 34.34,
|
| 33 |
+
"R": 24.27,
|
| 34 |
+
"F1": 28.44,
|
| 35 |
+
"n_ref": 375
|
| 36 |
+
},
|
| 37 |
+
";": {
|
| 38 |
+
"P": 0.0,
|
| 39 |
+
"R": 0.0,
|
| 40 |
+
"F1": 0.0,
|
| 41 |
+
"n_ref": 581
|
| 42 |
+
}
|
| 43 |
+
},
|
| 44 |
+
"f1_macro_5": 53.67,
|
| 45 |
+
"f1_micro_tous_signes": 66.63,
|
| 46 |
+
"casse_exactitude": 97.24,
|
| 47 |
+
"majuscule_F1": 80.08
|
| 48 |
+
},
|
| 49 |
+
"score": 63.394
|
| 50 |
+
}
|