Summary
Gemma 4 E2B/E4B audio input produces gibberish output for all model variants (bf16, 8bit, 4bit). Two independent issues were found:
- 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.
- 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
- Issue 1: Update the
convert command or mlx-community model cards to include feature_extractor in processor_config.json for E2B/E4B models.
- Issue 2: May need debugging in
Model.get_input_embeddings() → audio_tower() → embed_audio() → masked_scatter() path.
Summary
Gemma 4 E2B/E4B audio input produces gibberish output for all model variants (bf16, 8bit, 4bit). Two independent issues were found:
feature_extractorinprocessor_config.json— this causesGemma4Processor.feature_extractor = Noneand audio is silently ignored.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_extractorin processor_config.jsonThe original Google model (
google/gemma-4-e2b-it) hasfeature_extractorwithGemma4AudioFeatureExtractorconfig in itsprocessor_config.json. All mlx-community converted models are missing this section:Workaround: manually copy the
feature_extractorblock from the Google model'sprocessor_config.jsoninto the mlx-community model's cachedprocessor_config.json. After patching: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_featuresshape(1, 2998, 128)for 28s audio), and the audio tower exists with non-zero weights. However, the model output is garbage:صling用のling損失mont这款ling-につき..."Weight: 1.5, precision: 0.9..."<pad>tokensTested with both
temperature=0.0andtemperature=1.0, top_p=0.95, top_k=64(as recommended in the Gemma 4 README after #901). Same garbage output.Reproduce
Diagnosis
processor.feature_extractorisNonewithout patching → audio silently skippedinput_featuresshape is correct (mel spectrogram)audio_towerhas 12 conformer layers,output_projweight shape(1536, 1024), mean≈0, std≈0.031 — looks loaded correctlyaudio_token_id = 258881is present ininput_ids(750 audio tokens for 30s audio)masked_scatteringet_input_embeddings()should replace audio token positions with audio encoder output, but the final output suggests audio embeddings are not reaching the language model correctlySuggested fixes
convertcommand or mlx-community model cards to includefeature_extractorinprocessor_config.jsonfor E2B/E4B models.Model.get_input_embeddings()→audio_tower()→embed_audio()→masked_scatter()path.