Unable to reproduce benchmarks

#47
by abhinavkulkarni - opened

Hi,

Thanks to the Mistral team for open sourcing this incredible model!

Hoever, we are unable to reproduce benchmarks mentioned in the paper. The dataset details and inference logic is described below.


Benchmarking Details

Datasets and test split used for benchmarking ✅

# Dataset HuggingFace Path Config Split Test Samples Other Splits Available
1 librispeech_clean openslr/librispeech_asr clean test 2,620 train.100, train.360, validation
2 librispeech_other openslr/librispeech_asr other test 2,939 train.500, validation
3 voxpopuli facebook/voxpopuli en test 1,842 train, validation
4 fleurs_en google/fleurs en_us test 647 train, validation
5 fleurs_hi google/fleurs hi_in test 418 train, validation
6 earnings22 distil-whisper/earnings22 full test 125 (only test split exists)
7 meanwhile distil-whisper/meanwhile test 64 (only test split exists)
8 switchboard hhoangphuoc/switchboard test 51,501 train, validation
9 common_voice_en fixie-ai/common_voice_17_0 en test 16,393 train, validation, validated
10 common_voice_hi fixie-ai/common_voice_17_0 hi test 3,154 train, validation, other, invalidated, validated

📊 Reproduction Results — Voxtral Realtime (WER %)

English Short-Form (Table 5 columns)

Dataset Delay Paper WER Ours WER Δ Match?
LibriSpeech Clean 480ms 2.08 3.09 +1.01
960ms 1.96 2.96 +1.00
2400ms 1.82 2.86 +1.04
LibriSpeech Other 480ms 5.54 6.66 +1.12
960ms 4.59 5.88 +1.29
2400ms 4.03 5.40 +1.37
VoxPopuli 480ms 7.87 11.11 +3.24
960ms 7.23 10.41 +3.18
2400ms 7.06 10.12 +3.06

English Long-Form (Table 6)

Dataset Delay Paper WER Ours WER Δ Match?
Meanwhile 480ms 5.05 6.79 +1.74
960ms 4.14 5.19 +1.05
2400ms 4.03 5.03 +1.00

FLEURS (Table 7)

Dataset Delay Paper WER Ours WER Δ Match?
FLEURS EN 480ms 4.90 13.00 +8.10 ❌❌
960ms 4.34 9.11 +4.77
2400ms 4.05 7.03 +2.98
FLEURS HI 480ms 12.88 19.24 +6.36
960ms 11.82 17.62 +5.80
2400ms 10.73 17.06 +6.33

Mozilla Common Voice (Table 8)

Dataset Delay Paper WER Ours WER Δ Match?
Common Voice EN 480ms 15.18 15.46 +0.28 ✅ ~close
960ms 12.49 12.87 +0.38 ✅ ~close
2400ms 10.51 11.20 +0.69 ✅ ~close
Common Voice HI 480ms 17.22 21.70 +4.48
960ms 15.04 19.27 +4.23
2400ms 13.19 17.44 +4.25

Inference details

1. Text Normalization — Our Pipeline

_PUNCT_RE = re.compile(r"[^\w\s]", re.UNICODE)
_WS_RE = re.compile(r"\s+")

def normalise_text(text: str) -> str:
    text = text.lower()
    text = _PUNCT_RE.sub("", text)    # strip everything NOT \w or whitespace
    text = _WS_RE.sub(" ", text).strip()
    return text

Applied to both reference and hypothesis before WER/CER computation.

What this does:

Before After
"Hello, World!" "hello world"
"don't" "dont"
"25 to 30 year" "25 to 30 year"
"U.S.A." "usa"
"CONCORD RETURNED" "concord returned"

Important: Hindi/Devanagari issue ⚠️

The regex [^\w\s] strips all non-word, non-space characters. In Devanagari, dependent vowel signs (matras) like , , ि, are Unicode category Mark (Mn/Mc), NOT matched by \w. So our normalization strips them:

Input:  "नमस्ते, दुनिया!" 
Output: "नमसत दनय"  
         ↑ matras े, ु, ि, ा stripped!

This corrupts Hindi text. However, since the same normalization is applied to both reference and hypothesis, the WER impact depends on whether both sides lose the same characters. If the model faithfully reproduces the matras and the reference has them too, both get stripped symmetrically and WER may not be heavily affected. But any asymmetric stripping (e.g., a word boundary shifts due to padding) could create artificial mismatches.

2. Inference & Padding Logic — Detailed Walkthrough

Overview of the data flow

Raw audio (16 kHz) 
     │
     ▼
  _right_pad_audio()      ← add silence at the END
     │
     ▼
  processor() with is_streaming=False
     │  ├── left-pads the raw waveform with silence (32 adapter frames ≈ 2.56 s)
     │  ├── converts waveform → log-Mel spectrogram (128 bins, hop=160)
     │  └── tokenizes the audio into [STREAMING_PAD]*32 + audio tokens
     │
     ▼
  model.generate() with max_new_tokens
     │
     ▼
  processor.batch_decode(skip_special_tokens=True)
     │
     ▼
  normalise_text()        ← lower-case, strip punctuation, collapse whitespace
     │
     ▼
  evaluate_pair()         ← HuggingFace evaluate WER/CER

Right-padding: why τ + 10 frames?

The decoder emits one token per 80 ms adapter frame. For the last word in the utterance, its word-boundary token [W] fires τ frames after the acoustic evidence ends. Then the subword tokens of that word follow at τ+1, τ+2, … without right-padding, the encoder runs out of states and the decoder must hallucinate text without acoustic grounding → inflated WER. The 10-frame headroom covers subword tokens after [W] (~800 ms worth).

Left-padding: 32 [STREAMING_PAD] tokens

The model's AudioConfig has streaming_n_left_pad_tokens=32. When we call the processor with is_streaming=False, it:

  1. Prepends 32 adapter frames of silence (2.56 s) to the raw waveform
  2. Injects 32 [STREAMING_PAD] tokens at the start of input_ids

Per the paper's §6.3, this acts as attention sinks — the initial attention mass is absorbed by these dummy tokens, stabilizing the decoding. The paper tested 0, 16, and 32 frames and found 32 works best (Table 4).

Generation is greedy, no sampling

model.generate(**model_kwargs, max_new_tokens=batch_max_tokens)

No temperature, do_sample, top_k, or top_p — default greedy decoding (equivalent to temperature=0). Standard evaluation practice.


Thanks again and please reply for any missing details!

Could you please provide the value of max_new_tokens? I suspect that setting it too low is causing the audio tokens to be truncated, which results in frequent deletion errors.
@abhinavkulkarni

@Kaiqfu : I'm generating a single token for every 80ms audio chunk. There is no burst emission of tokens for a single 80ms audio chunk.

model.generate(**model_kwargs, max_new_tokens=batch_max_tokens),this is hf generate. and since the model emits one token every 80 ms, max_new_tokens must be at least as large as the audio duration divided by 80 ms (i.e., the total number of frames); otherwise, generation will be truncated before the full audio is processed. You can check it. @abhinavkulkarni

Sign up or log in to comment