Configuration Parsing Warning:Config file tokenizer_config.json cannot be fetched (too big)

Ghana Voice: Bassar (Ntcham)

Zero-reference text-to-speech for Bassar (Ntcham) (bud), fine-tuned from ghanaopenai/ghana-voice-base. It speaks in a single consistent female voice (clear-high-slow from africa-female-vc).

Part of the Ghana Voice collection, which has a model for each language.

Files

File Use
model.safetensors + tokenizer PyTorch (recommended): merged LoRA, for production on GPU or CPU
ghana-voice_bassar-Q8_0.gguf llama.cpp 8-bit, for resource-constrained use only, with care
ghana-voice_bassar-Q4_K_M.gguf llama.cpp 4-bit, smaller, for resource-constrained use only, with care
samples/ Audio generated from the GGUF Q8_0 model on CPU

PyTorch vs GGUF: use the PyTorch weights in production. The GGUF files are less reliable; use them with care, only where resources are constrained, and compare against PyTorch output.

Evaluation

Eval loss is the next-token loss on 158 held-out clips (3% of the data). No ASR-based CER is reported: available ASR models do not read every Ghanaian language reliably, so it would mislead. Listen to the samples to judge quality.

Metric Value
Eval loss at the released checkpoint (step 2880, epoch 18.0) 6.0327
Eval loss at first evaluation (step 80) 6.5144
Training stopped (eval loss plateau) step 2880, epoch 18.0

Training

  • Data: 5,272 clips, 9.99 h of Bassar (Ntcham) from ghanaopenai/ghana-speech-female-22khz_bible, all voice-converted to clear-high-slow; no human recordings or other voices mixed in. Clips with digits are dropped; 3-15 s clips, silence trimmed, word-separated IPA via africa-g2p, NeuCodec 50 Hz codes.
  • Method: LoRA (r=16, alpha=32) on ghana-voice-base, lr 5e-5, batch 8 x 4 grad-accum, up to 40 epochs, eval every 80 steps, early stopping when eval loss stops improving by 0.005 for 10 evaluations. The checkpoint with the lowest eval loss is released, merged into the base.

Samples (GGUF Q8_0, CPU)

Text Audio
M-bɔɔn, m yaa móotí a-nyunculiŋee.
Unimbɔti wannaaŋkaati gbanti kɔkɔ ŋá fám.
Niil mɔɔl làá kpā kí tin sāā taapu.
Úǹ u-ba làá tùkù-si a nín làá ŋá puee.
Tiŋman ti-Dindaan un di lēē Jeerusaleemee di làá kpìin̄-si.

Usage (PyTorch, recommended)

import torch, soundfile as sf
from transformers import AutoModelForCausalLM, AutoTokenizer
from neucodec import NeuCodec
from africa_g2p import AfricaPipeline

repo = "ghanaopenai/ghana-voice_bassar"
device = "cuda" if torch.cuda.is_available() else "cpu"
tok = AutoTokenizer.from_pretrained(repo)
model = AutoModelForCausalLM.from_pretrained(repo, torch_dtype=torch.bfloat16).to(device).eval()
codec = NeuCodec.from_pretrained("neuphonic/neucodec").to(device).eval()
g2p = AfricaPipeline(lang="bud", output="ipa")

ipa = g2p.run("YOUR BASSAR (NTCHAM) TEXT", sep="").strip()  # word-separated IPA, the training format
ids = tok.encode(f"<|TEXT_PROMPT_START|>{ipa}<|TEXT_PROMPT_END|><|SPEECH_GENERATION_START|>", add_special_tokens=False)
end_id = tok.convert_tokens_to_ids("<|SPEECH_GENERATION_END|>")
with torch.no_grad():
    out = model.generate(torch.tensor([ids], device=device), eos_token_id=end_id, max_new_tokens=600,
                         do_sample=True, temperature=0.7, top_p=0.8, top_k=50, repetition_penalty=1.05)
    codes = [t - 382 for t in out[0, len(ids):].tolist() if 382 <= t < 382 + 65536]  # <|speech_0|> is id 382
    wav = codec.decode_code(torch.tensor(codes, device=device)[None, None]).squeeze().float().cpu().numpy()
sf.write("out.wav", wav, 24000)

Give the model at least a full sentence at a time (about 40 characters or more): very short prompts make it pad the speech with repeats. For long text, split at sentence ends and merge short sentences with their neighbours. The ghana-voice library does this for you.

GGUF (resource-constrained only, use with care)

import re, torch, soundfile as sf
from huggingface_hub import hf_hub_download
from llama_cpp import Llama
from transformers import AutoTokenizer
from neucodec import NeuCodec
from africa_g2p import AfricaPipeline

repo = "ghanaopenai/ghana-voice_bassar"
tok = AutoTokenizer.from_pretrained(repo)
llm = Llama(model_path=hf_hub_download(repo, "ghana-voice_bassar-Q8_0.gguf"), n_ctx=2048, verbose=False)
codec = NeuCodec.from_pretrained("neuphonic/neucodec").eval()
g2p = AfricaPipeline(lang="bud", output="ipa")

ipa = g2p.run("YOUR BASSAR (NTCHAM) TEXT", sep="").strip()
ids = tok.encode(f"<|TEXT_PROMPT_START|>{ipa}<|TEXT_PROMPT_END|><|SPEECH_GENERATION_START|>", add_special_tokens=False)
end_id = tok.convert_tokens_to_ids("<|SPEECH_GENERATION_END|>")
codes = []
for t in llm.generate(ids, temp=0.7, top_p=0.8, top_k=50, min_p=0.0, repeat_penalty=1.05):
    if t == end_id or len(codes) >= 450:
        break
    m = re.match(r"<\|speech_(\d+)\|>", tok.convert_ids_to_tokens(t))
    if m:
        codes.append(int(m.group(1)))
with torch.no_grad():
    wav = codec.decode_code(torch.tensor(codes)[None, None]).squeeze().numpy()
sf.write("out.wav", wav, 24000)

Recommended sampling: temperature 0.7, top-p 0.8, top-k 50, repetition penalty 1.05.

License

CC BY-NC 4.0, following the training data.

Downloads last month
114
Safetensors
Model size
0.2B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for ghanaopenai/ghana-voice_bassar

Adapter
(9)
this model

Dataset used to train ghanaopenai/ghana-voice_bassar

Collection including ghanaopenai/ghana-voice_bassar