rzgar/qwen3-asr-sorani-kurdish-ckb-v1

🤗 On Hugging Faceautomatic-speech-recognitionapache-2.02B params4.1 GBsafetensors✓ Checksum-verifiedupdated 0d ago
Magnet

Qwen3-ASR Sorani Kurdish (ckb) v1

This repository contains the first fine-tuned version of Qwen3-ASR-1.7B specifically adapted for Central Kurdish (Sorani / ckb). This model bridges the gap between advanced Large Multimodal Models (LMMs) and low-resource Kurdish speech recognition.

🗣️ Performance on Sorani Kurdish (ckb)

  • Academic & Standard Kurdish: When the speaker reads standard, academic, or formal Sorani Kurdish (news broadcasts, audiobooks, formal speeches), the model performs very well, yielding near-perfect orthography, grammar, and punctuation.
  • Heavy Dialects & Conversational Speech: When faced with heavy regional dialects (such as Hewleri / Erbil dialect) or fast-paced conversational Kurdish, the model does its absolute best to phonetically and contextually capture what it hears. Rather than breaking down or outputting gibberish, its internal LLM logic helps it map dialectal pronunciations to their closest correct Kurdish spellings.

🚀 Perfect Base for Downstream Fine-Tuning

I have already done the hard work of teaching it Kurdish.

If you want to build an ASR model for a specific domain (Kurdish medical terminology, legal proceedings, or specific regional accents), this model is the perfect starting checkpoint. You no longer need to spend compute resources teaching a base model how to speak and spell in Sorani Kurdish. You can take this model and fine-tune it on a small, domain-specific dataset, and it will adapt rapidly while retaining its core Kurdish language capabilities.

demo_04.wav : "زۆر دەمێکە زۆر لەوانەی کە بوونە ناسیاوم هەڵیان پێچاوم کە سەر گوروشتەی ژیانی خۆم بنووسمە و بزانن چیم بەسەر هاتووە من وەک دەڵێن چوێلەکە خۆت چی و شۆرباو چی دەمزانی ژیانێکی زۆر قۆڕ و تورە هاتم ڕابواردووە و ڕووی مەجلیسی یارانی نیە

"

demo_01.wav : " لایەکی چاری ٢٠١٧ دەستم بەو ئیشەیەکتە تەکریبانە ٧ مانگ دەبیتەن دەستم پێکردیا و ٧/٧/٢٠١٧ ژێکەم ئیشم بوو کە لەگەڵ ستاف بیکەم و سیویەکی مانگیش ئینشاڵڵا سیویەکی دامانایەوەی سیپاڵەک لەناو بازاڕ بکەین بۆ ڕۆژی هەڵەوین "

demo_02.wav : "

لەسەر غاز بەرنامە ئەکا بۆ نموونە لەسەر نەوت بەرنامەکە ناو زانکۆکانی کوردستان پڕە لە دکتۆرا و پرۆفیسۆری ئەو ئیختصاصانە واز لەوانەیەنێ

"

demo_03.wav : "

لە ڕووداوێکی هاتوچۆی نێوان پاسێک و تەمکەرێکی سووتەمەنی لە زیقار ٢١ کەس گیانیان دەدەست داوە و نزیکەی نۆزاکەسی دێکەش بریندا بوون کە بەشێکیان جۆستایان سووتاوە "

▶️ How to use

ComfyUI-Qwen3-ASR:

  • https://github.com/DarioFT/ComfyUI-Qwen3-ASR

_Edit nodes.py and add this repo for automatic download - Model repo mappings, line 14_

  • Example 1:
import torch
from qwen_asr import Qwen3ASRModel # pip install qwen-asr || more info https://huggingface.co/Qwen/Qwen3-ASR-1.7B#environment-setup

# Clone the repo,
model = Qwen3ASRModel.from_pretrained(
    "./qwen3-asr-sorani-kurdish-ckb-v1",
    dtype=torch.bfloat16,
    device_map="cuda:0",
)

# Audio file
results = model.transcribe(
    audio="./demo_04.wav",
)

print("🎤 Qwen3-ASR Output:")
print("="*40)
print(results[0].text)
print("="*40)
  • Example 2:
import torch
import librosa
from qwen_asr import Qwen3ASRModel

# Define paths
model_path = "./qwen3-asr-sorani-kurdish-ckb-v1"
audio_path = "./demo_04.wav"
system_prompt = """You are an expert Sorani Kurdish (ckb) transcriptionist. 
Transcribe the audio into Sorani Kurdish with perfect orthography.
IMPORTANT: The speaker may use loanwords in Persian or Arabic. if you recognize any, 
please transcribe them in their standard writing form."""

print("🔄 Loading model and processor...")
# Load the model wrapper in bfloat16
asr_wrapper = Qwen3ASRModel.from_pretrained(
    model_path,
    dtype=torch.bfloat16,
    device_map="cuda:0",
)
model = asr_wrapper.model
processor = asr_wrapper.processor

print("🎵 Loading audio...")
# Load and resample audio to 16kHz (matches training)
audio_array, sr = librosa.load(audio_path, sr=16000)

print("⚙️ Processing inputs...")
# 4. Build the exact same message structure used in training
messages = [
    {"role": "system", "content": system_prompt},
    {"role": "user", "content": [{"type": "audio", "audio": audio_array}]},
]

# Apply chat template
text = processor.apply_chat_template([messages], add_generation_prompt=True, tokenize=False)[0]

# Process text and audio into model inputs
inputs = processor(text=text, audio=audio_array, return_tensors="pt").to("cuda:0")

# CRITICAL FIX: Cast all floating-point tensors to bfloat16 to match the model
for k, v in inputs.items():
    if torch.is_tensor(v) and v.is_floating_point():
        inputs[k] = v.to(torch.bfloat16)

print("🚀 Generating transcription...")
# Generate
with torch.no_grad():
    # Record the length of the input prompt so we only decode the NEW tokens
    input_len = inputs["input_ids"].shape[1]

    # Run generation
    output = model.generate(
        **inputs,
        max_new_tokens=256,
        num_beams=5,
        do_sample=False,
        pad_token_id=processor.tokenizer.pad_token_id,
        eos_token_id=processor.tokenizer.eos_token_id,
        length_penalty=0.8,
    )

    
    if isinstance(output, tuple):
        generated_ids = output[0]
    elif hasattr(output, 'sequences'):
        generated_ids = output.sequences
    else:
        generated_ids = output

    # Slice off the prompt tokens and decode only the generated text
    generated_ids = generated_ids[:, input_len:]
    result_text = processor.tokenizer.decode(generated_ids[0], skip_special_tokens=True).strip()

def normalize_dialect_to_academic(text: str) -> str:
    """
    Maps known dialectal mishearings or hallucinations to standard Academic Sorani Kurdish.
    This is highly efficient and preserves the model's perfect grammar.
    """
    # Example dictionary of dialectal words or What the model incorrectly hears and replace with preferred word
    dialect_map = {
        "سیپاڵەک": "فێستیڤاڵ",
        "تیشکی یەکەس": "تیشکی ئێکس",
        "جۆستا": "جەستە",
        "نۆزاکەسی": "نۆزدەکەس",
        "تەمکەر": "تەنکەر",
    }

    # Sort keys by length (descending) to replace longer phrases first
    for wrong, correct in sorted(dialect_map.items(), key=lambda x: len(x[0]), reverse=True):
        text = text.replace(wrong, correct)

    return text

# Apply the normalization
final_text = normalize_dialect_to_academic(result_text)

print("\n🎤 Qwen3-ASR Output (Raw):")
print(result_text)
print("=" * 60)
print("\n✅ Normalized Academic Kurdish:")
print(final_text)
print("=" * 60)

🙏 Acknowledgements