Views
No views yet
Qwen/Qwen3-ForcedAligner-0.6B — a single-pass forced aligner that takes any (audio, transcript) pair and predicts per-word/per-token timestamps. Reuses the Qwen3-ASR audio encoder + 28-layer LLM body but swaps the lm_head from (vocab, d) to (5000, d): each <timestamp> placeholder you embed in the input gets a 5000-class softmax over class * 80 ms timestamps.-am qwen3-forced-aligner-*.gguf to get word-level timing on any transcription backend — voxtral, voxtral4b, qwen3-asr, granite, parakeet, canary, cohere, even whisper. It's an alternative to the existing canary-ctc-aligner second-pass with broader language coverage and 80-ms resolution.| File | Size | Quantization | Notes |
|---|---|---|---|
qwen3-forced-aligner-0.6b-f16.gguf | 1.84 GB | F16 | Reference precision; matches PyTorch bfloat16 within float-noise tolerance |
qwen3-forced-aligner-0.6b-q8_0.gguf | 0.99 GB | Q8_0 | Effectively lossless |
qwen3-forced-aligner-0.6b-q5_0.gguf | 0.64 GB | Q5_0 | Slightly slower than Q4_K but a bit more accurate on edge cases |
qwen3-forced-aligner-0.6b-q4_k.gguf | 0.53 GB | Q4_K | 3.5× compressed; smallest reasonable choice |
| Qwen3-ASR-0.6B / 1.7B | Qwen3-ForcedAligner-0.6B | |
|---|---|---|
| Audio encoder | 24-layer, d_model 1024 | identical |
| Text decoder | Qwen3 28-layer | identical body |
lm_head shape | (vocab=152K, d) | (5000, d) — timestamp classes |
| Inference mode | Autoregressive (decode token by token) | Single forward pass over the whole input |
| Use case | Audio → text | (Audio, text) → per-word timestamps |
| Output | Generated tokens | argmax(lm_head)·80 ms at each <timestamp> placeholder |
output.weight.ne[1] — no separate backend, no separate library.1# Build crispasr (one-time)
2git clone https://github.com/CrispStrobe/CrispASR
3cd CrispASR
4cmake -B build -DCMAKE_BUILD_TYPE=Release
5cmake --build build -j$(nproc) --target whisper-cli
6
7# Word-level SRT from any transcription backend, using FA for timing.
8# `-am` (--aligner-model) auto-routes to the qwen3-fa path when the
9# filename contains "forced-aligner" (case-insensitive).
10
11# voxtral 3B + Qwen3-FA timing
12./build/bin/crispasr --backend voxtral \
13 -m voxtral-mini-3b-2507-q8_0.gguf \
14 -f my_audio.wav \
15 -am qwen3-forced-aligner-0.6b-q4_k.gguf \
16 -osrt -ml 1
17
18# parakeet + Qwen3-FA (parakeet has its own native word timestamps, but
19# you can override them with FA on the same audio)
20./build/bin/crispasr --backend parakeet \
21 -m parakeet-tdt-0.6b-v3-q4_k.gguf \
22 -f my_audio.wav \
23 -am qwen3-forced-aligner-0.6b-q4_k.gguf \
24 -osrt -ml 1
25
26# Granite, qwen3-asr, voxtral4b, cohere, canary all work the same way.Qwen3ForcedAligner.align(audio, text, language) from qwen-asr. Our C++ wrapper does the whole pipeline (mel → encoder → prompt build with <timestamp> placeholders → embed + audio splice → single FA forward → argmax at placeholder positions → ms conversion) in one call to qwen3_asr_align_words(ctx, samples, n_samples, words[], n_words, out_start_ms, out_end_ms).tokenize_japanese, tokenize_korean via soynlp). Adding char-level tokenization for CJK languages is a follow-up tracked in the CrispASR repo.1# 1. Download the base model from HF
2hf download Qwen/Qwen3-ForcedAligner-0.6B --local-dir ./Qwen3-ForcedAligner-0.6B
3
4# 2. Convert to F16 GGUF (the qwen3-asr converter handles both ASR and
5# ForcedAligner variants — sizes are read from config.json so the
6# same script handles both checkpoints)
7python models/convert-qwen3-asr-to-gguf.py \
8 --input ./Qwen3-ForcedAligner-0.6B \
9 --output qwen3-forced-aligner-0.6b-f16.gguf
10
11# 3. Quantize
12./build/bin/crispasr-quantize qwen3-forced-aligner-0.6b-f16.gguf qwen3-forced-aligner-0.6b-q8_0.gguf q8_0
13./build/bin/crispasr-quantize qwen3-forced-aligner-0.6b-f16.gguf qwen3-forced-aligner-0.6b-q5_0.gguf q5_0
14./build/bin/crispasr-quantize qwen3-forced-aligner-0.6b-f16.gguf qwen3-forced-aligner-0.6b-q4_k.gguf q4_kqwen3_asr_load_model now reads the actual output.weight.ne[1] instead of asserting it equals llm.vocab_size. For ASR models the two are equal (152K); for FA models the head is 5000 wide.qwen3_asr_run_aligner() extern "C" entry point runs build_graph_llm_kv(..., last_token_only=false) so the lm_head sees every token position, not just the last. The result is a (5000, T) logit matrix; qwen3_asr_align_words() reads argmax at the positions where input_id == 151705 (<timestamp> placeholder) and converts to ms via class * 80.1crispasr --backend voxtral -m voxtral-mini-3b-2507-q8_0.gguf \
2 -f samples/jfk.wav \
3 -am qwen3-forced-aligner-0.6b-q4_k.gguf -ml 1[00:00:00.320 --> 00:00:00.560] And
[00:00:00.960 --> 00:00:00.960] so,
[00:00:00.960 --> 00:00:01.280] my
[00:00:01.360 --> 00:00:01.680] fellow
[00:00:02.080 --> 00:00:02.160] Americans,
... (10 s total, 21 words)1@misc{qwen3asr,
2 title = {Qwen3-ASR},
3 author = {Qwen Team},
4 year = {2026},
5 url = {https://huggingface.co/Qwen/Qwen3-ForcedAligner-0.6B}
6}Qwen.apache-2.0. This repository redistributes under the same terms; it grants no rights the upstream licence does not.