zipformer-asr-turkish-v1

oayk/zipformer-asr-turkish-v1

作者
oayk
任务
语音识别
下载量
0
点赞数
6
趋势分
6
许可证
apache-2.0
收录时间
2026/11/12

language:

  • tr license: apache-2.0 library_name: icefall pipeline_tag: automatic-speech-recognition tags:
  • automatic-speech-recognition
  • speech
  • turkish
  • streaming
  • zipformer
  • transducer
  • rnn-t
  • onnx
  • sherpa-onnx
  • edge

zipformer-asr-turkish-v1

A ~20M-parameter streaming Turkish speech recognizer built for edge devices: a causal Zipformer2 encoder with a pruned RNN-T (transducer) head and an auxiliary CTC head, trained with icefall on ~2.1k hours of Turkish speech. It runs in real time on a single CPU core (ONNX int8) with 640 ms latency, and the same weights also decode with 320 ms chunks or with full (non-streaming) context.

With beam search + a small LSTM language model and internal-LM estimation (ILME), it reaches 5.28% WER on FLEURS Turkish (743 clips, balbal-normalizer; 6.50% with Damga) and 8.59% on MediaSpeech (Damga) while staying fully streaming at 640 ms.

Parameters19.9M (ASR) + 10.2M (optional LSTM LM)
Input16 kHz mono audio, 80-dim log-mel fbank
Outputlowercase spoken-form Turkish: no punctuation, numbers written as words, fillers kept
Latency640 ms chunks (also 320 ms or full context)
SpeedRTF 0.020 on one desktop CPU thread (int8, greedy, 640 ms)
VocabularySentencePiece unigram, 500 tokens

Results

WER (%) on FLEURS tr test (743 clips) and MediaSpeech tr (252 clips), scored with the two normalizations other open Turkish models report, so the numbers can be compared directly:

  • Damga (used e.g. by mihuai/turkish-stt-66m-v2): Damga written-to-spoken normalization (numbers, ordinals, abbreviations, units), Turkish lowercasing, apostrophe and circumflex removal, punctuation removal.
  • balbal-normalizer (used by balbal-v2): the balbal scoring normalizer (corpus_metrics_v2). On top of the above it ignores compound-spacing differences ("yurt dışı" = "yurtdışı"), compares numbers digit by digit (so "doksan" = "dokuz sıfır"), and treats "X ile" = "Xle".

dev/test are held-out in-house sets (~7.9 h each, not released). All rows are the same checkpoint (epoch 29, average of epochs 26-29).

DecodingRuntimeFLEURS DamgaFLEURS balbalMediaSpeech DamgaMediaSpeech balbaldevtest
640 ms, greedyPyTorch7.996.7510.3410.314.244.21
640 ms, beam 4PyTorch7.826.579.779.734.004.00
640 ms, beam 4 + LSTM LM + LODRPyTorch6.865.778.948.893.673.59
640 ms, beam 8 + LSTM LM + ILMEPyTorch6.505.288.598.443.473.51
640 ms, greedyONNX int8 (streaming-640ms)8.056.8010.2210.23
320 ms, greedyONNX int8 (streaming-320ms)8.667.4210.8710.81
full context, greedyONNX int8 (offline-full-context)7.095.918.738.71

ONNX int8 speed (one desktop CPU thread, greedy): RTF 0.020 at 640 ms, 0.028 at 320 ms, 0.013 full context.

Other systems on the same clips. Rows marked "card" are copied from that model's own model card; the rest are our runs. As a check, our Whisper large-v3 run scores 3.77 with the balbal-normalizer vs 3.71 on the balbal-v2 card.

SystemParamsStreamingFLEURS DamgaFLEURS balbalMediaSpeech DamgaMediaSpeech balbal
Google Chirp 3 (cloud)—no2.341.72——
ElevenLabs Scribe (cloud)—no2.842.419.676.59
Whisper large-v31.55Bno4.753.7711.1210.41
Whisper large-v3-turbo809Mno5.394.4210.249.46
balbal-v2 (card; KenLM)45.4Moffline eval—5.22——
mihuai/turkish-stt-66m-v2 (card; 320 ms, beam 4)66Myes6.215.26 (balbal card)10.95—
omniASR LLM 7B (balbal card)7Bno—5.31——
Qwen3-ASR 1.7B (balbal card)1.7Bno—6.52——
Duyu-small (balbal card)49.3Mno—6.62——
zipformer-asr-turkish-v1, beam 8 + LM + ILME, 640 ms19.9M + 10.2M LMyes6.505.288.598.44
zipformer-asr-turkish-v1, full context, greedy, ONNX int819.9Mno7.095.918.738.71
zipformer-asr-turkish-v1, 640 ms, greedy, ONNX int819.9Myes8.056.8010.2210.23

Usage

Streaming with sherpa-onnx (recommended for devices)

import numpy as np, soundfile as sf, sherpa_onnx

d = "streaming-640ms"   # or "streaming-320ms"
rec = sherpa_onnx.OnlineRecognizer.from_transducer(
    tokens=f"{d}/tokens.txt", encoder=f"{d}/encoder.int8.onnx",
    decoder=f"{d}/decoder.int8.onnx", joiner=f"{d}/joiner.int8.onnx",
    num_threads=1, sample_rate=16000, feature_dim=80, decoding_method="greedy_search",
    enable_endpoint_detection=True)

x, sr = sf.read("example.wav", dtype="float32")
s = rec.create_stream()
for i in range(0, len(x), 1600):          # feed 100 ms at a time, like a microphone
    s.accept_waveform(sr, x[i:i + 1600])
    while rec.is_ready(s):
        rec.decode_stream(s)
    print(rec.get_result(s), end="\r")
s.accept_waveform(sr, np.zeros(int(0.8 * sr), np.float32)); s.input_finished()
while rec.is_ready(s):
    rec.decode_stream(s)
print(rec.get_result(s))

Full context (non-streaming)

d = "offline-full-context"
rec = sherpa_onnx.OfflineRecognizer.from_transducer(
    tokens=f"{d}/tokens.txt", encoder=f"{d}/encoder.int8.onnx",
    decoder=f"{d}/decoder.int8.onnx", joiner=f"{d}/joiner.int8.onnx",
    num_threads=1, sample_rate=16000, feature_dim=80, decoding_method="greedy_search")
s = rec.create_stream(); s.accept_waveform(sr, x); rec.decode_stream(s); print(s.result.text)

Best accuracy: beam search + LM + ILME (PyTorch / icefall)

sherpa-onnx has no ILME, so the best setting runs in PyTorch with icefall. The script was tested with icefall at git commit cbc985f (git -C icefall checkout cbc985f); newer versions should work but are untested.

python inference/transcribe.py --icefall ~/icefall --method beam_lm_ilme --context 640ms example.wav
# --method greedy | beam | beam_lm_ilme   --context 640ms | 320ms | full   --beam 8 --lm-scale 0.45 --ilme-scale 0.2

Files

PathContent
exp/pretrained.ptPyTorch weights (icefall {"model": state_dict}), averaged epochs 26-29
streaming-640ms/, streaming-320ms/streaming transducer ONNX (encoder/decoder/joiner, fp32 and int8) + tokens.txt
offline-full-context/the same model exported for full-context decoding (fp32 and int8)
lm/epoch-4.pt, lm/lstm768-with-state*.onnx2×768 LSTM token LM (PyTorch; ONNX for sherpa-onnx streaming LM)
data/lang_bpe_500/SentencePiece model, vocab (for hotwords) and tokens
inference/transcribe.py and beam_search_ext.py (beam search with LM shallow fusion, LODR and ILME; derived from icefall)

Model

  • Encoder: causal Zipformer2, 6 stacks (2 layers each, dims 192-256-256-256-256-192, feed-forward 512-640), trained with mixed chunk sizes {320 ms, 640 ms, 1.28 s, full}, so one model serves several latencies.
  • Decoder / joiner: stateless predictor (2-token context), joiner dim 320; auxiliary CTC head.
  • Training: pruned RNN-T + CTC, 29 epochs on one RTX 5080. Augmentation from epoch 20: synthetic room impulse responses, MUSAN noise and music, street/mall/factory recordings, Turkish multi-talker babble, device simulation (band-limiting, AGC, clipping) and codecs (8 kHz μ-law/GSM, Opus 7-32 kbps, MP3).
  • Language model (optional): 2-layer LSTM (768, tied embeddings) on Turkish Wikipedia + mC4 + transcripts, sentences overlapping the test sets removed. Fused with λ = 0.45 (shallow fusion).
  • ILME: the transducer's predictor already learns an internal language model from the training transcripts, so adding an external LM counts that knowledge twice. ILME estimates the internal LM by running the joiner with the encoder contribution removed and subtracts it (weight 0.2), so the external LM adds new information instead of repeating what the model already knows.

Training data

~2.1k hours of Turkish speech from publicly available online videos, labelled automatically: a segment is kept where Whisper and an in-house Turkish CTC model agree, and disagreement regions are recovered with a rule-based selector (+ LLM and a third ASR system as tie-breakers). Train/dev/test are split by channel. The data is not released.

Limitations

  • English and foreign words: code-switched words ("laptop", "deadline") and foreign names are often written phonetically or dropped. This is the main focus of the next version.
  • Very quiet or far-field speech degrades noticeably.
  • Output format: spoken form only (no capitalization, punctuation or digits); add inverse text normalization if you need written form.
  • Heavy noise: error rates rise quickly below 10 dB SNR.

License

Apache-2.0 for the model weights and code. inference/beam_search_ext.py is derived from icefall (Apache-2.0).

Acknowledgements

Built on icefall, k2, lhotse and sherpa-onnx. Zipformer: Yao et al., Zipformer: A faster and better encoder for automatic speech recognition, ICLR 2024.

Türkçe özet

Uç cihazlar için ~20M parametreli, akışlı (640 ms) Türkçe konuşma tanıma modeli. Tek CPU çekirdeğinde gerçek zamanlı çalışır (ONNX int8). Işın araması + LSTM dil modeli + ILME ile FLEURS Türkçe'de (743 klip) %5,28 (balbal-normalizer) / %6,50 (Damga), MediaSpeech'te %8,59 WER (Damga). Çıktı konuşulduğu gibi: küçük harf, noktalama yok, sayılar yazıyla.

Author: Ömer Aykılıç

内容来源:Hugging Face 模型卡

在 Hugging Face 查看