| --- |
| license: apache-2.0 |
| language: |
| - ar |
| - de |
| - el |
| - en |
| - es |
| - fr |
| - it |
| - ja |
| - ko |
| - nl |
| - pl |
| - pt |
| - vi |
| - zh |
| pipeline_tag: automatic-speech-recognition |
| tags: |
| - audio |
| - speech-recognition |
| - transcription |
| - onnx |
| - directml |
| base_model: |
| - CohereLabs/cohere-transcribe-03-2026 |
| --- |
| |
| # Cohere Transcribe 03-2026 — DirectML-safe, fully-static ONNX |
|
|
| ONNX export of [CohereLabs/cohere-transcribe-03-2026](https://huggingface.co/CohereLabs/cohere-transcribe-03-2026) |
| restructured to run **correctly and fast on the ONNX Runtime DirectML EP** (and every other EP). |
| Weight bytes are identical to |
| [onnx-community/cohere-transcribe-03-2026-ONNX](https://huggingface.co/onnx-community/cohere-transcribe-03-2026-ONNX); |
| the encoder proto additionally embeds the cross-attention projection weights hoisted out of the |
| decoder (see below). |
|
|
| ## Why the stock export can't run well on DirectML |
|
|
| - The fused decoder attention (`com.microsoft.MultiHeadAttention` cross-attn + `GroupQueryAttention` |
| self-attn) **crashes / mis-computes** on the DML EP (ORT ≤ 1.24, unfixed). |
| - Even after decomposing to plain ops, the DML EP **re-fuses its graph on every autoregressive |
| shape change** (~34 ms/token fixed), so naïve GPU decode loses to CPU. |
|
|
| ## What changed (three passes, `tools/onnx/cohere_decompose_attention.py`) |
|
|
| 1. **Attention decomposed** to Reshape/Transpose/MatMul/Softmax/… with an explicit causal mask; the |
| two cross-attn `If` nodes flattened away. |
| 2. **Loop-invariant cross-KV hoisted into the encoder** — the cross-attention K/V (which depend only |
| on the audio) are computed once per utterance in `encoder_model*.onnx` (extra `cross_attn.*` |
| outputs) instead of on every decoded token. |
| 3. **Fully static decode** — the self-KV cache is a fixed-length buffer updated by a masked write, |
| the cross-KV are padded to a fixed length with a `cross_bias` mask, and every per-step shape is |
| constant. The DML EP now compiles the decoder **once** and reuses it, so per-token cost is |
| **constant ~4 ms regardless of audio length**, entirely on the GPU. |
|
|
| Two decoders are shipped per precision: |
|
|
| - `decoder_model_merged*.onnx` — **fully static** (fixed self- + cross-KV). Fastest on DirectML |
| (metadata `winstt_static_kv`). |
| - `decoder_model_merged*_dyn.onnx` — **growing self-KV** (cross still fixed). Faster on the CPU EP, |
| which doesn't benefit from static shapes. WinSTT loads this one when the decoder is CPU-bound. |
|
|
| Both are numerically identical to the fused graph on CPU (autoregressive logits parity: bit-exact |
| for fp32/int8/q4; fp16-rounding-level for fp16/q4f16). One padded encoder feeds both. |
|
|
| Measured decode per-token (fp32, ORT 1.24, RTX 3080 Ti), 3.4–4.4× faster than the prior hybrid: |
|
|
| | | DirectML (static) | CPU (dynamic) | |
| |---|---|---| |
| | any length (5 s … 66 s) | **~4 ms/token** | ~12 ms/token | |
|
|
| Produced by [WinSTT](https://github.com/dahshury/WinSTT). |
|
|
| ## Files |
|
|
| - `onnx/encoder_model[_fp16|_int8|_q4|_q4f16].onnx` (+ external data) — hoisted, cross-padded encoder. |
| - `onnx/decoder_model_merged[_fp16|_int8|_q4|_q4f16].onnx` (+ `_dyn`) — static / dynamic decoders. |
| - Tokenizer / configs — unchanged. |
|
|