Defuser 0.0.20__tar.gz → 0.0.22__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.20 → defuser-0.0.22}/Defuser.egg-info/PKG-INFO +40 -40
- {defuser-0.0.20 → defuser-0.0.22}/PKG-INFO +40 -40
- {defuser-0.0.20 → defuser-0.0.22}/README.md +39 -39
- {defuser-0.0.20 → defuser-0.0.22}/defuser/defuser.py +0 -1
- {defuser-0.0.20 → defuser-0.0.22}/defuser/model_registry.py +6 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/replace_modules.py +0 -1
- {defuser-0.0.20 → defuser-0.0.22}/pyproject.toml +1 -1
- {defuser-0.0.20 → defuser-0.0.22}/tests/test_convert_model.py +79 -0
- {defuser-0.0.20 → defuser-0.0.22}/tests/test_meta_model_defusion.py +48 -1
- {defuser-0.0.20 → defuser-0.0.22}/Defuser.egg-info/SOURCES.txt +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/Defuser.egg-info/dependency_links.txt +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/Defuser.egg-info/requires.txt +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/Defuser.egg-info/top_level.txt +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/LICENSE +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/__init__.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/checkpoint_ops.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/__init__.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/glm4v.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/model_patches.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/moe_experts_interface.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/runtime_defusion.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/unfused_moe/__init__.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/unfused_moe/common.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/unfused_moe/glm4_moe.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/unfused_moe/glm4_moe_lite.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/unfused_moe/mixtral.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/unfused_moe/qwen2_moe.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/unfused_moe/qwen3_moe.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/unfused_moe/qwen3_next.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/unfused_moe/qwen3_omni_moe.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/modeling/update_module.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/utils/__init__.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/utils/common.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/utils/device.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/utils/hf.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/defuser/utils/model.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/setup.cfg +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/setup.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/tests/test_candidate_coverage.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/tests/test_device_utils.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/tests/test_filter_rules.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/tests/test_moe_experts_interface.py +0 -0
- {defuser-0.0.20 → defuser-0.0.22}/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.22
|
|
4
4
|
Summary: Model defuser helper for HF Transformers.
|
|
5
5
|
Author-email: ModelCloud <qubitium@modelcloud.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -26,7 +26,7 @@ Dynamic: license-file
|
|
|
26
26
|
|
|
27
27
|
<div align=center>
|
|
28
28
|
<img width="50%" alt="image" src="https://github.com/user-attachments/assets/f801617b-8959-474a-a565-6b8897e2fcbf" />
|
|
29
|
-
<h1 align="center">Defuser
|
|
29
|
+
<h1 align="center">Defuser 🔧</h1>
|
|
30
30
|
</div>
|
|
31
31
|
|
|
32
32
|
<p align="center">
|
|
@@ -37,47 +37,47 @@ Dynamic: license-file
|
|
|
37
37
|
<a href="https://huggingface.co/modelcloud/"><img src="https://img.shields.io/badge/🤗%20Hugging%20Face-ModelCloud-%23ff8811.svg"></a>
|
|
38
38
|
</p>
|
|
39
39
|
|
|
40
|
-
Defuser converts select Hugging Face Transformers `5.3.0+` fused or stacked MoE and MLP blocks back into plain, per-expert `nn.Linear` modules. It keeps the forward math intact while exposing individual projections again so quantizers, activation capture, debugging hooks, and checkpoint tooling can work against a simple module layout instead of fused expert tensors.
|
|
40
|
+
🧩 Defuser converts select Hugging Face Transformers `5.3.0+` fused or stacked MoE and MLP blocks back into plain, per-expert `nn.Linear` modules. It keeps the forward math intact while exposing individual projections again so quantizers, activation capture, debugging hooks, and checkpoint tooling can work against a simple module layout instead of fused expert tensors.
|
|
41
41
|
|
|
42
|
-
Defuser is designed and CI-tested for `transformers>=5.3.0`, and support is only offered for that version range.
|
|
42
|
+
✅ Defuser is designed and CI-tested for `transformers>=5.3.0`, and support is only offered for that version range.
|
|
43
43
|
|
|
44
|
-
## Purpose
|
|
44
|
+
## 🎯 Purpose
|
|
45
45
|
|
|
46
46
|
Defuser exists for cases where newer Transformers modeling code optimizes model structure in ways that are good for runtime, but harder for tooling that needs direct access to individual projections.
|
|
47
47
|
|
|
48
48
|
Depending on the model family, Defuser can:
|
|
49
49
|
|
|
50
|
-
- patch a supported model class before load so HF instantiates a defused block directly
|
|
51
|
-
- split fused tensors such as `gate_up_proj` into `gate_proj` + `up_proj`
|
|
52
|
-
- convert 3D expert tensors, including registered expert buffers, into numbered expert `nn.Linear` modules
|
|
53
|
-
- preserve the original fused math while presenting a naive module structure again
|
|
50
|
+
- 🧵 patch a supported model class before load so HF instantiates a defused block directly
|
|
51
|
+
- ✂️ split fused tensors such as `gate_up_proj` into `gate_proj` + `up_proj`
|
|
52
|
+
- 🧱 convert 3D expert tensors, including registered expert buffers, into numbered expert `nn.Linear` modules
|
|
53
|
+
- 🧮 preserve the original fused math while presenting a naive module structure again
|
|
54
54
|
|
|
55
|
-
Public API:
|
|
55
|
+
🛠️ Public API:
|
|
56
56
|
|
|
57
57
|
```python
|
|
58
58
|
from defuser import convert_model, replace_fused_blocks
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
- `replace_fused_blocks(model_type)` patches supported HF model classes before `from_pretrained()` or direct model construction.
|
|
62
|
-
- `convert_model(model, cleanup_original=False, 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.
|
|
63
|
-
- 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.
|
|
64
|
-
- Some model families appear in both support tables. Full models can be prepatched with `replace_fused_blocks(...)`, while standalone fused expert modules from those same families can still be runtime-defused with `convert_model(...)`.
|
|
61
|
+
- 🧰 `replace_fused_blocks(model_type)` patches supported HF model classes before `from_pretrained()` or direct model construction.
|
|
62
|
+
- 🔄 `convert_model(model, cleanup_original=False, 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.
|
|
63
|
+
- 🧪 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.
|
|
64
|
+
- 🧭 Some model families appear in both support tables. Full models can be prepatched with `replace_fused_blocks(...)`, while standalone fused expert modules from those same families can still be runtime-defused with `convert_model(...)`.
|
|
65
65
|
|
|
66
66
|
`filter` is an optional list of PCRE regex rules evaluated against full module paths such as `model.layers.0.mlp.experts`:
|
|
67
67
|
|
|
68
|
-
- `+:regex` explicitly includes matching candidate module paths
|
|
69
|
-
- `-:regex` explicitly excludes matching candidate module paths
|
|
70
|
-
- `regex` is shorthand for `+:regex`
|
|
71
|
-
- negative rules take priority over positive rules
|
|
72
|
-
- when `filter` is provided, a candidate module is defused only if it matches at least one positive rule and no negative rules
|
|
68
|
+
- ✅ `+:regex` explicitly includes matching candidate module paths
|
|
69
|
+
- 🚫 `-:regex` explicitly excludes matching candidate module paths
|
|
70
|
+
- ➕ `regex` is shorthand for `+:regex`
|
|
71
|
+
- 🛡️ negative rules take priority over positive rules
|
|
72
|
+
- 🎯 when `filter` is provided, a candidate module is defused only if it matches at least one positive rule and no negative rules
|
|
73
73
|
|
|
74
|
-
## Supported Models
|
|
74
|
+
## ✅ Supported Models
|
|
75
75
|
|
|
76
76
|
Defuser currently supports the following `transformers>=5.3.0` `model_type` values.
|
|
77
77
|
|
|
78
|
-
### `replace_fused_blocks(model_type)` before load
|
|
78
|
+
### 🧰 `replace_fused_blocks(model_type)` before load
|
|
79
79
|
|
|
80
|
-
| Model type | Defused op performed |
|
|
80
|
+
| Model type | Defused op performed ⚙️ |
|
|
81
81
|
| --- | --- |
|
|
82
82
|
| `glm4_moe` | Replaces `Glm4MoeMoE` with a defused per-expert linear MoE block. |
|
|
83
83
|
| `glm4_moe_lite` | Replaces `Glm4MoeLiteMoE` with a defused per-expert linear MoE block.|
|
|
@@ -88,21 +88,21 @@ Defuser currently supports the following `transformers>=5.3.0` `model_type` valu
|
|
|
88
88
|
| `qwen3_next` | Replaces `Qwen3NextSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
89
89
|
| `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()`. |
|
|
90
90
|
|
|
91
|
-
### `convert_model(model)` after load
|
|
91
|
+
### 🔄 `convert_model(model)` after load
|
|
92
92
|
|
|
93
|
-
| Pattern | Supported model types
|
|
94
|
-
| ---
|
|
95
|
-
| 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 or registered expert buffers into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
96
|
-
| Mixed sparse and shared experts | `deepseek_v3`, `glm_moe_dsa`, `qwen3_5_moe`, `qwen3_5_moe_text`
|
|
97
|
-
| Transposed or packed expert tensors | `gpt_oss`, `phimoe`
|
|
98
|
-
| Flattened expert layout | `dbrx`
|
|
99
|
-
| Batched expert-input execution | `llama4`
|
|
100
|
-
| Non-gated expert MLPs | `nemotron_h`
|
|
101
|
-
| Parallel expert blocks | `granitemoe`, `granitemoehybrid`, `granitemoeshared`, `jetmoe`
|
|
102
|
-
| Routed experts with identity experts | `longcat_flash`
|
|
103
|
-
| Fused dense `gate_up_proj` MLPs | `dia`, `glm`, `glm4`, `glm_image`, `glm_ocr`, `phi3`, `phi4_multimodal`, `zamba2`
|
|
93
|
+
| Pattern | Supported model types | Defused op performed ⚙️ |
|
|
94
|
+
| --- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
|
|
95
|
+
| 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`, `laguna`, `lfm2_moe`, `minimax`, `minimax_m2`, `olmoe`, `qwen3_vl_moe`, `solar_open` | Splits fused expert tensors or registered expert buffers into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
96
|
+
| Mixed sparse and shared experts | `deepseek_v3`, deepseek_v4`, `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. |
|
|
97
|
+
| 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. |
|
|
98
|
+
| Flattened expert layout | `dbrx` | Rebuilds the flattened DBRX expert FFN weights into numbered expert `gate_proj`, `up_proj`, and `down_proj` `nn.Linear` modules. |
|
|
99
|
+
| Batched expert-input execution | `llama4` | Runtime expert tensor defusion plus preservation of the llama4 batched expert-input execution contract. |
|
|
100
|
+
| Non-gated expert MLPs | `nemotron_h` | Converts routed expert tensors into numbered `up_proj` and `down_proj` `nn.Linear` modules for non-gated experts. |
|
|
101
|
+
| Parallel expert blocks | `granitemoe`, `granitemoehybrid`, `granitemoeshared`, `jetmoe` | Converts packed expert weight tensors into numbered expert `linear` modules while keeping grouped expert execution intact. |
|
|
102
|
+
| 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. |
|
|
103
|
+
| 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. |
|
|
104
104
|
|
|
105
|
-
## Workflow Summary
|
|
105
|
+
## 🔁 Workflow Summary
|
|
106
106
|
|
|
107
107
|
Use `replace_fused_blocks()` for model families that Defuser can patch before load:
|
|
108
108
|
|
|
@@ -129,7 +129,7 @@ print(converted) # True when runtime defusion happened
|
|
|
129
129
|
|
|
130
130
|
`convert_model(model)` also preserves meta-device construction for supported meta-initialized models, so structural validation can run without materializing weights.
|
|
131
131
|
|
|
132
|
-
Use `filter` when only specific blocks should be defused
|
|
132
|
+
Use `filter` when only specific blocks should be defused 🎯:
|
|
133
133
|
|
|
134
134
|
```python
|
|
135
135
|
from defuser import convert_model
|
|
@@ -143,11 +143,11 @@ convert_model(
|
|
|
143
143
|
)
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
## Real Qwen3.5 MoE Example
|
|
146
|
+
## 🧪 Real Qwen3.5 MoE Example
|
|
147
147
|
|
|
148
148
|
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`.
|
|
149
149
|
|
|
150
|
-
### Fused Weights Before And After
|
|
150
|
+
### 🔬 Fused Weights Before And After
|
|
151
151
|
|
|
152
152
|
Before `convert_model(model)`:
|
|
153
153
|
|
|
@@ -175,7 +175,7 @@ After `convert_model(model)`:
|
|
|
175
175
|
+-----------------------------------------------------------------+--------------------------------------+
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
### Sample 1: Inspect The Conversion In Place
|
|
178
|
+
### 🧭 Sample 1: Inspect The Conversion In Place
|
|
179
179
|
|
|
180
180
|
```python
|
|
181
181
|
from defuser import convert_model
|
|
@@ -213,7 +213,7 @@ print(after[:6])
|
|
|
213
213
|
# ]
|
|
214
214
|
```
|
|
215
215
|
|
|
216
|
-
### Sample 2: Convert And Keep Using The Model Normally
|
|
216
|
+
### 🚀 Sample 2: Convert And Keep Using The Model Normally
|
|
217
217
|
|
|
218
218
|
```python
|
|
219
219
|
import torch
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Defuser
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.22
|
|
4
4
|
Summary: Model defuser helper for HF Transformers.
|
|
5
5
|
Author-email: ModelCloud <qubitium@modelcloud.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -26,7 +26,7 @@ Dynamic: license-file
|
|
|
26
26
|
|
|
27
27
|
<div align=center>
|
|
28
28
|
<img width="50%" alt="image" src="https://github.com/user-attachments/assets/f801617b-8959-474a-a565-6b8897e2fcbf" />
|
|
29
|
-
<h1 align="center">Defuser
|
|
29
|
+
<h1 align="center">Defuser 🔧</h1>
|
|
30
30
|
</div>
|
|
31
31
|
|
|
32
32
|
<p align="center">
|
|
@@ -37,47 +37,47 @@ Dynamic: license-file
|
|
|
37
37
|
<a href="https://huggingface.co/modelcloud/"><img src="https://img.shields.io/badge/🤗%20Hugging%20Face-ModelCloud-%23ff8811.svg"></a>
|
|
38
38
|
</p>
|
|
39
39
|
|
|
40
|
-
Defuser converts select Hugging Face Transformers `5.3.0+` fused or stacked MoE and MLP blocks back into plain, per-expert `nn.Linear` modules. It keeps the forward math intact while exposing individual projections again so quantizers, activation capture, debugging hooks, and checkpoint tooling can work against a simple module layout instead of fused expert tensors.
|
|
40
|
+
🧩 Defuser converts select Hugging Face Transformers `5.3.0+` fused or stacked MoE and MLP blocks back into plain, per-expert `nn.Linear` modules. It keeps the forward math intact while exposing individual projections again so quantizers, activation capture, debugging hooks, and checkpoint tooling can work against a simple module layout instead of fused expert tensors.
|
|
41
41
|
|
|
42
|
-
Defuser is designed and CI-tested for `transformers>=5.3.0`, and support is only offered for that version range.
|
|
42
|
+
✅ Defuser is designed and CI-tested for `transformers>=5.3.0`, and support is only offered for that version range.
|
|
43
43
|
|
|
44
|
-
## Purpose
|
|
44
|
+
## 🎯 Purpose
|
|
45
45
|
|
|
46
46
|
Defuser exists for cases where newer Transformers modeling code optimizes model structure in ways that are good for runtime, but harder for tooling that needs direct access to individual projections.
|
|
47
47
|
|
|
48
48
|
Depending on the model family, Defuser can:
|
|
49
49
|
|
|
50
|
-
- patch a supported model class before load so HF instantiates a defused block directly
|
|
51
|
-
- split fused tensors such as `gate_up_proj` into `gate_proj` + `up_proj`
|
|
52
|
-
- convert 3D expert tensors, including registered expert buffers, into numbered expert `nn.Linear` modules
|
|
53
|
-
- preserve the original fused math while presenting a naive module structure again
|
|
50
|
+
- 🧵 patch a supported model class before load so HF instantiates a defused block directly
|
|
51
|
+
- ✂️ split fused tensors such as `gate_up_proj` into `gate_proj` + `up_proj`
|
|
52
|
+
- 🧱 convert 3D expert tensors, including registered expert buffers, into numbered expert `nn.Linear` modules
|
|
53
|
+
- 🧮 preserve the original fused math while presenting a naive module structure again
|
|
54
54
|
|
|
55
|
-
Public API:
|
|
55
|
+
🛠️ Public API:
|
|
56
56
|
|
|
57
57
|
```python
|
|
58
58
|
from defuser import convert_model, replace_fused_blocks
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
- `replace_fused_blocks(model_type)` patches supported HF model classes before `from_pretrained()` or direct model construction.
|
|
62
|
-
- `convert_model(model, cleanup_original=False, 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.
|
|
63
|
-
- 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.
|
|
64
|
-
- Some model families appear in both support tables. Full models can be prepatched with `replace_fused_blocks(...)`, while standalone fused expert modules from those same families can still be runtime-defused with `convert_model(...)`.
|
|
61
|
+
- 🧰 `replace_fused_blocks(model_type)` patches supported HF model classes before `from_pretrained()` or direct model construction.
|
|
62
|
+
- 🔄 `convert_model(model, cleanup_original=False, 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.
|
|
63
|
+
- 🧪 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.
|
|
64
|
+
- 🧭 Some model families appear in both support tables. Full models can be prepatched with `replace_fused_blocks(...)`, while standalone fused expert modules from those same families can still be runtime-defused with `convert_model(...)`.
|
|
65
65
|
|
|
66
66
|
`filter` is an optional list of PCRE regex rules evaluated against full module paths such as `model.layers.0.mlp.experts`:
|
|
67
67
|
|
|
68
|
-
- `+:regex` explicitly includes matching candidate module paths
|
|
69
|
-
- `-:regex` explicitly excludes matching candidate module paths
|
|
70
|
-
- `regex` is shorthand for `+:regex`
|
|
71
|
-
- negative rules take priority over positive rules
|
|
72
|
-
- when `filter` is provided, a candidate module is defused only if it matches at least one positive rule and no negative rules
|
|
68
|
+
- ✅ `+:regex` explicitly includes matching candidate module paths
|
|
69
|
+
- 🚫 `-:regex` explicitly excludes matching candidate module paths
|
|
70
|
+
- ➕ `regex` is shorthand for `+:regex`
|
|
71
|
+
- 🛡️ negative rules take priority over positive rules
|
|
72
|
+
- 🎯 when `filter` is provided, a candidate module is defused only if it matches at least one positive rule and no negative rules
|
|
73
73
|
|
|
74
|
-
## Supported Models
|
|
74
|
+
## ✅ Supported Models
|
|
75
75
|
|
|
76
76
|
Defuser currently supports the following `transformers>=5.3.0` `model_type` values.
|
|
77
77
|
|
|
78
|
-
### `replace_fused_blocks(model_type)` before load
|
|
78
|
+
### 🧰 `replace_fused_blocks(model_type)` before load
|
|
79
79
|
|
|
80
|
-
| Model type | Defused op performed |
|
|
80
|
+
| Model type | Defused op performed ⚙️ |
|
|
81
81
|
| --- | --- |
|
|
82
82
|
| `glm4_moe` | Replaces `Glm4MoeMoE` with a defused per-expert linear MoE block. |
|
|
83
83
|
| `glm4_moe_lite` | Replaces `Glm4MoeLiteMoE` with a defused per-expert linear MoE block.|
|
|
@@ -88,21 +88,21 @@ Defuser currently supports the following `transformers>=5.3.0` `model_type` valu
|
|
|
88
88
|
| `qwen3_next` | Replaces `Qwen3NextSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
89
89
|
| `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()`. |
|
|
90
90
|
|
|
91
|
-
### `convert_model(model)` after load
|
|
91
|
+
### 🔄 `convert_model(model)` after load
|
|
92
92
|
|
|
93
|
-
| Pattern | Supported model types
|
|
94
|
-
| ---
|
|
95
|
-
| 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 or registered expert buffers into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
96
|
-
| Mixed sparse and shared experts | `deepseek_v3`, `glm_moe_dsa`, `qwen3_5_moe`, `qwen3_5_moe_text`
|
|
97
|
-
| Transposed or packed expert tensors | `gpt_oss`, `phimoe`
|
|
98
|
-
| Flattened expert layout | `dbrx`
|
|
99
|
-
| Batched expert-input execution | `llama4`
|
|
100
|
-
| Non-gated expert MLPs | `nemotron_h`
|
|
101
|
-
| Parallel expert blocks | `granitemoe`, `granitemoehybrid`, `granitemoeshared`, `jetmoe`
|
|
102
|
-
| Routed experts with identity experts | `longcat_flash`
|
|
103
|
-
| Fused dense `gate_up_proj` MLPs | `dia`, `glm`, `glm4`, `glm_image`, `glm_ocr`, `phi3`, `phi4_multimodal`, `zamba2`
|
|
93
|
+
| Pattern | Supported model types | Defused op performed ⚙️ |
|
|
94
|
+
| --- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
|
|
95
|
+
| 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`, `laguna`, `lfm2_moe`, `minimax`, `minimax_m2`, `olmoe`, `qwen3_vl_moe`, `solar_open` | Splits fused expert tensors or registered expert buffers into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
96
|
+
| Mixed sparse and shared experts | `deepseek_v3`, deepseek_v4`, `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. |
|
|
97
|
+
| 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. |
|
|
98
|
+
| Flattened expert layout | `dbrx` | Rebuilds the flattened DBRX expert FFN weights into numbered expert `gate_proj`, `up_proj`, and `down_proj` `nn.Linear` modules. |
|
|
99
|
+
| Batched expert-input execution | `llama4` | Runtime expert tensor defusion plus preservation of the llama4 batched expert-input execution contract. |
|
|
100
|
+
| Non-gated expert MLPs | `nemotron_h` | Converts routed expert tensors into numbered `up_proj` and `down_proj` `nn.Linear` modules for non-gated experts. |
|
|
101
|
+
| Parallel expert blocks | `granitemoe`, `granitemoehybrid`, `granitemoeshared`, `jetmoe` | Converts packed expert weight tensors into numbered expert `linear` modules while keeping grouped expert execution intact. |
|
|
102
|
+
| 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. |
|
|
103
|
+
| 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. |
|
|
104
104
|
|
|
105
|
-
## Workflow Summary
|
|
105
|
+
## 🔁 Workflow Summary
|
|
106
106
|
|
|
107
107
|
Use `replace_fused_blocks()` for model families that Defuser can patch before load:
|
|
108
108
|
|
|
@@ -129,7 +129,7 @@ print(converted) # True when runtime defusion happened
|
|
|
129
129
|
|
|
130
130
|
`convert_model(model)` also preserves meta-device construction for supported meta-initialized models, so structural validation can run without materializing weights.
|
|
131
131
|
|
|
132
|
-
Use `filter` when only specific blocks should be defused
|
|
132
|
+
Use `filter` when only specific blocks should be defused 🎯:
|
|
133
133
|
|
|
134
134
|
```python
|
|
135
135
|
from defuser import convert_model
|
|
@@ -143,11 +143,11 @@ convert_model(
|
|
|
143
143
|
)
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
## Real Qwen3.5 MoE Example
|
|
146
|
+
## 🧪 Real Qwen3.5 MoE Example
|
|
147
147
|
|
|
148
148
|
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`.
|
|
149
149
|
|
|
150
|
-
### Fused Weights Before And After
|
|
150
|
+
### 🔬 Fused Weights Before And After
|
|
151
151
|
|
|
152
152
|
Before `convert_model(model)`:
|
|
153
153
|
|
|
@@ -175,7 +175,7 @@ After `convert_model(model)`:
|
|
|
175
175
|
+-----------------------------------------------------------------+--------------------------------------+
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
### Sample 1: Inspect The Conversion In Place
|
|
178
|
+
### 🧭 Sample 1: Inspect The Conversion In Place
|
|
179
179
|
|
|
180
180
|
```python
|
|
181
181
|
from defuser import convert_model
|
|
@@ -213,7 +213,7 @@ print(after[:6])
|
|
|
213
213
|
# ]
|
|
214
214
|
```
|
|
215
215
|
|
|
216
|
-
### Sample 2: Convert And Keep Using The Model Normally
|
|
216
|
+
### 🚀 Sample 2: Convert And Keep Using The Model Normally
|
|
217
217
|
|
|
218
218
|
```python
|
|
219
219
|
import torch
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align=center>
|
|
2
2
|
<img width="50%" alt="image" src="https://github.com/user-attachments/assets/f801617b-8959-474a-a565-6b8897e2fcbf" />
|
|
3
|
-
<h1 align="center">Defuser
|
|
3
|
+
<h1 align="center">Defuser 🔧</h1>
|
|
4
4
|
</div>
|
|
5
5
|
|
|
6
6
|
<p align="center">
|
|
@@ -11,47 +11,47 @@
|
|
|
11
11
|
<a href="https://huggingface.co/modelcloud/"><img src="https://img.shields.io/badge/🤗%20Hugging%20Face-ModelCloud-%23ff8811.svg"></a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
|
-
Defuser converts select Hugging Face Transformers `5.3.0+` fused or stacked MoE and MLP blocks back into plain, per-expert `nn.Linear` modules. It keeps the forward math intact while exposing individual projections again so quantizers, activation capture, debugging hooks, and checkpoint tooling can work against a simple module layout instead of fused expert tensors.
|
|
14
|
+
🧩 Defuser converts select Hugging Face Transformers `5.3.0+` fused or stacked MoE and MLP blocks back into plain, per-expert `nn.Linear` modules. It keeps the forward math intact while exposing individual projections again so quantizers, activation capture, debugging hooks, and checkpoint tooling can work against a simple module layout instead of fused expert tensors.
|
|
15
15
|
|
|
16
|
-
Defuser is designed and CI-tested for `transformers>=5.3.0`, and support is only offered for that version range.
|
|
16
|
+
✅ Defuser is designed and CI-tested for `transformers>=5.3.0`, and support is only offered for that version range.
|
|
17
17
|
|
|
18
|
-
## Purpose
|
|
18
|
+
## 🎯 Purpose
|
|
19
19
|
|
|
20
20
|
Defuser exists for cases where newer Transformers modeling code optimizes model structure in ways that are good for runtime, but harder for tooling that needs direct access to individual projections.
|
|
21
21
|
|
|
22
22
|
Depending on the model family, Defuser can:
|
|
23
23
|
|
|
24
|
-
- patch a supported model class before load so HF instantiates a defused block directly
|
|
25
|
-
- split fused tensors such as `gate_up_proj` into `gate_proj` + `up_proj`
|
|
26
|
-
- convert 3D expert tensors, including registered expert buffers, into numbered expert `nn.Linear` modules
|
|
27
|
-
- preserve the original fused math while presenting a naive module structure again
|
|
24
|
+
- 🧵 patch a supported model class before load so HF instantiates a defused block directly
|
|
25
|
+
- ✂️ split fused tensors such as `gate_up_proj` into `gate_proj` + `up_proj`
|
|
26
|
+
- 🧱 convert 3D expert tensors, including registered expert buffers, into numbered expert `nn.Linear` modules
|
|
27
|
+
- 🧮 preserve the original fused math while presenting a naive module structure again
|
|
28
28
|
|
|
29
|
-
Public API:
|
|
29
|
+
🛠️ Public API:
|
|
30
30
|
|
|
31
31
|
```python
|
|
32
32
|
from defuser import convert_model, replace_fused_blocks
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
- `replace_fused_blocks(model_type)` patches supported HF model classes before `from_pretrained()` or direct model construction.
|
|
36
|
-
- `convert_model(model, cleanup_original=False, 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
|
-
- 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
|
-
- Some model families appear in both support tables. Full models can be prepatched with `replace_fused_blocks(...)`, while standalone fused expert modules from those same families can still be runtime-defused with `convert_model(...)`.
|
|
35
|
+
- 🧰 `replace_fused_blocks(model_type)` patches supported HF model classes before `from_pretrained()` or direct model construction.
|
|
36
|
+
- 🔄 `convert_model(model, cleanup_original=False, 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
|
+
- 🧪 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
|
+
- 🧭 Some model families appear in both support tables. Full models can be prepatched with `replace_fused_blocks(...)`, while standalone fused expert modules from those same families can still be runtime-defused with `convert_model(...)`.
|
|
39
39
|
|
|
40
40
|
`filter` is an optional list of PCRE regex rules evaluated against full module paths such as `model.layers.0.mlp.experts`:
|
|
41
41
|
|
|
42
|
-
- `+:regex` explicitly includes matching candidate module paths
|
|
43
|
-
- `-:regex` explicitly excludes matching candidate module paths
|
|
44
|
-
- `regex` is shorthand for `+:regex`
|
|
45
|
-
- negative rules take priority over positive rules
|
|
46
|
-
- when `filter` is provided, a candidate module is defused only if it matches at least one positive rule and no negative rules
|
|
42
|
+
- ✅ `+:regex` explicitly includes matching candidate module paths
|
|
43
|
+
- 🚫 `-:regex` explicitly excludes matching candidate module paths
|
|
44
|
+
- ➕ `regex` is shorthand for `+:regex`
|
|
45
|
+
- 🛡️ negative rules take priority over positive rules
|
|
46
|
+
- 🎯 when `filter` is provided, a candidate module is defused only if it matches at least one positive rule and no negative rules
|
|
47
47
|
|
|
48
|
-
## Supported Models
|
|
48
|
+
## ✅ Supported Models
|
|
49
49
|
|
|
50
50
|
Defuser currently supports the following `transformers>=5.3.0` `model_type` values.
|
|
51
51
|
|
|
52
|
-
### `replace_fused_blocks(model_type)` before load
|
|
52
|
+
### 🧰 `replace_fused_blocks(model_type)` before load
|
|
53
53
|
|
|
54
|
-
| Model type | Defused op performed |
|
|
54
|
+
| Model type | Defused op performed ⚙️ |
|
|
55
55
|
| --- | --- |
|
|
56
56
|
| `glm4_moe` | Replaces `Glm4MoeMoE` with a defused per-expert linear MoE block. |
|
|
57
57
|
| `glm4_moe_lite` | Replaces `Glm4MoeLiteMoE` with a defused per-expert linear MoE block.|
|
|
@@ -62,21 +62,21 @@ Defuser currently supports the following `transformers>=5.3.0` `model_type` valu
|
|
|
62
62
|
| `qwen3_next` | Replaces `Qwen3NextSparseMoeBlock` with a defused per-expert linear MoE block. |
|
|
63
63
|
| `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()`. |
|
|
64
64
|
|
|
65
|
-
### `convert_model(model)` after load
|
|
65
|
+
### 🔄 `convert_model(model)` after load
|
|
66
66
|
|
|
67
|
-
| Pattern | Supported model types
|
|
68
|
-
| ---
|
|
69
|
-
| 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 or registered expert buffers into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
70
|
-
| Mixed sparse and shared experts | `deepseek_v3`, `glm_moe_dsa`, `qwen3_5_moe`, `qwen3_5_moe_text`
|
|
71
|
-
| Transposed or packed expert tensors | `gpt_oss`, `phimoe`
|
|
72
|
-
| Flattened expert layout | `dbrx`
|
|
73
|
-
| Batched expert-input execution | `llama4`
|
|
74
|
-
| Non-gated expert MLPs | `nemotron_h`
|
|
75
|
-
| Parallel expert blocks | `granitemoe`, `granitemoehybrid`, `granitemoeshared`, `jetmoe`
|
|
76
|
-
| Routed experts with identity experts | `longcat_flash`
|
|
77
|
-
| Fused dense `gate_up_proj` MLPs | `dia`, `glm`, `glm4`, `glm_image`, `glm_ocr`, `phi3`, `phi4_multimodal`, `zamba2`
|
|
67
|
+
| Pattern | Supported model types | Defused op performed ⚙️ |
|
|
68
|
+
| --- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
|
|
69
|
+
| 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`, `laguna`, `lfm2_moe`, `minimax`, `minimax_m2`, `olmoe`, `qwen3_vl_moe`, `solar_open` | Splits fused expert tensors or registered expert buffers into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
70
|
+
| Mixed sparse and shared experts | `deepseek_v3`, deepseek_v4`, `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. |
|
|
71
|
+
| 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. |
|
|
72
|
+
| Flattened expert layout | `dbrx` | Rebuilds the flattened DBRX expert FFN weights into numbered expert `gate_proj`, `up_proj`, and `down_proj` `nn.Linear` modules. |
|
|
73
|
+
| Batched expert-input execution | `llama4` | Runtime expert tensor defusion plus preservation of the llama4 batched expert-input execution contract. |
|
|
74
|
+
| Non-gated expert MLPs | `nemotron_h` | Converts routed expert tensors into numbered `up_proj` and `down_proj` `nn.Linear` modules for non-gated experts. |
|
|
75
|
+
| Parallel expert blocks | `granitemoe`, `granitemoehybrid`, `granitemoeshared`, `jetmoe` | Converts packed expert weight tensors into numbered expert `linear` modules while keeping grouped expert execution intact. |
|
|
76
|
+
| 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. |
|
|
77
|
+
| 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
78
|
|
|
79
|
-
## Workflow Summary
|
|
79
|
+
## 🔁 Workflow Summary
|
|
80
80
|
|
|
81
81
|
Use `replace_fused_blocks()` for model families that Defuser can patch before load:
|
|
82
82
|
|
|
@@ -103,7 +103,7 @@ print(converted) # True when runtime defusion happened
|
|
|
103
103
|
|
|
104
104
|
`convert_model(model)` also preserves meta-device construction for supported meta-initialized models, so structural validation can run without materializing weights.
|
|
105
105
|
|
|
106
|
-
Use `filter` when only specific blocks should be defused
|
|
106
|
+
Use `filter` when only specific blocks should be defused 🎯:
|
|
107
107
|
|
|
108
108
|
```python
|
|
109
109
|
from defuser import convert_model
|
|
@@ -117,11 +117,11 @@ convert_model(
|
|
|
117
117
|
)
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
## Real Qwen3.5 MoE Example
|
|
120
|
+
## 🧪 Real Qwen3.5 MoE Example
|
|
121
121
|
|
|
122
122
|
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`.
|
|
123
123
|
|
|
124
|
-
### Fused Weights Before And After
|
|
124
|
+
### 🔬 Fused Weights Before And After
|
|
125
125
|
|
|
126
126
|
Before `convert_model(model)`:
|
|
127
127
|
|
|
@@ -149,7 +149,7 @@ After `convert_model(model)`:
|
|
|
149
149
|
+-----------------------------------------------------------------+--------------------------------------+
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
-
### Sample 1: Inspect The Conversion In Place
|
|
152
|
+
### 🧭 Sample 1: Inspect The Conversion In Place
|
|
153
153
|
|
|
154
154
|
```python
|
|
155
155
|
from defuser import convert_model
|
|
@@ -187,7 +187,7 @@ print(after[:6])
|
|
|
187
187
|
# ]
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
### Sample 2: Convert And Keep Using The Model Normally
|
|
190
|
+
### 🚀 Sample 2: Convert And Keep Using The Model Normally
|
|
191
191
|
|
|
192
192
|
```python
|
|
193
193
|
import torch
|
|
@@ -75,7 +75,6 @@ def replace_fused_blocks(model_type: str) -> bool:
|
|
|
75
75
|
try:
|
|
76
76
|
orig_module = importlib.import_module(orig_module_path)
|
|
77
77
|
custom_module = importlib.import_module(custom_module_path)
|
|
78
|
-
print("orig_module", orig_module, orig_class_name)
|
|
79
78
|
# Validate class existence before patching
|
|
80
79
|
if not hasattr(orig_module, orig_class_name):
|
|
81
80
|
raise PatchError(f"Original class[{orig_class_name}] not found: {orig_module}")
|
|
@@ -25,6 +25,9 @@ MODEL_CONFIG = {
|
|
|
25
25
|
"deepseek_v3": {
|
|
26
26
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
27
27
|
},
|
|
28
|
+
"deepseek_v4": {
|
|
29
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
30
|
+
},
|
|
28
31
|
"dia": {
|
|
29
32
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
30
33
|
},
|
|
@@ -195,6 +198,9 @@ MODEL_CONFIG = {
|
|
|
195
198
|
"jetmoe": {
|
|
196
199
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
197
200
|
},
|
|
201
|
+
"laguna": {
|
|
202
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
203
|
+
},
|
|
198
204
|
"llama4": {
|
|
199
205
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
200
206
|
PATCH.EXPERTS_DEFUSE: [
|
|
@@ -17,6 +17,7 @@ from transformers.models.glm4_moe_lite.modeling_glm4_moe_lite import (
|
|
|
17
17
|
)
|
|
18
18
|
from transformers.models.glm4v.configuration_glm4v import Glm4vConfig
|
|
19
19
|
from transformers.models.glm4v.modeling_glm4v import Glm4vForConditionalGeneration
|
|
20
|
+
from transformers.models.laguna.modeling_laguna import LagunaConfig, LagunaForCausalLM
|
|
20
21
|
from transformers.models.mixtral.configuration_mixtral import MixtralConfig
|
|
21
22
|
from transformers.models.mixtral.modeling_mixtral import MixtralForCausalLM, MixtralSparseMoeBlock
|
|
22
23
|
from transformers.models.qwen2_moe.modeling_qwen2_moe import (
|
|
@@ -255,6 +256,29 @@ def _tiny_gpt_oss_config():
|
|
|
255
256
|
)
|
|
256
257
|
|
|
257
258
|
|
|
259
|
+
def _tiny_laguna_config():
|
|
260
|
+
return LagunaConfig(
|
|
261
|
+
vocab_size=128,
|
|
262
|
+
hidden_size=64,
|
|
263
|
+
intermediate_size=128,
|
|
264
|
+
num_hidden_layers=2,
|
|
265
|
+
num_attention_heads=4,
|
|
266
|
+
num_attention_heads_per_layer=[4, 4],
|
|
267
|
+
num_key_value_heads=1,
|
|
268
|
+
head_dim=16,
|
|
269
|
+
num_experts=4,
|
|
270
|
+
num_experts_per_tok=2,
|
|
271
|
+
moe_intermediate_size=32,
|
|
272
|
+
shared_expert_intermediate_size=32,
|
|
273
|
+
mlp_layer_types=["dense", "sparse"],
|
|
274
|
+
layer_types=["full_attention", "full_attention"],
|
|
275
|
+
hidden_act="silu",
|
|
276
|
+
pad_token_id=0,
|
|
277
|
+
bos_token_id=1,
|
|
278
|
+
eos_token_id=2,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
|
|
258
282
|
def _tiny_llama4_config():
|
|
259
283
|
return Llama4Config(
|
|
260
284
|
text_config={
|
|
@@ -1173,6 +1197,61 @@ def test_gpt_oss_split_forward_matches_fused_math():
|
|
|
1173
1197
|
# The split experts path should exactly reproduce the original fused experts math.
|
|
1174
1198
|
torch.testing.assert_close(actual, expected)
|
|
1175
1199
|
|
|
1200
|
+
|
|
1201
|
+
def test_laguna():
|
|
1202
|
+
from transformers.models.laguna.modeling_laguna import LagunaExperts
|
|
1203
|
+
|
|
1204
|
+
model = LagunaForCausalLM(_tiny_laguna_config())
|
|
1205
|
+
assert model.config.model_type == "laguna"
|
|
1206
|
+
|
|
1207
|
+
original_moe_block = model.model.layers[1].mlp
|
|
1208
|
+
experts = original_moe_block.experts
|
|
1209
|
+
assert isinstance(experts, LagunaExperts)
|
|
1210
|
+
|
|
1211
|
+
hidden_dim = experts.gate_up_proj.shape[-1]
|
|
1212
|
+
intermediate_dim = experts.gate_up_proj.shape[1] // 2
|
|
1213
|
+
|
|
1214
|
+
expected_gate = experts.gate_up_proj[0, :intermediate_dim, :hidden_dim].contiguous().clone()
|
|
1215
|
+
expected_up = experts.gate_up_proj[0, intermediate_dim:, :hidden_dim].contiguous().clone()
|
|
1216
|
+
expected_down = experts.down_proj[0, :hidden_dim, :intermediate_dim].contiguous().clone()
|
|
1217
|
+
|
|
1218
|
+
converted = convert_model(model, cleanup_original=False, max_layers=2)
|
|
1219
|
+
assert converted
|
|
1220
|
+
|
|
1221
|
+
experts = model.model.layers[1].mlp.experts
|
|
1222
|
+
_assert_unfused_expert_module(experts)
|
|
1223
|
+
expert0 = getattr(experts, "0")
|
|
1224
|
+
|
|
1225
|
+
materialize_model(model.model.layers[1])
|
|
1226
|
+
|
|
1227
|
+
torch.testing.assert_close(expert0.gate_proj.weight, expected_gate)
|
|
1228
|
+
torch.testing.assert_close(expert0.up_proj.weight, expected_up)
|
|
1229
|
+
torch.testing.assert_close(expert0.down_proj.weight, expected_down)
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
def test_laguna_split_forward_matches_fused_math():
|
|
1233
|
+
model = LagunaForCausalLM(_tiny_laguna_config())
|
|
1234
|
+
fused_experts = model.model.layers[1].mlp.experts
|
|
1235
|
+
|
|
1236
|
+
hidden_states = torch.randn(5, model.config.hidden_size, dtype=torch.float32)
|
|
1237
|
+
top_k_index = torch.zeros((hidden_states.size(0), 1), dtype=torch.long)
|
|
1238
|
+
top_k_weights = torch.ones((hidden_states.size(0), 1), dtype=hidden_states.dtype)
|
|
1239
|
+
|
|
1240
|
+
with torch.no_grad():
|
|
1241
|
+
expected = fused_experts(hidden_states, top_k_index, top_k_weights)
|
|
1242
|
+
|
|
1243
|
+
converted = convert_model(model, cleanup_original=False, max_layers=2)
|
|
1244
|
+
assert converted
|
|
1245
|
+
|
|
1246
|
+
split_experts = model.model.layers[1].mlp.experts
|
|
1247
|
+
_assert_unfused_expert_module(split_experts)
|
|
1248
|
+
materialize_model(model.model.layers[1])
|
|
1249
|
+
with torch.no_grad():
|
|
1250
|
+
actual = split_experts(hidden_states, top_k_index, top_k_weights)
|
|
1251
|
+
|
|
1252
|
+
torch.testing.assert_close(actual, expected)
|
|
1253
|
+
|
|
1254
|
+
|
|
1176
1255
|
def test_llama4():
|
|
1177
1256
|
from transformers.models.llama4.modeling_llama4 import Llama4TextMoe
|
|
1178
1257
|
|
|
@@ -167,12 +167,32 @@ def _build_model_config(case: dict):
|
|
|
167
167
|
elif model_type == "glm_moe_dsa":
|
|
168
168
|
config.mlp_layer_types = ["sparse"] * config.num_hidden_layers
|
|
169
169
|
elif model_type == "granitemoehybrid":
|
|
170
|
-
|
|
170
|
+
# Keep this meta-structure test on the attention path. The mamba path
|
|
171
|
+
# lazy-loads optional hub kernels during construction, which is outside
|
|
172
|
+
# the Defuser behavior being validated here.
|
|
173
|
+
config.layer_types = ["attention", "attention"]
|
|
171
174
|
config.shared_intermediate_size = 64
|
|
172
175
|
config.mamba_n_heads = 8
|
|
176
|
+
elif model_type == "jamba":
|
|
177
|
+
# Keep this meta-structure test on the attention path. The mamba path
|
|
178
|
+
# lazy-loads optional hub kernels during construction, which is outside
|
|
179
|
+
# the Defuser behavior being validated here.
|
|
180
|
+
config.attn_layer_period = 1
|
|
181
|
+
config.attn_layer_offset = 0
|
|
182
|
+
config.expert_layer_period = 1
|
|
183
|
+
config.expert_layer_offset = 0
|
|
173
184
|
elif model_type == "lfm2_moe":
|
|
174
185
|
config.layer_types = ["full_attention", "short_conv"]
|
|
175
186
|
config.num_dense_layers = 0
|
|
187
|
+
elif model_type == "laguna":
|
|
188
|
+
config.layer_types = ["full_attention"] * config.num_hidden_layers
|
|
189
|
+
config.mlp_layer_types = ["dense"] + ["sparse"] * (config.num_hidden_layers - 1)
|
|
190
|
+
config.num_attention_heads_per_layer = [config.num_attention_heads] * config.num_hidden_layers
|
|
191
|
+
elif model_type == "nemotron_h":
|
|
192
|
+
# Keep this meta-structure test on MoE blocks. Mamba blocks lazy-load
|
|
193
|
+
# optional hub kernels during construction, which is outside the
|
|
194
|
+
# Defuser behavior being validated here.
|
|
195
|
+
config.layers_block_type = ["moe"] * config.num_hidden_layers
|
|
176
196
|
elif model_type == "qwen3_omni_moe":
|
|
177
197
|
config.enable_audio_output = True
|
|
178
198
|
config.talker_config.spatial_merge_size = 2
|
|
@@ -188,6 +208,12 @@ def _build_model_config(case: dict):
|
|
|
188
208
|
num_key_value_heads=1,
|
|
189
209
|
head_dim=16,
|
|
190
210
|
)
|
|
211
|
+
elif model_type == "zamba2":
|
|
212
|
+
# Zamba2 always constructs Mamba layers, even for hybrid blocks. Keep
|
|
213
|
+
# the optional hub kernels disabled while preserving hybrid MLP targets.
|
|
214
|
+
config.use_mamba_kernels = False
|
|
215
|
+
config.layers_block_type = ["hybrid"] * config.num_hidden_layers
|
|
216
|
+
config.hybrid_layer_ids = list(range(config.num_hidden_layers))
|
|
191
217
|
|
|
192
218
|
return config
|
|
193
219
|
|
|
@@ -305,6 +331,17 @@ META_MODEL_CASES = [
|
|
|
305
331
|
"validator": "experts",
|
|
306
332
|
"min_targets": 2,
|
|
307
333
|
},
|
|
334
|
+
{
|
|
335
|
+
"model_type": "deepseek_v4",
|
|
336
|
+
"mode": "convert",
|
|
337
|
+
"model_module": "transformers.models.deepseek_v4.modeling_deepseek_v4",
|
|
338
|
+
"model_class": "DeepseekV4ForCausalLM",
|
|
339
|
+
"config_module": "transformers.models.deepseek_v4.configuration_deepseek_v4",
|
|
340
|
+
"config_class": "DeepseekV4Config",
|
|
341
|
+
"target_class_paths": ("transformers.models.deepseek_v4.modeling_deepseek_v4.DeepseekV4Experts",),
|
|
342
|
+
"validator": "experts",
|
|
343
|
+
"min_targets": 2,
|
|
344
|
+
},
|
|
308
345
|
{
|
|
309
346
|
"model_type": "dia",
|
|
310
347
|
"mode": "convert",
|
|
@@ -537,6 +574,16 @@ META_MODEL_CASES = [
|
|
|
537
574
|
"validator": "parallel",
|
|
538
575
|
"min_targets": 4,
|
|
539
576
|
},
|
|
577
|
+
{
|
|
578
|
+
"model_type": "laguna",
|
|
579
|
+
"mode": "convert",
|
|
580
|
+
"model_module": "transformers.models.laguna.modeling_laguna",
|
|
581
|
+
"model_class": "LagunaForCausalLM",
|
|
582
|
+
"config_module": "transformers.models.laguna.configuration_laguna",
|
|
583
|
+
"config_class": "LagunaConfig",
|
|
584
|
+
"target_class_paths": ("transformers.models.laguna.modeling_laguna.LagunaExperts",),
|
|
585
|
+
"validator": "experts",
|
|
586
|
+
},
|
|
540
587
|
{
|
|
541
588
|
"model_type": "lfm2_moe",
|
|
542
589
|
"mode": "convert",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|