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

Ghana Voice: Asante Twi

Zero-reference text-to-speech for Asante Twi (twi), 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_asante_twi-Q8_0.gguf llama.cpp 8-bit, for resource-constrained use only, with care
ghana-voice_asante_twi-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

CER is measured during training: the model speaks 3 held-out Asante Twi sentences and an OmniASR 300M CTC model transcribes them. With so few sentences the score is noisy; treat it as a guide and listen to the samples.

Metric Value
CER at the released checkpoint (step 960, epoch 8.1) 20.6%
Lowest CER during training 20.6%
CER at first evaluation (step 80) 33.9%
Eval loss at the released checkpoint 6.0938
Training stopped (joint loss + CER plateau) step 2080, epoch 17.6

Training

  • Data: 3,874 clips, 7.25 h of Asante Twi from ghanaopenai/ghana-speech-female-22khz, 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 both eval loss and CER plateau (patience 10). The checkpoint with the best joint loss/CER score is released, merged into the base.

Samples (GGUF Q8_0, CPU)

Text Audio
Wo ho te sɛn? Me ho yɛ, na wo nso ɛ?
Akwaaba, yɛbɔ mpae sɛ Onyankopɔn bɛhyira wo ne wo fiefoɔ nyinaa.
Me kÉ” sukuu da biara na mepÉ› sÉ› mesua ade pa ara.
Woforo dua pa a, na yɛpia wo.
Kofi ne Ama kɔɔ fie kɔbɔɔ amanneɛ kyerɛɛ wɔn awofoɔ nyinaa.
Yɛsrɛ wo, bue pono no ma me kakra na mframa mmra mu.

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_asante_twi"
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="twi", output="ipa")

ipa = g2p.run("YOUR ASANTE TWI 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_asante_twi"
tok = AutoTokenizer.from_pretrained(repo)
llm = Llama(model_path=hf_hub_download(repo, "ghana-voice_asante_twi-Q8_0.gguf"), n_ctx=2048, verbose=False)
codec = NeuCodec.from_pretrained("neuphonic/neucodec").eval()
g2p = AfricaPipeline(lang="twi", output="ipa")

ipa = g2p.run("YOUR ASANTE TWI 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
239
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_asante_twi

Adapter
(9)
this model

Dataset used to train ghanaopenai/ghana-voice_asante_twi

Collection including ghanaopenai/ghana-voice_asante_twi