Add Hy3 (Tencent Hunyuan V3) model support - #4704
Draft
weikuo0506 wants to merge 1 commit into
Draft
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
weikuo0506
force-pushed
the
add-hy3-support
branch
4 times, most recently
from
August 6, 2026 04:46
b5a5f15 to
e9a99b2
Compare
Adds support for Hy3 (Tencent Hunyuan V3, tencent/Hy3 on HF, 295B total / 21B active MoE). Hy3 combines standard GQA + QK-Norm attention (as in Qwen3) with a DeepSeek-V3-style aux-loss-free sigmoid+bias routed MoE (1 shared expert) and a dense first layer (first_num_dense_layers). It reuses DeepSeekGenericLayer's dense/MoE scaffolding and moe.RoutedAndSharedMoE rather than introducing new attention math — the new decoder layer (Hy3DenseLayer/Hy3MoELayer in src/maxtext/models/hy3.py) subclasses DeepSeekGenericLayer and overrides self_attention with plain GQA, following the pattern DeepSeek4DecoderLayer uses in deepseek4.py. Key changes: - Wire DecoderBlockType.HY3 through decoders.py/nnx_decoders.py, moe.py routing gates, and types.py validation guards. - Add src/maxtext/models/hy3.py with Hy3DenseLayer and Hy3MoELayer. - Add hy3-tiny.yml and hy3-295b.yml configs. - Register Hy3 in the checkpoint conversion framework (hf_model_configs.py, param_mapping.py, hf_shape.py, globals.py) with native HYV3Config try/fallback. - Update FLOPs/MFU calculations (get_dense_moe_layers in maxtext_utils.py) and param export. - Add unit tests in tests/unit/hy3_vs_reference_test.py and tests/unit/nnx_decoders_test.py. - Add end-to-end user guide: tests/end_to_end/tpu/hy3/Run_Hy3.md. - Handle MoE block name mapping in train.py and document auxiliary load balancing behavior.
weikuo0506
force-pushed
the
add-hy3-support
branch
from
August 6, 2026 07:15
441d2a1 to
eb04fe6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds support for Hy3 (Tencent Hunyuan V3,
tencent/Hy3on HF, 295B total / 21B active MoE).Hy3 combines standard GQA + QK-Norm attention (as in Qwen3) with a DeepSeek-V3-style
aux-loss-free sigmoid+bias routed MoE (1 shared expert) and a dense first layer
(
first_num_dense_layers). It has no MLA and no compressed/sparse attention, so itreuses
DeepSeekGenericLayer's dense/MoE scaffolding andmoe.RoutedAndSharedMoErather than introducing new attention math — the new decoder layer (
Hy3DenseLayer/Hy3MoELayerinsrc/maxtext/models/hy3.py) subclassesDeepSeekGenericLayerandoverrides
self_attentionwith plain GQA, following the same patternDeepSeek4DecoderLayeruses indeepseek4.py.Changes
DecoderBlockType.HY3wired throughdecoders.py/nnx_decoders.py(both thelegacy Linen path and the default pure-NNX path),
moe.py's DeepSeek-V3-stylerouting gates (pre-bias logits capture +
routed_scaling_factorapplication),and the relevant
types.pyvalidation guards (including theModelNameliteralallowlist and the loss-free-load-balancing decoder_block check).
src/maxtext/models/hy3.py:Hy3DenseLayer/Hy3MoELayer.hy3-tiny.yml/hy3-295b.ymlmodel configs.hf_model_configs.py,param_mapping.py,hf_shape.py,globals.py'sHF_IDS).transformersalready ships a native
HYV3Config;hf_model_configs.pyuses it via thesame try/except native-class-with-
PTConfig-fallback pattern already usedfor Gemma 4, so no
trust_remote_code-only workaround is needed. HF tensornames/shapes were cross-checked against the real
tencent/Hy3checkpoint'smodel.safetensors.index.json(46,545 non-MTP tensors, zero missing/extra).MTP layer weights are intentionally left unmapped (randomly initialized on
conversion) for this pass.
tests/unit/hy3_vs_reference_test.py: from-scratch PyTorch referenceimplementations of Hy3's attention/MoE blocks, compared directly against the
MaxText JAX layers (random weights, no HF download needed).
tests/unit/nnx_decoders_test.py:NNXDecoderforward-pass test with thehy3decoder block (1 dense + 3 MoE layers, unscanned).tests/end_to_end/tpu/hy3/Run_Hy3.md: end-to-end user guide (checkpointconversion, pre-training, fine-tuning, decoding, logit verification).
maxtext_utils.py):get_dense_moe_layersplus 3more branches in
calculate_tflops_training_per_devicethat were stillmissing HY3 and would otherwise fall through to a generic path that sizes
experts with
mlp_diminstead ofmoe_mlp_dimand skips the shared expert.Verified by measuring the function's output for
hy3-295bbefore/after —learnable_weight_tflopschanges measurably, confirming this wasn't a no-op.grpo_utils.py/generate_param_only_checkpoint.py: extended thedense+MoE two-stack layer-group handling to HY3, so RL (GRPO) parameter
resharding and params-only checkpoint construction handle Hy3's
first_num_dense_layerssplit correctly instead of mishandling it.train.py: the aux-loss-free router-bias update path hardcodes the MoEsubmodule attribute name
"DeepSeekMoeBlock_0". Extended the lookup toinclude HY3 for forward-compatibility. This does not make the update path
functional today — see "Known limitation 1" below; it only prevents Hy3
from crashing on top of a pre-existing, unrelated bug.
Known limitation 1: MoE load balancing does not currently work during training
Both of Hy3's optional training-time load-balancing mechanisms
(
routed_bias_update_rateandload_balance_loss_weight) are non-functionalwith
scan_layers=true(silently no-op —nnx_decoders.py's scanned-layerapplication discards
nnx.Intermediatestate, including the sownmoe_bias_updates/moe_lb_loss, beforetrain.pycan read it), androuted_bias_update_rate>0additionally crashes withAttributeErrorwhenscan_layers=false(that code path assumes a single stackedmoe_layersattribute that only exists when scanned; unscanned layers are named
moe_layers_0,moe_layers_1, ... instead).This is not specific to Hy3. Reproduced locally (CPU) that
deepseek3-tinywith the same config overrides exhibits identical behavior in both modes,
confirming this is a pre-existing MaxText gap in the shared DeepSeek-V3-style
code path, not something this PR introduces. It is separate from the
DeepSeek-V4-specific
MoEBiasVarmigration in #4753 (still open as of thiswriting), which explicitly leaves the legacy
nnx.Param-based path — the oneHy3 and DeepSeek V3 share — untouched.
Until fixed upstream,
hy3-tiny.yml/hy3-295b.ymlleave both settings attheir defaults (
0.0). Inference and plain next-token-loss training areunaffected; only these two optional load-balancing signals are. Full
explanation in the new section of
Run_Hy3.md.Verification scope: not yet tested at full 80-layer/295B distributed scale
What's verified: numerical correctness (real-checkpoint 2-layer golden-logits
comparison + a from-scratch PyTorch-reference unit test covering every
distinct code path — attention, dense MLP, MoE routing, shared expert),
basic single-host training mechanics (CPU smoke test, loss decreasing
normally), and — at the full declared 80-layer/295B config — AOT compilation
at
compile_topology=v5p-256(train_compile.py,ici_fsdp_parallelism=-1):compiles successfully, ~70GB/device (13.8GB args + 44.4GB temp, well under
v5p's ~95GB HBM/chip). This exercises the full-scale scanned-layer
construction and sharding/mesh-partitioning logic without needing real
weights or a live TPU pod. Since Hy3 has no per-layer-varying architecture,
together these give good confidence in per-layer numerical correctness at
any layer count and in shape/sharding-spec correctness at the full 80-layer
config.
What's not verified: an actual multi-host training run at that scale
(only compiled, not executed) — in particular, expert parallelism
(
ici_expert_parallelism/dcn_expert_parallelism> 1) has never beenexercised at all, even in the AOT compile above (
ici_fsdp_parallelism=-1used FSDP, not EP). Real multi-host MoE collective behavior at runtime is a
distinct risk class from both numerical correctness and compile-time
shape/sharding checks. Running this needs the full ~598GB checkpoint and a
multi-chip TPU pod; a live HF-vs-MaxText logits comparison at full scale
additionally needs ~590GB of host RAM for the PyTorch reference model
(exceeds a single TPU VM host).
Not added: Muon optimizer / layerwise-quantization allowlists
Both are optional MaxText features not required by Hy3's own architecture,
and neither has been tested against Hy3, so they're left out rather than
claiming untested support. Happy to add either in a follow-up if useful.
Tests
pytest tests/unit/configs_test.py— full suite (78 tests) passes.pytest tests/unit/hy3_vs_reference_test.py— 2/2 pass: MaxText's attentionand MoE routing layers match from-scratch PyTorch reference implementations.
pytest tests/unit/nnx_decoders_test.py -k hy3— 1/1 pass.hy3-tiny.yml,scan_layers=True/False,with
mtp_num_layers=1(MTP reuses the last decoder layer class genericallyper
models.py's existing wiring — verified this works for Hy3 too), and areal 3-step training run confirming loss decreases normally.
scan_layers=False:converted the real
tencent/Hy3checkpoint truncated to 2 layers viato_maxtext.py, generated golden logits from the real HF reference model(also truncated to 2 layers, same real weights), compared via
forward_pass_logit_checker.py:overlap 10/10
(Both far under the
--max_kl_div=0.5threshold used.)deepseek3-tinyanddeepseek4-284b-familyconfig/training paths behave identically before and after every change in
this PR (re-ran
test_deepseek_configs, and manually reproduced theaux-loss-free bias-update behavior on
deepseek3-tinyto confirm it'sunaffected by the
train.pychange).Checklist
### Hy3entry todocs/reference/models/supported_models_and_architectures.mdand a
Run_Hy3.mdend-to-end guide, including the load-balancing limitation).