w2v-bert-2.0-shona

A Shona automatic speech recognition (ASR) model, continued fine-tuning from keystats/w2v-bert-2.0-shona-main-best-3 on the same combined Shona speech corpus (see Training data below).

Model description

This checkpoint is the third link in a fine-tuning chain: facebook/w2v-bert-2.0 → w2v-bert-2.0-shona-main-best → w2v-bert-2.0-shona-main-best-3 → this model. Each stage continues training the previous checkpoint for a further, typically shorter, run at a progressively lower learning rate on the same data, rather than starting from scratch.

Text casing note: training targets were kept in their raw, cased form, the same convention used throughout this fine-tuning chain — the vocabulary and outputs preserve casing and punctuation, not lowercased text.

Training data

Same training pool and split treatment as every checkpoint in this chain:

Source Role
google/WaxalNLP (sna_asr config) train split pooled into training; validation split held out untouched as the fixed evaluation benchmark
badrex/shona-speech All splits (train/val/test) pooled into training
Beijuka/DigitalUmuganda_AfriVoice_shona All splits pooled into training (only has train)
shunyalabs/shona-speech-dataset All splits (train/val/test) pooled into training
realtime-speech/shona2 All splits (train/val/test) pooled into training

None of the four additional sources overlap with WAXAL's own train/validation/test split boundaries, so pooling every split from them carries no evaluation leakage risk. WAXAL's validation split is the only data used for evaluation, and it was never included in training.

Training procedure

  • Starting checkpoint: keystats/w2v-bert-2.0-shona-main-best-3 (continued fine-tuning, not trained from scratch)
  • Architecture: Wav2Vec2BertForCTC, add_adapter=True
  • Processor: Wav2Vec2BertProcessor — 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)
  • Sample rate: 16 kHz mono
  • Epochs: 1 (short continuation pass, not a full retrain)
  • Effective batch size: 32 (per-device batch size 4 × gradient accumulation 8)
  • Learning rate: 1e-5, cosine schedule, 10% warmup
  • Precision: fp16, gradient checkpointing enabled
  • Regularization: attention/hidden/feature-projection dropout 0.05
  • Data filtering: clips whose transcript is too long for CTC to align within the available encoder output length ("CTC-impossible" clips, roughly output_steps < 2 * label_length) are dropped from both train and validation before training
  • Seed: 42 (deterministic — same seed for Python/NumPy/PyTorch/CUDA)

Evaluation results

Evaluated on the WAXAL Shona validation 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.

How to use

Two ways to use this model, depending on your needs:

  • Option 1 — model alone (greedy decoding): faster, no extra dependencies, slightly lower accuracy.
  • Option 2 — model + KLM (recommended): requires pyctcdecode + KenLM, noticeably higher accuracy via beam-search decoding with a matching language model.

Option 1 — model alone (greedy decoding)

import torch
import librosa
from transformers import Wav2Vec2BertForCTC, Wav2Vec2BertProcessor

MODEL_ID = "keystats/w2v-bert-2.0-shona"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"

processor = Wav2Vec2BertProcessor.from_pretrained(MODEL_ID)
model = Wav2Vec2BertForCTC.from_pretrained(MODEL_ID).to(DEVICE).eval()

audio_array, sr = librosa.load("path/to/audio.wav", sr=16000, mono=True)
inputs = processor(audio_array, sampling_rate=16000, return_tensors="pt")
with torch.no_grad():
    logits = model(input_features=inputs.input_features.to(DEVICE)).logits

predicted_ids = torch.argmax(logits, dim=-1)
transcription = processor.batch_decode(predicted_ids)[0]

print(transcription)  # cased, punctuated Shona text

Option 2 — model + KLM (recommended, higher accuracy)

A companion n-gram KenLM language model, trained on the same raw/cased text convention as this ASR model, is available at keystats/waxal-kenlm-models-best (shona/shona_5gram_correct.arpa). Pairing this ASR model with its matching KLM via beam-search decoding gives a significant accuracy improvement over greedy decoding alone.

Important: use the matching KLM variant for whichever ASR checkpoint you're using — this model pairs with the cased keystats/waxal-kenlm-models-best repo, while the normalized (lowercased) keystats/w2v-bert-2.0-shona-main checkpoint pairs with the separate keystats/waxal-kenlm-models repo instead. Mixing a cased-text ASR model with a normalized-text KLM (or vice versa) will cause a vocabulary mismatch during decoding.

# pip install pyctcdecode
# pip install https://github.com/kpu/kenlm/archive/master.zip

import torch
import librosa
from huggingface_hub import hf_hub_download
from transformers import Wav2Vec2BertForCTC, Wav2Vec2BertProcessor
from pyctcdecode import build_ctcdecoder

MODEL_ID = "keystats/w2v-bert-2.0-shona"
KLM_REPO_ID = "keystats/waxal-kenlm-models-best"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"

processor = Wav2Vec2BertProcessor.from_pretrained(MODEL_ID)
model = Wav2Vec2BertForCTC.from_pretrained(MODEL_ID).to(DEVICE).eval()

klm_path = hf_hub_download(repo_id=KLM_REPO_ID, repo_type="dataset",
                            filename="shona/shona_5gram_correct.arpa")

def build_vocab_list(tokenizer, vocab_size):
    vocab_dict = tokenizer.get_vocab()
    vocab_list = [None] * vocab_size
    for tok, idx in sorted(vocab_dict.items(), key=lambda kv: kv[1]):
        if idx < vocab_size:
            vocab_list[idx] = tok
    pad_id = tokenizer.pad_token_id
    if pad_id is not None and pad_id < len(vocab_list):
        vocab_list[pad_id] = ""
    word_delim = getattr(tokenizer, "word_delimiter_token", None)
    if word_delim:
        delim_id = vocab_dict.get(word_delim)
        if delim_id is not None:
            vocab_list[delim_id] = " "
    return vocab_list

vocab_list = build_vocab_list(processor.tokenizer, model.config.vocab_size)
decoder = build_ctcdecoder(
    vocab_list,
    kenlm_model_path=klm_path,
    alpha=0.5,   # LM weight -- tune against your own validation set
    beta=0.7,    # word insertion bonus -- tune against your own validation set
)

audio_array, sr = librosa.load("path/to/audio.wav", sr=16000, mono=True)
inputs = processor(audio_array, sampling_rate=16000, return_tensors="pt")
with torch.no_grad():
    logits = model(input_features=inputs.input_features.to(DEVICE)).logits

transcription = decoder.decode(logits.cpu().numpy()[0], beam_width=100)
print(transcription)

Note on alpha/beta: the values above are starting points, not universal defaults — grid-search them against your own labeled validation set, since optimal weights depend on your specific audio domain.

Intended uses & limitations

  • Intended for transcribing spoken Shona audio into cased, punctuated text.
  • As a CTC-based model, it assumes single-speaker, forward-only audio and has no mechanism for overlapping speech from multiple speakers.
  • Trained on a mix of WAXAL and four community-contributed Shona datasets; acoustic conditions, recording quality, and dialectal coverage reflect that combined pool, not any single controlled source.
  • This is a continued fine-tune of -main-best-3, itself a continued fine-tune of -main-best — treat this as a short, low-learning-rate refinement pass over an already-trained checkpoint and the same data, not an independent model. Given the short 1-epoch continuation, gains over -main-best-3 may be modest; compare both checkpoints on your own validation set before assuming this one is strictly better.

Citation

If you use this model, please cite the training/fine-tuning work and the underlying datasets:

@misc{keystats_wav2vec2bert_shona,
  title={w2v-bert-2.0-shona: A Shona ASR model continued fine-tuned from w2v-bert-2.0-shona-main-best-3},
  author={keystats},
  year={2026},
  howpublished={\url{https://huggingface.co/keystats/w2v-bert-2.0-shona}}
}

@misc{waxal,
  title={WAXAL: A Multilingual African Speech Dataset},
  author={Google},
  howpublished={\url{https://huggingface.co/datasets/google/WaxalNLP}}
}

@misc{badrex_shona_speech,
  title={shona-speech},
  author={badrex},
  howpublished={\url{https://huggingface.co/datasets/badrex/shona-speech}}
}

@misc{beijuka_shona,
  title={DigitalUmuganda\_AfriVoice\_shona},
  author={Beijuka},
  howpublished={\url{https://huggingface.co/datasets/Beijuka/DigitalUmuganda_AfriVoice_shona}}
}

@misc{shunyalabs_shona,
  title={shona-speech-dataset},
  author={shunyalabs},
  howpublished={\url{https://huggingface.co/datasets/shunyalabs/shona-speech-dataset}}
}

@misc{realtime_speech_shona,
  title={shona2},
  author={realtime-speech},
  howpublished={\url{https://huggingface.co/datasets/realtime-speech/shona2}}
}

@inproceedings{w2vbert2,
  title={Seamless: Multilingual Expressive and Streaming Speech Translation},
  author={Seamless Communication and others},
  year={2023},
  howpublished={\url{https://huggingface.co/facebook/w2v-bert-2.0}}
}
Downloads last month
41
Safetensors
Model size
0.6B params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for keystats/w2v-bert-2.0-shona

Datasets used to train keystats/w2v-bert-2.0-shona