Views
No views yet
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.-best checkpoint when you want
cased/punctuated output directly from the acoustic model; use the
keystats/w2v-bert-2.0-lingala-main
sibling checkpoint if you'd rather have the model focus purely on
word content.train split only from WAXAL, all splits pooled from
the Kasule dataset, deduplicated by audio hash and by
transcription-within-source):| Source | Role |
|---|---|
google/WaxalNLP (lin_asr config) | Benchmark dataset — train split pooled into training, validation split held out untouched as the fixed evaluation benchmark |
| KasuleTrevor/Lingala_100hrs | All splits pooled into training |
validation 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 + validation transcriptions (character-level
vocabulary, raw/cased text, | as the word delimiter, [PAD]
doubling as the CTC blank token)output_steps < 2 * label_length) are dropped from
both train and validation before trainingvalidation split, 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.pyctcdecode +
KenLM, noticeably higher accuracy via beam-search decoding with a
matching language model.1import torch
2import librosa
3from transformers import Wav2Vec2BertForCTC, Wav2Vec2BertProcessor
4
5MODEL_ID = "keystats/w2v-bert-2.0-lingala-main-best"
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 textlingala/lingala_5gram_correct-best.arpa). Pairing this ASR model
with its matching KLM via beam-search decoding gives a significant
accuracy improvement over greedy decoding alone.-best model pairs with the cased
keystats/waxal-kenlm-models-best repo, while the -main sibling
checkpoint pairs with the separate keystats/waxal-kenlm-models repo
(normalized text). Mixing a cased-text ASR model with a
normalized-text KLM (or vice versa) will cause a vocabulary mismatch
during decoding.1# 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"
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, # LM weight -- tune against your own validation set
41 beta=0.7, # word insertion bonus -- tune against your own validation set
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)1@misc{keystats_wav2vec2bert_lingala_best,
2 title={w2v-bert-2.0-lingala-main-best: 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}}
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}