Configuration Parsing Warning:In adapter_config.json: "peft.task_type" must be a string

Griot ASR — LoRA ewe (Whisper large-v3-turbo)

Adaptateur LoRA pour l'ewe (ee, famille Gbe, Ghana et Togo), entraîné sur 50 h de google/WaxalNLP config ewe_asr, splits ASR_v2 speaker-disjoint.

Premier volet d'une étude comparant LoRA sur turbo, LoRA sur large-v3 et full fine-tuning à budget de données constant, menée par Bivariant dans le cadre du projet Griot.

Résultats — test v2 (1 883 énoncés, 9,97 h), beam 4

checkpoint CER WER sub del ins
1000 29,99 52,46 16 549 14 455 2 457
1500 (publié) 29,28 51,29 16 288 13 815 2 610
2000 30,37 51,83 15 893 14 930 2 235
2500 30,16 51,55 15 915 14 666 2 300
2995 29,97 51,62 16 026 14 599 2 304

Le classement est identique en greedy (validation) et en beam 4 (test).

diac_gap vaut 0,16–0,17 sur tous les checkpoints. Ce n'est pas un indicateur de qualité tonale : le corpus ewe de Waxal ne marque le ton que sur ~1,3 % des mots. Les diacritiques y sont dominés par le tilde combinant U+0303, qui note la nasalisation (230 des 306 diacritiques du test set).

Ces chiffres comportent un artefact identifié, décrit plus bas.

Utilisation

from peft import PeftModel
from transformers import (WhisperForConditionalGeneration, WhisperProcessor,
                          WhisperTokenizer)

REPO = "arnauld-a/griot-lora-ewe-turbo"
BASE = "openai/whisper-large-v3-turbo"

tok  = WhisperTokenizer.from_pretrained(REPO)     # vocabulaire étendu (51 882)
proc = WhisperProcessor.from_pretrained(REPO)
m = WhisperForConditionalGeneration.from_pretrained(BASE)
m.resize_token_embeddings(len(tok))               # AVANT l'adaptateur
m = PeftModel.from_pretrained(m, REPO).merge_and_unload()

out = m.generate(features, language="<|ewe|>", task="transcribe",
                 num_beams=4, repetition_penalty=1.0, no_repeat_ngram_size=14,
                 max_new_tokens=440, length_penalty=1.2)

Trois points obligatoires. Le tokenizer doit venir de ce dépôt, pas du modèle de base. resize_token_embeddings doit précéder le chargement de l'adaptateur, qui porte une table [51882, 1280]. Et generate(language="<|ewe|>") ne fonctionne que si generation_config.lang_to_id contient le token — il est inclus dans generation_config.json.

length_penalty=1.2 doit retomber à 1.0 sur le chemin long-form (>30 s) : sur une fenêtre ne portant que quelques secondes de parole, il pousse le décodeur à combler le vide (166 % de WER mesuré contre 42 % à 1.0). Sans objet ici, MAX_DURATION=30 s garantissant qu'aucun énoncé n'emprunte ce chemin.

Données

ewe_asr filtré à 1–30 s, sous-échantillonné à 50 h par locuteurs entiers (264 des 431 locuteurs, 9 572 énoncés, seed 20260906).

Les splits v2 sont indispensables : le split v1 présente v1_speaker_leak_max = 0,9975, et le rebrassage est quasi total — seuls 176 des 1 884 énoncés du test v2 proviennent du test v1, les 80 % restants sont dans le train v1. On ne peut donc pas se contenter des splits d'origine.

ewe_asr est le seul corpus Waxal en stéréo 44,1 kHz : downmix mono et rééchantillonnage 16 kHz explicites, aucun débruitage.

Extension du vocabulaire

51 866 → 51 882 tokens : 14 caractères ewe (ŋ Ŋ ɖ Ɖ ƒ Ƒ ɣ Ɣ ɔ Ɔ ɛ Ɛ ʋ Ʋ), le tilde combinant U+0303, et <|ewe|>.

Le tokenizer Whisper traite l'ewe à 0,624 token par caractère contre 0,407 pour le shona — 53 % de surcoût dû au repli byte-level sur ces 14 caractères. L'extension n'est donc pas cosmétique.

Chaque nouvelle ligne est initialisée depuis un token existant proche : ɖ←d, ɔ←o, ŋ←n, et <|ewe|><|yo|> (yoruba : branche Volta-Niger, tonale, Afrique de l'Ouest), suivant Song et al. 2024.

Liste blanche explicite, pas d'extraction automatique. Une extraction naïve des caractères du corpus ajoute les artefacts de nettoyage : sur un modèle wolof antérieur, elle avait introduit \r, \n, , œ et surtout l'espace comme token régulier — Whisper encodant l'espace en préfixe Ġ, cela crée une ambiguïté de segmentation.

Embeddings pré-entraînés gelés

resize_token_embeddings impose modules_to_save=["decoder.embed_tokens", "proj_out"], ce qui rend la table entière entraînable — 132,8 M de paramètres contre 49,2 M pour les adaptateurs LoRA.

Mesuré sur un modèle wolof antérieur sans protection :

zone du vocabulaire dérive
ids 0–199 (BPE octets, jamais utilisés) 22,22 %
ids 20000–20199 (BPE courants) 10,33 %
ids 50000–50199 (BPE rares) 6,69 %
écart embed_tokens / proj_out 0,00 % → 46,89 %

Whisper a tie_word_embeddings=True ; PEFT en crée deux copies indépendantes qui divergent librement.

Deux protections combinées ici, l'une ne suffisant pas sans l'autre :

  • masque de gradient sur les lignes 0..51 865 (approche Soft Language Code Tuning, Qian et al., arXiv:2407.06800) ;
  • weight decay nul sur les embeddings — AdamW applique p ← p − lr·wd·p indépendamment du gradient, donc le masque seul laisserait les lignes gelées rétrécir à chaque pas. C'est ce mécanisme qui produit les 22 % ci-dessus.

Plus ensure_weight_tying=True (peft ≥ 0.20), qui préserve le partage de poids et évite les complications au merge_and_unload().

Résultat vérifié sur ce modèle :

zone dérive
ids 0–199 0,000 %
ids 20000–20199 0,000 %
ids 50000–50199 0,000 %
écart embed_tokens / proj_out 0,000 %
tokens ajoutés ewe 40,76 %

Les nouveaux tokens apprennent autant que sans protection (40,76 % contre 41,49 % sur le wolof) : le masque supprime les dégâts collatéraux sans brider l'apprentissage utile. Il a en revanche un effet de bord, décrit ci-dessous.

Limite connue — boucle sur les tokens ajoutés (13 % des énoncés)

Le ratio mots générés / mots de référence est de 0,823. Le déficit se concentre sur une population précise :

population part des énoncés ratio ponctuation finale
effondrés (≤ 10 mots générés) 13,3 % 0,169 0 %
reste 86,7 % 0,920 78 %

Les hypothèses effondrées s'arrêtent en plein milieu d'un mot (Akɔɖ pour Akɔɖu, Enudzɔdzɔŋ pour Enudzɔdzɔ ŋɔŋlɔ ɖi).

Diagnostic — cause identifiée

100 % des séquences effondrées se terminent sur un token ajouté, contre 26 % chez les témoins et 25,4 % en base de comparaison (part des tokens ajoutés toutes positions confondues). Sur 25 séquences, la probabilité d'un tel écart par hasard est de l'ordre de 10⁻¹⁵.

L'inspection des logits au point de coupure donne le mécanisme. Le modèle ne cherche pas à s'arrêter — <|endoftext|> est à −8 à −10 logprob de marge, absent du top 6. Ce qu'il veut, c'est redoubler le token qu'il vient d'émettre :

contexte se termine par token le plus probable logprob
ɖ ɖ −0,184
ƒ ƒ −0,598
̃ ̃ −0,111
ƒ ƒ −0,627
ɔ ɔ −0,822

Cinq cas sur cinq, avec des probabilités de 0,44 à 0,89 — massivement dominantes. Le modèle part en boucle sur les nouveaux tokens (ɖɖɖɖ…). no_repeat_ngram_size=14 interdit cette répétition ; il ne reste que des candidats à −3,5 ou pire, et le faisceau termine la séquence.

C'est pourquoi retirer no_repeat_ngram_size ne change rien : sans lui la sortie partirait en boucle et serait tout aussi fausse, length_penalty=1.2 finissant de toute façon par préférer une séquence courte. Deux chemins, même résultat.

Origine — un effet de bord du gel sélectif

Les 16 tokens ajoutés sont les seules lignes entraînables de la table d'embeddings, tout le reste étant gelé par le masque de gradient décrit plus haut. Ils ont dérivé de 40,76 %.

Avec ensure_weight_tying=True, leur ligne de sortie dans proj_out est partagée avec l'embedding d'entrée. Elle est donc poussée vers le haut à chaque occurrence du caractère, sans que les tokens anciens puissent se réajuster en compensation puisqu'ils sont gelés. Après avoir émis ɖ, l'état caché ressemble à celui qui prédit ɖ, et rien ne vient contrebalancer.

Le gel supprime la dérive des embeddings pré-entraînés — 22 % → 0,000 % — mais crée un déséquilibre entre lignes gelées et lignes libres. Ce compromis n'est, à notre connaissance, documenté nulle part dans la littérature sur l'extension de vocabulaire en ASR.

Hypothèses écartées par la mesure

hypothèse test verdict
no_repeat_ngram_size=14 ablation du paramètre identique au centième
interaction beam × contrainte greedy ratio 0,169 → 0,254 seulement
troncature du décodage MP3 durée décodée vs octets ratio 1,000
référence plus longue que l'audio VAD + mots/s de parole 2,49 vs 2,55 — identique au témoin
voix mal généralisées répartition par locuteur 38 locuteurs sur 54, 8,7–26,8 %
mécanisme appris (label smoothing) taux par checkpoint plat, 12,9–13,5 % de 1 000 à 2 995 pas
norme des nouveaux embeddings normes L2 0,659 vs 0,635 — normale
plafond de 448 tokens ~100 tokens par énoncé hors de cause
masquage EOS / labels inspection du collator 1 seul EOT par séquence, bien placé
TimeStampLogitsProcessor décodage sans skip_special_tokens 0 token d'horodatage
logits processors en général génération sans aucun processeur ratio 0,829 → 0,829

Le modèle de base non fine-tuné, sur ces mêmes énoncés, produit un ratio de 1,085 à 1,173. L'audio contient donc bien la parole sur toute sa durée, et les références sont plausibles.

Pistes de correction

Le correctif est à l'entraînement. min_new_tokens est inopérant : forcer la génération produirait ɖɖɖ…, le modèle voulant précisément répéter.

piste principe risque
LR séparé sur les embeddings — 1e-5 au lieu de 2e-4 limite la dérive des lignes libres sans les figer le plus simple, à tester en premier
dégeler la table, weight decay nul les anciens tokens se réajustent en compensation réintroduit une dérive, à mesurer
sans ensure_weight_tying proj_out évolue indépendamment de l'embedding d'entrée complications documentées au merge_and_unload()
renormalisation périodique des lignes libres sur la moyenne des gelées contrainte explicite plutôt qu'implicite non testé

Conséquence pratique

Le WER de 51,29 comporte une composante d'artefact estimée à plusieurs points. Il reste valide comme point de comparaison à condition que les modèles comparés subissent le même protocole, ce qui est le cas des autres volets de l'étude.

Les fichiers hyp_*.jsonl (hypothèses par checkpoint, alignées sur les références) sont disponibles sur demande.

Entraînement

Base openai/whisper-large-v3-turbo
LoRA r=64, α=128, dropout 0.1
Cibles q_proj, k_proj, v_proj, o_proj, fc1, fc2
Paramètres entraînables 115,6 M / 924,5 M (12,50 %)
LR 2e-4, cosine, 149 pas de warmup
Batch effectif 16 (16 × 1)
Epochs 5 (2 995 pas)
Précision bf16, gradient checkpointing
Label smoothing 0.05
Seed 42 (données : 20260906)
Matériel A100-SXM4 40 Go, 1 h 20

Configuration complète dans griot_run.json.

L'évaluation en cours d'entraînement est en greedy pour ne pas allonger le run ; les checkpoints ont été réévalués en beam 4 a posteriori.

Licence

CC-BY-4.0, suivant google/WaxalNLP (Université du Ghana). Modèle de base sous Apache-2.0.

Références

  • Qian et al., Learn and Don't Forget: Adding a New Language to ASR Foundation Models, arXiv:2407.06800
  • Song et al., LoRA-Whisper: Parameter-Efficient and Extensible Multilingual ASR, 2024
  • Nahabwe et al., Data efficiency benchmark for African ASR, arXiv:2512.10968
Downloads last month
21
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for arnauld-a/griot-lora-ewe-turbo

Adapter
(144)
this model

Dataset used to train arnauld-a/griot-lora-ewe-turbo

Papers for arnauld-a/griot-lora-ewe-turbo

Evaluation results