Add Liger Kernel support for Gemma 4 Unified (gemma4_unified) - #1
Conversation
Mirrors the gemma4 (omni) support: apply_liger_kernel_to_gemma4_unified / ..._text with FLCE forwards for Gemma4UnifiedForCausalLM and Gemma4UnifiedForConditionalGeneration, RMSNorm/GeGLU class swaps targeting modeling_gemma4_unified, registry entries for both model types, monkey-patch unit tests, and bf16/fp32 convergence coverage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review — style/convention, verbosity, sibling parity, framingReviewed against the direct sibling ( 1. Size & shape vs. sibling model-support PRs
This is the largest single model-support PR in the set, but that's because it bundles text + multimodal in one PR, which gemma4 shipped as two (linkedin#1196 + linkedin#1203 = ~1393 adds combined). Per-scope this is actually slightly leaner than the sibling, and it's 0-deletion / additive, so risk is low. Worth deciding deliberately: keep as one PR, or split into 2. Convention / template
3. Deliberate divergences from the sibling (both look correct — calling them out so review is explicit)
4. VerbosityComment density is at parity with the sibling The only comments that exceed the sibling are the net-new "Unlike gemma4 (omni), …" comparison notes (e.g. 5. Framing (feature-first, not platform-first)Mostly clean — the motivation is generalized correctly (memory-gated CE at vocab=262,144; the concrete 64k-FFT-OOMs-at-step-1 → trains-cleanly data point is exactly the right upstream justification for FLCE, and reads as a general training story, not an internal one). One small leak to neutralize:
Minor
|
|
Thanks — integrated the review and restructured for upstream. This draft is now superseded by two
Changes vs this draft: split text/multimodal into the two PRs above; guarded FLCE rebind retained |
Summary
Adds Liger Kernel support for the Gemma 4 Unified models (
model_type: gemma4_unified/gemma4_unified_text, e.g.google/gemma-4-12B-it):apply_liger_kernel_to_gemma4_unifiedandapply_liger_kernel_to_gemma4_unified_textwith RMSNorm, GeGLU, CrossEntropyLoss, and FusedLinearCrossEntropy, mirroring the gemma4 (omni) support from linkedin#1203.Resolves linkedin#1308.
These checkpoints currently no-op with
no Liger kernels supported for model type: gemma4_unified— the gemma4 (omni) patches can't reach them because the unified models live in their own transformers module (separate class objects). The family shares the 262,144-token vocabulary, so the FusedLinearCrossEntropy win is unusually large at any sequence length: the[B, T, 262144]logits tensor is ~4 GiB in bf16 per 8k-token row (~32 GiB at 64k), doubling again when the loss path upcasts to fp32.Details
Gemma4UnifiedRMSNorm/Gemma4UnifiedTextMLPare implementation-identical to gemma4's (ones-init no-offset RMSNorm with fp32 compute and awith_scale=Falsevariant; GeGLU with the double-wide KV-shared handling), so the existingLigerRMSNormForGemma4/LigerGEGLUMLPForGemma4wrappers are reused — only the patch targets change (modeling_gemma4_unified.*). Cross-module application of the gemma4 patch functions is not possible (different class objects), hence the parallel apply functions.Gemma4UnifiedCausalLMOutputWithPast(extended with liger'stoken_accuracy/predicted_tokens), passingshared_kv_statesthrough.final_logit_softcapping(30.0 on the 12B checkpoint) flows through both the fused and non-fused loss paths.Gemma4UnifiedForCausalLM— a bareGemma4UnifiedTextModelhas nolm_head, so binding the causal forward to it would crash on first use. (The gemma4 sibling has the unguarded version of this; happy to fix it in a follow-up.)roperemains an accepted no-op (upstream uses a single-tensorapply_rotary_pos_emb, incompatible withliger_rotary_pos_emb), matching gemma4.gemma4_unified_assistantmodel_type, LayerNorm kernels for the encoder-free vision embedder, and the audio path in convergence tests (all mirroring the sibling's scope).pytest -k gemma4now also matches the gemma4_unified tests; use-k "gemma4 and not unified"to isolate the omni family.Testing Done
Environment: Modal H100, torch 2.13.0, triton 3.7.1; suites run at both transformers 5.10.1 (the version we deploy) and 5.14.1 (current floated resolve of
.[dev]).New-code results (both transformers versions unless noted):
make checkstyle: pass.test/transformers/test_monkey_patch.py -k gemma4: 4/4 pass (both new instance tests + both sibling tests).mini_gemma4_unified_textpassesbf16/test_mini_models.pyandbf16/test_mini_models_with_logits.py.Gemma4UnifiedForConditionalGenerationnumerical check (20 fp32 training steps, HF vs liger from identical init; the multimodal convergence harness runs with FLCE disabled by design, so this covers the fused path through the CondGen class): fused ≡ unfused at step 0 (delta 1.4e-06), max relative loss drift 9.7e-04 over 20 optimizer steps,generate()clean after patching.google/gemma-4-12B-it(transformers 5.10.1, torch 2.13.0, FSDP full-shard + flex_attention, 8×H100-class) that OOMs at step 1 without this port (128.6/139.8 GiB per GPU already used after the forward; the unfused loss then tries to materialize ~32 GiB of logits) trains cleanly with it — finite, decreasing loss (1.98 → 0.91 over the first 15 steps).Pre-existing failures in these environments, reproduced on upstream
mainwithout this PR (control run at bb715c9):mini_gemma4_text(omni sibling) bf16 convergence fails at transformers ≥5.10 (tolerance drift vs the version it was calibrated on).Multimodal convergence fails for BOTH families: at 5.10.1 due to an operator-precedence bug in the upstream processors (
audio is not None and self.audio_token is None or self.boa_token is None or ...raises "Audio inputs were provided" with no audio when the test tokenizer lacksboa_token); at 5.14.1 due to family-wide logprob drift. The newmini_gemma4_unifiedrows behave identically to the sibling rows in these environments — they are included for parity and will pass wherever the sibling's do.Hardware Type: H100 (Modal)
run
make testto ensure correctness — full suite (transformers 5.14.1, torch 2.13.0): 3905 passed, 1181 skipped, 4 failed; all 4 failures are bf16-tolerance flakes in kernels untouched by this diff (test_poly_norm×2,test_fused_add_rms_norm×2) on this torch/GPU combo — every gemma4/gemma4_unified unit test passesrun
make checkstyleto ensure code stylerun
make test-convergenceto ensure convergence (gemma4-filtered runs as above; full-matrix behavior documented)