Skip to content

Add Liger Kernel support for Gemma 4 Unified text (gemma4_unified_text) - #1311

Open
lefft wants to merge 1 commit into
linkedin:mainfrom
lefft:gemma4-unified-text
Open

Add Liger Kernel support for Gemma 4 Unified text (gemma4_unified_text)#1311
lefft wants to merge 1 commit into
linkedin:mainfrom
lefft:gemma4-unified-text

Conversation

@lefft

@lefft lefft commented Jul 22, 2026

Copy link
Copy Markdown

Disclosure: This PR was written by Fable 5 at my (@lefft) direction, and also reviewed by me. It has been validated in a production long-context (64k) SFT pipeline with gemma-4-12B-it on an 8xH200 node. Resulting checkpoints produce expected performance gains on internal benchmark tasks (e.g. https://huggingface.co/datasets/lefft/needleif-bench). Our internal fork of liger-kernel includes the code in both #1311 and #1312. I've split it into two here for reviewability.

Summary

Adds Liger Kernel support for Gemma 4 Unified text models (model_type: gemma4_unified_text):
apply_liger_kernel_to_gemma4_unified_text, with RMSNorm, GeGLU, CrossEntropyLoss, and
FusedLinearCrossEntropy.

Liger Kernel already supports the other Gemma 4 families — gemma4_text (dense text, #1196) and
gemma4 (omni multimodal, #1203). But google/gemma-4-12B / gemma-4-12B-it ship as a distinct
model_type: gemma4_unified
with their own modeling_gemma4_unified module, so those existing
patches don't reach them — _apply_liger_kernel no-ops with
no Liger kernels supported for model type: gemma4_unified. This PR adds the text half of that
missing coverage; the multimodal class the 12B checkpoints actually load as is added in the
follow-on PR (see Details).

Part of #1308.

Details

  • No new kernels. Gemma4UnifiedRMSNorm / Gemma4UnifiedTextMLP are implementation-identical to
    gemma4's (ones-init no-offset RMSNorm with fp32 compute and a with_scale=False variant; GeGLU
    with the double-wide KV-shared handling), so the existing LigerRMSNormForGemma4 /
    LigerGEGLUMLPForGemma4 wrappers 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 function.
  • causal_forward returns the upstream Gemma4UnifiedCausalLMOutputWithPast (extended with liger's
    token_accuracy / predicted_tokens), passing shared_kv_states through — unlike the gemma4
    sibling's causal path, which uses the generic LigerCausalLMOutputWithPast. The model-specific
    class is used deliberately so the unified model's shared_kv_states field isn't dropped.
  • final_logit_softcapping (30.0 on the 12B checkpoint) flows through both the fused and non-fused
    loss paths.
  • The FLCE instance rebind is guarded to Gemma4UnifiedForCausalLM — a bare
    Gemma4UnifiedTextModel has no lm_head, so binding the causal forward to it would crash on first
    use. (The gemma4 sibling has the unguarded version of this; happy to align it in a follow-up.)
  • rope remains an accepted no-op (upstream uses a single-tensor apply_rotary_pos_emb,
    incompatible with liger_rotary_pos_emb), matching gemma4.
  • Rebased onto current main (post-Fix convergence test failures #1313). The new mini_gemma4_unified_text convergence rows
    carry the same logprobs_atol (7e-1) that Fix convergence test failures #1313 calibrated for the mini_gemma4_text sibling —
    same architecture family, same bf16 near-tie fragility in the eval top-k logprobs metric.
  • Follow-on PR: multimodal support (apply_liger_kernel_to_gemma4_unified for
    Gemma4UnifiedForConditionalGeneration, the class gemma-4-12B-it loads as) stacks on this one —
    Add Liger Kernel support for Gemma 4 Unified multimodal (gemma4_unified) #1312. This mirrors how gemma4 shipped ([Gemma 4] Add apply_liger_kernel_to_gemma4_text (dense text, 31B-targeted) #1196 text → [Gemma 4] Add multimodal support (apply_liger_kernel_to_gemma4 for Gemma4ForConditionalGeneration) #1203 multimodal).
  • Test-selection note: pytest -k gemma4 now 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+cu130, triton 3.7.1; all suites re-run after rebasing onto
main @ ed08f6e, at transformers 5.10.1 and 5.14.1 (current .[dev] resolve). Results
identical at both versions: every gemma4-filtered command exits 0.

make checkstyle:

All checks passed!
317 files left unchanged
===== EXIT 0: make checkstyle

test/transformers/test_monkey_patch.py -k gemma4 — 3/3 (the new text instance test plus both
gemma4 omni sibling tests as regression check):

test/transformers/test_monkey_patch.py::test_apply_liger_kernel_to_instance_for_gemma4_text PASSED
test/transformers/test_monkey_patch.py::test_apply_liger_kernel_to_instance_for_gemma4_conditional_generation PASSED
test/transformers/test_monkey_patch.py::test_apply_liger_kernel_to_instance_for_gemma4_unified_text PASSED
======================= 3 passed, 60 deselected in 2.07s =======================

Convergence, new text rows — these run FLCE-enabled, so this PR exercises the fused path on its
own (result lines condensed from the -v live logs; the sibling mini_gemma4_text rows are included
by the same filter and pass alongside):

test/convergence/bf16/test_mini_models.py::test_mini_model[mini_gemma4_text-32-1e-05-dtype26-0.05-0.01-0.7-0.01-0.01-0.01] PASSED
test/convergence/bf16/test_mini_models.py::test_mini_model[mini_gemma4_unified_text-32-1e-05-dtype27-0.05-0.01-0.7-0.01-0.01-0.01] PASSED
====================== 2 passed, 35 deselected in 13.91s =======================

test/convergence/bf16/test_mini_models_with_logits.py::test_mini_model[mini_gemma4_text-32-1e-05-dtype23-0.05-0.05-0.7-0.01-0.01-0.01] PASSED
test/convergence/bf16/test_mini_models_with_logits.py::test_mini_model[mini_gemma4_unified_text-32-1e-05-dtype24-0.05-0.05-0.7-0.01-0.01-0.01] PASSED
====================== 2 passed, 33 deselected in 10.90s =======================

The multimodal convergence files are also swept by the -k gemma4 filter; on this branch they
contain only the mini_gemma4 sibling row, which lands as the xfail #1313 marked it:

test/convergence/bf16/test_mini_models_multimodal.py: 15 deselected, 1 xfailed, 3 warnings in 13.25s
test/convergence/fp32/test_mini_models_multimodal.py: 15 deselected, 1 xfailed, 3 warnings in 14.59s
  • Hardware Type: H100 (Modal)
  • run make test to ensure correctness — run on the follow-on multimodal PR's branch tip, whose tree is the union of both PRs and so covers everything here (transformers 5.14.1): 3907 passed, 1181 skipped, 14 xfailed in 43:06 — zero failures
  • run make checkstyle to ensure code style
  • run make test-convergence to ensure convergence — gemma4-filtered rows as above; all commands exit 0 at both transformers versions

Mirrors the gemma4_text (omni) support: apply_liger_kernel_to_gemma4_unified_text
with an FLCE causal forward for Gemma4UnifiedForCausalLM, RMSNorm/GeGLU class
swaps targeting modeling_gemma4_unified (reusing the Gemma4 wrapper classes),
the shared LigerGemma4UnifiedCausalLMOutputWithPast output class, a registry
entry for model_type gemma4_unified_text, monkey-patch unit tests, and bf16
convergence coverage (FLCE-enabled text rows).

Multimodal support (apply_liger_kernel_to_gemma4_unified for
Gemma4UnifiedForConditionalGeneration) follows in a stacked PR, mirroring how
gemma4 shipped (linkedin#1196 text -> linkedin#1203 multimodal).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lefft
lefft force-pushed the gemma4-unified-text branch from 0470783 to e0f914b Compare July 24, 2026 17:58
@lefft

lefft commented Jul 24, 2026

Copy link
Copy Markdown
Author

Rebased onto current main (ed08f6e) and re-validated.

#1313 landed after this PR was opened and reworked the same convergence files, which moved the
tolerance the new mini_gemma4_unified_text rows were calibrated against. Aligned them with the
sibling: logprobs_atol 5e-1 → 7e-1, matching what #1313 set for mini_gemma4_text — same
architecture family, same bf16 near-tie fragility in the eval top-k logprobs metric.

Re-ran the gemma4-filtered suites on the rebased HEAD at transformers 5.10.1 and 5.14.1
(Modal H100, torch 2.13.0+cu130, triton 3.7.1). Every command exits 0 at both versions, so the
"pre-existing sibling failures" the original description documented are gone — #1313 fixed them
upstream. Description updated with the fresh logs.

  • make checkstyle: pass
  • test_monkey_patch.py -k gemma4: 3 passed
  • bf16/test_mini_models.py -k gemma4: 2 passed (mini_gemma4_text + new mini_gemma4_unified_text)
  • bf16/test_mini_models_with_logits.py -k gemma4: 2 passed
  • multimodal files (sibling row only on this branch): 1 xfailed each, per Fix convergence test failures #1313
  • full make test (union tree — see the description's provenance note): 3907 passed, 1181 skipped,
    14 xfailed, 0 failed

@lefft

lefft commented Jul 24, 2026

Copy link
Copy Markdown
Author

Hi @Mecoli1219 , can the workflow run here be approved so I can see the CI report? Happy to address any issues that arise.

This PR is self-contained, but #1312 is stacked on top of it, so I've flipped that to draft until this one is resolved.

(And BTW I'm new here -- I've read contributing.md and I think ducks are in a row, but LMK if anything looks off. Tagging you since I saw you reviewed related gemma4 PRs #1196 and #1203. Thanks!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant