Skip to content

Gemma 4 E2B/E4B audio produces gibberish — two issues found #903

Description

@fredchu

Summary

Gemma 4 E2B/E4B audio input produces gibberish output for all model variants (bf16, 8bit, 4bit). Two independent issues were found:

  1. All mlx-community Gemma 4 E2B/E4B models are missing feature_extractor in processor_config.json — this causes Gemma4Processor.feature_extractor = None and audio is silently ignored.
  2. Even after patching feature_extractor, audio output is garbage — the audio tower loads correctly but embeddings don't reach the language model properly.

Environment

Issue 1: Missing feature_extractor in processor_config.json

The original Google model (google/gemma-4-e2b-it) has feature_extractor with Gemma4AudioFeatureExtractor config in its processor_config.json. All mlx-community converted models are missing this section:

from mlx_vlm import load
model, processor = load("mlx-community/gemma-4-e2b-it-8bit")
print(processor.feature_extractor)  # None

Workaround: manually copy the feature_extractor block from the Google model's processor_config.json into the mlx-community model's cached processor_config.json. After patching:

print(type(processor.feature_extractor))
# <class 'mlx_vlm.models.gemma4.audio_feature_extractor.Gemma4AudioFeatureExtractor'>

This affects all E2B and E4B models in the mlx-community/gemma-4 collection. The 31B and 26B models don't support audio, so they are unaffected.

Issue 2: Audio output is gibberish after patching

After patching feature_extractor, audio features are correctly produced (input_features shape (1, 2998, 128) for 28s audio), and the audio tower exists with non-zero weights. However, the model output is garbage:

Model Output Peak RAM
E2B-IT bf16 Multi-language gibberish: صling用のling損失mont这款ling-につき... 11.04 GB
E2B-IT 8bit Hallucinated ML metrics: "Weight: 1.5, precision: 0.9..." 6.95 GB
E4B-IT 4bit All <pad> tokens 6.34 GB

Tested with both temperature=0.0 and temperature=1.0, top_p=0.95, top_k=64 (as recommended in the Gemma 4 README after #901). Same garbage output.

Reproduce

from mlx_vlm import load, generate
from huggingface_hub import hf_hub_download
import json

model_id = "mlx-community/gemma-4-e2b-it-bf16"  # or any E2B/E4B variant

# Step 1: Patch processor_config.json (only needed once)
proc_path = hf_hub_download(model_id, "processor_config.json")
with open(proc_path) as f:
    config = json.load(f)
if "feature_extractor" not in config:
    config["feature_extractor"] = {
        "feature_extractor_type": "Gemma4AudioFeatureExtractor",
        "feature_size": 128, "fft_length": 512, "fft_overdrive": False,
        "frame_length": 320, "hop_length": 160, "sampling_rate": 16000,
        "max_frequency": 8000.0, "min_frequency": 0.0, "mel_floor": 0.001,
        "dither": 0.0, "input_scale_factor": 1.0, "padding_side": "right",
        "padding_value": 0.0, "preemphasis": 0.0, "preemphasis_htk_flavor": True,
        "return_attention_mask": True, "per_bin_mean": None, "per_bin_stddev": None,
    }
    config["audio_ms_per_token"] = 40
    with open(proc_path, "w") as f:
        json.dump(config, f, indent=2)

# Step 2: Run ASR
model, processor = load(model_id)
print(f"Feature extractor: {type(processor.feature_extractor)}")  # Should not be None

prompt = (
    "<bos><|turn>user\n"
    "<|audio|>Transcribe the following speech segment in its original language. "
    "Only output the transcription. Add proper punctuation.<turn|>\n"
    "<|turn>model\n"
)

output = generate(
    model, processor, prompt,
    audio=["path/to/any_audio.wav"],  # any 16kHz WAV, <30s
    max_tokens=200, temperature=1.0, top_p=0.95,
)
print(output.text)  # Expect transcription, get gibberish

Diagnosis

  • processor.feature_extractor is None without patching → audio silently skipped
  • After patching, input_features shape is correct (mel spectrogram)
  • audio_tower has 12 conformer layers, output_proj weight shape (1536, 1024), mean≈0, std≈0.031 — looks loaded correctly
  • audio_token_id = 258881 is present in input_ids (750 audio tokens for 30s audio)
  • masked_scatter in get_input_embeddings() should replace audio token positions with audio encoder output, but the final output suggests audio embeddings are not reaching the language model correctly

Suggested fixes

  1. Issue 1: Update the convert command or mlx-community model cards to include feature_extractor in processor_config.json for E2B/E4B models.
  2. Issue 2: May need debugging in Model.get_input_embeddings()audio_tower()embed_audio()masked_scatter() path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions