Instructions to use kangkyu/icefall-asr-ko-streaming-zipformer-72m with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- K2
How to use kangkyu/icefall-asr-ko-streaming-zipformer-72m with K2:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
icefall-asr-ko-streaming-zipformer-72m
π νκ΅μ΄ README 보기 / Korean version
A Korean causal ASR built with the icefall recipe β 71.4M-parameter Zipformer transducer trained on KsponSpeech only.
- Training data: KsponSpeech, ~1,000 hours
- KsponSpeech eval CER (no-punctuation, CPU inference)
- 7.68% β offline best (chunk=β1 PyTorch + KenLM, RTF 0.057)
- 8.25% β streaming best (sherpa-onnx int8 chunk-64, RTF 0.038)
- Release: PyTorch
.pt+ sherpa-onnx int8 ONNX (chunk 16/32/64, ready to use out-of-the-box)
Model
Based on icefall Zipformer transducer.
- causal architecture (multichunk training: chunk-16 / 32 / 64 / -1)
- Korean syllable vocabulary (2,460 tokens)
- FP16 training
- Files included: PyTorch
epoch-99.pt(3-way state-dict avg, 273 MB) + 9 sherpa-onnx int8 ONNX (chunk 16/32/64 Γ encoder/decoder/joiner). Ready to use out-of-the-box, no extra export step required
Performance (KsponSpeech eval, CER %, no-punctuation)
eval_clean 3,000 + eval_other 3,000 = 6,000 cuts. All measurements on the same set, beam=8, 3-way state-dict avg.
Decoding modes β choose by latency budget
| Mode | chunk | clean | other | OVR | RTF | latency |
|---|---|---|---|---|---|---|
| chunk=β1 PyTorch + KenLM Ξ»=0.10 (offline best) | β1 | 7.63 | 7.72 | 7.68 | 0.057 (CPU 2t) | utterance end |
| sherpa-onnx int8 chunk-64 | 64 | 8.28 | 8.22 | 8.25 | 0.038 (CPU 1t) | 1.28 s |
| sherpa-onnx int8 chunk-32 | 32 | 8.45 | 8.33 | 8.39 | 0.039 (CPU 1t) | 640 ms |
| sherpa-onnx int8 chunk-16 | 16 | 8.63 | 8.64 | 8.635 | 0.052 (CPU 1t) | 320 ms |
streaming numbers measured with 1x trailing padding (CHUNK * 0.04 s). RTF averaged over clean/other.
chunk=β1 (offline best 7.68%) is PyTorch-only: sherpa-onnx supports streaming chunks (16/32/64) only. To get the offline best CER, run
epoch-99.ptdirectly via the icefall recipe β see "Option 1 β PyTorch (epoch-99.pt) with icefall recipe" below.
Quantization safety β fp32 vs int8 β
Direct sweep on the same KsponSpeech 6,000-cut eval set, identical 1x padding, CPU 1-thread:
| chunk | fp32 OVR | int8 OVR | Ξ CER | fp32 RTF | int8 RTF | speedup |
|---|---|---|---|---|---|---|
| 16 | 8.645 | 8.635 | β0.010 | 0.078 | 0.052 | 1.50Γ |
| 32 | 8.340 | 8.390 | +0.050 | 0.060 | 0.039 | 1.54Γ |
| 64 | 8.115 | 8.250 | +0.135 | 0.057 | 0.038 | 1.50Γ |
int8 quantization preserves CER (avg Ξ +0.06%p, max +0.135) while running ~1.5Γ faster on CPU β this is the reason only .int8.onnx is shipped. If you need fp32, re-export from epoch-99.pt with the icefall recipe.
Direct comparison on the same eval set (KsponSpeech 6,000 cuts)
| Model | params | clean | other | OVR |
|---|---|---|---|---|
| sibling 174M model | 155.7M | 7.18 | 7.14 | 7.16 |
| This model (72M) | 71.4M | 7.63 | 7.72 | 7.68 |
| Qwen3-ASR-1.7B (Alibaba) | 1,700M | 9.76 | 10.22 | 9.99 |
| OpenAI Whisper-large-v2 | 1,550M | 13.70 | 12.64 | 13.17 |
All models measured directly on the same KsponSpeech eval set. Whisper / Qwen run with default GPU FP16 transformers options; this model uses chunk=β1 PyTorch + KenLM rescore.
Decoding stack ablation
| Stack | clean | other | OVR |
|---|---|---|---|
| epoch-40 single, no LM | 8.11 | 8.06 | 8.08 |
| 3-way state-dict avg, no LM | 7.81 | 7.84 | 7.83 |
| 3-way avg + KenLM Ξ»=0.10 (the stack reported above) | 7.63 | 7.72 | 7.68 |
3-way avg = state_dict average of best-valid + best-train + epoch-40.
Architecture
| Encoder | Zipformer2 causal, 6 stacks |
| Encoder dim | 192,256,384,512,384,256 |
| Encoder layers | 2,2,3,4,3,2 |
| Feedforward dim | 512,768,1024,1536,1024,768 |
| Heads | 4,4,4,8,4,4 |
| Decoder | Stateless transducer decoder, dim 512 |
| Joiner | dim 512 |
| Vocabulary | Korean syllables, 2,460 tokens |
| Total params | 71.4 M |
| Causal | Yes |
| Training chunk-size | 16, 32, 64, β1 multichunk |
Training
| Data | KsponSpeech 1,000h (AIHub dataset 123) |
| Precision | FP16 |
| Loss | Pruned-RNNT + CTC aux (scale 0.2) |
| Augmentation | SpecAugment (musan/RIR optional) |
| Optimizer | ScaledAdam |
| Total epochs | 40 |
| Released checkpoint | 3-way state_dict avg of best-valid + best-train + epoch-40 |
Usage
Option 1 β PyTorch (epoch-99.pt) with icefall recipe
Load the weights with the icefall recipe (egs/librispeech/ASR/zipformer). chunk size, beam, KenLM, ONNX export, quantization etc. can be tuned freely.
chunk=β1 (offline best 7.68%) example:
# Place epoch-99.pt as epoch-99.pt in your icefall exp dir, then:
python zipformer/decode.py \
--epoch 99 --avg 1 --use-averaged-model 0 \
--causal 1 --chunk-size -1 --left-context-frames -1 \
--decoding-method modified_beam_search --beam-size 8
Add KenLM rescore (Ξ»=0.10) to reproduce 7.68%. See icefall docs for detailed options and KenLM usage.
Option 2 β sherpa-onnx streaming (recommended for CPU deployment)
# Install: pip install sherpa-onnx soundfile
import sherpa_onnx
import soundfile as sf
# Choose chunk: 16 (320ms latency) / 32 / 64
# Choose variant: ".int8" (included in release, ~1/2 size, almost no CER loss)
# "" (fp32, NOT included β re-export from epoch-99.pt if needed)
CHUNK = 16
VARIANT = ".int8"
recognizer = sherpa_onnx.OnlineRecognizer.from_transducer(
encoder=f"encoder-epoch-99-avg-1-chunk-{CHUNK}-left-128{VARIANT}.onnx",
decoder=f"decoder-epoch-99-avg-1-chunk-{CHUNK}-left-128{VARIANT}.onnx",
joiner=f"joiner-epoch-99-avg-1-chunk-{CHUNK}-left-128{VARIANT}.onnx",
tokens="tokens.txt",
num_threads=2,
sample_rate=16000,
feature_dim=80,
decoding_method="modified_beam_search",
max_active_paths=8,
)
audio, sr = sf.read("speech.wav")
stream = recognizer.create_stream()
stream.accept_waveform(sr, audio)
# Add trailing silence so the last chunk is fully decoded
import numpy as np
stream.accept_waveform(16000, np.zeros(int(CHUNK * 0.04 * 16000), dtype=np.float32))
stream.input_finished()
while recognizer.is_ready(stream):
recognizer.decode_stream(stream)
print(recognizer.get_result(stream))
Chunk size guide
| chunk | latency | OVR | use case |
|---|---|---|---|
| 16 | 320 ms | 8.635 | real-time UX, lowest interactive latency |
| 32 | 640 ms | 8.39 | balanced |
| 64 | 1.28 s | 8.25 | best streaming CER (only +0.57 vs offline 7.68) |
Files
epoch-99.pt # PyTorch 3-way state_dict avg (273 MB)
tokens.txt # syllable vocab 2,460
README.md / README_ko.md
encoder-epoch-99-avg-1-chunk-{16,32,64}-left-128.int8.onnx # int8 encoder
decoder-epoch-99-avg-1-chunk-{16,32,64}-left-128.int8.onnx
joiner-epoch-99-avg-1-chunk-{16,32,64}-left-128.int8.onnx
Sibling
β icefall-asr-ko-streaming-zipformer-174m β 155.7M params, KSP + AIHub 6,500h.
License
Apache 2.0.
Citation
@misc{ko-zipformer-72m,
title = {icefall-asr-ko-streaming-zipformer-72m: Korean ASR (KsponSpeech)},
author = {Ken (kangkyu)},
year = {2026},
howpublished = {\url{https://huggingface.co/kangkyu/icefall-asr-ko-streaming-zipformer-72m}}
}
Built on: