File size: 8,646 Bytes
15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 7f0d463 48f6e72 15bcb35 48f6e72 7f0d463 ffd7f3e 15bcb35 ab6821a 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 7f0d463 48f6e72 7f0d463 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 7f0d463 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 7f0d463 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 7f0d463 ffd7f3e 48f6e72 ffd7f3e 48f6e72 15bcb35 ffd7f3e ab6821a 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 15bcb35 48f6e72 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | ---
language: en
license: other
base_model:
- JetLM/SDAR-1.7B-Chat
- Qwen/Qwen3-ASR-0.6B
datasets:
- librispeech_asr
tags:
- automatic-speech-recognition
- speech
- diffusion
- lora
- block-diffusion
pipeline_tag: automatic-speech-recognition
---
# Diffusion ASR
Speech recognition with a **block-diffusion language model as the decoder**. A frozen audio
encoder and a frozen text diffusion LM are joined by a small trained adapter plus LoRA β about
1 % of the stack is trained.
**4.60 % WER on LibriSpeech test-clean** (all 2,620 clips).
> **These are trained deltas, not a standalone model.** This repo holds the 5.25 M-parameter
> adapter and the 17.4 M-parameter LoRA. Running them also needs the two frozen base models
> below and the inference code from
> [ipritamdash/diffusion-asr-research](https://github.com/ipritamdash/diffusion-asr-research).
## Why this exists
The audio encoder came from a working ASR system. That system's own decoder is thrown away here
and a text-only diffusion model put in its place, connected through roughly 5 M trained
parameters.
The question is whether a language model that has never seen audio can be conditioned on
acoustic evidence through a thin learned interface β and whether writing several tokens per
forward pass buys anything.
Compare it against other frozen-encoder + frozen-LM + adapter systems. Do not compare it against
end-to-end ASR trained on thousands of hours; it saw 30.
## Results
Full LibriSpeech test-clean, 2,620 clips, corpus WER after Whisper English normalisation.
| Decoder | Config | WER | vs real time |
|---|---|---:|---:|
| **Reference loop** | block 2 | **4.60** | β |
| Reference loop | block 4 | 5.23 | β |
| **LMDeploy engine, swept config** | **block 4 Β· 4 steps Β· `sequential`** | **4.78** | 7.0Γ |
| LMDeploy engine, old default | block 2 Β· dynamic | 5.00 | 9.1Γ |
| LMDeploy engine, old default | block 4 Β· dynamic | 5.63 | 13.5Γ |
Speed is single-stream real-time multiple on an A100; batched serving measured far higher
(~800Γ on one L40S in an earlier benchmark). Under the engine's original confidence-ordered
commit rule it read a constant 0.40 worse than the reference loop; the swept `sequential`
commit order closes half of that (engine best 4.78 vs reference 4.60). The remaining +0.18 is
unexplained and stated rather than left out.
### Error analysis
Computed over every clip of the 4.60 decode.
**Output health** β zero empty transcripts, zero repeat loops, zero length blow-ups, and no clip
hit the token budget.
**Composition** β of 53,029 reference words: 3.75 % substituted, 0.40 % deleted, 0.45 %
inserted. Four errors in five are a *wrong* word rather than a missing one; the model commits to
a plausible word from unclear evidence instead of failing to produce one.
**Substitutions** β 40 % are within a small character edit of the reference (*jailer* β
*jailor*, *holmes* β *homes*), which reads as blurred acoustic detail rather than a language
failure.
**Multi-piece words** β words the tokeniser splits into several subword pieces are 7.5 % of
words but carry 52 % of errors, at 13Γ the failure rate of single-piece words. This is the
dominant failure mode.
**Length and position** β the shortest quarter of clips is hardest (5.85 % against 4.37 % on the
longest), and quality is flat across an utterance. A fixed token budget and a block decoder could
have degraded on long input; neither did.
## Files
| File | Contents | Params |
|---|---|---:|
| `adapter.pt` | audio adapter + `gain`, CTC logit scale, CTC blank projection | 5,250,049 (+2,050) |
| `lora.pt` | LoRA on the decoder, PEFT state dict, 392 tensors | 17,432,576 |
`adapter.pt` keys: `adapter`, `log_s`, `blank_w`, `blank_b`, `epoch`, `adapter_kind` (`conv`),
`adapter_stride` (`1`), `attn_mode` (`stair`), plus dev metrics.
`lora.pt` keys: `lora`, `epoch`, `dev_ce`, `objective`.
`log_s`, `blank_w` and `blank_b` belong to the CTC alignment loss used during training. They are
not used at decode time, and are kept so training can be resumed or audited.
## Base models β frozen, download separately
| Role | Repo | Pinned revision |
|---|---|---|
| Decoder | `JetLM/SDAR-1.7B-Chat` | `97cf0dc9a7e85433f3f71a6bbac6fec9d381294f` |
| Encoder | `Qwen/Qwen3-ASR-0.6B` | `5eb144179a02acc5e5ba31e748d22b0cf3e303b0` |
Use these exact revisions β the weights here were trained against them.
> The decoder must be loaded through the project repo's patched loader
> (`qwen_sdar/sdar.py::load_patched_sdar`), which swaps CUDA-only flash-attn for SDPA and adds
> the embeddings-input path. Plain `from_pretrained` will not reproduce these numbers.
## Architecture
```
audio 16 kHz β Qwen3-ASR-0.6B encoder β adapter β SDAR-1.7B decoder + LoRA β text
frozen, 1024-d @ 12.5 Hz trained frozen trained
```
**Adapter** β `Conv1d(1024β1024, k=3, stride=1)` β GELU β `Linear(1024β2048)` β
`LayerNorm(2048)` β Γ`gain`. Stride 1 keeps the encoder's native 12.5 Hz; pooling to 6.25 Hz
measured worse across three seeds per arm, at roughly half the training cost.
`gain` initialises to the RMS of the decoder's token embeddings. A random projection emits
vectors 10β50Γ the size of real word vectors, and a decoder fed a prefix that loud learns to
ignore it; matching the embedding scale is what makes the audio legible at all.
**LoRA** β `r=16`, `alpha=32`, dropout 0, on `q,k,v,o,gate,up,down` in all 28 layers. LoRA scales
updates by `alpha/r`, so the effective learning rate on those weights is 2Γ nominal.
**Attention is block-causal at every stage**, training and inference alike. Each block sees the
audio, all earlier blocks, and itself. Training and serving therefore use the same convention,
and the model runs on a standard diffusion inference engine without retraining.
## Training
30 hours of LibriSpeech `train-clean-100`, speaker-disjoint from the held-out split. Three
stages, never joint:
| Stage | Trains | Loss |
|---|---|---|
| Grounding | adapter | CTC only, decoder detached |
| SFT | adapter | CTC + diffusion cross-entropy |
| LoRA | LoRA | diffusion cross-entropy only, adapter frozen |
Roughly 32 GPU-hours in total.
## Decoding
Block-causal, blocks written left to right, earlier blocks frozen once committed.
| | |
|---|---|
| Block width | 4 (engine, swept) Β· 2 (reference loop) |
| Denoising steps per block | up to 4 |
| Commit rule | reference loop: confidence β₯ 0.95 with forced fallback; engine: **`sequential` unmasking** (position order), found best in a 50-arm sweep |
| Token budget | 160 |
A 50-arm sweep of every engine dial (widths 1β64, steps, thresholds, all three unmasking
strategies), confirmed on the full dev set, chose the engine config above; decoding wider than
the trained block-4 width collapses (width 8 already mass-produces empty output), so the
frontier honestly ends at width 4.
With the original confidence-ordered commits, width 2 (below the paper's swept range) measured
best; with `sequential` commits the trained width 4 wins β commit order, not width alone, was
carrying that result.
## Usage
The encoder and decoder have conflicting dependencies, so inference runs in two steps in two
environments. Both need a CUDA GPU.
```bash
git clone https://github.com/ipritamdash/diffusion-asr-research
cd diffusion-asr-research
huggingface-cli download Tachyeon/diffusion-asr adapter.pt lora.pt --local-dir .
# 1. encoder environment: audio -> features
pip install -r requirements-encoder.txt
python featurize.py --audio clip.wav --out features.pt
# 2. decoder environment: features -> transcript
pip install -r requirements-decode.txt
python decode.py --features features.pt --adapter adapter.pt --lora lora.pt --block_length 2
```
`decode.py` reads `adapter_stride` from the checkpoint and refuses to run if it is not recorded.
A stride-1 and a stride-2 adapter have identical tensor shapes, so guessing wrong loads cleanly
and silently emits a prefix of the wrong length.
For serving, merge the LoRA and run on LMDeploy's PyTorch diffusion engine; see `engine/` in the
project repo.
## Scope
A research model for **English read speech**. LibriSpeech is clean, read audiobook material and
these numbers describe that domain.
## Licence
The adapter and LoRA here are trained deltas. Any use depends on the two frozen base models,
which carry **their own licences** β check `JetLM/SDAR-1.7B-Chat` and `Qwen/Qwen3-ASR-0.6B`
before redistributing anything, and do not redistribute a merged checkpoint (which embeds the
base weights) without confirming their terms.
|