Open benchmarking toolkit for evaluating NLP models on low-resource Burkinabe languages. Developed as part of a UNICEF consultancy on NLP for linguistic inclusion in West and Central Africa. Currently focuses on Moore and Dioula across three evaluation tasks: MT, ASR, and TTS.
| Language | ISO 639-3 | HF Code | Resource Level | MT | ASR | TTS |
|---|---|---|---|---|---|---|
| Moore | mos | mos_Latn | Low | Yes | Yes | Yes |
| Dioula | dyu | dyu_Latn | Low | Yes | Yes | Yes |
| Task | Primary Metric | Baselines | Description |
|---|---|---|---|
| MT | chrF++ | NLLB-200 (3.3B, 1.3B, 600M) | Machine Translation (FR <-> target) |
| ASR | WER | Whisper, MMS | Automatic Speech Recognition |
| TTS | UTMOS | MMS-TTS | Text-to-Speech (naturalness + intelligibility) |
PycergBench uses a provider-based architecture inspired by the companion ai-service project. Every model or API is wrapped in a provider that implements a standard interface (MTProvider, ASRProvider, TTSProvider). The evaluators are completely model-agnostic.
providers/
base.py # Abstract interfaces: MTProvider, ASRProvider, TTSProvider
factory.py # Resolves a model_id to the right provider
api.py # HTTP API providers (for Docker microservices)
mt/hf_seq2seq.py # HuggingFace seq2seq models (NLLB, mBART, ...)
asr/hf_pipeline.py # HuggingFace ASR pipeline (Whisper, MMS, ...)
tts/hf_tts.py # HuggingFace TTS models (MMS-TTS, VITS, ...)
To add a new model backend, implement the relevant abstract class and register it in the factory.
git clone https://github.com/pycerg-ai/pycergai-benchmark.git
cd pycergai-benchmark
python -m venv .venv
.venv\Scripts\activate # Windows
pip install -e .
# Optional: TTS naturalness scoring
pip install git+https://github.com/sarulab-speech/UTMOSv2.git
Create a .env file at the repo root:
HF_TOKEN=hf_your_token_here
Or set the environment variable directly:
export HF_TOKEN=hf_your_token_here
Datasets are configured in configs/datasets/<language>.yaml. Each source specifies the HF repo, column mappings, and benchmark groups -- named subsets of splits to evaluate together.
language: mos_Latn
tasks:
mt:
sources:
- id: goai_flores_fra_mos
type: huggingface
hf_dataset: goaicorp/flores-plus-fra-mos
source_column: sentence_fra_Latn
target_column: sentence_mos_Latn
source_lang: fra_Latn
target_lang: mos_Latn
benchmark_groups:
general:
splits: [test, devtest]
description: "FLORES+ general knowledge (test + devtest)"
health:
splits: [health]
education:
splits: [education]
child_protection:
splits: [child_protection]
environment:
splits: [environment]
Moore (mos_Latn)
| Task | Dataset | Benchmark Groups |
|---|---|---|
| MT | goaicorp/flores-plus-fra-mos | general (2009), health (180), education (551), child_protection (28), environment (334) |
| ASR | goaicorp/GOAI-MooreSpeechCorpora | general (1012) |
| TTS | goaicorp/flores-plus-fra-mos | general (2009), health (180), education (551), child_protection (28), environment (334) |
Dioula (dyu_Latn)
| Task | Dataset | Benchmark Groups |
|---|---|---|
| MT | goaicorp/flores-plus-fra-dyu | general (2009), health (180), education (551), child_protection (28), environment (334) |
| ASR | goaicorp/goai-dioula-speech | general (dev split) |
| TTS | goaicorp/flores-plus-fra-dyu | general (2009), health (180), education (551), child_protection (28), environment (334) |
By default (no --split or --group), the script evaluates all benchmark groups defined in the YAML config. The model is loaded once and reused across all groups.
Evaluates general knowledge and all 4 domains in a single command:
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model facebook/nllb-200-distilled-600M \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model facebook/nllb-200-distilled-600M \
--group health \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model facebook/nllb-200-distilled-600M \
--source-lang mos_Latn \
--target-lang fra_Latn \
--source-column sentence_mos_Latn \
--target-column sentence_fra_Latn \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model facebook/nllb-200-distilled-600M \
--source-lang mos_Latn \
--target-lang fra_Latn \
--source-column sentence_mos_Latn \
--target-column sentence_fra_Latn \
--group general \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task asr \
--language mos_Latn \
--model openai/whisper-large-v3 \
--verbose
python scripts/run_benchmark.py \
--task tts \
--language mos_Latn \
--model facebook/mms-tts-mos \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language dyu_Latn \
--model facebook/nllb-200-distilled-600M \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language dyu_Latn \
--model facebook/nllb-200-distilled-600M \
--source-lang dyu_Latn \
--target-lang fra_Latn \
--source-column sentence_dyu_Latn \
--target-column sentence_fra_Latn \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task asr \
--language dyu_Latn \
--model openai/whisper-large-v3 \
--verbose
python scripts/run_benchmark.py \
--task tts \
--language dyu_Latn \
--model facebook/mms-tts-dyu \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model api:http://localhost:5003 \
--source-lang fra_Latn \
--target-lang mos_Latn
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model facebook/nllb-200-distilled-600M \
--split test \
--no-comet \
--verbose
Results are saved in a clean hierarchy:
results/
mos_Latn/
mt/
fra_Latn_mos_Latn/ # MT: source_target (avoids clash with reverse or other langs)
nllb-200-distilled-600M/
general.json
health.json
...
summary.json
comparison.json
comparison.md
mos_Latn_fra_Latn/ # reverse direction
...
asr/
whisper-large-v3/
general.json
summary.json
comparison.json
comparison.md
tts/
mms-tts-mos/
general.json
summary.json
dyu_Latn/
mt/
fra_Latn_dyu_Latn/
...
Paths use CLI --language first, then task. MT adds <source_lang>_<target_lang>. Each JSON includes benchmark_language and (for MT) mt_pair.
groups_evaluated.Each JSON contains metrics, model info, and a UTC timestamp. When a group is re-evaluated, the file is overwritten (latest run = reference).
After evaluating multiple models, generate (or refresh) comparison tables:
# Compare all MT models for Moore
python scripts/compare_results.py --language mos_Latn --task mt
# Compare all ASR models for Dioula
python scripts/compare_results.py --language dyu_Latn --task asr
# Compare all tasks for a language
python scripts/compare_results.py --language mos_Latn --task all
This reads existing result files and produces comparison.json + comparison.md in results/<language>/<task>/.
python scripts/run_benchmark.py [OPTIONS]
Options:
--task [mt|asr|tts] Evaluation task (required)
--language TEXT HF language code (required)
--model TEXT Model ID, path, or api:<url> (required)
--source-lang TEXT Source language (MT only)
--target-lang TEXT Target language (MT only)
--source-column TEXT Override source column name (MT)
--target-column TEXT Override target column name (MT)
--dataset TEXT Explicit source: hf:<id> or local:<path>
--split TEXT HF dataset split (bypasses groups)
--group TEXT Single benchmark group to evaluate
--output TEXT Root output directory [default: results]
--batch-size INTEGER [default: 8]
--max-samples INTEGER Limit samples per group
--device [cpu|cuda|mps|auto] [default: auto]
--submit-to-leaderboard Append to leaderboard
--loopback-asr-model TEXT TTS loopback model
--no-comet Skip COMET (MT)
--verbose Debug logging
--version Show version
MTProvider).providers/factory.py or pass it directly via the Python API.from pycergai_benchmark.providers.base import MTProvider, ProviderInfo
from pycergai_benchmark.tasks.mt import MTEvaluator
class MyCustomMT(MTProvider):
def translate(self, text, source_lang, target_lang):
return my_custom_translate(text, source_lang, target_lang)
def info(self):
return ProviderInfo(name="my_custom", model_id="custom/v1")
provider = MyCustomMT()
evaluator = MTEvaluator(provider, "fra_Latn", "mos_Latn")
result = evaluator.evaluate(data)
from pycergai_benchmark.core.config_loader import ConfigLoader
from pycergai_benchmark.core.data_loader import DataLoader
from pycergai_benchmark.providers.factory import create_mt_provider
from pycergai_benchmark.tasks.mt import MTEvaluator
cfg = ConfigLoader()
loader = DataLoader()
source_cfg = cfg.get_dataset_source("mos_Latn", "mt")
data = loader.load_from_config(source_cfg, "mt", split="test")
provider = create_mt_provider("facebook/nllb-200-distilled-600M")
evaluator = MTEvaluator(provider, "fra_Latn", "mos_Latn")
result = evaluator.evaluate(data, compute_comet=False)
print(f"chrF++: {result.overall_chrf:.1f}")
print(f"BLEU: {result.overall_bleu:.1f}")
@misc{pycergai_benchmark_2026,
title={PycergAI Benchmark: Open Evaluation Platform for
West and Central African Language Models},
author={PycergAI Benchmark Contributors},
year={2026},
howpublished={\url{https://github.com/pycerg-ai/pycergai-benchmark}},
note={Developed under UNICEF WCARO consultancy on NLP
for linguistic inclusion}
}
Apache 2.0 -- see LICENSE.
18 commits
Python
95.5%
Jupyter Notebook
2.8%
Open benchmarking toolkit for evaluating NLP models on low-resource Burkinabe languages. Developed as part of a UNICEF consultancy on NLP for linguistic inclusion in West and Central Africa. Currently focuses on Moore and Dioula across three evaluation tasks: MT, ASR, and TTS.
| Language | ISO 639-3 | HF Code | Resource Level | MT | ASR | TTS |
|---|---|---|---|---|---|---|
| Moore | mos | mos_Latn | Low | Yes | Yes | Yes |
| Dioula | dyu | dyu_Latn | Low | Yes | Yes | Yes |
| Task | Primary Metric | Baselines | Description |
|---|---|---|---|
| MT | chrF++ | NLLB-200 (3.3B, 1.3B, 600M) | Machine Translation (FR <-> target) |
| ASR | WER | Whisper, MMS | Automatic Speech Recognition |
| TTS | UTMOS | MMS-TTS | Text-to-Speech (naturalness + intelligibility) |
PycergBench uses a provider-based architecture inspired by the companion ai-service project. Every model or API is wrapped in a provider that implements a standard interface (MTProvider, ASRProvider, TTSProvider). The evaluators are completely model-agnostic.
providers/
base.py # Abstract interfaces: MTProvider, ASRProvider, TTSProvider
factory.py # Resolves a model_id to the right provider
api.py # HTTP API providers (for Docker microservices)
mt/hf_seq2seq.py # HuggingFace seq2seq models (NLLB, mBART, ...)
asr/hf_pipeline.py # HuggingFace ASR pipeline (Whisper, MMS, ...)
tts/hf_tts.py # HuggingFace TTS models (MMS-TTS, VITS, ...)
To add a new model backend, implement the relevant abstract class and register it in the factory.
git clone https://github.com/pycerg-ai/pycergai-benchmark.git
cd pycergai-benchmark
python -m venv .venv
.venv\Scripts\activate # Windows
pip install -e .
# Optional: TTS naturalness scoring
pip install git+https://github.com/sarulab-speech/UTMOSv2.git
Create a .env file at the repo root:
HF_TOKEN=hf_your_token_here
Or set the environment variable directly:
export HF_TOKEN=hf_your_token_here
Datasets are configured in configs/datasets/<language>.yaml. Each source specifies the HF repo, column mappings, and benchmark groups -- named subsets of splits to evaluate together.
language: mos_Latn
tasks:
mt:
sources:
- id: goai_flores_fra_mos
type: huggingface
hf_dataset: goaicorp/flores-plus-fra-mos
source_column: sentence_fra_Latn
target_column: sentence_mos_Latn
source_lang: fra_Latn
target_lang: mos_Latn
benchmark_groups:
general:
splits: [test, devtest]
description: "FLORES+ general knowledge (test + devtest)"
health:
splits: [health]
education:
splits: [education]
child_protection:
splits: [child_protection]
environment:
splits: [environment]
Moore (mos_Latn)
| Task | Dataset | Benchmark Groups |
|---|---|---|
| MT | goaicorp/flores-plus-fra-mos | general (2009), health (180), education (551), child_protection (28), environment (334) |
| ASR | goaicorp/GOAI-MooreSpeechCorpora | general (1012) |
| TTS | goaicorp/flores-plus-fra-mos | general (2009), health (180), education (551), child_protection (28), environment (334) |
Dioula (dyu_Latn)
| Task | Dataset | Benchmark Groups |
|---|---|---|
| MT | goaicorp/flores-plus-fra-dyu | general (2009), health (180), education (551), child_protection (28), environment (334) |
| ASR | goaicorp/goai-dioula-speech | general (dev split) |
| TTS | goaicorp/flores-plus-fra-dyu | general (2009), health (180), education (551), child_protection (28), environment (334) |
By default (no --split or --group), the script evaluates all benchmark groups defined in the YAML config. The model is loaded once and reused across all groups.
Evaluates general knowledge and all 4 domains in a single command:
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model facebook/nllb-200-distilled-600M \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model facebook/nllb-200-distilled-600M \
--group health \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model facebook/nllb-200-distilled-600M \
--source-lang mos_Latn \
--target-lang fra_Latn \
--source-column sentence_mos_Latn \
--target-column sentence_fra_Latn \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model facebook/nllb-200-distilled-600M \
--source-lang mos_Latn \
--target-lang fra_Latn \
--source-column sentence_mos_Latn \
--target-column sentence_fra_Latn \
--group general \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task asr \
--language mos_Latn \
--model openai/whisper-large-v3 \
--verbose
python scripts/run_benchmark.py \
--task tts \
--language mos_Latn \
--model facebook/mms-tts-mos \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language dyu_Latn \
--model facebook/nllb-200-distilled-600M \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language dyu_Latn \
--model facebook/nllb-200-distilled-600M \
--source-lang dyu_Latn \
--target-lang fra_Latn \
--source-column sentence_dyu_Latn \
--target-column sentence_fra_Latn \
--no-comet \
--verbose
python scripts/run_benchmark.py \
--task asr \
--language dyu_Latn \
--model openai/whisper-large-v3 \
--verbose
python scripts/run_benchmark.py \
--task tts \
--language dyu_Latn \
--model facebook/mms-tts-dyu \
--verbose
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model api:http://localhost:5003 \
--source-lang fra_Latn \
--target-lang mos_Latn
python scripts/run_benchmark.py \
--task mt \
--language mos_Latn \
--model facebook/nllb-200-distilled-600M \
--split test \
--no-comet \
--verbose
Results are saved in a clean hierarchy:
results/
mos_Latn/
mt/
fra_Latn_mos_Latn/ # MT: source_target (avoids clash with reverse or other langs)
nllb-200-distilled-600M/
general.json
health.json
...
summary.json
comparison.json
comparison.md
mos_Latn_fra_Latn/ # reverse direction
...
asr/
whisper-large-v3/
general.json
summary.json
comparison.json
comparison.md
tts/
mms-tts-mos/
general.json
summary.json
dyu_Latn/
mt/
fra_Latn_dyu_Latn/
...
Paths use CLI --language first, then task. MT adds <source_lang>_<target_lang>. Each JSON includes benchmark_language and (for MT) mt_pair.
groups_evaluated.Each JSON contains metrics, model info, and a UTC timestamp. When a group is re-evaluated, the file is overwritten (latest run = reference).
After evaluating multiple models, generate (or refresh) comparison tables:
# Compare all MT models for Moore
python scripts/compare_results.py --language mos_Latn --task mt
# Compare all ASR models for Dioula
python scripts/compare_results.py --language dyu_Latn --task asr
# Compare all tasks for a language
python scripts/compare_results.py --language mos_Latn --task all
This reads existing result files and produces comparison.json + comparison.md in results/<language>/<task>/.
python scripts/run_benchmark.py [OPTIONS]
Options:
--task [mt|asr|tts] Evaluation task (required)
--language TEXT HF language code (required)
--model TEXT Model ID, path, or api:<url> (required)
--source-lang TEXT Source language (MT only)
--target-lang TEXT Target language (MT only)
--source-column TEXT Override source column name (MT)
--target-column TEXT Override target column name (MT)
--dataset TEXT Explicit source: hf:<id> or local:<path>
--split TEXT HF dataset split (bypasses groups)
--group TEXT Single benchmark group to evaluate
--output TEXT Root output directory [default: results]
--batch-size INTEGER [default: 8]
--max-samples INTEGER Limit samples per group
--device [cpu|cuda|mps|auto] [default: auto]
--submit-to-leaderboard Append to leaderboard
--loopback-asr-model TEXT TTS loopback model
--no-comet Skip COMET (MT)
--verbose Debug logging
--version Show version
MTProvider).providers/factory.py or pass it directly via the Python API.from pycergai_benchmark.providers.base import MTProvider, ProviderInfo
from pycergai_benchmark.tasks.mt import MTEvaluator
class MyCustomMT(MTProvider):
def translate(self, text, source_lang, target_lang):
return my_custom_translate(text, source_lang, target_lang)
def info(self):
return ProviderInfo(name="my_custom", model_id="custom/v1")
provider = MyCustomMT()
evaluator = MTEvaluator(provider, "fra_Latn", "mos_Latn")
result = evaluator.evaluate(data)
from pycergai_benchmark.core.config_loader import ConfigLoader
from pycergai_benchmark.core.data_loader import DataLoader
from pycergai_benchmark.providers.factory import create_mt_provider
from pycergai_benchmark.tasks.mt import MTEvaluator
cfg = ConfigLoader()
loader = DataLoader()
source_cfg = cfg.get_dataset_source("mos_Latn", "mt")
data = loader.load_from_config(source_cfg, "mt", split="test")
provider = create_mt_provider("facebook/nllb-200-distilled-600M")
evaluator = MTEvaluator(provider, "fra_Latn", "mos_Latn")
result = evaluator.evaluate(data, compute_comet=False)
print(f"chrF++: {result.overall_chrf:.1f}")
print(f"BLEU: {result.overall_bleu:.1f}")
@misc{pycergai_benchmark_2026,
title={PycergAI Benchmark: Open Evaluation Platform for
West and Central African Language Models},
author={PycergAI Benchmark Contributors},
year={2026},
howpublished={\url{https://github.com/pycerg-ai/pycergai-benchmark}},
note={Developed under UNICEF WCARO consultancy on NLP
for linguistic inclusion}
}
Apache 2.0 -- see LICENSE.
18 commits
Python
95.5%
Jupyter Notebook
2.8%