Views
No views yet
validation split was
folded into the training pool, and WAXAL's test split was used
as the held-out evaluation set instead.test rather than WAXAL validation, WER/CER numbers from
this checkpoint are not directly comparable to main-best or
main-best-3, which were evaluated on validation. Only compare this
checkpoint's scores against other checkpoints also evaluated on
WAXAL test.facebook/w2v-bert-2.0 — a large-scale, multilingual self-supervised
speech encoder pretrained with a BERT-style masked prediction
objective — is used as the backbone, with a from-scratch
character-level CTC (Connectionist Temporal Classification) head
fine-tuned specifically for Lingala.| Source | Role |
|---|---|
google/WaxalNLP (lin_asr config) | train and validation splits both pooled into training; test split held out untouched as the fixed evaluation benchmark |
| KasuleTrevor/Lingala_100hrs | All splits pooled into training |
test split is the only data used for
evaluation, and it was never included in training.facebook/w2v-bert-2.0Wav2Vec2BertForCTC, add_adapter=TrueWav2Vec2BertProcessor — SeamlessM4TFeatureExtractor
for audio features + a Wav2Vec2CTCTokenizer built from scratch on
the combined training + evaluation transcriptions (character-level
vocabulary, raw/cased text, | as the word delimiter, [PAD]
doubling as the CTC blank token)test split (used here as the
held-out benchmark, since validation was moved into training),
greedy decoding vs. greedy + KenLM
(keystats/waxal-kenlm-models-best). Adding the KLM gives a
consistent, meaningful WER/CER improvement over greedy decoding
alone — pair the two for the best results.1import torch
2import librosa
3from transformers import Wav2Vec2BertForCTC, Wav2Vec2BertProcessor
4
5MODEL_ID = "keystats/w2v-bert-2.0-lingala-main-best-2"
6DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
7
8processor = Wav2Vec2BertProcessor.from_pretrained(MODEL_ID)
9model = Wav2Vec2BertForCTC.from_pretrained(MODEL_ID).to(DEVICE).eval()
10
11audio_array, sr = librosa.load("path/to/audio.wav", sr=16000, mono=True)
12inputs = processor(audio_array, sampling_rate=16000, return_tensors="pt")
13with torch.no_grad():
14 logits = model(input_features=inputs.input_features.to(DEVICE)).logits
15
16predicted_ids = torch.argmax(logits, dim=-1)
17transcription = processor.batch_decode(predicted_ids)[0]
18
19print(transcription) # cased, punctuated Lingala text1# pip install pyctcdecode
2# pip install https://github.com/kpu/kenlm/archive/master.zip
3
4import torch
5import librosa
6from huggingface_hub import hf_hub_download
7from transformers import Wav2Vec2BertForCTC, Wav2Vec2BertProcessor
8from pyctcdecode import build_ctcdecoder
9
10MODEL_ID = "keystats/w2v-bert-2.0-lingala-main-best-2"
11KLM_REPO_ID = "keystats/waxal-kenlm-models-best"
12DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
13
14processor = Wav2Vec2BertProcessor.from_pretrained(MODEL_ID)
15model = Wav2Vec2BertForCTC.from_pretrained(MODEL_ID).to(DEVICE).eval()
16
17klm_path = hf_hub_download(repo_id=KLM_REPO_ID, repo_type="dataset",
18 filename="lingala/lingala_5gram_correct-best.arpa")
19
20def build_vocab_list(tokenizer, vocab_size):
21 vocab_dict = tokenizer.get_vocab()
22 vocab_list = [None] * vocab_size
23 for tok, idx in sorted(vocab_dict.items(), key=lambda kv: kv[1]):
24 if idx < vocab_size:
25 vocab_list[idx] = tok
26 pad_id = tokenizer.pad_token_id
27 if pad_id is not None and pad_id < len(vocab_list):
28 vocab_list[pad_id] = ""
29 word_delim = getattr(tokenizer, "word_delimiter_token", None)
30 if word_delim:
31 delim_id = vocab_dict.get(word_delim)
32 if delim_id is not None:
33 vocab_list[delim_id] = " "
34 return vocab_list
35
36vocab_list = build_vocab_list(processor.tokenizer, model.config.vocab_size)
37decoder = build_ctcdecoder(
38 vocab_list,
39 kenlm_model_path=klm_path,
40 alpha=0.5,
41 beta=0.7,
42)
43
44audio_array, sr = librosa.load("path/to/audio.wav", sr=16000, mono=True)
45inputs = processor(audio_array, sampling_rate=16000, return_tensors="pt")
46with torch.no_grad():
47 logits = model(input_features=inputs.input_features.to(DEVICE)).logits
48
49transcription = decoder.decode(logits.cpu().numpy()[0], beam_width=100)
50print(transcription)validation was used in training here (unlike other
checkpoints in this family), don't use WAXAL validation to
evaluate this specific checkpoint — it's no longer held out.1@misc{keystats_wav2vec2bert_lingala_best2,
2 title={w2v-bert-2.0-lingala-main-best-2: A Lingala ASR model fine-tuned from facebook/w2v-bert-2.0},
3 author={keystats},
4 year={2026},
5 howpublished={\url{https://huggingface.co/keystats/w2v-bert-2.0-lingala-main-best-2}}
6}
7
8@misc{waxal,
9 title={WAXAL: A Multilingual African Speech Dataset},
10 author={Google},
11 howpublished={\url{https://huggingface.co/datasets/google/WaxalNLP}}
12}
13
14@misc{kasule_lingala_100hrs,
15 title={Lingala\_100hrs},
16 author={KasuleTrevor},
17 howpublished={\url{https://huggingface.co/datasets/KasuleTrevor/Lingala_100hrs}}
18}
19
20@inproceedings{w2vbert2,
21 title={Seamless: Multilingual Expressive and Streaming Speech Translation},
22 author={Seamless Communication and others},
23 year={2023},
24 howpublished={\url{https://huggingface.co/facebook/w2v-bert-2.0}}
25}