ghana-speech-id

Language identification for 41 Ghanaian and West African languages, over speech transcripts.

It sits on top of Omnilingual ASR: that model turns audio into text, and this one says which language the text is in.

audio ──[sherpa-onnx + omniASR CTC]──▶ transcript ──[this]──▶ language

One head, in 300m/, built on the omniASR CTC 300M front end. GhanaSpeechId.load() finds it with no argument — there is nothing to choose.

in-domain out-of-domain size
95.30% 77.6% 8.2 MB

How much audio to give it

Five seconds minimum, ten for the best result. This matters more than the choice of variant, the feature count, or anything else you can tune. Measured on ghana-speech-eval without its bible_* configs — the out-of-domain set described under Evaluation below — on real audio of each length, not on truncated transcripts, which flatter short input by about six points because the recogniser still had the whole clip:

audio accuracy clips scored mean characters empty transcripts
3 s 0.506 57,840 19.7 4.6%
5 s 0.657 32,816 33.4 0.7%
7 s 0.759 20,439 47.7 0.1%
whole clips (mean 9.7 s) 0.777 13,963 80.0 0.9%

Five seconds is a floor, not a target — it buys 0.66, well short of what the model can do. The curve then flattens: seven seconds is within two points of whole clips averaging 9.7 s, so ten seconds is where the returns run out.

Short audio does not simply produce less text, it produces worse text. Three seconds yields 19.7 characters on average and 4.6% of clips come back empty, against 80 characters and 0.9% for whole clips — the recogniser needs context and starves without it.

Check that most of it is speech. A recording that is half silence carries half the evidence its duration suggests. The reference service runs silero VAD and rejects anything below 80% speech before transcribing.

What is in this repository

path what it is size
300m/ the language-ID head — the model this card describes 8.2 MB
asr/ the Omnilingual ASR recogniser that produces the transcripts it reads 279 MB

The head classifies text and cannot read audio, so asr/ is mirrored here to keep both in one place. That model is Meta's, not ours, released under Apache 2.0 — asr/LICENSE and asr/ATTRIBUTION.md travel with it.

Usage

import soundfile as sf
import sherpa_onnx
from ghana_speech_id import GhanaSpeechId

model, tokens = GhanaSpeechId.download_recogniser()   # asr/, 279 MB, once
rec = sherpa_onnx.OfflineRecognizer.from_omnilingual_asr_ctc(model=model, tokens=tokens)
lid = GhanaSpeechId.load()                            # 300m/, 8.2 MB

wav, sr = sf.read("clip.wav", dtype="float32")
s = rec.create_stream()
s.accept_waveform(sr, wav)
rec.decode_stream(s)
print(lid.classify(s.result.text))

load() pulls only the head; the recogniser downloads when you ask for it and not before.

On device there is no Python: sherpa-onnx produces the transcript and the head runs in onnxruntime through a C API, with Kotlin and Swift bindings. See the repository.

Evaluation

In-domain holds out the last 15% of each language by id. The audio is scripture read in order, so a contiguous tail approximates holding out whole books; a random split scores about two points higher, and that gap is passage-local memorisation.

Out-of-domain is ghana-speech-eval without its bible_* configs, since those are the training domain. Five unrelated domains, 13,963 scored clips.

finance jw lds unicef waxal
42% 76% 81% 92% 89%

How it is built

A linear classifier over character n-grams of the transcript, with tf-idf folded into the ONNX graph. Trained on 40-character windows (about 3.3 s) rather than whole transcripts, which is worth +1.1 points out of domain and +4.7 at one second of speech. Inference classifies the whole transcript in one pass.

Limitations

Closed set. The head always names one of its 41 classes. Ga, Ahanta and Ikposo are not among them and return their nearest relative — Ga as Dangme, Ahanta as Nzema. The top-1 minus top-2 margin gives a rejection signal, but a weak one: at 80% of in-set answers retained it rejects about half of out-of-set speech.

Fante collapses into Twi out of domain, 0.12–0.52 across three configs, despite 0.98 F1 on clean text. Recognition noise erases an Akan boundary the head can otherwise learn.

No English class. The available Ghanaian English corpus is low-passed — 93% of its energy below 1 kHz — and a real ASR returns nothing for 82% of it.

Domain still matters. 95% in-domain against 78% out of domain, and finance recordings are the weakest at 42%.

Licence

Code Apache-2.0. Model weights and data CC BY-NC 4.0, following the source corpora.

Downloads last month
13
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Datasets used to train ghanaopenai/ghana-speech-id