Defuser 0.0.28__tar.gz → 0.0.29__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.28 → defuser-0.0.29}/Defuser.egg-info/PKG-INFO +2 -2
- {defuser-0.0.28 → defuser-0.0.29}/PKG-INFO +2 -2
- {defuser-0.0.28 → defuser-0.0.29}/README.md +1 -1
- {defuser-0.0.28 → defuser-0.0.29}/defuser/model_registry.py +30 -0
- {defuser-0.0.28 → defuser-0.0.29}/pyproject.toml +1 -1
- {defuser-0.0.28 → defuser-0.0.29}/tests/test_convert_model.py +208 -0
- {defuser-0.0.28 → defuser-0.0.29}/tests/test_meta_model_defusion.py +27 -0
- {defuser-0.0.28 → defuser-0.0.29}/tests/test_moe_experts_interface.py +12 -0
- {defuser-0.0.28 → defuser-0.0.29}/Defuser.egg-info/SOURCES.txt +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/Defuser.egg-info/dependency_links.txt +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/Defuser.egg-info/requires.txt +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/Defuser.egg-info/top_level.txt +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/LICENSE +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/__init__.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/checkpoint_ops.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/defuser.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/__init__.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/glm4v.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/model_patches.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/moe_experts_interface.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/replace_modules.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/runtime_defusion.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/unfused_moe/__init__.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/unfused_moe/common.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/unfused_moe/glm4_moe.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/unfused_moe/glm4_moe_lite.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/unfused_moe/mixtral.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/unfused_moe/qwen2_moe.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/unfused_moe/qwen3_moe.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/unfused_moe/qwen3_next.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/unfused_moe/qwen3_omni_moe.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/modeling/update_module.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/utils/__init__.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/utils/common.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/utils/device.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/utils/hf.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/defuser/utils/model.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/setup.cfg +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/setup.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/tests/test_candidate_coverage.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/tests/test_device_utils.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/tests/test_filter_rules.py +0 -0
- {defuser-0.0.28 → defuser-0.0.29}/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.29
|
|
4
4
|
Summary: Model defuser helper for HF Transformers.
|
|
5
5
|
Author-email: ModelCloud <qubitium@modelcloud.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -92,7 +92,7 @@ Defuser currently supports the following `transformers>=5.3.0` `model_type` valu
|
|
|
92
92
|
|
|
93
93
|
| Pattern | Supported model types | Defused op performed ⚙️ |
|
|
94
94
|
| --- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
|
|
95
|
-
| Standard routed expert tensors 🧱 | `deepseek_v2`, `diffusion_gemma`, `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`, `solar_open2` | Splits fused expert tensors or registered expert buffers into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
95
|
+
| Standard routed expert tensors 🧱 | `afmoe`, `bailing_hybrid`, `deepseek_v2`, `diffusion_gemma`, `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`, `solar_open2` | 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
96
|
| Mixed sparse and shared experts | `deepseek_v3`, `deepseek_v32`, `deepseek_v4`, `glm5_next`, `glm_moe_dsa`, `qwen3_5_moe`, `qwen3_5_moe_text`, `qwen4_exp`, `qwen4_exp_text` | Runtime expert tensor defusion for routed experts while preserving the model's shared-expert path. |
|
|
97
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
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. |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Defuser
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.29
|
|
4
4
|
Summary: Model defuser helper for HF Transformers.
|
|
5
5
|
Author-email: ModelCloud <qubitium@modelcloud.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -92,7 +92,7 @@ Defuser currently supports the following `transformers>=5.3.0` `model_type` valu
|
|
|
92
92
|
|
|
93
93
|
| Pattern | Supported model types | Defused op performed ⚙️ |
|
|
94
94
|
| --- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
|
|
95
|
-
| Standard routed expert tensors 🧱 | `deepseek_v2`, `diffusion_gemma`, `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`, `solar_open2` | Splits fused expert tensors or registered expert buffers into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
95
|
+
| Standard routed expert tensors 🧱 | `afmoe`, `bailing_hybrid`, `deepseek_v2`, `diffusion_gemma`, `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`, `solar_open2` | 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
96
|
| Mixed sparse and shared experts | `deepseek_v3`, `deepseek_v32`, `deepseek_v4`, `glm5_next`, `glm_moe_dsa`, `qwen3_5_moe`, `qwen3_5_moe_text`, `qwen4_exp`, `qwen4_exp_text` | Runtime expert tensor defusion for routed experts while preserving the model's shared-expert path. |
|
|
97
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
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. |
|
|
@@ -66,7 +66,7 @@ Defuser currently supports the following `transformers>=5.3.0` `model_type` valu
|
|
|
66
66
|
|
|
67
67
|
| Pattern | Supported model types | Defused op performed ⚙️ |
|
|
68
68
|
| --- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
|
|
69
|
-
| Standard routed expert tensors 🧱 | `deepseek_v2`, `diffusion_gemma`, `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`, `solar_open2` | Splits fused expert tensors or registered expert buffers into numbered expert `nn.Linear` modules with per-expert `gate_proj`, `up_proj`, and `down_proj`. |
|
|
69
|
+
| Standard routed expert tensors 🧱 | `afmoe`, `bailing_hybrid`, `deepseek_v2`, `diffusion_gemma`, `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`, `solar_open2` | 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
70
|
| Mixed sparse and shared experts | `deepseek_v3`, `deepseek_v32`, `deepseek_v4`, `glm5_next`, `glm_moe_dsa`, `qwen3_5_moe`, `qwen3_5_moe_text`, `qwen4_exp`, `qwen4_exp_text` | Runtime expert tensor defusion for routed experts while preserving the model's shared-expert path. |
|
|
71
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
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. |
|
|
@@ -16,9 +16,27 @@ class PATCH(str, Enum):
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
MODEL_CONFIG = {
|
|
19
|
+
"afmoe": {
|
|
20
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
21
|
+
PATCH.EXPERTS_DEFUSE: [
|
|
22
|
+
{
|
|
23
|
+
"module_class": "transformers.models.afmoe.modeling_afmoe.AfmoeExperts",
|
|
24
|
+
"forward_impl": "linear_loop",
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
},
|
|
19
28
|
"axk2": {
|
|
20
29
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
21
30
|
},
|
|
31
|
+
"bailing_hybrid": {
|
|
32
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
33
|
+
PATCH.EXPERTS_DEFUSE: [
|
|
34
|
+
{
|
|
35
|
+
"module_class": "transformers.models.bailing_hybrid.modeling_bailing_hybrid.BailingHybridExperts",
|
|
36
|
+
"forward_impl": "linear_loop",
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
},
|
|
22
40
|
"cohere2_moe": {
|
|
23
41
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
24
42
|
},
|
|
@@ -76,6 +94,18 @@ MODEL_CONFIG = {
|
|
|
76
94
|
"flex_olmo": {
|
|
77
95
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
78
96
|
},
|
|
97
|
+
"flint": {
|
|
98
|
+
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
99
|
+
PATCH.EXPERTS_DEFUSE: [
|
|
100
|
+
{
|
|
101
|
+
# Flint keeps gate/up and down projections as 3-D expert
|
|
102
|
+
# parameters. Expand them into per-expert Linear modules so
|
|
103
|
+
# GPTQModel can discover and quantize every routed expert.
|
|
104
|
+
"module_class": "transformers.models.flint.modeling_flint.FlintExperts",
|
|
105
|
+
"forward_impl": "linear_loop",
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
},
|
|
79
109
|
"glm": {
|
|
80
110
|
"min_transformers_version": MIN_SUPPORTED_TRANSFORMERS_VERSION,
|
|
81
111
|
},
|
|
@@ -9,6 +9,13 @@ import torch
|
|
|
9
9
|
from safetensors.torch import save_file
|
|
10
10
|
from torch import nn
|
|
11
11
|
from transformers.core_model_loading import WeightConverter
|
|
12
|
+
from transformers.models.afmoe.configuration_afmoe import AfmoeConfig
|
|
13
|
+
from transformers.models.afmoe.modeling_afmoe import AfmoeExperts, AfmoeForCausalLM
|
|
14
|
+
from transformers.models.bailing_hybrid.configuration_bailing_hybrid import BailingHybridConfig
|
|
15
|
+
from transformers.models.bailing_hybrid.modeling_bailing_hybrid import (
|
|
16
|
+
BailingHybridExperts,
|
|
17
|
+
BailingHybridForCausalLM,
|
|
18
|
+
)
|
|
12
19
|
from transformers.models.glm4_moe.modeling_glm4_moe import Glm4MoeConfig, Glm4MoeForCausalLM, Glm4MoeMoE
|
|
13
20
|
from transformers.models.glm4_moe_lite.configuration_glm4_moe_lite import Glm4MoeLiteConfig
|
|
14
21
|
from transformers.models.glm4_moe_lite.modeling_glm4_moe_lite import (
|
|
@@ -85,6 +92,53 @@ def _tiny_moe_config(config_cls, num_hidden_layers: int=1):
|
|
|
85
92
|
)
|
|
86
93
|
|
|
87
94
|
|
|
95
|
+
def _tiny_afmoe_config(num_hidden_layers: int = 1):
|
|
96
|
+
return AfmoeConfig(
|
|
97
|
+
num_hidden_layers=num_hidden_layers,
|
|
98
|
+
# Keep every layer on the MoE path so the routed experts are present.
|
|
99
|
+
num_dense_layers=0,
|
|
100
|
+
hidden_size=64,
|
|
101
|
+
intermediate_size=128,
|
|
102
|
+
moe_intermediate_size=32,
|
|
103
|
+
num_attention_heads=4,
|
|
104
|
+
num_key_value_heads=4,
|
|
105
|
+
head_dim=16,
|
|
106
|
+
num_experts=4,
|
|
107
|
+
num_experts_per_tok=2,
|
|
108
|
+
num_shared_experts=2,
|
|
109
|
+
vocab_size=128,
|
|
110
|
+
max_position_embeddings=64,
|
|
111
|
+
pad_token_id=0,
|
|
112
|
+
bos_token_id=1,
|
|
113
|
+
eos_token_id=2,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _tiny_bailing_hybrid_config(num_hidden_layers: int = 2):
|
|
118
|
+
return BailingHybridConfig(
|
|
119
|
+
num_hidden_layers=num_hidden_layers,
|
|
120
|
+
# Keep every layer on the MoE path so the routed experts are present.
|
|
121
|
+
first_k_dense_replace=0,
|
|
122
|
+
layer_types=(
|
|
123
|
+
["linear_attention", "full_attention"] * ((num_hidden_layers + 1) // 2)
|
|
124
|
+
)[:num_hidden_layers],
|
|
125
|
+
hidden_size=64,
|
|
126
|
+
intermediate_size=128,
|
|
127
|
+
moe_intermediate_size=32,
|
|
128
|
+
num_attention_heads=4,
|
|
129
|
+
num_key_value_heads=4,
|
|
130
|
+
head_dim=16,
|
|
131
|
+
num_local_experts=4,
|
|
132
|
+
num_experts_per_tok=2,
|
|
133
|
+
n_shared_experts=2,
|
|
134
|
+
vocab_size=128,
|
|
135
|
+
max_position_embeddings=64,
|
|
136
|
+
pad_token_id=0,
|
|
137
|
+
bos_token_id=1,
|
|
138
|
+
eos_token_id=2,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
88
142
|
def _tiny_qwen3_omni_config():
|
|
89
143
|
return Qwen3OmniMoeConfig(
|
|
90
144
|
initializer_range=0.02,
|
|
@@ -1251,6 +1305,160 @@ def test_gpt_oss_split_forward_matches_fused_math():
|
|
|
1251
1305
|
torch.testing.assert_close(actual, expected)
|
|
1252
1306
|
|
|
1253
1307
|
|
|
1308
|
+
def test_afmoe_registry_and_defusion():
|
|
1309
|
+
from defuser.modeling.moe_experts_interface import linear_loop_experts_forward
|
|
1310
|
+
|
|
1311
|
+
assert MODEL_CONFIG["afmoe"][PATCH.EXPERTS_DEFUSE] == [
|
|
1312
|
+
{
|
|
1313
|
+
"module_class": "transformers.models.afmoe.modeling_afmoe.AfmoeExperts",
|
|
1314
|
+
"forward_impl": "linear_loop",
|
|
1315
|
+
}
|
|
1316
|
+
]
|
|
1317
|
+
|
|
1318
|
+
model = AfmoeForCausalLM(_tiny_afmoe_config())
|
|
1319
|
+
assert model.config.model_type == "afmoe"
|
|
1320
|
+
|
|
1321
|
+
experts = model.model.layers[0].mlp.experts
|
|
1322
|
+
assert isinstance(experts, AfmoeExperts)
|
|
1323
|
+
num_experts = model.config.num_experts
|
|
1324
|
+
hidden_dim = model.config.hidden_size
|
|
1325
|
+
intermediate_dim = model.config.moe_intermediate_size
|
|
1326
|
+
assert tuple(experts.gate_up_proj.shape) == (num_experts, 2 * intermediate_dim, hidden_dim)
|
|
1327
|
+
assert tuple(experts.down_proj.shape) == (num_experts, hidden_dim, intermediate_dim)
|
|
1328
|
+
|
|
1329
|
+
expected_gate = experts.gate_up_proj[0, :intermediate_dim, :].clone()
|
|
1330
|
+
expected_up = experts.gate_up_proj[0, intermediate_dim:, :].clone()
|
|
1331
|
+
expected_down = experts.down_proj[0, :, :].clone()
|
|
1332
|
+
|
|
1333
|
+
converted = convert_model(model, cleanup_original=False, max_layers=1)
|
|
1334
|
+
assert converted
|
|
1335
|
+
|
|
1336
|
+
experts = model.model.layers[0].mlp.experts
|
|
1337
|
+
assert isinstance(experts, AfmoeExperts)
|
|
1338
|
+
assert not hasattr(experts, "gate_up_proj")
|
|
1339
|
+
assert not hasattr(experts, "down_proj")
|
|
1340
|
+
assert experts.forward.__func__ is linear_loop_experts_forward
|
|
1341
|
+
|
|
1342
|
+
containers = [name for name, _ in experts.named_children() if name.isdigit()]
|
|
1343
|
+
assert sorted(containers, key=int) == [str(idx) for idx in range(num_experts)]
|
|
1344
|
+
|
|
1345
|
+
expert0 = getattr(experts, "0")
|
|
1346
|
+
assert isinstance(expert0.gate_proj, nn.Linear)
|
|
1347
|
+
assert isinstance(expert0.up_proj, nn.Linear)
|
|
1348
|
+
assert isinstance(expert0.down_proj, nn.Linear)
|
|
1349
|
+
|
|
1350
|
+
torch.testing.assert_close(expert0.gate_proj.weight, expected_gate)
|
|
1351
|
+
torch.testing.assert_close(expert0.up_proj.weight, expected_up)
|
|
1352
|
+
torch.testing.assert_close(expert0.down_proj.weight, expected_down)
|
|
1353
|
+
|
|
1354
|
+
|
|
1355
|
+
def test_afmoe_split_forward_matches_fused_math():
|
|
1356
|
+
model = AfmoeForCausalLM(_tiny_afmoe_config())
|
|
1357
|
+
fused_experts = model.model.layers[0].mlp.experts
|
|
1358
|
+
assert isinstance(fused_experts, AfmoeExperts)
|
|
1359
|
+
|
|
1360
|
+
input_ids = torch.randint(0, model.config.vocab_size, (2, 7))
|
|
1361
|
+
hidden_states = torch.randn(5, model.config.hidden_size, dtype=torch.float32)
|
|
1362
|
+
top_k_index = torch.randint(0, model.config.num_experts, (hidden_states.size(0), 2))
|
|
1363
|
+
top_k_weights = torch.rand((hidden_states.size(0), 2), dtype=hidden_states.dtype)
|
|
1364
|
+
|
|
1365
|
+
with torch.no_grad():
|
|
1366
|
+
expected_experts = fused_experts(hidden_states, top_k_index, top_k_weights)
|
|
1367
|
+
expected_logits = model(input_ids).logits
|
|
1368
|
+
|
|
1369
|
+
converted = convert_model(model, cleanup_original=False, max_layers=1)
|
|
1370
|
+
assert converted
|
|
1371
|
+
|
|
1372
|
+
split_experts = model.model.layers[0].mlp.experts
|
|
1373
|
+
_assert_unfused_expert_module(split_experts)
|
|
1374
|
+
with torch.no_grad():
|
|
1375
|
+
actual_experts = split_experts(hidden_states, top_k_index, top_k_weights)
|
|
1376
|
+
actual_logits = model(input_ids).logits
|
|
1377
|
+
|
|
1378
|
+
# The defused linear_loop path should reproduce the original fused experts math.
|
|
1379
|
+
torch.testing.assert_close(actual_experts, expected_experts, atol=1e-5, rtol=1e-5)
|
|
1380
|
+
torch.testing.assert_close(actual_logits, expected_logits, atol=1e-5, rtol=1e-5)
|
|
1381
|
+
|
|
1382
|
+
|
|
1383
|
+
def test_bailing_hybrid_registry_and_defusion():
|
|
1384
|
+
from defuser.modeling.moe_experts_interface import linear_loop_experts_forward
|
|
1385
|
+
|
|
1386
|
+
assert MODEL_CONFIG["bailing_hybrid"][PATCH.EXPERTS_DEFUSE] == [
|
|
1387
|
+
{
|
|
1388
|
+
"module_class": (
|
|
1389
|
+
"transformers.models.bailing_hybrid.modeling_bailing_hybrid."
|
|
1390
|
+
"BailingHybridExperts"
|
|
1391
|
+
),
|
|
1392
|
+
"forward_impl": "linear_loop",
|
|
1393
|
+
}
|
|
1394
|
+
]
|
|
1395
|
+
|
|
1396
|
+
model = BailingHybridForCausalLM(_tiny_bailing_hybrid_config())
|
|
1397
|
+
assert model.config.model_type == "bailing_hybrid"
|
|
1398
|
+
|
|
1399
|
+
experts = model.model.layers[0].mlp.experts
|
|
1400
|
+
assert isinstance(experts, BailingHybridExperts)
|
|
1401
|
+
num_experts = model.config.num_local_experts
|
|
1402
|
+
hidden_dim = model.config.hidden_size
|
|
1403
|
+
intermediate_dim = model.config.moe_intermediate_size
|
|
1404
|
+
assert tuple(experts.gate_up_proj.shape) == (num_experts, 2 * intermediate_dim, hidden_dim)
|
|
1405
|
+
assert tuple(experts.down_proj.shape) == (num_experts, hidden_dim, intermediate_dim)
|
|
1406
|
+
|
|
1407
|
+
expected_gate = experts.gate_up_proj[0, :intermediate_dim, :].clone()
|
|
1408
|
+
expected_up = experts.gate_up_proj[0, intermediate_dim:, :].clone()
|
|
1409
|
+
expected_down = experts.down_proj[0, :, :].clone()
|
|
1410
|
+
|
|
1411
|
+
converted = convert_model(model, cleanup_original=False, max_layers=1)
|
|
1412
|
+
assert converted
|
|
1413
|
+
|
|
1414
|
+
experts = model.model.layers[0].mlp.experts
|
|
1415
|
+
assert isinstance(experts, BailingHybridExperts)
|
|
1416
|
+
assert not hasattr(experts, "gate_up_proj")
|
|
1417
|
+
assert not hasattr(experts, "down_proj")
|
|
1418
|
+
assert experts.forward.__func__ is linear_loop_experts_forward
|
|
1419
|
+
|
|
1420
|
+
containers = [name for name, _ in experts.named_children() if name.isdigit()]
|
|
1421
|
+
assert sorted(containers, key=int) == [str(idx) for idx in range(num_experts)]
|
|
1422
|
+
|
|
1423
|
+
expert0 = getattr(experts, "0")
|
|
1424
|
+
assert isinstance(expert0.gate_proj, nn.Linear)
|
|
1425
|
+
assert isinstance(expert0.up_proj, nn.Linear)
|
|
1426
|
+
assert isinstance(expert0.down_proj, nn.Linear)
|
|
1427
|
+
|
|
1428
|
+
torch.testing.assert_close(expert0.gate_proj.weight, expected_gate)
|
|
1429
|
+
torch.testing.assert_close(expert0.up_proj.weight, expected_up)
|
|
1430
|
+
torch.testing.assert_close(expert0.down_proj.weight, expected_down)
|
|
1431
|
+
|
|
1432
|
+
|
|
1433
|
+
def test_bailing_hybrid_split_forward_matches_fused_math():
|
|
1434
|
+
model = BailingHybridForCausalLM(_tiny_bailing_hybrid_config())
|
|
1435
|
+
fused_experts = model.model.layers[0].mlp.experts
|
|
1436
|
+
assert isinstance(fused_experts, BailingHybridExperts)
|
|
1437
|
+
|
|
1438
|
+
hidden_states = torch.randn(5, model.config.hidden_size, dtype=torch.float32)
|
|
1439
|
+
top_k_index = torch.randint(
|
|
1440
|
+
0, model.config.num_local_experts, (hidden_states.size(0), 2)
|
|
1441
|
+
)
|
|
1442
|
+
top_k_weights = torch.rand((hidden_states.size(0), 2), dtype=hidden_states.dtype)
|
|
1443
|
+
|
|
1444
|
+
with torch.no_grad():
|
|
1445
|
+
expected_experts = fused_experts(hidden_states, top_k_index, top_k_weights)
|
|
1446
|
+
|
|
1447
|
+
converted = convert_model(model, cleanup_original=False, max_layers=1)
|
|
1448
|
+
assert converted
|
|
1449
|
+
|
|
1450
|
+
split_experts = model.model.layers[0].mlp.experts
|
|
1451
|
+
_assert_unfused_expert_module(split_experts)
|
|
1452
|
+
with torch.no_grad():
|
|
1453
|
+
actual_experts = split_experts(hidden_states, top_k_index, top_k_weights)
|
|
1454
|
+
|
|
1455
|
+
# The defused linear_loop path should reproduce the original fused experts
|
|
1456
|
+
# math. The full decoder forward is not exercised here because
|
|
1457
|
+
# ``bailing_hybrid``'s delta-attention path calls ``causal_conv1d``, which
|
|
1458
|
+
# requires a CUDA device.
|
|
1459
|
+
torch.testing.assert_close(actual_experts, expected_experts, atol=1e-5, rtol=1e-5)
|
|
1460
|
+
|
|
1461
|
+
|
|
1254
1462
|
def test_laguna():
|
|
1255
1463
|
from transformers.models.laguna.modeling_laguna import LagunaExperts
|
|
1256
1464
|
|
|
@@ -197,6 +197,11 @@ def _build_model_config(case: dict):
|
|
|
197
197
|
config.ffn_config.ffn_act_fn = {"name": "silu"}
|
|
198
198
|
elif model_type == "deepseek_v3":
|
|
199
199
|
config.first_k_dense_replace = 0
|
|
200
|
+
elif model_type == "bailing_hybrid":
|
|
201
|
+
# The default config keeps the first layers dense, so the routed
|
|
202
|
+
# experts would not be constructed. Force every tiny layer onto MoE.
|
|
203
|
+
config.first_k_dense_replace = 0
|
|
204
|
+
config.mlp_layer_types = ["sparse"] * config.num_hidden_layers
|
|
200
205
|
elif model_type == "deepseek_v32":
|
|
201
206
|
config.mlp_layer_types = ["sparse"] * config.num_hidden_layers
|
|
202
207
|
elif model_type == "ernie4_5_vl_moe":
|
|
@@ -346,6 +351,28 @@ def _validate_defused_module(case: dict, module) -> None:
|
|
|
346
351
|
|
|
347
352
|
|
|
348
353
|
META_MODEL_CASES = [
|
|
354
|
+
{
|
|
355
|
+
"model_type": "afmoe",
|
|
356
|
+
"mode": "convert",
|
|
357
|
+
"model_module": "transformers.models.afmoe.modeling_afmoe",
|
|
358
|
+
"model_class": "AfmoeForCausalLM",
|
|
359
|
+
"config_module": "transformers.models.afmoe.configuration_afmoe",
|
|
360
|
+
"config_class": "AfmoeConfig",
|
|
361
|
+
"target_class_paths": ("transformers.models.afmoe.modeling_afmoe.AfmoeExperts",),
|
|
362
|
+
"validator": "experts",
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"model_type": "bailing_hybrid",
|
|
366
|
+
"mode": "convert",
|
|
367
|
+
"model_module": "transformers.models.bailing_hybrid.modeling_bailing_hybrid",
|
|
368
|
+
"model_class": "BailingHybridForCausalLM",
|
|
369
|
+
"config_module": "transformers.models.bailing_hybrid.configuration_bailing_hybrid",
|
|
370
|
+
"config_class": "BailingHybridConfig",
|
|
371
|
+
"target_class_paths": (
|
|
372
|
+
"transformers.models.bailing_hybrid.modeling_bailing_hybrid.BailingHybridExperts",
|
|
373
|
+
),
|
|
374
|
+
"validator": "experts",
|
|
375
|
+
},
|
|
349
376
|
{
|
|
350
377
|
"model_type": "axk2",
|
|
351
378
|
"mode": "convert",
|
|
@@ -9,6 +9,7 @@ import torch
|
|
|
9
9
|
import torch.nn as nn
|
|
10
10
|
|
|
11
11
|
from defuser.modeling.moe_experts_interface import _detect_expert_projections, _unfuse_experts_weights_inplace
|
|
12
|
+
from defuser.model_registry import MODEL_CONFIG, PATCH
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class _UnknownExpertsWithLossProperty(nn.Module):
|
|
@@ -60,3 +61,14 @@ def test_unfuse_experts_supports_registered_buffers() -> None:
|
|
|
60
61
|
torch.testing.assert_close(expert0.gate_proj.weight, expected_gate_proj)
|
|
61
62
|
torch.testing.assert_close(expert0.up_proj.weight, expected_up_proj)
|
|
62
63
|
torch.testing.assert_close(expert0.down_proj.weight, expected_down_proj)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_flint_registry_defuses_stacked_expert_weights() -> None:
|
|
67
|
+
specs = MODEL_CONFIG["flint"][PATCH.EXPERTS_DEFUSE]
|
|
68
|
+
|
|
69
|
+
assert specs == [
|
|
70
|
+
{
|
|
71
|
+
"module_class": "transformers.models.flint.modeling_flint.FlintExperts",
|
|
72
|
+
"forward_impl": "linear_loop",
|
|
73
|
+
}
|
|
74
|
+
]
|
|
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
|
|
File without changes
|