sayedM commited on
Commit
662b996
Β·
verified Β·
1 Parent(s): 71c20bd

Card: name the training data, explain the CTC head, add profile analysis and figures

Browse files
Files changed (4) hide show
  1. .gitattributes +2 -0
  2. README.md +105 -27
  3. ctc_effect.png +3 -0
  4. profile.png +3 -0
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ ctc_effect.png filter=lfs diff=lfs merge=lfs -text
37
+ profile.png filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -13,6 +13,8 @@ tags:
13
  - speculative-decoding
14
  - ctc
15
  base_model: CohereLabs/cohere-transcribe-arabic-07-2026
 
 
16
  pipeline_tag: automatic-speech-recognition
17
  ---
18
 
@@ -30,6 +32,94 @@ Two things are packaged here that the original does not have:
30
 
31
  Together: **158.5 s β†’ 4.82 s** on a 33-second Arabic clip, on a six-core desktop CPU.
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  ---
34
 
35
  ## Performance
@@ -63,10 +153,9 @@ int8, batch 16, 12 threads, preallocated KV cache, draft head enabled:
63
  | 14-minute talk (ar) | 832.8 s | 98.36 s | 8.47Γ— |
64
  | **total** | **954.4 s** | **117.3 s** | **8.14Γ—** |
65
 
66
- ### What the draft head is worth
67
 
68
- Five clips, batch 1, referenced to **fp32** β€” the only reference that separates *different* from
69
- *worse*:
70
 
71
  | configuration | wall | vs int8 | WER from fp32 |
72
  |---|---|---|---|
@@ -97,7 +186,8 @@ and the 14-minute recording β€” the only statistically meaningful one at 2,086 w
97
 
98
  A GPU is both faster **and** more accurate β€” bf16 is a far lighter perturbation than int8. This repo
99
  is for when there is no GPU. Worth knowing: the gap is 4.2Γ— at batch 1 and 13.2Γ— batched, because
100
- only the GPU gains from being fed more work.
 
101
 
102
  ---
103
 
@@ -129,9 +219,7 @@ Loading takes about 10 s and needs ~2.9 GB of RAM.
129
 
130
  ---
131
 
132
- ## How it works
133
-
134
- ### int8 dynamic quantization
135
 
136
  Every `nn.Linear` β€” 1.81 B of the 2.07 B parameters β€” is stored as int8. Convolutions, layer norms
137
  and embeddings stay fp32. Weights use one per-tensor scale, `float32(max|W| / 127.5)`, with a zero
@@ -142,24 +230,11 @@ That last detail has a visible consequence: there is one activation scale for th
142
  handing a layer a wider tensor quantizes it more coarsely. This is why the model's output shifts
143
  slightly with batch size, and why speculative decoding cannot be bit-identical to greedy.
144
 
145
- ### The Arabic CTC draft head
146
-
147
- At batch 1 the decoder is bound by per-call overhead rather than arithmetic β€” a 32-token forward
148
- costs only 1.30Γ— a 1-token forward. A CTC head over the encoder output exploits that: it is
149
- frame-synchronous and non-autoregressive, so it emits an entire hypothesis in **one** pass
150
- (**22.8 ms** against a 1069 ms decoder), and the decoder then verifies up to K+1 tokens for roughly
151
- the price of one.
152
-
153
- Architecture: one `ParakeetEncoderBlock` copied from `encoder.layers[47]`, then a projection, a
154
- LayerNorm and an output matrix copied from the base model's `decoder.proj`, `decoder.norm` and
155
- `proj_out`. Blank is `pad_token_id` = 2, which never appears in a transcript. Trained by
156
- self-distillation on 18.3 hours of Egyptian Arabic labelled by the model itself β€” no annotation
157
- involved. Acceptance 0.331, **2.66 tokens per decoder forward**.
158
-
159
- **A wrong guess cannot produce a wrong word.** The verifier keeps only the prefix its own argmax
160
- agrees with, then appends one token taken straight from the decoder, so a drafted token reaches the
161
- output only if the decoder would have produced it anyway. A completely wrong draft degrades to
162
- ordinary greedy decoding. What a bad draft costs is time, not accuracy.
163
 
164
  ---
165
 
@@ -176,6 +251,7 @@ ordinary greedy decoding. What a bad draft costs is time, not accuracy.
176
  this model to CUDA raises `NotImplementedError`. For a GPU, use the base model in bf16.
177
  * **int8 costs accuracy.** 2.06% WER from fp32. If you have the RAM and can spare the speed, fp32 is
178
  more faithful; if you have a GPU, bf16 is both faster and more accurate.
 
179
  * Inherits every limitation of the base model, including its language and domain coverage.
180
 
181
  ---
@@ -207,11 +283,13 @@ the weights bit-perfect.
207
 
208
  ## License and attribution
209
 
210
- Apache 2.0, inherited from [CohereLabs/cohere-transcribe-arabic-07-2026]( https://huggingface.co/CohereLabs/cohere-transcribe-arabic-07-2026 ), copyright Cohere Labs.
211
 
212
  **Changes made to the original** (Apache 2.0 Β§4b): weights of all `nn.Linear` modules quantized to
213
  int8 as described above; a separately trained CTC draft head added; no change to the architecture,
214
  the tokenizer, or the training data of the base model.
215
 
216
  The base repository is **gated**. This derivative is not, so if you need the original weights,
217
- please obtain them from Cohere Labs directly and accept their terms.
 
 
 
13
  - speculative-decoding
14
  - ctc
15
  base_model: CohereLabs/cohere-transcribe-arabic-07-2026
16
+ datasets:
17
+ - oddadmix/lahgtna-v3-small
18
  pipeline_tag: automatic-speech-recognition
19
  ---
20
 
 
32
 
33
  Together: **158.5 s β†’ 4.82 s** on a 33-second Arabic clip, on a six-core desktop CPU.
34
 
35
+ ![What the Arabic CTC draft head does](ctc_effect.png)
36
+
37
+ ---
38
+
39
+ ## The draft head, and why it helps
40
+
41
+ The decoder here is autoregressive: one forward pass per token, ~130 per chunk. On a CPU at batch 1
42
+ that is not an arithmetic problem β€” it is a *per-call* problem. Measured on this machine:
43
+
44
+ | tokens per decoder forward | 1 | 4 | 8 | 16 | 32 |
45
+ |---|---|---|---|---|---|
46
+ | ms per forward | 22.06 | 24.55 | 27.38 | 27.17 | **28.72** |
47
+ | ms per token | 22.06 | 6.14 | 3.42 | 1.70 | **0.90** |
48
+
49
+ **Thirty-two tokens cost 1.30Γ— what one token costs.** The per-token price falls 24-fold. Almost all
50
+ of a decoder call is fixed overhead, and the arithmetic in between is nearly free.
51
+
52
+ That is exactly the condition speculative decoding needs. If something cheap can *guess* the next K
53
+ tokens, the decoder can check all of them in a single forward for roughly the price of checking one.
54
+
55
+ **The CTC head is that cheap thing.** It sits on the encoder output and is frame-synchronous and
56
+ non-autoregressive β€” CTC assumes tokens are conditionally independent given the audio, so the whole
57
+ hypothesis comes out of **one** forward pass. That independence assumption makes it a poor
58
+ transcriber and an excellent drafter. Measured cost: **22.8 ms** on a 10-second clip, against a
59
+ 1069 ms decoder β€” a 2.1% tax.
60
+
61
+ The effect, measured: **2.66 tokens retired per decoder call** instead of 1, which is
62
+ **1.29×–1.48Γ—** wall clock depending on the clip, as shown in the figure above.
63
+
64
+ ### A wrong guess cannot produce a wrong word
65
+
66
+ The verifier keeps only the prefix its own argmax agrees with, then appends one token taken straight
67
+ from the decoder. **A drafted token reaches the output only if the decoder would have produced it
68
+ anyway.** A completely wrong draft degrades to ordinary greedy decoding. What a bad draft costs is
69
+ time, not accuracy β€” which is why the accuracy column below barely moves.
70
+
71
+ ---
72
+
73
+ ## Training data for the CTC head
74
+
75
+ | | |
76
+ |---|---|
77
+ | dataset | [`oddadmix/lahgtna-v3-small`](https://huggingface.co/datasets/oddadmix/lahgtna-v3-small) β€” multi-dialect Arabic speech |
78
+ | used for training | **4,256 clips Β· 18.33 h** from the `train` split (median clip 15.3 s, filtered to 1–30 s) |
79
+ | held out | **1,024 clips Β· 4.58 h** from the `test` split, never trained on |
80
+ | labels | **the base model's own transcripts** β€” self-distillation |
81
+ | epochs | 12, AdamW, `F.ctc_loss` with blank = `pad_token_id` = 2 |
82
+
83
+ **Only the audio from that dataset was used.** The targets are not its transcripts β€” they are what
84
+ [CohereLabs/cohere-transcribe-arabic-07-2026](https://huggingface.co/CohereLabs/cohere-transcribe-arabic-07-2026) itself emits on that audio. The drafter's job is to predict
85
+ *this model's* output, so training it on anything else would teach it to propose tokens the verifier
86
+ would reject. It also means no human annotation was involved anywhere in this head.
87
+
88
+ Labels were produced by an fp32 GPU replica of the deployed int8 model rather than by bf16, which
89
+ cut label noise against the deployed model from 2.81% to 1.74% WER.
90
+
91
+ **Vocabulary saturates almost immediately.** Only 274 of the 16,384 vocabulary entries contain an
92
+ Arabic character at all β€” the rest belong to other scripts the multilingual base model supports β€” and
93
+ the tokenizer is sub-word for Arabic at ~2.4 tokens per word. After **0.83 hours**, the types seen
94
+ 10+ times already covered **99.24% of all token occurrences**. Past that point more data buys
95
+ acoustic quality, not new symbols.
96
+
97
+ The head is **data-limited, not capacity-limited**: acceptance went 0.143 at 0.83 h to 0.331 at
98
+ 18.3 h, and test loss was still improving on the metric that matters when labelling stopped for
99
+ time. More hours would help.
100
+
101
+ ---
102
+
103
+ ## Where the time goes
104
+
105
+ ![Latency profile](profile.png)
106
+
107
+ Two independent measurements agree on this: forward hooks attributing time to architectural
108
+ components, and `torch.profiler` attributing it to ATen kernels. The kernel view:
109
+
110
+ | ATen kernel | self CPU | share | calls | what it is |
111
+ |---|---|---|---|---|
112
+ | `quantized::linear_dynamic` | 900.9 ms | 58.5% | 4,079 | the int8 GEMMs β€” the real work |
113
+ | `aten::mkldnn_convolution` | 264.4 ms | 17.2% | 149 | conformer convs, still fp32 |
114
+ | SDPA flash attention | 55.1 ms | 3.6% | 928 | attention proper |
115
+ | `aten::aminmax` | 36.1 ms | 2.3% | 4,079 | the per-call activation scale β€” pure dynamic-quant overhead |
116
+ | `aten::cat` | 34.9 ms | 2.3% | 952 | KV cache growing one token at a time |
117
+ | view / empty / as_strided | ~74 ms | 4.8% | 48,600 | dispatch overhead, no arithmetic |
118
+
119
+ `aminmax` fires exactly once per `linear_dynamic` β€” that is dynamic quantization recomputing an
120
+ activation scale on every single call. And 48,600 calls to pure-metadata operations do no arithmetic
121
+ at all. The decoder issues roughly 3,520 of the 4,079 GEMM calls, each on a 1Γ—1024 vector.
122
+
123
  ---
124
 
125
  ## Performance
 
153
  | 14-minute talk (ar) | 832.8 s | 98.36 s | 8.47Γ— |
154
  | **total** | **954.4 s** | **117.3 s** | **8.14Γ—** |
155
 
156
+ ### What the draft head is worth, referenced to fp32
157
 
158
+ fp32 is the only reference that separates *different* from *worse*:
 
159
 
160
  | configuration | wall | vs int8 | WER from fp32 |
161
  |---|---|---|---|
 
186
 
187
  A GPU is both faster **and** more accurate β€” bf16 is a far lighter perturbation than int8. This repo
188
  is for when there is no GPU. Worth knowing: the gap is 4.2Γ— at batch 1 and 13.2Γ— batched, because
189
+ only the GPU gains from being fed more work. The draft head also transfers to the GPU, where it
190
+ gives 1.40Γ— at batch 1 β€” almost exactly what it gives on the CPU, for the same underlying reason.
191
 
192
  ---
193
 
 
219
 
220
  ---
221
 
222
+ ## How the quantization works
 
 
223
 
224
  Every `nn.Linear` β€” 1.81 B of the 2.07 B parameters β€” is stored as int8. Convolutions, layer norms
225
  and embeddings stay fp32. Weights use one per-tensor scale, `float32(max|W| / 127.5)`, with a zero
 
230
  handing a layer a wider tensor quantizes it more coarsely. This is why the model's output shifts
231
  slightly with batch size, and why speculative decoding cannot be bit-identical to greedy.
232
 
233
+ The draft head architecture: one `ParakeetEncoderBlock` copied from `encoder.layers[47]`, then a
234
+ projection, a LayerNorm and an output matrix copied from the base model's `decoder.proj`,
235
+ `decoder.norm` and `proj_out`. Folding those three into a single matrix β€” the obvious "free"
236
+ initialisation β€” measured **worse than random init**, because the real path contains a LayerNorm and
237
+ a LayerNorm cannot be folded into a matrix product.
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
  ---
240
 
 
251
  this model to CUDA raises `NotImplementedError`. For a GPU, use the base model in bf16.
252
  * **int8 costs accuracy.** 2.06% WER from fp32. If you have the RAM and can spare the speed, fp32 is
253
  more faithful; if you have a GPU, bf16 is both faster and more accurate.
254
+ * The draft head saw 18.3 h of one dataset's dialect mix. Acoustics far from it will accept less.
255
  * Inherits every limitation of the base model, including its language and domain coverage.
256
 
257
  ---
 
283
 
284
  ## License and attribution
285
 
286
+ Apache 2.0, inherited from [CohereLabs/cohere-transcribe-arabic-07-2026](https://huggingface.co/CohereLabs/cohere-transcribe-arabic-07-2026), copyright Cohere Labs.
287
 
288
  **Changes made to the original** (Apache 2.0 Β§4b): weights of all `nn.Linear` modules quantized to
289
  int8 as described above; a separately trained CTC draft head added; no change to the architecture,
290
  the tokenizer, or the training data of the base model.
291
 
292
  The base repository is **gated**. This derivative is not, so if you need the original weights,
293
+ please obtain them from Cohere Labs directly and accept their terms. Speech data for the draft head
294
+ came from [`oddadmix/lahgtna-v3-small`](https://huggingface.co/datasets/oddadmix/lahgtna-v3-small); please observe that dataset's own
295
+ licence and terms for any redistribution of the audio itself (none of it is included here).
ctc_effect.png ADDED

Git LFS Details

  • SHA256: d5f6057a014824358d2e4984bde6b6ead6b6edd54bcfcedc32990211275d8fdd
  • Pointer size: 131 Bytes
  • Size of remote file: 130 kB
profile.png ADDED

Git LFS Details

  • SHA256: 8bd0262266da6bf9ca68ca331155af9853f7ec7f5dda9eb4c6f139a16241fa1f
  • Pointer size: 131 Bytes
  • Size of remote file: 197 kB