Qwen3-ASR-Radio (1.7B) — robust ASR for degraded VHF airband radio
Fine-tune of Qwen/Qwen3-ASR-1.7B for
poor-quality VHF voice radio (air-traffic-control airband). Off-the-shelf Qwen3-ASR is
excellent on clean speech but collapses on real radio; this model roughly halves the word
error rate on real airband while preserving clean-speech accuracy.
Results (WER, lower is better)
Faithful greedy decoding (qwen-asr .transcribe, max_new_tokens=64), n=200.
| eval set | base | this model |
|---|---|---|
| UWB-ATCC test (held-out, in-domain) | 0.766 | 0.317 (−59%) |
| ATCO2 (different ATC network, unseen speakers) | 0.609 | 0.327 (−46%) |
| LibriSpeech (clean, forgetting check) | 0.013 | 0.010 (no regression) |
The improvement generalizes to a different ATC corpus and unseen speakers, and does not degrade clean speech.
Intended use & limitations
- Use: assistive transcription of degraded airband ATC radio — human-in-the-loop, gisting, keyword/alert spotting, search/indexing.
- Not for: unattended verbatim transcription or automated safety decisions (~32% WER; errors concentrate on callsigns and digit strings).
- Scope: validated on aviation airband (English) only. Maritime / public-safety (DMR/P25) and non-English are untested. Contextual callsign biasing would substantially help the dominant errors.
How to use
This is a merged full model (drop-in for the base). Via the qwen-asr toolkit
(pip install qwen-asr):
import torch
from qwen_asr import Qwen3ASRModel
m = Qwen3ASRModel.from_pretrained("chrullis/qwen3-asr-radio-1.7b",
dtype=torch.bfloat16, device_map="cuda:0",
max_new_tokens=64) # keep a length cap as a guard
print(m.transcribe(audio=("clip.wav", 16000), language=None)[0].text)
Serve with vLLM (OpenAI-compatible)
It's a merged, drop-in Qwen3-ASR, so vLLM serves it straight from the Hub. Qwen3-ASR
audio decoding needs soundfile, which isn't in the stock vLLM image — build a 2-line
derived image once:
printf 'FROM vllm/vllm-openai:v0.20.2\nRUN pip install --no-cache-dir soundfile\n' > Dockerfile
docker build -t vllm-qwen3asr .
docker run --gpus all -p 8000:8000 vllm-qwen3asr \
--model chrullis/qwen3-asr-radio-1.7b \
--served-model-name qwen3-asr-radio \
--dtype float16 --gpu-memory-utilization 0.80 --max-model-len 4096
Transcribe via the best-quality endpoint (plain text):
curl -s http://localhost:8000/v1/audio/transcriptions \
-F model=qwen3-asr-radio -F file=@clip.wav
Notes:
- Requires a vLLM build with Qwen3-ASR support (e.g.
v0.20.2). Fits an 8 GB GPU at fp16. - Prefer
/v1/audio/transcriptions. The default Qwen3-ASR chat template emits a spurious empty system turn that degrades/v1/chat/completionsquality — if you need the chat endpoint, pass a fixed template +--chat-template ... --trust-request-chat-template. - Keep a generation length cap (
max_new_tokens≈64) as a decode guard.
For the LoRA adapter (and how to apply/merge it yourself), see
chrullis/qwen3-asr-radio-1.7b-lora.
Training
- Base: Qwen/Qwen3-ASR-1.7B; method: LoRA (r=16, α=32) on the Thinker's audio encoder
and text decoder (
all-linear), bf16 + gradient checkpointing, 2000 steps, lr 5e-5, effective batch 8. Fits an 8 GB consumer GPU. - Data (50/50 mix): real UWB-ATCC airband (train split, EOS-terminated targets) + synthetic channel-degraded LibriSpeech + ATCOSIM (bandlimit, SNR-swept noise, codec2 vocoder + frame errors, dropouts, signaling tones).
- Key detail: training targets include the EOS token (
<|im_end|>) — without it the model never learns to stop and runs away at inference.
Evaluation protocol
UWB-ATCC train minus an 800-clip validation set for tuning; the official test split and ATCO2 were touched only for the final numbers above. WER over normalized text.
Licensing & data provenance
Derivative of Qwen3-ASR (see base-model license) trained on UWB-ATCC, ATCOSIM, and LibriSpeech. UWB-ATCC and ATCOSIM are research/academic corpora — verify their terms before any commercial redistribution of this model; they may restrict use.