Defuser 0.0.17__tar.gz → 0.0.18__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {defuser-0.0.17 → defuser-0.0.18}/Defuser.egg-info/PKG-INFO +2 -1
  2. {defuser-0.0.17 → defuser-0.0.18}/Defuser.egg-info/SOURCES.txt +1 -0
  3. {defuser-0.0.17 → defuser-0.0.18}/PKG-INFO +2 -1
  4. {defuser-0.0.17 → defuser-0.0.18}/README.md +1 -0
  5. {defuser-0.0.17 → defuser-0.0.18}/defuser/__init__.py +13 -3
  6. {defuser-0.0.17 → defuser-0.0.18}/defuser/model_registry.py +6 -0
  7. defuser-0.0.18/defuser/modeling/unfused_moe/glm4_moe_lite.py +100 -0
  8. {defuser-0.0.17 → defuser-0.0.18}/pyproject.toml +1 -1
  9. {defuser-0.0.17 → defuser-0.0.18}/tests/test_convert_model.py +32 -2
  10. {defuser-0.0.17 → defuser-0.0.18}/tests/test_meta_model_defusion.py +3 -3
  11. {defuser-0.0.17 → defuser-0.0.18}/Defuser.egg-info/dependency_links.txt +0 -0
  12. {defuser-0.0.17 → defuser-0.0.18}/Defuser.egg-info/requires.txt +0 -0
  13. {defuser-0.0.17 → defuser-0.0.18}/Defuser.egg-info/top_level.txt +0 -0
  14. {defuser-0.0.17 → defuser-0.0.18}/LICENSE +0 -0
  15. {defuser-0.0.17 → defuser-0.0.18}/defuser/checkpoint_ops.py +0 -0
  16. {defuser-0.0.17 → defuser-0.0.18}/defuser/defuser.py +0 -0
  17. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/__init__.py +0 -0
  18. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/glm4v.py +0 -0
  19. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/model_patches.py +0 -0
  20. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/moe_experts_interface.py +0 -0
  21. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/replace_modules.py +0 -0
  22. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/runtime_defusion.py +0 -0
  23. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/unfused_moe/__init__.py +0 -0
  24. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/unfused_moe/common.py +0 -0
  25. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/unfused_moe/glm4_moe.py +0 -0
  26. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/unfused_moe/mixtral.py +0 -0
  27. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/unfused_moe/qwen2_moe.py +0 -0
  28. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/unfused_moe/qwen3_moe.py +0 -0
  29. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/unfused_moe/qwen3_next.py +0 -0
  30. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/unfused_moe/qwen3_omni_moe.py +0 -0
  31. {defuser-0.0.17 → defuser-0.0.18}/defuser/modeling/update_module.py +0 -0
  32. {defuser-0.0.17 → defuser-0.0.18}/defuser/utils/__init__.py +0 -0
  33. {defuser-0.0.17 → defuser-0.0.18}/defuser/utils/common.py +0 -0
  34. {defuser-0.0.17 → defuser-0.0.18}/defuser/utils/device.py +0 -0
  35. {defuser-0.0.17 → defuser-0.0.18}/defuser/utils/hf.py +0 -0
  36. {defuser-0.0.17 → defuser-0.0.18}/defuser/utils/model.py +0 -0
  37. {defuser-0.0.17 → defuser-0.0.18}/setup.cfg +0 -0
  38. {defuser-0.0.17 → defuser-0.0.18}/setup.py +0 -0
  39. {defuser-0.0.17 → defuser-0.0.18}/tests/test_candidate_coverage.py +0 -0
  40. {defuser-0.0.17 → defuser-0.0.18}/tests/test_device_utils.py +0 -0
  41. {defuser-0.0.17 → defuser-0.0.18}/tests/test_filter_rules.py +0 -0
  42. {defuser-0.0.17 → defuser-0.0.18}/tests/test_replace_modules_tracker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Defuser
3
- Version: 0.0.17
3
+ Version: 0.0.18
4
4
  Summary: Model defuser helper for HF Transformers.
5
5
  Author-email: ModelCloud <qubitium@modelcloud.ai>
6
6
  License-Expression: Apache-2.0
@@ -78,6 +78,7 @@ Defuser currently supports the following `transformers==5.3.0` `model_type` valu
78
78
  | Model type | Defused op performed |
79
79
  | --- | --- |
80
80
  | `glm4_moe` | Replaces `Glm4MoeMoE` with a defused per-expert linear MoE block. |
81
+ | `glm4_moe_lite` | Replaces `Glm4MoeLiteMoE` with a defused per-expert linear MoE block.|
81
82
  | `glm4v` | Replaces the fused text MLP with split `gate_proj`, `up_proj`, and `down_proj` layers. Also splits fused checkpoint `mlp.gate_up_proj.weight` into `mlp.gate_proj.weight` + `mlp.up_proj.weight`. |
82
83
  | `mixtral` | Replaces `MixtralSparseMoeBlock` with `LinearMixtralSparseMoeBlock`. Also remaps legacy Mixtral checkpoint keys and splits fused expert `gate_up_proj` tensors into per-expert `gate_proj` and `up_proj`, plus per-expert `down_proj`. |
83
84
  | `qwen2_moe` | Replaces `Qwen2MoeSparseMoeBlock` with a defused per-expert linear MoE block. |
@@ -21,6 +21,7 @@ defuser/modeling/update_module.py
21
21
  defuser/modeling/unfused_moe/__init__.py
22
22
  defuser/modeling/unfused_moe/common.py
23
23
  defuser/modeling/unfused_moe/glm4_moe.py
24
+ defuser/modeling/unfused_moe/glm4_moe_lite.py
24
25
  defuser/modeling/unfused_moe/mixtral.py
25
26
  defuser/modeling/unfused_moe/qwen2_moe.py
26
27
  defuser/modeling/unfused_moe/qwen3_moe.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Defuser
3
- Version: 0.0.17
3
+ Version: 0.0.18
4
4
  Summary: Model defuser helper for HF Transformers.
5
5
  Author-email: ModelCloud <qubitium@modelcloud.ai>
6
6
  License-Expression: Apache-2.0
@@ -78,6 +78,7 @@ Defuser currently supports the following `transformers==5.3.0` `model_type` valu
78
78
  | Model type | Defused op performed |
79
79
  | --- | --- |
80
80
  | `glm4_moe` | Replaces `Glm4MoeMoE` with a defused per-expert linear MoE block. |
81
+ | `glm4_moe_lite` | Replaces `Glm4MoeLiteMoE` with a defused per-expert linear MoE block.|
81
82
  | `glm4v` | Replaces the fused text MLP with split `gate_proj`, `up_proj`, and `down_proj` layers. Also splits fused checkpoint `mlp.gate_up_proj.weight` into `mlp.gate_proj.weight` + `mlp.up_proj.weight`. |
82
83
  | `mixtral` | Replaces `MixtralSparseMoeBlock` with `LinearMixtralSparseMoeBlock`. Also remaps legacy Mixtral checkpoint keys and splits fused expert `gate_up_proj` tensors into per-expert `gate_proj` and `up_proj`, plus per-expert `down_proj`. |
83
84
  | `qwen2_moe` | Replaces `Qwen2MoeSparseMoeBlock` with a defused per-expert linear MoE block. |
@@ -53,6 +53,7 @@ Defuser currently supports the following `transformers==5.3.0` `model_type` valu
53
53
  | Model type | Defused op performed |
54
54
  | --- | --- |
55
55
  | `glm4_moe` | Replaces `Glm4MoeMoE` with a defused per-expert linear MoE block. |
56
+ | `glm4_moe_lite` | Replaces `Glm4MoeLiteMoE` with a defused per-expert linear MoE block.|
56
57
  | `glm4v` | Replaces the fused text MLP with split `gate_proj`, `up_proj`, and `down_proj` layers. Also splits fused checkpoint `mlp.gate_up_proj.weight` into `mlp.gate_proj.weight` + `mlp.up_proj.weight`. |
57
58
  | `mixtral` | Replaces `MixtralSparseMoeBlock` with `LinearMixtralSparseMoeBlock`. Also remaps legacy Mixtral checkpoint keys and splits fused expert `gate_up_proj` tensors into per-expert `gate_proj` and `up_proj`, plus per-expert `down_proj`. |
58
59
  | `qwen2_moe` | Replaces `Qwen2MoeSparseMoeBlock` with a defused per-expert linear MoE block. |
@@ -3,19 +3,29 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  # Contact: qubitium@modelcloud.ai, x.com/qubitium
5
5
 
6
- from defuser.utils.common import env_flag
6
+ from defuser.utils.common import env_flag, warn_if_public_api_transformers_unsupported
7
+ from logbar import LogBar
7
8
 
8
9
  DEBUG_ON = env_flag("DEBUG")
9
10
 
10
- def convert_model(*args, **kwargs):
11
+ logger = LogBar(__name__)
12
+
13
+
14
+ def convert_model(*args, **kwargs) -> bool:
11
15
  """Lazily import conversion entrypoint to avoid import-time cycles."""
16
+ if warn_if_public_api_transformers_unsupported("convert_model()", logger):
17
+ return False
18
+
12
19
  from .defuser import convert_model as _convert_model
13
20
 
14
21
  return _convert_model(*args, **kwargs)
15
22
 
16
23
 
17
- def replace_fused_blocks(*args, **kwargs):
24
+ def replace_fused_blocks(*args, **kwargs) -> bool:
18
25
  """Lazily import conversion entrypoint to avoid import-time cycles."""
26
+ if warn_if_public_api_transformers_unsupported("replace_fused_blocks()", logger):
27
+ return False
28
+
19
29
  from .defuser import replace_fused_blocks as _replace_fused_blocks
20
30
 
21
31
  return _replace_fused_blocks(*args, **kwargs)
@@ -135,6 +135,12 @@ MODEL_CONFIG = {
135
135
  },
136
136
  "glm4_moe_lite": {
137
137
  "min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
138
+ PATCH.REPLACE_MODULE: [
139
+ (
140
+ "transformers.models.glm4_moe_lite.modeling_glm4_moe_lite.Glm4MoeLiteMoE",
141
+ "defuser.modeling.unfused_moe.glm4_moe_lite.LinearGlm4MoeLiteMoE",
142
+ )
143
+ ],
138
144
  },
139
145
  "glm4v": {
140
146
  "min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
@@ -0,0 +1,100 @@
1
+ # SPDX-FileCopyrightText: 2026 ModelCloud.ai
2
+ # SPDX-FileCopyrightText: 2026 qubitium@modelcloud.ai
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ # Contact: qubitium@modelcloud.ai, x.com/qubitium
5
+
6
+ # Adapted from intel/auto-round
7
+ # at https://github.com/intel/auto-round/blob/main/auto_round/modeling/unfused_moe/glm_moe_light.py
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+
12
+ class LinearGlm4MoeLiteMoE(nn.Module):
13
+ """
14
+ A mixed expert module containing shared experts.
15
+ """
16
+
17
+ def __init__(self, config):
18
+ super().__init__()
19
+ self.config = config
20
+ self.num_experts = config.num_local_experts
21
+ from transformers.models.glm4_moe_lite.modeling_glm4_moe_lite import Glm4MoeLiteMLP, Glm4MoeLiteTopkRouter
22
+
23
+ self.experts = nn.ModuleList(
24
+ [Glm4MoeLiteMLP(config, intermediate_size=config.moe_intermediate_size) for _ in range(self.num_experts)]
25
+ )
26
+
27
+ self.gate = Glm4MoeLiteTopkRouter(config)
28
+ self.shared_experts = Glm4MoeLiteMLP(
29
+ config=config, intermediate_size=config.moe_intermediate_size * config.n_shared_experts
30
+ )
31
+ self.n_routed_experts = config.n_routed_experts
32
+ self.n_group = config.n_group
33
+ self.topk_group = config.topk_group
34
+ self.norm_topk_prob = config.norm_topk_prob
35
+ self.routed_scaling_factor = config.routed_scaling_factor
36
+ self.top_k = config.num_experts_per_tok
37
+
38
+ def experts_forward(
39
+ self,
40
+ hidden_states: torch.Tensor,
41
+ top_k_index: torch.Tensor,
42
+ top_k_weights: torch.Tensor,
43
+ ) -> torch.Tensor:
44
+ """ """
45
+ final_hidden_states = torch.zeros_like(hidden_states)
46
+ with torch.no_grad():
47
+ expert_mask = torch.nn.functional.one_hot(top_k_index, num_classes=self.num_experts)
48
+ expert_mask = expert_mask.permute(2, 1, 0)
49
+ expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero()
50
+
51
+ for expert_idx in expert_hit:
52
+ expert_idx = expert_idx[0]
53
+ if expert_idx == self.num_experts:
54
+ continue
55
+ top_k_pos, token_idx = torch.where(expert_mask[expert_idx])
56
+ current_state = hidden_states[token_idx]
57
+ expert_layer = self.experts[expert_idx]
58
+ current_hidden_states = expert_layer(current_state)
59
+ # gate, up = nn.functional.linear(current_state, self.gate_up_proj[expert_idx]).chunk(2, dim=-1)
60
+ # current_hidden_states = self.act_fn(gate) * up
61
+ # current_hidden_states = nn.functional.linear(current_hidden_states, self.down_proj[expert_idx])
62
+ current_hidden_states = current_hidden_states * top_k_weights[token_idx, top_k_pos, None]
63
+ final_hidden_states.index_add_(0, token_idx, current_hidden_states.to(final_hidden_states.dtype))
64
+
65
+ return final_hidden_states
66
+
67
+ def route_tokens_to_experts(self, router_logits):
68
+ router_logits = router_logits.sigmoid()
69
+ router_logits_for_choice = router_logits + self.gate.e_score_correction_bias
70
+ group_scores = (
71
+ router_logits_for_choice.view(-1, self.n_group, self.n_routed_experts // self.n_group)
72
+ .topk(2, dim=-1)[0]
73
+ .sum(dim=-1)
74
+ )
75
+ group_idx = torch.topk(group_scores, k=self.topk_group, dim=-1, sorted=False)[1]
76
+ group_mask = torch.zeros_like(group_scores)
77
+ group_mask.scatter_(1, group_idx, 1)
78
+ score_mask = (
79
+ group_mask.unsqueeze(-1)
80
+ .expand(-1, self.n_group, self.n_routed_experts // self.n_group)
81
+ .reshape(-1, self.n_routed_experts)
82
+ )
83
+ scores_for_choice = router_logits_for_choice.masked_fill(~score_mask.bool(), 0.0)
84
+ topk_indices = torch.topk(scores_for_choice, k=self.top_k, dim=-1, sorted=False)[1]
85
+ topk_weights = router_logits.gather(1, topk_indices)
86
+ if self.norm_topk_prob:
87
+ denominator = topk_weights.sum(dim=-1, keepdim=True) + 1e-20
88
+ topk_weights /= denominator
89
+ topk_weights = topk_weights * self.routed_scaling_factor
90
+ return topk_indices, topk_weights
91
+
92
+ def forward(self, hidden_states):
93
+ residuals = hidden_states
94
+ orig_shape = hidden_states.shape
95
+ router_logits = self.gate(hidden_states)
96
+ topk_indices, topk_weights = self.route_tokens_to_experts(router_logits)
97
+ hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
98
+ hidden_states = self.experts_forward(hidden_states, topk_indices, topk_weights).view(*orig_shape)
99
+ hidden_states = hidden_states + self.shared_experts(residuals)
100
+ return hidden_states
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
 
10
10
  [project]
11
11
  name = "Defuser"
12
- version = "0.0.17"
12
+ version = "0.0.18"
13
13
  description = "Model defuser helper for HF Transformers."
14
14
  readme = "README.md"
15
15
  requires-python = ">=3.9"
@@ -10,6 +10,11 @@ from safetensors.torch import save_file
10
10
  from torch import nn
11
11
  from transformers.core_model_loading import WeightConverter
12
12
  from transformers.models.glm4_moe.modeling_glm4_moe import Glm4MoeConfig, Glm4MoeForCausalLM, Glm4MoeMoE
13
+ from transformers.models.glm4_moe_lite.configuration_glm4_moe_lite import Glm4MoeLiteConfig
14
+ from transformers.models.glm4_moe_lite.modeling_glm4_moe_lite import (
15
+ Glm4MoeLiteForCausalLM,
16
+ Glm4MoeLiteMoE,
17
+ )
13
18
  from transformers.models.glm4v.configuration_glm4v import Glm4vConfig
14
19
  from transformers.models.glm4v.modeling_glm4v import Glm4vForConditionalGeneration
15
20
  from transformers.models.mixtral.configuration_mixtral import MixtralConfig
@@ -48,6 +53,7 @@ from defuser.checkpoint_ops import OwnedChunk, SplitFusedExpertDownProj, SplitFu
48
53
  from defuser.model_registry import MODEL_CONFIG, PATCH
49
54
  from defuser.modeling.replace_modules import ReplacementModuleBase, apply_replacements, materialize_model
50
55
  from defuser.modeling.unfused_moe.glm4_moe import LinearGlm4MoeMoE
56
+ from defuser.modeling.unfused_moe.glm4_moe_lite import LinearGlm4MoeLiteMoE
51
57
  from defuser.modeling.unfused_moe.mixtral import LinearMixtralSparseMoeBlock
52
58
  from defuser.modeling.unfused_moe.qwen2_moe import LinearQwen2MoeSparseMoeBlock
53
59
  from defuser.modeling.unfused_moe.qwen3_moe import LinearQwen3MoeSparseMoeBlock
@@ -61,9 +67,9 @@ from defuser.utils.common import MIN_SUPPORTED_TRANSFORMERS_VERSION
61
67
 
62
68
 
63
69
 
64
- def _tiny_moe_config(config_cls):
70
+ def _tiny_moe_config(config_cls, num_hidden_layers: int=1):
65
71
  return config_cls(
66
- num_hidden_layers=1,
72
+ num_hidden_layers=num_hidden_layers,
67
73
  hidden_size=64,
68
74
  intermediate_size=128,
69
75
  moe_intermediate_size=32,
@@ -78,6 +84,7 @@ def _tiny_moe_config(config_cls):
78
84
 
79
85
  def _tiny_qwen3_omni_config():
80
86
  return Qwen3OmniMoeConfig(
87
+ initializer_range=0.02,
81
88
  enable_audio_output=False,
82
89
  thinker_config={
83
90
  "text_config": {
@@ -936,6 +943,29 @@ def test_defused_models_preserve_output_router_logits_capture():
936
943
  assert len(outputs.router_logits) == 1
937
944
  assert outputs.router_logits[0].shape == (3, model.config.num_experts)
938
945
 
946
+ def test_glm4_moe_lite():
947
+ model_type = "glm4_moe_lite"
948
+ replace_fused_blocks(model_type)
949
+
950
+ model = Glm4MoeLiteForCausalLM(_tiny_moe_config(Glm4MoeLiteConfig, num_hidden_layers=2))
951
+ assert model.config.model_type == model_type
952
+
953
+ # In GLM4-MoE-Lite, the `mlp.experts` module is present only starting from the second layer.
954
+ converted = convert_model(model, max_layers=2)
955
+ assert not converted
956
+
957
+ _assert_unfused_expert_module(model.model.layers[1].mlp.experts)
958
+
959
+
960
+ def test_glm4_moe_lite_defused_forward_matches_fused_math():
961
+ config = _tiny_moe_config(Glm4MoeLiteConfig)
962
+ hidden_states = torch.randn(2, 3, config.hidden_size, dtype=torch.float32)
963
+
964
+ _assert_sparse_moe_defused_matches_fused_math(
965
+ Glm4MoeLiteMoE(config),
966
+ LinearGlm4MoeLiteMoE(config),
967
+ hidden_states,
968
+ )
939
969
 
940
970
  def test_glm4v_checkpoint_mapping_splits_gate_up_proj():
941
971
  from defuser.defuser import get_checkpoint_conversion_mapping
@@ -382,13 +382,13 @@ META_MODEL_CASES = [
382
382
  },
383
383
  {
384
384
  "model_type": "glm4_moe_lite",
385
- "mode": "convert",
385
+ "mode": "replace",
386
386
  "model_module": "transformers.models.glm4_moe_lite.modeling_glm4_moe_lite",
387
387
  "model_class": "Glm4MoeLiteForCausalLM",
388
388
  "config_module": "transformers.models.glm4_moe_lite.configuration_glm4_moe_lite",
389
389
  "config_class": "Glm4MoeLiteConfig",
390
- "target_class_paths": ("transformers.models.glm4_moe_lite.modeling_glm4_moe_lite.Glm4MoeLiteNaiveMoe",),
391
- "validator": "experts",
390
+ "target_class_paths": ("defuser.modeling.unfused_moe.glm4_moe_lite.LinearGlm4MoeLiteMoE",),
391
+ "validator": "sparse_block",
392
392
  },
393
393
  {
394
394
  "model_type": "glm4v",
File without changes
File without changes
File without changes
File without changes
File without changes