Qwen3-ASR

Qwen3-ASR is a state-of-the-art multilingual automatic speech recognition model. It runs on-device using Metal GPU acceleration via MLX, with 4-bit, 5-bit, or 8-bit quantization for efficient memory usage. Available in 0.6B and 1.7B parameter variants. 5-bit is the new accuracy/memory sweet spot — see benchmarks.

Pipeline

The Qwen3-ASR inference pipeline processes audio through four stages:

StageDescription
Audio InputRaw audio resampled to 16 kHz mono
Mel Spectrogram128-bin mel filterbank features extracted from the waveform
Audio Encoder18-layer transformer with block attention, processes mel frames into audio embeddings
Text Decoder28-layer Qwen3 transformer with grouped-query attention (GQA) and rotary position embeddings (RoPE), autoregressively generates text tokens

Performance

BackendRTFPeak MemoryNotes
Qwen3-ASR 1.7B MLX 5-bit0.0271.92 GBWER 1.32% (new accuracy leader)
Qwen3-ASR 1.7B MLX 8-bit0.0332.7 GBWER 1.52%
Qwen3-ASR 0.6B MLX 8-bit0.0151.3 GBWER 1.82%
Qwen3-ASR 0.6B MLX 5-bit0.0141.03 GBWER 1.74% (near-8-bit at ~4-bit RAM)
Qwen3-ASR 0.6B MLX 4-bit0.0121.0 GBWER 2.20%
Qwen3-ASR 0.6B CoreML INT80.0981.4 GBWER 3.02% (chunked-attn rebuild)

Apple M5 Pro, 48 GB. LibriSpeech test-clean n=200, isolated per-engine. RTF < 1.0 = faster than real-time.

Model Variants

ModelBackendSizeHuggingFace
Qwen3-ASR-0.6B (4-bit)MLX680 MBaufklarer/Qwen3-ASR-0.6B-MLX-4bit
Qwen3-ASR-0.6B (5-bit)MLX1.0 GBaufklarer/Qwen3-ASR-0.6B-MLX-5bit
Qwen3-ASR-0.6B (8-bit)MLX1.0 GBaufklarer/Qwen3-ASR-0.6B-MLX-8bit
Qwen3-ASR-0.6B (CoreML INT8)CoreML180 MBaufklarer/Qwen3-ASR-CoreML
Qwen3-ASR-1.7B (4-bit)MLX2.1 GBaufklarer/Qwen3-ASR-1.7B-MLX-4bit
Qwen3-ASR-1.7B (5-bit)MLX2.3 GBaufklarer/Qwen3-ASR-1.7B-MLX-5bit
Qwen3-ASR-1.7B (8-bit)MLX3.2 GBaufklarer/Qwen3-ASR-1.7B-MLX-8bit

CLI Usage

Transcribe an audio file with the default Qwen3-ASR model:

.build/release/speech transcribe recording.wav

Options

# Use the larger 1.7B model
.build/release/speech transcribe recording.wav --model 1.7b

# Specify language
.build/release/speech transcribe recording.wav --language en

# Streaming mode with partial results
.build/release/speech transcribe recording.wav --stream --partial

Swift API

Use the Qwen3ASR module to transcribe audio programmatically:

import Qwen3ASR

// Load the model (downloads from HuggingFace on first use)
let model = try await Qwen3ASRModel.fromPretrained()

// Transcribe audio samples (16 kHz mono Float)
let text = model.transcribe(audio: audioSamples, sampleRate: 16000)
print(text)

CoreML Encoder (Neural Engine)

Run the audio encoder on the Neural Engine via CoreML, with the text decoder on GPU via MLX. This hybrid approach lowers power consumption and frees the GPU for concurrent workloads.

import Qwen3ASR

let encoder = try await CoreMLASREncoder.fromPretrained()
let model = try await Qwen3ASRModel.fromPretrained()
let text = try model.transcribe(
    audio: samples, sampleRate: 16000,
    coremlEncoder: encoder
)
# CLI
.build/release/speech transcribe recording.wav --engine qwen3-coreml

INT8 palettized (180 MB, cosine similarity > 0.999) is the default. An INT4 variant (90 MB) is also available for size-constrained deployments.

Streaming Mode

Streaming mode uses VAD (voice activity detection) to segment audio into chunks and transcribe them incrementally. This is useful for long recordings or real-time processing.

# Stream with default segment size
.build/release/speech transcribe recording.wav --stream

# Control maximum segment duration
.build/release/speech transcribe recording.wav --stream --max-segment 15

# Show partial (in-progress) results as they arrive
.build/release/speech transcribe recording.wav --stream --partial

The --max-segment flag controls the maximum chunk duration in seconds. The --partial flag enables partial result output, showing words as they are decoded.

Supported Formats

Qwen3-ASR accepts the following audio formats. All input is automatically resampled to 16 kHz mono internally.

Important

Models are downloaded from HuggingFace on first use and cached in ~/Library/Caches/qwen3-speech/. The 4-bit 0.6B model is approximately 1.5 GB.

Cooperative cancellation

For MLX transcription, Qwen3ASRModel.transcribeCheckingCancellation(audio:sampleRate:options:) throws CancellationError when task cancellation is observed before feature extraction, encoding, decoder prefill, or a decoding step. GPU work already submitted can finish. The synchronous transcribe and transcribeBatch APIs retain their non-throwing behavior and ignore task cancellation. speech-server uses the cancellation-aware entry point for Qwen3-ASR.

MLX cache budget

Loading Qwen3-ASR bounds the process-wide MLX pool of reusable Metal buffers: min(1 GiB, RAM / 8) for 0.6B and min(4 GiB, RAM / 4) for 1.7B. Other MLX models share this pool. The ceilings apply to cached scratch buffers, not active weights or the total process footprint.

The effective limit is the lowest of the caller’s existing MLX.Memory.cacheLimit and every loaded ASR instance’s ceiling. Instances can unload in any order; the last unload or instance destruction restores the caller’s budget. A tighter limit set by the caller while models are loaded is preserved.

Single-input transcription clears reusable buffers when its decoder returns or throws, including cancellation inside the decoder. Active arrays and model weights remain loaded. The greedy transcribeBatch decoder retains the bounded pool between batches. No API options or CLI flags change.

Clearing the buffer pool lowers retained memory but requires new allocations on later requests, so short requests can take longer. Waiting for submitted decoder work to finish makes cleanup deterministic, including cancellation. Actual latency and process memory depend on the hardware, model, and audio.

Optional model ownership

SpeechRuntime provides shared loading, cancellable FIFO admission and serialized execution. Qwen3ASRRuntime selects MLX or full CoreML through explicit descriptors; native APIs and CLI flags remain available.

The audio server holds one execution permit through each complete request, including streamed output, with up to 32 requests waiting. ASR failures produce HTTP errors or WebSocket error events; valid empty transcripts remain successful. Playback preserves overflow samples. Cached models and pending playback audio still occupy memory.

Managed CoreML transcription releases temporary buffers after each request, including repeated calls from a background task.

import Qwen3ASRRuntime

let runtime = Qwen3ASRRuntime(descriptor: Qwen3ASRDescriptors.small)
let text = try await runtime.transcribe(audio: samples, sampleRate: 16000)
try await runtime.unload()