ghana-speech-id
Language identification for 41 Ghanaian and West African languages.
Speech goes through Omnilingual ASR, which turns audio into text; this library says which language the text is in.
audio ──[sherpa-onnx + omniASR CTC]──▶ transcript ──[this]──▶ language
CPU only, 8.2 MB, about 0.06 ms per classification. The inference core is C++ with a C API, so there is no Python on the device.
pip install ghana-speech-id
How the model behaves, what it scores and where it fails is on the model card. This file is about using it.
Quick start
The head classifies text and cannot read audio, so it needs a recogniser in front of it. Both come from the same repo:
import soundfile as sf
import sherpa_onnx
from ghana_speech_id import GhanaSpeechId
model, tokens = GhanaSpeechId.download_recogniser() # omniASR, 279 MB, once
rec = sherpa_onnx.OfflineRecognizer.from_omnilingual_asr_ctc(model=model, tokens=tokens)
lid = GhanaSpeechId.load() # the head, 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)) # Ewe_ewe (0.50)
load() pulls only the head. The recogniser is downloaded when you ask for it and not
before, so a process that already has transcripts never fetches 279 MB it will not use.
There is one head and nothing to configure.
classify() returns None when no n-gram matched, meaning there was no basis for a
decision. Report that as unknown rather than naming whichever language scored least badly.
p = lid.classify(text)
if p is None:
print("unknown")
else:
print(p.language, p.confidence, p.margin) # margin = top-1 minus top-2
The head is closed-set. It always names one of its 41 languages, including for speech in
a language it has never seen. margin is the signal to threshold on if you need to reject
those — a weak one, so read the model card before relying on it.
How much audio to give it
Five seconds minimum, ten for the best result. This matters more than anything else you control:
| audio | accuracy |
|---|---|
| 3 s | 0.51 |
| 5 s | 0.66 |
| 7 s | 0.76 |
| 10 s | 0.78 |
Past about ten seconds the curve is flat. Check that most of the audio is speech before
transcribing: a recording that is half silence carries half the evidence its duration
suggests. The reference service uses
silero VAD (0.6 MB, bundled with sherpa-onnx) and
rejects anything below 80% speech — demo/modal_app.py has the whole gate.
Command line
ghana-speech-id "obiara na enyi nyɛden dɛ ɔbɔbɔ no nkenyan"
ghana-speech-id --top 3 < transcripts.txt
ghana-speech-id --file transcripts.txt --threads 4
C
GsidConfig cfg;
gsid_config_init(&cfg);
cfg.onnx_path = "300m/head.onnx";
cfg.ngrams_path = "300m/ngrams.txt";
cfg.labels_path = "300m/labels.txt";
cfg.config_path = "300m/head_config.txt";
char err[512];
GsidHead *h = gsid_create(&cfg, err, sizeof err);
GsidResult r = gsid_classify(h, transcript);
if (r.index >= 0) printf("%s %.3f\n", gsid_language(h, r.index), r.confidence);
else printf("unknown\n");
gsid_destroy(h);
index == -1 is the same "no basis for a decision" case as None in Python.
Android and iOS
bindings/android has the JNI shim, a Kotlin wrapper and a CMake file.
bindings/ios has a Swift wrapper and a module map, so the C API imports with no
Objective-C shim and no bridging header.
Speed and footprint
CPU only, and deliberately so: the head is a vocabulary lookup and one sparse gather, so a GPU would spend longer on transfers than on arithmetic. Both runtimes pin the CPU execution provider.
| runtime | per classification | throughput | resident |
|---|---|---|---|
| C++ (Xeon 8558) | 0.064 ms | 15,700/s | 36 MB |
| Python (Ryzen 5 4500U) | 0.09–0.20 ms | 5,500–10,000/s | 90 MB |
Roughly four orders of magnitude cheaper than the speech recognition in front of it. On any device that can run the ASR, language identification is free.
Building from source
cmake -S . -B build -DONNXRUNTIME_ROOT=/path/to/onnxruntime
cmake --build build -j
GSID_MODEL_DIR=model ./build/gsid_selftest
Only dependency is onnxruntime. The ONNX graph uses opset-13 core operators only — no
com.microsoft contrib ops — so it runs in mobile onnxruntime builds. The tf-idf arithmetic
is inside the graph:
inputs indices int64[K], counts float32[K]
tf = 1+log(counts) → ×idf → L2 normalise → Gather(W) → ReduceSum → +b → softmax
outputs logits float32[C], probs float32[C]
The caller supplies n-gram indices and counts. Reproducing scikit-learn's char_wb exactly
is the delicate part, and it has two traps that fail silently rather than raising — see
docs-char-tokenisation.md. Every release is checked with
scripts/cpp_parity.py: sklearn, the Python package and the C++ CLI must agree on all 200
held-out transcripts.
Demo
The Space takes an
uploaded file, a microphone recording, or a one-click sample in any of the 41 languages.
demo/modal_app.py is the whole service — VAD gate, recogniser and head — in one file.
Training and evaluation
scripts/ reproduces the model.
| script | what it does |
|---|---|
setup_lean.sh |
venv and dependencies |
pull_ipa.py |
corpus text without downloading the audio |
decode_base.py |
transcribe with a base omniASR model via sherpa-onnx |
decode_chunked.py |
transcribe fixed-length windows |
build_base_corpus.py |
assemble the training corpus |
train_head.py |
train and evaluate one configuration |
export_onnx.py |
export to ONNX, check parity against sklearn |
cpp_parity.py |
check the C++ and Python runtimes against the trainer |
ood_eval.py |
the out-of-domain evaluation |
eval_duration_curve.sh |
accuracy against real audio duration |
publish_hf.py |
publish to the Hub |
Licence
Code Apache-2.0. Models and data follow the source corpora, CC BY-NC 4.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ghana_speech_id-0.2.0.tar.gz.
File metadata
- Download URL: ghana_speech_id-0.2.0.tar.gz
- Upload date:
- Size: 58.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
141ba361b2a9d337a132704fe0db5949eb1f8702139601491323aa14607c7482
|
|
| MD5 |
6c26313d8dbdab23f28553f78d292b30
|
|
| BLAKE2b-256 |
06fdefb6925cccd20d724f96ee880c6a9f7fc6b81473eed06b5064819f629fd9
|
Provenance
The following attestation bundles were made for ghana_speech_id-0.2.0.tar.gz:
Publisher:
release.yml on GhanaNLP/ghana-speech-id
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ghana_speech_id-0.2.0.tar.gz -
Subject digest:
141ba361b2a9d337a132704fe0db5949eb1f8702139601491323aa14607c7482 - Sigstore transparency entry: 2617742762
- Sigstore integration time:
-
Permalink:
GhanaNLP/ghana-speech-id@4c5550803f6c99c06eb382c764036d58af6f84ab -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/GhanaNLP
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4c5550803f6c99c06eb382c764036d58af6f84ab -
Trigger Event:
release
-
Statement type:
File details
Details for the file ghana_speech_id-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ghana_speech_id-0.2.0-py3-none-any.whl
- Upload date:
- Size: 12.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d9736427fb82ab31ba710bd12f10fb73b2dd08ef4c5b629884371f9ceb3990d
|
|
| MD5 |
664ea5d1c4ca95a45a69ae741520d106
|
|
| BLAKE2b-256 |
c7ae083dc11f16903705db3feb2ef338b217d5678162414afcb9a279a50d4dc3
|
Provenance
The following attestation bundles were made for ghana_speech_id-0.2.0-py3-none-any.whl:
Publisher:
release.yml on GhanaNLP/ghana-speech-id
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ghana_speech_id-0.2.0-py3-none-any.whl -
Subject digest:
3d9736427fb82ab31ba710bd12f10fb73b2dd08ef4c5b629884371f9ceb3990d - Sigstore transparency entry: 2617742769
- Sigstore integration time:
-
Permalink:
GhanaNLP/ghana-speech-id@4c5550803f6c99c06eb382c764036d58af6f84ab -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/GhanaNLP
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4c5550803f6c99c06eb382c764036d58af6f84ab -
Trigger Event:
release
-
Statement type: