Gemma 4 12B Unified (encoder-free audio): audio comprehension collapses past ~450–500 tokens of absolute position — vision on the same checkpoint is unaffected, E2B/E4B (Conformer) are unaffected

#45
by frandagostino - opened

Gemma 4 12B Unified: encoder-free audio comprehension degrades deterministically past ~450–500 tokens of absolute position

TL;DR. On gemma4_unified (the encoder-free multimodal 12B), audio placed beyond roughly 450–500 tokens of absolute position in the sequence is no longer understood — deterministically, at temperature 0. Three controls isolate the cause to the encoder-free audio path itself: (a) vision on the same checkpoint survives at ~800 tokens (OCR included), (b) E2B and E4B (USM-Conformer audio encoder) survive at every position we tested in the same runtime, and (c) giving audio tokens intra-block bidirectional attention (off-spec) largely restores comprehension. Our working hypothesis: encoder-free audio tokens (raw 640-sample frames through a linear projection) build their representations via causal attention inside the LLM, and those representations are fragile to long prefixes never seen in training; vision is robust because its tokens get intra-block bidirectional attention. The practical fallout: direct-audio multi-turn chat is unreliable — any prior turn (even text-only) pushes the current clip past the cliff.

We would love to know whether this matches the training distribution of the unified audio path (audio always near the start of the sequence?), and whether there is a recommended maximum prefix length for audio inputs that could be documented.

Setup

Checkpoint under test mlx-community/gemma-4-12B-it-4bit (model_type: gemma4_unified, encoder-free audio: 640-sample frames @ 16 kHz → linear projection, 40 ms/token)
Control checkpoints mlx-community/gemma-4-E2B-it-qat-4bit, mlx-community/gemma-4-E4B-it-qat-4bit (USM-Conformer audio encoder)
Runtime Swift/MLX — fdagostino/mlx-swift-lm, branch feat/gemma4-mtp, commit 47a2432, on an Apple-silicon Mac
Decoding temperature 0.0 everywhere; every result below is deterministic and reproduces run-to-run
Test clip macOS TTS: say -v Paulina "Mi número de la suerte es el diecisiete." -o t2.wav --data-format=LEI16@16000 → 2.46 s ≈ 61 audio tokens ("My lucky number is seventeen" — any short sentence carrying one verifiable fact works)
Clip conditioning tail padded with 0.25 s of digital zeros and rounded to a multiple of 640 samples (see secondary finding below — this removes an orthogonal failure mode first)

The audio is attached to the first user turn together with a short instruction ("The person said this to you by voice. Reply to what they said."). The absolute position of the audio block is pushed by growing the system prompt: a fixed ~250-token persona followed by semantically null filler (repeated "- "), so no distracting content is added — position is the only variable. With template overhead, the audio sits at roughly 320 absolute tokens in the control case. Fresh session (empty KV cache) for every case.

Finding 1: positional cliff, audio only

12B Unified, null filler (does the reply engage with "seventeen"?):

Filler Audio position (approx. absolute tokens) Result
none (control, persona only) ~320 ✅ engages with "diecisiete"
+80 ~400
+240 ~560 ❌ no comprehension of the clip
+480 ~800

The cliff sits somewhere around 450–500 absolute tokens. With semantic filler (real sentences) instead of null filler it arrives slightly earlier (fails from ~+120), but the null-filler runs show the effect needs no distracting content: it is positional, not attentional-competition.

Controls, same protocol:

Control Result
Vision, same 12B checkpoint, probe image at ~800 absolute tokens ✅ perfect description in all cases, including OCR of rendered text ("OJO 2026")
E2B (Conformer), audio at the same four positions ✅ exact "diecisiete" at every position
E4B (Conformer), audio at the same four positions (up to ~820 absolute) ✅ exact at every position

Practical consequence (multi-turn): with the same clip and prompt, A) fresh session → ✅ "Ah, el diecisiete…"; B) one text-only prior turn in context → ❌ "No tengo ni idea de qué estás hablando. ¿Qué es eso del 17?" (echoes a fragment, no comprehension of the utterance); C) one prior audio turn → ❌. Any conversational prefix is enough to cross the cliff, so encoder-free direct-audio chat only works reliably on the first turn of a session.

Finding 2 (secondary): extreme sensitivity to the clip tail

Same 12B checkpoint, transcription prompt, temperature 0, one-shot:

Clip variant Transcription
full clip ✅ "…el diecisiete"
last 24 ms trimmed (samples otherwise identical) ❌ "Diez"
last 0.1 s trimmed ❌ "Diecinueve"
last 0.2 s trimmed ❌ "el 11"
any of the above + 0.25 s of zeros appended ✅ rescued to "el diecisiete"

The zero-pad doesn't just prevent the failure, it rescues already-truncated clips. Gain is irrelevant (raw / peak-normalized / 4× / 10× all behave identically once the tail is padded). Consistent with the encoder-free ingestion: an abrupt ending or a partially-filled final 640-sample frame corrupts the last-frame feature exactly where the closing word lives. Easy workaround, but worth documenting; a Conformer front-end wouldn't care.

What we ruled out (implementation audit)

Since the reproduction runs on our Swift/MLX port, we audited it line-by-line against two references before concluding anything about the model:

  • Attention mask: in both HF transformers (modular_gemma4.py) and google-deepmind/gemma (gemma4/_transformer.py), audio tokens get causal attention only — the intra-block bidirectional override applies to vision types only. Our port matches.
  • Positions: uniform arange + past_seen_tokens, no audio-specific position handling anywhere in the references. Our port matches.
  • RoPE: partial_rotary_factor=0.25 with the "proportional" parameterization (modeling_rope_utils.py) — frequencies computed over the full head_dim and the (i, i+half) pairing — verified identical. Attention scale 1.0 verified. norm→rope order verified.
  • Placeholder scatter: token-id–based with a count guard; the vision control (which shares mask/RoPE/scale/scatter with audio) surviving at ~800 tokens is the strongest evidence the shared stack is healthy.
  • Discriminating experiment: patching the mask (off-spec) so audio blocks also get intra-block bidirectional attention largely restores positional comprehension (at +240/+480 the model at least understands "a number was said" where causal understood nothing) — but destabilizes the chat template (control-token leakage), as expected for an out-of-training-distribution mask. Not a fix; evidence for the mechanism.

Honest caveats

  • The reproduction stack is Swift/MLX. To our knowledge there is no runnable Python reference for 12B unified audio to cross-check against (mlx-vlm implements Gemma 4 audio only for the 2B/4B Conformer models; running the 12B in PyTorch transformers was impractical locally). A residual possibility remains that a positional-extrapolation bug is shared with our reading of the references — but the vision control and the E2B/E4B controls run through the very same code paths in our stack, and they don't degrade.
  • All checkpoints are 4-bit quantized (mlx-community conversions). We consider it unlikely that quantization produces a clean position threshold that spares vision, but we haven't run an fp16 control.
  • Clips are macOS TTS (voice "Paulina", es-CL Spanish). The effect reproduces with different sentences and clip lengths; we have not swept languages.

Reproduction

  1. Fork + branch above (git checkout 47a2432), or any runtime that can feed gemma4_unified audio.
  2. Generate the clip with the say command in Setup; load at 16 kHz mono float32; append 0.25 s of zeros, round length up to a multiple of 640 samples.
  3. System prompt = any ~250-token persona + String(repeating: "- ", count: N) for N ∈ {0, 80, 240, 480}.
  4. First user turn: text "The person said this to you by voice. Reply to what they said." + the audio clip. Temperature 0, fresh session per case.
  5. Score: does the reply engage with the fact in the clip ("seventeen")?
  6. Vision control: same protocol, replace the clip with any image containing shapes + rendered text; ask for a full description.
  7. E2B/E4B control: same protocol, swap the checkpoint.

Happy to share the full experiment harness (a small Swift CLI with all the modes used here: position, imgposition, context, tail, prompts) and raw transcripts if useful.


Related but separate issue we hit along the way: generation_config.json's suppress_tokens lists only 258882/258883 — the rest of the multimodal placeholder family (image/video soft tokens, boi/eoi/boa/eoa markers) is not listed, and runtimes that don't implement suppress_tokens sample even the listed ones into text-only replies (reported independently in jundot/omlx#1670; we hit it too and fixed it runtime-side). Might be worth completing the list in the shipped generation_config.json.

Google org

Hi @frandagostino ,

Thanks for addressing the issue. Could you please share us the full experiment harness which include a small Swift CLI with all the modes used for the issue reproduction: position, imgposition, context, tail, prompts and raw transcripts which will be helpful for reproduction.

Sign up or log in to comment