CoolFace
Modelpublic

keystats/kiswahili_sahihi_asr_adapted_1

sourceHugging Faceapache-2.0updated 11mo agoView on Hugging Face
0likes10downloads
Model Card

๐Ÿ—ฃ๏ธ Kiswahili Sahihi ASR Adapted 1 (Adapter-Fused Whisper Model)

Overview

`keystats/kiswahili_sahihi_asr_adapted_1` is a refined Swahili automatic speech recognition (ASR) model optimized for on-device use and low-resource settings. It extends the Whisper Medium architecture through parameter-efficient fine-tuning (PEFT) and LoRA adapters, achieving high transcription accuracy while keeping the model lightweight and deployable.

This model builds upon the foundation of Kiswahili Sahihi ASR and was developed for the Your Voice, Your Device, Your Language Challenge, advancing accessible speech technology for over 200 million Swahili speakers.


๐Ÿงฉ Base Model


โš™๏ธ Training Configuration

ParameterValue
DatasetSunbird/salt (studio-swa)
Noise AugmentationSunbird/urban-noise-uganda-61k
Effective Batch Size8 (per_device_train_batch_size=4, gradient_accumulation_steps=2)
Learning Rate1e-5
Warmup Steps500
Epochs3
PrecisionMixed precision (fp16)
Quantization8-bit via bitsandbytes
Memory Optimizationgradient_checkpointing=True

๐Ÿง  Training Summary

text
โœ… Added <|pad|> token to tokenizer
โœ… Resized embeddings to 51866 tokens
โœ… PEFT + LoRA adapters merged
โœ… Dataset: Train=3,758 | Validation=77
โœ… 1,000 noise clips used for augmentation

Final Training Snapshot:

StepTraining LossValidation LossWER (%)CER (%)
2000.67470.667516.234.82
4000.58600.561615.564.74
6000.53680.485212.094.11
8000.46460.444712.584.23
10000.42670.415412.424.23
12000.42790.394911.424.03
14000.39650.390211.594.11

๐Ÿงฉ Model Usage

python
# ============================================
# ๐Ÿช„ Full Swahili ASR Transcription with Adapted Model 
# ============================================
# ๐Ÿ“ฆ Installation
!pip install -q transformers "datasets<4.0.0"
!pip install -q torchaudio==2.6.0 torchvision==0.21.0 jiwer evaluate
!pip install -q soundfile librosa accelerate>=0.26.0 tensorboard bitsandbytes
!pip install -q pydub
!apt-get -y install ffmpeg

# ============================================
# 1๏ธโƒฃ Imports
# ============================================
import torch
import librosa
import numpy as np
from pydub import AudioSegment
from transformers import WhisperProcessor, WhisperForConditionalGeneration
from peft import PeftModel, PeftConfig
import imageio_ffmpeg as ffmpeg_lib
import os

# ============================================
# 2๏ธโƒฃ Register ffmpeg for pydub (no sudo)
# ============================================
from pydub.utils import which

ffmpeg_path = ffmpeg_lib.get_ffmpeg_exe()
AudioSegment.converter = ffmpeg_path
AudioSegment.ffmpeg = ffmpeg_path
AudioSegment.ffprobe = ffmpeg_path

print("โœ… ffmpeg and ffprobe linked successfully!")

# ============================================
# 3๏ธโƒฃ Load Adapted Model (with vocab fix)
# ============================================
base_model_id = "keystats/kiswahili_sahihi_asr"
adapter_model_path = "keystats/kiswahili_sahihi_asr_adapted_1"

print(f"๐Ÿ”น Loading processor from: {adapter_model_path}")
processor = WhisperProcessor.from_pretrained(adapter_model_path)
vocab_size = len(processor.tokenizer)
print(f"๐Ÿ”น Tokenizer vocab size: {vocab_size}")

# Load PEFT config
peft_config = PeftConfig.from_pretrained(adapter_model_path)
print(f"๐Ÿ”น PEFT base model: {peft_config.base_model_name_or_path}")

# Load base Whisper model
base_model = WhisperForConditionalGeneration.from_pretrained(
    peft_config.base_model_name_or_path,
    ignore_mismatched_sizes=True,
)

# Fix vocab size mismatch
base_model.resize_token_embeddings(vocab_size)
print(f"โœ… Resized token embeddings to match vocab size ({vocab_size})")

# Load and merge adapter
model = PeftModel.from_pretrained(base_model, adapter_model_path)
print("โœ… Adapter loaded successfully")

model = model.merge_and_unload()
print("โœ… Adapter merged and unloaded")

# Move to device
device = "cuda" if torch.cuda.is_available() else "cpu"
model = model.to(device, dtype=torch.float32)
model.eval()
print(f"๐Ÿš€ Model ready on {device.upper()}")

# ============================================
# 4๏ธโƒฃ Convert Any Format to WAV
# ============================================
def convert_to_wav(input_path, output_path="converted.wav"):
    """Convert MP3, M4A, or any audio file to mono 16kHz WAV."""
    try:
        audio = AudioSegment.from_file(input_path)
        audio = audio.set_frame_rate(16000).set_channels(1)
        audio.export(output_path, format="wav")
        return output_path
    except Exception as e:
        raise RuntimeError(f"โŒ Could not convert file. Error: {e}")

# ๐ŸŽง Replace this with your Swahili audio file
audio_path = "Your audio here"
wav_path = convert_to_wav(audio_path)
print(f"โœ… Converted to: {wav_path}")

# ============================================
# 5๏ธโƒฃ Load Audio and Chunk
# ============================================
audio_input, sr = librosa.load(wav_path, sr=16000, mono=True)
chunk_length_s = 60  # seconds
chunk_size = chunk_length_s * sr
num_chunks = int(np.ceil(len(audio_input) / chunk_size))
print(f"๐Ÿ”น Total length: {len(audio_input)/sr:.2f}s | Splitting into {num_chunks} chunks...")

# ============================================
# 6๏ธโƒฃ Transcribe (without forced_decoder_ids)
# ============================================
full_transcription = []

# Add language token manually for safety
lang_token = processor.tokenizer.convert_tokens_to_ids("<|swahili|>")
task_token = processor.tokenizer.convert_tokens_to_ids("<|transcribe|>")
start_tokens = torch.tensor([[lang_token, task_token]], device=device)

for i in range(num_chunks):
    start = i * chunk_size
    end = min((i + 1) * chunk_size, len(audio_input))
    chunk = audio_input[start:end]

    inputs = processor(
        chunk,
        sampling_rate=16000,
        return_tensors="pt",
        return_attention_mask=True,   
    ).to(device, dtype=torch.float32)

    with torch.no_grad():
        generated_ids = model.generate(
            **inputs,
            max_new_tokens=256,
            num_beams=2,
            repetition_penalty=1.1,
        )

    text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
    full_transcription.append(text.strip())

    print(f"๐ŸŸข Chunk {i+1}/{num_chunks} done")


# ============================================
# 7๏ธโƒฃ Combine Final Transcript
# ============================================
final_text = " ".join(full_transcription)
print("\n๐Ÿ“ Final Transcription:\n")
print(final_text)

๐Ÿ’ก Key Features

  • โ€”๐Ÿง  Adapter-based fine-tuning (LoRA) โ€” trains <1% of parameters
  • โ€”๐Ÿชถ Lightweight & efficient โ€” runs on edge or T4 GPUs
  • โ€”๐Ÿ”Š Noise-robust โ€” trained with real-world East African noise
  • โ€”๐Ÿ•Š๏ธ Privacy-preserving โ€” suitable for offline/edge deployment
  • โ€”๐ŸŒ Focused on accessibility โ€” targets Swahili and low-resource regions

๐Ÿงพ Acknowledgments

This model builds upon the architecture and open-source contributions of [Salifou Abdourahamane](https://github.com/SalifouAbdourahamane) โ€” creator of the excellent swahili_asr_sota_model.


๐Ÿ“š Citation

If you use this model, please credit:

Jackson Kahungu, Kiswahili Sahihi ASR Adapted 1 Hugging Face: keystats/kiswahili_sahihi_asr_adapted_1