Defuser 0.0.16__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.
- {defuser-0.0.16 → defuser-0.0.18}/Defuser.egg-info/PKG-INFO +52 -14
- {defuser-0.0.16 → defuser-0.0.18}/Defuser.egg-info/SOURCES.txt +5 -0
- defuser-0.0.18/Defuser.egg-info/requires.txt +2 -0
- {defuser-0.0.16 → defuser-0.0.18}/PKG-INFO +52 -14
- {defuser-0.0.16 → defuser-0.0.18}/README.md +50 -13
- {defuser-0.0.16 → defuser-0.0.18}/defuser/__init__.py +13 -3
- {defuser-0.0.16 → defuser-0.0.18}/defuser/defuser.py +6 -5
- {defuser-0.0.16 → defuser-0.0.18}/defuser/model_registry.py +109 -0
- defuser-0.0.18/defuser/modeling/model_patches.py +355 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/moe_experts_interface.py +36 -7
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/replace_modules.py +21 -5
- defuser-0.0.18/defuser/modeling/runtime_defusion.py +313 -0
- defuser-0.0.18/defuser/modeling/unfused_moe/glm4_moe_lite.py +100 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/unfused_moe/qwen3_omni_moe.py +44 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/update_module.py +2 -1
- defuser-0.0.18/defuser/utils/common.py +177 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/utils/hf.py +2 -4
- {defuser-0.0.16 → defuser-0.0.18}/pyproject.toml +2 -1
- defuser-0.0.18/tests/test_candidate_coverage.py +663 -0
- {defuser-0.0.16 → defuser-0.0.18}/tests/test_convert_model.py +67 -3
- defuser-0.0.18/tests/test_filter_rules.py +198 -0
- defuser-0.0.18/tests/test_meta_model_defusion.py +763 -0
- defuser-0.0.16/Defuser.egg-info/requires.txt +0 -1
- defuser-0.0.16/defuser/modeling/model_patches.py +0 -124
- defuser-0.0.16/defuser/utils/common.py +0 -73
- {defuser-0.0.16 → defuser-0.0.18}/Defuser.egg-info/dependency_links.txt +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/Defuser.egg-info/top_level.txt +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/LICENSE +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/checkpoint_ops.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/__init__.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/glm4v.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/unfused_moe/__init__.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/unfused_moe/common.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/unfused_moe/glm4_moe.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/unfused_moe/mixtral.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/unfused_moe/qwen2_moe.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/unfused_moe/qwen3_moe.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/modeling/unfused_moe/qwen3_next.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/utils/__init__.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/utils/device.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/defuser/utils/model.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/setup.cfg +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/setup.py +0 -0
- {defuser-0.0.16 → defuser-0.0.18}/tests/test_device_utils.py +0 -0
- {defuser-0.0.16 → 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.
|
|
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
|
|
@@ -19,6 +19,7 @@ Classifier: Topic :: Text Processing :: Linguistic
|
|
|
19
19
|
Requires-Python: >=3.9
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
+
Requires-Dist: pypcre>=0.2.13
|
|
22
23
|
Requires-Dist: transformers
|
|
23
24
|
Dynamic: license-file
|
|
24
25
|
|
|
@@ -57,24 +58,47 @@ from defuser import convert_model, replace_fused_blocks
|
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
- `replace_fused_blocks(model_type)` patches supported HF model classes before `from_pretrained()` or direct model construction.
|
|
60
|
-
- `convert_model(model, cleanup_original=True, max_layers=None)` converts an already loaded model in place. This is the runtime defusion path
|
|
61
|
+
- `convert_model(model, cleanup_original=True, max_layers=None, filter=None)` converts an already loaded model in place. This is the runtime defusion path for supported post-load expert and MLP conversions, including `qwen3_5_moe` style checkpoints.
|
|
61
62
|
- Defuser is designed and CI-tested for `transformers>=5.3.0`, and support is only offered for that version range. Older versions log a warning on these public APIs and are skipped as unsupported.
|
|
62
63
|
|
|
64
|
+
`filter` is an optional list of PCRE regex rules evaluated against full module paths such as `model.layers.0.mlp.experts`:
|
|
65
|
+
|
|
66
|
+
- `+:regex` explicitly includes matching candidate module paths
|
|
67
|
+
- `-:regex` explicitly excludes matching candidate module paths
|
|
68
|
+
- `regex` is shorthand for `+:regex`
|
|
69
|
+
- negative rules take priority over positive rules
|
|
70
|
+
- when `filter` is provided, a candidate module is defused only if it matches at least one positive rule and no negative rules
|
|
71
|
+
|
|
63
72
|
## Supported Models
|
|
64
73
|
|
|
65
|
-
|
|
74
|
+
Defuser currently supports the following `transformers==5.3.0` `model_type` values.
|
|
75
|
+
|
|
76
|
+
### `replace_fused_blocks(model_type)` before load
|
|
77
|
+
|
|
78
|
+
| Model type | Defused op performed |
|
|
79
|
+
| --- | --- |
|
|
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.|
|
|
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`. |
|
|
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`. |
|
|
84
|
+
| `qwen2_moe` | Replaces `Qwen2MoeSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
85
|
+
| `qwen3_moe` | Replaces `Qwen3MoeSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
86
|
+
| `qwen3_next` | Replaces `Qwen3NextSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
87
|
+
| `qwen3_omni_moe` | Replaces both thinker and talker text sparse MoE blocks with defused per-expert linear blocks and applies small runtime compatibility patches for text `forward()` and `generate()`. |
|
|
88
|
+
|
|
89
|
+
### `convert_model(model)` after load
|
|
90
|
+
|
|
91
|
+
| Pattern | Supported model types | Defused op performed |
|
|
66
92
|
| --- | --- | --- |
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
| `gpt_oss` | `convert_model(model)` after load | Runtime expert tensor defusion. Splits fused transposed expert `gate_up_proj` into per-expert `gate_proj` + `up_proj`, carries over expert biases, and converts fused expert tensors into numbered expert `nn.Linear` modules. |
|
|
77
|
-
| `llama4` | `convert_model(model)` after load | Runtime expert tensor defusion. Splits fused transposed expert `gate_up_proj` into per-expert `gate_proj` + `up_proj`, converts fused expert tensors into numbered expert `nn.Linear` modules, and preserves the llama4 batched expert-input execution contract. |
|
|
93
|
+
| Standard routed expert tensors | `deepseek_v2`, `dots1`, `ernie4_5_moe`, `ernie4_5_vl_moe`, `exaone_moe`, `flex_olmo`, `glm4_moe_lite`, `glm4v_moe`, `hunyuan_v1_moe`, `jamba`, `lfm2_moe`, `minimax`, `minimax_m2`, `olmoe`, `qwen3_vl_moe`, `solar_open` | Splits fused expert tensors into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
94
|
+
| Mixed sparse and shared experts | `deepseek_v3`, `glm_moe_dsa`, `qwen3_5_moe`, `qwen3_5_moe_text` | Runtime expert tensor defusion for routed experts while preserving the model's shared-expert path. |
|
|
95
|
+
| Transposed or packed expert tensors | `gpt_oss`, `phimoe` | Splits transposed fused expert `gate_up_proj` tensors into per-expert `gate_proj` + `up_proj`, preserves expert bias when present, and converts expert tensors into numbered expert `nn.Linear` modules. |
|
|
96
|
+
| Flattened expert layout | `dbrx` | Rebuilds the flattened DBRX expert FFN weights into numbered expert `gate_proj`, `up_proj`, and `down_proj` `nn.Linear` modules. |
|
|
97
|
+
| Batched expert-input execution | `llama4` | Runtime expert tensor defusion plus preservation of the llama4 batched expert-input execution contract. |
|
|
98
|
+
| Non-gated expert MLPs | `nemotron_h` | Converts routed expert tensors into numbered `up_proj` and `down_proj` `nn.Linear` modules for non-gated experts. |
|
|
99
|
+
| Parallel expert blocks | `granitemoe`, `granitemoehybrid`, `granitemoeshared`, `jetmoe` | Converts packed expert weight tensors into numbered expert `linear` modules while keeping grouped expert execution intact. |
|
|
100
|
+
| Routed experts with identity experts | `longcat_flash` | Defuses routed experts into numbered `gate_proj`, `up_proj`, and `down_proj` modules and preserves zero or identity experts. |
|
|
101
|
+
| Fused dense `gate_up_proj` MLPs | `dia`, `glm`, `glm4`, `glm_image`, `glm_ocr`, `phi3`, `phi4_multimodal`, `zamba2` | Splits fused dense `gate_up_proj` layers into `gate_proj` + `up_proj` and updates the block `forward()` to preserve the original MLP math. |
|
|
78
102
|
|
|
79
103
|
## Workflow Summary
|
|
80
104
|
|
|
@@ -101,6 +125,20 @@ converted = convert_model(model)
|
|
|
101
125
|
print(converted) # True when runtime defusion happened
|
|
102
126
|
```
|
|
103
127
|
|
|
128
|
+
Use `filter` when only specific blocks should be defused:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from defuser import convert_model
|
|
132
|
+
|
|
133
|
+
convert_model(
|
|
134
|
+
model,
|
|
135
|
+
filter=[
|
|
136
|
+
r"+:^model\.layers\.0\.mlp\.experts$",
|
|
137
|
+
r"-:^model\.layers\.0\.mlp\.experts\.shared_",
|
|
138
|
+
],
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
104
142
|
## Real Qwen3.5 MoE Example
|
|
105
143
|
|
|
106
144
|
The example below is written for the `transformers==5.3.0` public API surface and uses the real Hugging Face model `Qwen/Qwen3.5-35B-A3B-Instruct`. Defuser supports `transformers>=5.3.0`.
|
|
@@ -16,10 +16,12 @@ defuser/modeling/glm4v.py
|
|
|
16
16
|
defuser/modeling/model_patches.py
|
|
17
17
|
defuser/modeling/moe_experts_interface.py
|
|
18
18
|
defuser/modeling/replace_modules.py
|
|
19
|
+
defuser/modeling/runtime_defusion.py
|
|
19
20
|
defuser/modeling/update_module.py
|
|
20
21
|
defuser/modeling/unfused_moe/__init__.py
|
|
21
22
|
defuser/modeling/unfused_moe/common.py
|
|
22
23
|
defuser/modeling/unfused_moe/glm4_moe.py
|
|
24
|
+
defuser/modeling/unfused_moe/glm4_moe_lite.py
|
|
23
25
|
defuser/modeling/unfused_moe/mixtral.py
|
|
24
26
|
defuser/modeling/unfused_moe/qwen2_moe.py
|
|
25
27
|
defuser/modeling/unfused_moe/qwen3_moe.py
|
|
@@ -30,6 +32,9 @@ defuser/utils/common.py
|
|
|
30
32
|
defuser/utils/device.py
|
|
31
33
|
defuser/utils/hf.py
|
|
32
34
|
defuser/utils/model.py
|
|
35
|
+
tests/test_candidate_coverage.py
|
|
33
36
|
tests/test_convert_model.py
|
|
34
37
|
tests/test_device_utils.py
|
|
38
|
+
tests/test_filter_rules.py
|
|
39
|
+
tests/test_meta_model_defusion.py
|
|
35
40
|
tests/test_replace_modules_tracker.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Defuser
|
|
3
|
-
Version: 0.0.
|
|
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
|
|
@@ -19,6 +19,7 @@ Classifier: Topic :: Text Processing :: Linguistic
|
|
|
19
19
|
Requires-Python: >=3.9
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
+
Requires-Dist: pypcre>=0.2.13
|
|
22
23
|
Requires-Dist: transformers
|
|
23
24
|
Dynamic: license-file
|
|
24
25
|
|
|
@@ -57,24 +58,47 @@ from defuser import convert_model, replace_fused_blocks
|
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
- `replace_fused_blocks(model_type)` patches supported HF model classes before `from_pretrained()` or direct model construction.
|
|
60
|
-
- `convert_model(model, cleanup_original=True, max_layers=None)` converts an already loaded model in place. This is the runtime defusion path
|
|
61
|
+
- `convert_model(model, cleanup_original=True, max_layers=None, filter=None)` converts an already loaded model in place. This is the runtime defusion path for supported post-load expert and MLP conversions, including `qwen3_5_moe` style checkpoints.
|
|
61
62
|
- Defuser is designed and CI-tested for `transformers>=5.3.0`, and support is only offered for that version range. Older versions log a warning on these public APIs and are skipped as unsupported.
|
|
62
63
|
|
|
64
|
+
`filter` is an optional list of PCRE regex rules evaluated against full module paths such as `model.layers.0.mlp.experts`:
|
|
65
|
+
|
|
66
|
+
- `+:regex` explicitly includes matching candidate module paths
|
|
67
|
+
- `-:regex` explicitly excludes matching candidate module paths
|
|
68
|
+
- `regex` is shorthand for `+:regex`
|
|
69
|
+
- negative rules take priority over positive rules
|
|
70
|
+
- when `filter` is provided, a candidate module is defused only if it matches at least one positive rule and no negative rules
|
|
71
|
+
|
|
63
72
|
## Supported Models
|
|
64
73
|
|
|
65
|
-
|
|
74
|
+
Defuser currently supports the following `transformers==5.3.0` `model_type` values.
|
|
75
|
+
|
|
76
|
+
### `replace_fused_blocks(model_type)` before load
|
|
77
|
+
|
|
78
|
+
| Model type | Defused op performed |
|
|
79
|
+
| --- | --- |
|
|
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.|
|
|
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`. |
|
|
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`. |
|
|
84
|
+
| `qwen2_moe` | Replaces `Qwen2MoeSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
85
|
+
| `qwen3_moe` | Replaces `Qwen3MoeSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
86
|
+
| `qwen3_next` | Replaces `Qwen3NextSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
87
|
+
| `qwen3_omni_moe` | Replaces both thinker and talker text sparse MoE blocks with defused per-expert linear blocks and applies small runtime compatibility patches for text `forward()` and `generate()`. |
|
|
88
|
+
|
|
89
|
+
### `convert_model(model)` after load
|
|
90
|
+
|
|
91
|
+
| Pattern | Supported model types | Defused op performed |
|
|
66
92
|
| --- | --- | --- |
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
| `gpt_oss` | `convert_model(model)` after load | Runtime expert tensor defusion. Splits fused transposed expert `gate_up_proj` into per-expert `gate_proj` + `up_proj`, carries over expert biases, and converts fused expert tensors into numbered expert `nn.Linear` modules. |
|
|
77
|
-
| `llama4` | `convert_model(model)` after load | Runtime expert tensor defusion. Splits fused transposed expert `gate_up_proj` into per-expert `gate_proj` + `up_proj`, converts fused expert tensors into numbered expert `nn.Linear` modules, and preserves the llama4 batched expert-input execution contract. |
|
|
93
|
+
| Standard routed expert tensors | `deepseek_v2`, `dots1`, `ernie4_5_moe`, `ernie4_5_vl_moe`, `exaone_moe`, `flex_olmo`, `glm4_moe_lite`, `glm4v_moe`, `hunyuan_v1_moe`, `jamba`, `lfm2_moe`, `minimax`, `minimax_m2`, `olmoe`, `qwen3_vl_moe`, `solar_open` | Splits fused expert tensors into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
94
|
+
| Mixed sparse and shared experts | `deepseek_v3`, `glm_moe_dsa`, `qwen3_5_moe`, `qwen3_5_moe_text` | Runtime expert tensor defusion for routed experts while preserving the model's shared-expert path. |
|
|
95
|
+
| Transposed or packed expert tensors | `gpt_oss`, `phimoe` | Splits transposed fused expert `gate_up_proj` tensors into per-expert `gate_proj` + `up_proj`, preserves expert bias when present, and converts expert tensors into numbered expert `nn.Linear` modules. |
|
|
96
|
+
| Flattened expert layout | `dbrx` | Rebuilds the flattened DBRX expert FFN weights into numbered expert `gate_proj`, `up_proj`, and `down_proj` `nn.Linear` modules. |
|
|
97
|
+
| Batched expert-input execution | `llama4` | Runtime expert tensor defusion plus preservation of the llama4 batched expert-input execution contract. |
|
|
98
|
+
| Non-gated expert MLPs | `nemotron_h` | Converts routed expert tensors into numbered `up_proj` and `down_proj` `nn.Linear` modules for non-gated experts. |
|
|
99
|
+
| Parallel expert blocks | `granitemoe`, `granitemoehybrid`, `granitemoeshared`, `jetmoe` | Converts packed expert weight tensors into numbered expert `linear` modules while keeping grouped expert execution intact. |
|
|
100
|
+
| Routed experts with identity experts | `longcat_flash` | Defuses routed experts into numbered `gate_proj`, `up_proj`, and `down_proj` modules and preserves zero or identity experts. |
|
|
101
|
+
| Fused dense `gate_up_proj` MLPs | `dia`, `glm`, `glm4`, `glm_image`, `glm_ocr`, `phi3`, `phi4_multimodal`, `zamba2` | Splits fused dense `gate_up_proj` layers into `gate_proj` + `up_proj` and updates the block `forward()` to preserve the original MLP math. |
|
|
78
102
|
|
|
79
103
|
## Workflow Summary
|
|
80
104
|
|
|
@@ -101,6 +125,20 @@ converted = convert_model(model)
|
|
|
101
125
|
print(converted) # True when runtime defusion happened
|
|
102
126
|
```
|
|
103
127
|
|
|
128
|
+
Use `filter` when only specific blocks should be defused:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from defuser import convert_model
|
|
132
|
+
|
|
133
|
+
convert_model(
|
|
134
|
+
model,
|
|
135
|
+
filter=[
|
|
136
|
+
r"+:^model\.layers\.0\.mlp\.experts$",
|
|
137
|
+
r"-:^model\.layers\.0\.mlp\.experts\.shared_",
|
|
138
|
+
],
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
104
142
|
## Real Qwen3.5 MoE Example
|
|
105
143
|
|
|
106
144
|
The example below is written for the `transformers==5.3.0` public API surface and uses the real Hugging Face model `Qwen/Qwen3.5-35B-A3B-Instruct`. Defuser supports `transformers>=5.3.0`.
|
|
@@ -33,24 +33,47 @@ from defuser import convert_model, replace_fused_blocks
|
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
- `replace_fused_blocks(model_type)` patches supported HF model classes before `from_pretrained()` or direct model construction.
|
|
36
|
-
- `convert_model(model, cleanup_original=True, max_layers=None)` converts an already loaded model in place. This is the runtime defusion path
|
|
36
|
+
- `convert_model(model, cleanup_original=True, max_layers=None, filter=None)` converts an already loaded model in place. This is the runtime defusion path for supported post-load expert and MLP conversions, including `qwen3_5_moe` style checkpoints.
|
|
37
37
|
- Defuser is designed and CI-tested for `transformers>=5.3.0`, and support is only offered for that version range. Older versions log a warning on these public APIs and are skipped as unsupported.
|
|
38
38
|
|
|
39
|
+
`filter` is an optional list of PCRE regex rules evaluated against full module paths such as `model.layers.0.mlp.experts`:
|
|
40
|
+
|
|
41
|
+
- `+:regex` explicitly includes matching candidate module paths
|
|
42
|
+
- `-:regex` explicitly excludes matching candidate module paths
|
|
43
|
+
- `regex` is shorthand for `+:regex`
|
|
44
|
+
- negative rules take priority over positive rules
|
|
45
|
+
- when `filter` is provided, a candidate module is defused only if it matches at least one positive rule and no negative rules
|
|
46
|
+
|
|
39
47
|
## Supported Models
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
Defuser currently supports the following `transformers==5.3.0` `model_type` values.
|
|
50
|
+
|
|
51
|
+
### `replace_fused_blocks(model_type)` before load
|
|
52
|
+
|
|
53
|
+
| Model type | Defused op performed |
|
|
54
|
+
| --- | --- |
|
|
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.|
|
|
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`. |
|
|
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`. |
|
|
59
|
+
| `qwen2_moe` | Replaces `Qwen2MoeSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
60
|
+
| `qwen3_moe` | Replaces `Qwen3MoeSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
61
|
+
| `qwen3_next` | Replaces `Qwen3NextSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
62
|
+
| `qwen3_omni_moe` | Replaces both thinker and talker text sparse MoE blocks with defused per-expert linear blocks and applies small runtime compatibility patches for text `forward()` and `generate()`. |
|
|
63
|
+
|
|
64
|
+
### `convert_model(model)` after load
|
|
65
|
+
|
|
66
|
+
| Pattern | Supported model types | Defused op performed |
|
|
42
67
|
| --- | --- | --- |
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
| `gpt_oss` | `convert_model(model)` after load | Runtime expert tensor defusion. Splits fused transposed expert `gate_up_proj` into per-expert `gate_proj` + `up_proj`, carries over expert biases, and converts fused expert tensors into numbered expert `nn.Linear` modules. |
|
|
53
|
-
| `llama4` | `convert_model(model)` after load | Runtime expert tensor defusion. Splits fused transposed expert `gate_up_proj` into per-expert `gate_proj` + `up_proj`, converts fused expert tensors into numbered expert `nn.Linear` modules, and preserves the llama4 batched expert-input execution contract. |
|
|
68
|
+
| Standard routed expert tensors | `deepseek_v2`, `dots1`, `ernie4_5_moe`, `ernie4_5_vl_moe`, `exaone_moe`, `flex_olmo`, `glm4_moe_lite`, `glm4v_moe`, `hunyuan_v1_moe`, `jamba`, `lfm2_moe`, `minimax`, `minimax_m2`, `olmoe`, `qwen3_vl_moe`, `solar_open` | Splits fused expert tensors into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
69
|
+
| Mixed sparse and shared experts | `deepseek_v3`, `glm_moe_dsa`, `qwen3_5_moe`, `qwen3_5_moe_text` | Runtime expert tensor defusion for routed experts while preserving the model's shared-expert path. |
|
|
70
|
+
| Transposed or packed expert tensors | `gpt_oss`, `phimoe` | Splits transposed fused expert `gate_up_proj` tensors into per-expert `gate_proj` + `up_proj`, preserves expert bias when present, and converts expert tensors into numbered expert `nn.Linear` modules. |
|
|
71
|
+
| Flattened expert layout | `dbrx` | Rebuilds the flattened DBRX expert FFN weights into numbered expert `gate_proj`, `up_proj`, and `down_proj` `nn.Linear` modules. |
|
|
72
|
+
| Batched expert-input execution | `llama4` | Runtime expert tensor defusion plus preservation of the llama4 batched expert-input execution contract. |
|
|
73
|
+
| Non-gated expert MLPs | `nemotron_h` | Converts routed expert tensors into numbered `up_proj` and `down_proj` `nn.Linear` modules for non-gated experts. |
|
|
74
|
+
| Parallel expert blocks | `granitemoe`, `granitemoehybrid`, `granitemoeshared`, `jetmoe` | Converts packed expert weight tensors into numbered expert `linear` modules while keeping grouped expert execution intact. |
|
|
75
|
+
| Routed experts with identity experts | `longcat_flash` | Defuses routed experts into numbered `gate_proj`, `up_proj`, and `down_proj` modules and preserves zero or identity experts. |
|
|
76
|
+
| Fused dense `gate_up_proj` MLPs | `dia`, `glm`, `glm4`, `glm_image`, `glm_ocr`, `phi3`, `phi4_multimodal`, `zamba2` | Splits fused dense `gate_up_proj` layers into `gate_proj` + `up_proj` and updates the block `forward()` to preserve the original MLP math. |
|
|
54
77
|
|
|
55
78
|
## Workflow Summary
|
|
56
79
|
|
|
@@ -77,6 +100,20 @@ converted = convert_model(model)
|
|
|
77
100
|
print(converted) # True when runtime defusion happened
|
|
78
101
|
```
|
|
79
102
|
|
|
103
|
+
Use `filter` when only specific blocks should be defused:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from defuser import convert_model
|
|
107
|
+
|
|
108
|
+
convert_model(
|
|
109
|
+
model,
|
|
110
|
+
filter=[
|
|
111
|
+
r"+:^model\.layers\.0\.mlp\.experts$",
|
|
112
|
+
r"-:^model\.layers\.0\.mlp\.experts\.shared_",
|
|
113
|
+
],
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
80
117
|
## Real Qwen3.5 MoE Example
|
|
81
118
|
|
|
82
119
|
The example below is written for the `transformers==5.3.0` public API surface and uses the real Hugging Face model `Qwen/Qwen3.5-35B-A3B-Instruct`. Defuser supports `transformers>=5.3.0`.
|
|
@@ -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
|
-
|
|
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)
|
|
@@ -12,10 +12,10 @@ from defuser.modeling.model_patches import apply_model_class_patches, apply_mode
|
|
|
12
12
|
from defuser.modeling.update_module import update_module
|
|
13
13
|
from defuser.utils.common import (
|
|
14
14
|
MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
15
|
+
is_version_at_least,
|
|
15
16
|
is_supported_transformers_version,
|
|
16
17
|
warn_if_public_api_transformers_unsupported,
|
|
17
18
|
)
|
|
18
|
-
from packaging import version
|
|
19
19
|
import transformers
|
|
20
20
|
from logbar import LogBar
|
|
21
21
|
|
|
@@ -69,7 +69,7 @@ def replace_fused_blocks(model_type: str) -> bool:
|
|
|
69
69
|
custom_class = getattr(custom_module, custom_class_name)
|
|
70
70
|
setattr(orig_module, orig_class_name, custom_class)
|
|
71
71
|
|
|
72
|
-
if
|
|
72
|
+
if is_version_at_least(transformers.__version__, MIN_SUPPORTED_TRANSFORMERS_VERSION):
|
|
73
73
|
from transformers import conversion_mapping
|
|
74
74
|
|
|
75
75
|
if not hasattr(conversion_mapping, "orig_get_checkpoint_conversion_mapping"):
|
|
@@ -102,8 +102,7 @@ def check_model_compatibility(model: nn.Module) -> bool:
|
|
|
102
102
|
return False
|
|
103
103
|
|
|
104
104
|
min_ver = MODEL_CONFIG[model_type].get("min_transformers_version")
|
|
105
|
-
|
|
106
|
-
if min_ver and current_ver < version.parse(min_ver):
|
|
105
|
+
if min_ver and not is_version_at_least(transformers.__version__, min_ver):
|
|
107
106
|
logger.warn(
|
|
108
107
|
f"Skip conversion for model_type={model_type}: "
|
|
109
108
|
f"requires transformers>={min_ver}, current version is {transformers.__version__}."
|
|
@@ -117,6 +116,7 @@ def convert_model(
|
|
|
117
116
|
model: nn.Module,
|
|
118
117
|
cleanup_original: bool = False,
|
|
119
118
|
max_layers: int | None = None,
|
|
119
|
+
filter: list[str] | None = None,
|
|
120
120
|
) -> bool:
|
|
121
121
|
"""Convert one loaded model in place from fused experts to defused modules."""
|
|
122
122
|
if warn_if_public_api_transformers_unsupported("convert_model()", logger):
|
|
@@ -200,7 +200,7 @@ def convert_model(
|
|
|
200
200
|
if not check_model_compatibility(model):
|
|
201
201
|
return False
|
|
202
202
|
|
|
203
|
-
apply_model_patches(model)
|
|
203
|
+
apply_model_patches(model, max_layers=max_layers, filter_rules=filter)
|
|
204
204
|
|
|
205
205
|
# If fused blocks have already been structurally replaced at load model before,
|
|
206
206
|
# there is no need to perform runtime defusing again
|
|
@@ -214,6 +214,7 @@ def convert_model(
|
|
|
214
214
|
model,
|
|
215
215
|
cleanup_original=cleanup_original,
|
|
216
216
|
max_layers=max_layers,
|
|
217
|
+
filter_rules=filter,
|
|
217
218
|
)
|
|
218
219
|
|
|
219
220
|
return True
|
|
@@ -16,6 +16,39 @@ class PATCH(str, Enum):
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
MODEL_CONFIG = {
|
|
19
|
+
"dbrx": {
|
|
20
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
21
|
+
},
|
|
22
|
+
"deepseek_v2": {
|
|
23
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
24
|
+
},
|
|
25
|
+
"deepseek_v3": {
|
|
26
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
27
|
+
},
|
|
28
|
+
"dia": {
|
|
29
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
30
|
+
},
|
|
31
|
+
"dots1": {
|
|
32
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
33
|
+
},
|
|
34
|
+
"ernie4_5_moe": {
|
|
35
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
36
|
+
},
|
|
37
|
+
"ernie4_5_vl_moe": {
|
|
38
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
39
|
+
},
|
|
40
|
+
"exaone_moe": {
|
|
41
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
42
|
+
},
|
|
43
|
+
"flex_olmo": {
|
|
44
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
45
|
+
},
|
|
46
|
+
"glm": {
|
|
47
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
48
|
+
},
|
|
49
|
+
"glm4": {
|
|
50
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
51
|
+
},
|
|
19
52
|
"mixtral": {
|
|
20
53
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
21
54
|
PATCH.REPLACE_MODULE: [
|
|
@@ -84,6 +117,10 @@ MODEL_CONFIG = {
|
|
|
84
117
|
(
|
|
85
118
|
"transformers.models.qwen3_omni_moe.modeling_qwen3_omni_moe.Qwen3OmniMoeThinkerTextSparseMoeBlock",
|
|
86
119
|
"defuser.modeling.unfused_moe.qwen3_omni_moe.LinearQwen3OmniMoeThinkerTextSparseMoeBlock",
|
|
120
|
+
),
|
|
121
|
+
(
|
|
122
|
+
"transformers.models.qwen3_omni_moe.modeling_qwen3_omni_moe.Qwen3OmniMoeTalkerTextSparseMoeBlock",
|
|
123
|
+
"defuser.modeling.unfused_moe.qwen3_omni_moe.LinearQwen3OmniMoeTalkerTextSparseMoeBlock",
|
|
87
124
|
)
|
|
88
125
|
],
|
|
89
126
|
},
|
|
@@ -96,6 +133,15 @@ MODEL_CONFIG = {
|
|
|
96
133
|
)
|
|
97
134
|
],
|
|
98
135
|
},
|
|
136
|
+
"glm4_moe_lite": {
|
|
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
|
+
],
|
|
144
|
+
},
|
|
99
145
|
"glm4v": {
|
|
100
146
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
101
147
|
PATCH.REPLACE_MODULE: [
|
|
@@ -116,9 +162,39 @@ MODEL_CONFIG = {
|
|
|
116
162
|
),
|
|
117
163
|
],
|
|
118
164
|
},
|
|
165
|
+
"glm4v_moe": {
|
|
166
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
167
|
+
},
|
|
168
|
+
"glm_image": {
|
|
169
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
170
|
+
},
|
|
171
|
+
"glm_moe_dsa": {
|
|
172
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
173
|
+
},
|
|
174
|
+
"glm_ocr": {
|
|
175
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
176
|
+
},
|
|
119
177
|
"gpt_oss": {
|
|
120
178
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
121
179
|
},
|
|
180
|
+
"granitemoe": {
|
|
181
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
182
|
+
},
|
|
183
|
+
"granitemoehybrid": {
|
|
184
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
185
|
+
},
|
|
186
|
+
"granitemoeshared": {
|
|
187
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
188
|
+
},
|
|
189
|
+
"hunyuan_v1_moe": {
|
|
190
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
191
|
+
},
|
|
192
|
+
"jamba": {
|
|
193
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
194
|
+
},
|
|
195
|
+
"jetmoe": {
|
|
196
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
197
|
+
},
|
|
122
198
|
"llama4": {
|
|
123
199
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
124
200
|
PATCH.EXPERTS_DEFUSE: [
|
|
@@ -128,7 +204,40 @@ MODEL_CONFIG = {
|
|
|
128
204
|
}
|
|
129
205
|
],
|
|
130
206
|
},
|
|
207
|
+
"lfm2_moe": {
|
|
208
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
209
|
+
},
|
|
210
|
+
"longcat_flash": {
|
|
211
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
212
|
+
},
|
|
213
|
+
"minimax": {
|
|
214
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
215
|
+
},
|
|
216
|
+
"minimax_m2": {
|
|
217
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
218
|
+
},
|
|
219
|
+
"nemotron_h": {
|
|
220
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
221
|
+
},
|
|
222
|
+
"olmoe": {
|
|
223
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
224
|
+
},
|
|
225
|
+
"phi3": {
|
|
226
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
227
|
+
},
|
|
228
|
+
"phi4_multimodal": {
|
|
229
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
230
|
+
},
|
|
131
231
|
"phimoe": {
|
|
132
232
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
133
233
|
},
|
|
234
|
+
"qwen3_vl_moe": {
|
|
235
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
236
|
+
},
|
|
237
|
+
"solar_open": {
|
|
238
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
239
|
+
},
|
|
240
|
+
"zamba2": {
|
|
241
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
242
|
+
},
|
|
134
243
|
}
|