huggingface / huggingface/candle

mimi uses the interleaved RoPE convention but the checkpoints are stored in the transformers layout

Open
#3,948 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
21k
Forks
1.8k
Avg merge
16h 42m
Merged PRs (30d)
25

Description

The mimi transformer applies the interleaved (GPT-J style) rotary embedding, but the
`kyutai/mimi` checkpoint that `Model::new` loads is stored in the `transformers` layout, which
rotates the two halves of the head dimension (NeoX style). Attention scores are therefore
computed against the wrong frequency pairing at every position but the first, and both the
encoder and the decoder drift away from the reference implementation as the sequence grows.

https://github.com/huggingface/candle/blob/c30b26cd3bb59e5b39927eebff00884b0c0729d5/candle-transformers/src/models/mimi/transformer.rs#L82

Transformers uses `rotate_half` for this model, [models/mimi/modeling_mimi.py](https://github.com/huggingface/transformers/blob/v4.57.3/src/transformers/models/mimi/modeling_mimi.py#L542),
which is what `candle_nn::rotary_emb::rope` implements.

## Impact

Everything that goes through `models::mimi`, which is the `mimi` and `csm` examples. Encoded
codes are wrong and decoded audio is measurably degraded, but it is a quiet failure. Audio still
sounds like speech, and nothing errors out.

## Reproduction

A 5.04 s mono clip at 24 kHz, 63 frames, f32 on CPU, candle at d5fee525, where this file is
identical to the permalink above.

Codes from `transformers` 4.57.3:

```python
import torch, soundfile as sf
from transformers import MimiModel

wav, _ = sf.read("audio.wav", dtype="float32") # mono, 24 kHz
m = MimiModel.from_pretrained("kyutai/mimi", dtype=torch.float32).eval()
with torch.no_grad():
codes = m.encode(torch.from_numpy(wav)[None, None, :], num_quantizers=16).audio_codes
```

Codes from candle, same checkpoint, same audio:

```bash
cargo run --example mimi --features mimi -r -- --cpu audio-to-code audio.wav codes.safetensors
```

**359 of the 1008 codes match, and not one of the 63 frames matches in full.** Agreement is
highest in the first frame, 14 of its 16 codes, and falls off immediately after, which is the
signature of a position-dependent difference rather than a numerical one.

Feeding the `transformers` codes back through `Model::decode` and comparing samples with
`MimiModel.decode`:

| quantizers | max abs sample difference | correlation |
|---|---|---|
| 16 | 1.2e-01 | 0.9959 |
| 32, as used by `csm` | 5.1e-02 | 0.9978 |

Comparing codes is the cleaner check of the model itself, since the `mimi` example applies
`normalize_loudness` before writing its wav.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in candle-transformers/src/models/mimi/transformer.rs around the rotary-embedding setup at line 82, then inspect candle_nn::rotary_emb::rope and the Transformers Mimi implementation linked in the issue. Reproduce the provided audio-to-code command and compare codes against the reference checkpoint. Done means Mimi and CSM encode with the Transformers layout and their codes match the reference across frames.

Written by the indexing model from the issue text.

Assessment

Tech stack
huggingface, rust
Domain
audio-video-rtc, machine-learning
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.