Skip to content

Commit c687cda

Browse files
model: support MiMo-V2-Flash (#18328)
* mimov2: convert ok * rename mimov2 --> mimo2 * fix conversion * runnable not incorrect * use sink * add_sliding_window_pattern * add swa and per-layer n_head_kv * correct params * somewhat working * correct gating func * nits * mimo2: wire RMS eps + MoE bias + converter guards * add co-author Co-authored-by: Aaryan-Kapoor <Aaryan-Kapoor@users.noreply.github.com> * use add_rope_freq_base_swa --------- Co-authored-by: Aaryan Kapoor <aaryankapoor2006@gmail.com> Co-authored-by: Aaryan-Kapoor <Aaryan-Kapoor@users.noreply.github.com>
1 parent 6555420 commit c687cda

11 files changed

Lines changed: 324 additions & 4 deletions

convert_hf_to_gguf.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7362,6 +7362,90 @@ def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None):
73627362
return super().modify_tensors(data_torch, name, bid)
73637363

73647364

7365+
@ModelBase.register("MiMoV2FlashForCausalLM")
7366+
class MimoV2Model(TextModel):
7367+
model_arch = gguf.MODEL_ARCH.MIMO2
7368+
7369+
def set_gguf_parameters(self):
7370+
super().set_gguf_parameters()
7371+
7372+
assert self.hparams["swa_head_dim"] == self.hparams["head_dim"]
7373+
assert self.hparams["swa_num_attention_heads"] == self.hparams["num_attention_heads"]
7374+
assert self.hparams["swa_v_head_dim"] == self.hparams["v_head_dim"]
7375+
assert self.hparams["topk_method"] == "noaux_tc"
7376+
7377+
n_head_kv = self.hparams["num_key_value_heads"]
7378+
n_head_kv_swa = self.hparams["swa_num_key_value_heads"]
7379+
n_head_kv_arr = [n_head_kv_swa if use_swa == 1 else n_head_kv for use_swa in self.hparams["hybrid_layer_pattern"]]
7380+
self.gguf_writer.add_head_count_kv(n_head_kv_arr)
7381+
7382+
self.gguf_writer.add_sliding_window(self.hparams["sliding_window"])
7383+
self.gguf_writer.add_sliding_window_pattern(self.hparams["hybrid_layer_pattern"])
7384+
self.gguf_writer.add_rope_freq_base_swa(self.hparams["swa_rope_theta"])
7385+
self.gguf_writer.add_value_length(self.hparams["v_head_dim"])
7386+
self.gguf_writer.add_expert_count(self.hparams["n_routed_experts"])
7387+
self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"])
7388+
7389+
rope_dim = int(self.hparams["head_dim"] * self.hparams["partial_rotary_factor"])
7390+
self.gguf_writer.add_rope_dimension_count(rope_dim)
7391+
7392+
self.gguf_writer.add_layer_norm_rms_eps(self.hparams.get("layernorm_epsilon", 1e-5))
7393+
7394+
_experts: list[dict[str, Tensor]] | None = None
7395+
7396+
def modify_tensors(self, data_torch, name, bid):
7397+
if name.endswith("e_score_correction_bias"):
7398+
name = name.replace("e_score_correction_bias", "e_score_correction.bias")
7399+
7400+
if "attention_sink" in name and not name.endswith(".weight"):
7401+
name += ".weight"
7402+
7403+
# TODO: mimo v2 does not indicate the number of next-token-prediction layers, therefore we cannot do the same way as GLM4_MOE
7404+
if "model.mtp." in name:
7405+
return []
7406+
7407+
# process the experts separately
7408+
if name.find("mlp.experts") != -1:
7409+
n_experts = self.hparams["n_routed_experts"]
7410+
assert bid is not None
7411+
7412+
if self._experts is None:
7413+
self._experts = [{} for _ in range(self.block_count)]
7414+
7415+
self._experts[bid][name] = data_torch
7416+
7417+
if len(self._experts[bid]) >= n_experts * 3:
7418+
tensors: list[tuple[str, Tensor]] = []
7419+
7420+
# merge the experts into a single 3d tensor
7421+
for w_name in ["gate_proj", "up_proj", "down_proj"]:
7422+
datas: list[Tensor] = []
7423+
7424+
for xid in range(n_experts):
7425+
ename_to_retrieve = f"model.layers.{bid}.mlp.experts.{xid}.{w_name}.weight"
7426+
datas.append(self._experts[bid][ename_to_retrieve])
7427+
del self._experts[bid][ename_to_retrieve]
7428+
7429+
data_torch = torch.stack(datas, dim=0)
7430+
merged_name = f"model.layers.{bid}.mlp.experts.{w_name}.weight"
7431+
new_name = self.map_tensor_name(merged_name)
7432+
tensors.append((new_name, data_torch))
7433+
7434+
return tensors
7435+
else:
7436+
return []
7437+
return [(self.map_tensor_name(name), data_torch)]
7438+
7439+
def prepare_tensors(self):
7440+
super().prepare_tensors()
7441+
7442+
if self._experts is not None:
7443+
# flatten `list[dict[str, Tensor]]` into `list[str]`
7444+
experts = [k for d in self._experts for k in d.keys()]
7445+
if len(experts) > 0:
7446+
raise ValueError(f"Unprocessed experts: {experts}")
7447+
7448+
73657449
@ModelBase.register("PanguEmbeddedForCausalLM")
73667450
class PanguEmbeddedModel(TextModel):
73677451
model_arch = gguf.MODEL_ARCH.PANGU_EMBED

gguf-py/gguf/constants.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ class MODEL_ARCH(IntEnum):
449449
RND1 = auto()
450450
PANGU_EMBED = auto()
451451
MISTRAL3 = auto()
452+
MIMO2 = auto()
452453
LLAMA_EMBED = auto()
453454

454455

@@ -845,6 +846,7 @@ class MODEL_TENSOR(IntEnum):
845846
MODEL_ARCH.RND1: "rnd1",
846847
MODEL_ARCH.PANGU_EMBED: "pangu-embedded",
847848
MODEL_ARCH.MISTRAL3: "mistral3",
849+
MODEL_ARCH.MIMO2: "mimo2",
848850
MODEL_ARCH.LLAMA_EMBED: "llama-embed",
849851
}
850852

@@ -3198,6 +3200,26 @@ class MODEL_TENSOR(IntEnum):
31983200
MODEL_TENSOR.FFN_DOWN_EXP,
31993201
MODEL_TENSOR.FFN_UP_EXP,
32003202
],
3203+
MODEL_ARCH.MIMO2: [
3204+
MODEL_TENSOR.TOKEN_EMBD,
3205+
MODEL_TENSOR.OUTPUT_NORM,
3206+
MODEL_TENSOR.OUTPUT,
3207+
MODEL_TENSOR.ATTN_NORM,
3208+
MODEL_TENSOR.ATTN_Q,
3209+
MODEL_TENSOR.ATTN_K,
3210+
MODEL_TENSOR.ATTN_V,
3211+
MODEL_TENSOR.ATTN_SINKS,
3212+
MODEL_TENSOR.ATTN_OUT,
3213+
MODEL_TENSOR.FFN_NORM,
3214+
MODEL_TENSOR.FFN_GATE,
3215+
MODEL_TENSOR.FFN_DOWN,
3216+
MODEL_TENSOR.FFN_UP,
3217+
MODEL_TENSOR.FFN_GATE_INP,
3218+
MODEL_TENSOR.FFN_GATE_EXP,
3219+
MODEL_TENSOR.FFN_DOWN_EXP,
3220+
MODEL_TENSOR.FFN_UP_EXP,
3221+
MODEL_TENSOR.FFN_EXP_PROBS_B,
3222+
],
32013223
MODEL_ARCH.LLAMA_EMBED: [
32023224
MODEL_TENSOR.TOKEN_EMBD,
32033225
MODEL_TENSOR.OUTPUT_NORM,
@@ -3217,7 +3239,7 @@ class MODEL_TENSOR(IntEnum):
32173239
MODEL_TENSOR.FFN_GATE_EXP,
32183240
MODEL_TENSOR.FFN_DOWN_EXP,
32193241
MODEL_TENSOR.FFN_UP_EXP,
3220-
]
3242+
],
32213243
# TODO
32223244
}
32233245

gguf-py/gguf/tensor_mapping.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ class TensorNameMap:
320320

321321
MODEL_TENSOR.ATTN_SINKS: (
322322
"model.layers.{bid}.self_attn.sinks", # openai-moe
323+
"model.layers.{bid}.self_attn.attention_sink_bias", # mimov2
323324
),
324325

325326
MODEL_TENSOR.ATTN_GATE: (

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ add_library(llama
8888
models/llama-iswa.cpp
8989
models/llama.cpp
9090
models/mamba.cpp
91+
models/mimo2-iswa.cpp
9192
models/minicpm3.cpp
9293
models/minimax-m2.cpp
9394
models/modern-bert.cpp

src/llama-arch.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ static const std::map<llm_arch, const char *> LLM_ARCH_NAMES = {
115115
{ LLM_ARCH_RND1, "rnd1" },
116116
{ LLM_ARCH_PANGU_EMBED, "pangu-embedded" },
117117
{ LLM_ARCH_MISTRAL3, "mistral3" },
118+
{ LLM_ARCH_MIMO2, "mimo2" },
118119
{ LLM_ARCH_LLAMA_EMBED, "llama-embed" },
119120
{ LLM_ARCH_UNKNOWN, "(unknown)" },
120121
};
@@ -2190,6 +2191,27 @@ static std::set<llm_tensor> llm_get_tensor_names(llm_arch arch) {
21902191
LLM_TENSOR_VISEXP_FFN_DOWN,
21912192
LLM_TENSOR_VISEXP_FFN_UP,
21922193
};
2194+
case LLM_ARCH_MIMO2:
2195+
return {
2196+
LLM_TENSOR_TOKEN_EMBD,
2197+
LLM_TENSOR_OUTPUT_NORM,
2198+
LLM_TENSOR_OUTPUT,
2199+
LLM_TENSOR_ATTN_NORM,
2200+
LLM_TENSOR_ATTN_Q,
2201+
LLM_TENSOR_ATTN_K,
2202+
LLM_TENSOR_ATTN_V,
2203+
LLM_TENSOR_ATTN_SINKS,
2204+
LLM_TENSOR_ATTN_OUT,
2205+
LLM_TENSOR_FFN_NORM,
2206+
LLM_TENSOR_FFN_GATE,
2207+
LLM_TENSOR_FFN_DOWN,
2208+
LLM_TENSOR_FFN_UP,
2209+
LLM_TENSOR_FFN_GATE_INP,
2210+
LLM_TENSOR_FFN_GATE_EXPS,
2211+
LLM_TENSOR_FFN_DOWN_EXPS,
2212+
LLM_TENSOR_FFN_UP_EXPS,
2213+
LLM_TENSOR_FFN_EXP_PROBS_B,
2214+
};
21932215
case LLM_ARCH_GPTJ:
21942216
case LLM_ARCH_UNKNOWN:
21952217
return {

src/llama-arch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ enum llm_arch {
119119
LLM_ARCH_RND1,
120120
LLM_ARCH_PANGU_EMBED,
121121
LLM_ARCH_MISTRAL3,
122+
LLM_ARCH_MIMO2,
122123
LLM_ARCH_LLAMA_EMBED,
123124
LLM_ARCH_UNKNOWN,
124125
};

src/llama-hparams.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,11 @@ struct llama_hparams {
123123
llama_swa_type swa_type = LLAMA_SWA_TYPE_NONE;
124124
// the size of the sliding window (0 - no SWA)
125125
uint32_t n_swa = 0;
126-
// if swa_layers[il] == true, then layer il is SWA
127-
// if swa_layers[il] == false, then layer il is dense (i.e. non-SWA)
126+
// if swa_layers[il] == 1, then layer il is SWA
127+
// if swa_layers[il] == 0, then layer il is dense (i.e. non-SWA)
128128
// by default, all layers are dense
129-
std::array<bool, LLAMA_MAX_LAYERS> swa_layers;
129+
// note: using uint32_t type for compatibility reason
130+
std::array<uint32_t, LLAMA_MAX_LAYERS> swa_layers;
130131

131132
// for State Space Models
132133
uint32_t ssm_d_conv = 0;

src/llama-model.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ const char * llm_type_name(llm_type type) {
130130
case LLM_TYPE_230B_A10B: return "230B.A10B";
131131
case LLM_TYPE_235B_A22B: return "235B.A22B";
132132
case LLM_TYPE_300B_A47B: return "300B.A47B";
133+
case LLM_TYPE_310B_A15B: return "310B.A15B";
133134
case LLM_TYPE_355B_A32B: return "355B.A32B";
134135
case LLM_TYPE_E2B: return "E2B";
135136
case LLM_TYPE_E4B: return "E4B";
@@ -2339,6 +2340,22 @@ void llama_model::load_hparams(llama_model_loader & ml) {
23392340
default: type = LLM_TYPE_UNKNOWN;
23402341
}
23412342
} break;
2343+
case LLM_ARCH_MIMO2:
2344+
{
2345+
ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps);
2346+
2347+
hparams.swa_type = LLAMA_SWA_TYPE_STANDARD;
2348+
2349+
ml.get_key(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, hparams.n_ff_exp);
2350+
ml.get_key(LLM_KV_ATTENTION_SLIDING_WINDOW, hparams.n_swa);
2351+
ml.get_key(LLM_KV_ROPE_FREQ_BASE_SWA, hparams.rope_freq_base_train_swa);
2352+
ml.get_key_or_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, hparams.swa_layers, hparams.n_layer);
2353+
2354+
switch (hparams.n_layer) {
2355+
case 48: type = LLM_TYPE_310B_A15B; break;
2356+
default: type = LLM_TYPE_UNKNOWN;
2357+
}
2358+
} break;
23422359
default: throw std::runtime_error("unsupported model architecture");
23432360
}
23442361

@@ -6648,6 +6665,44 @@ bool llama_model::load_tensors(llama_model_loader & ml) {
66486665
layer.ffn_down_shexp = create_tensor(tn(LLM_TENSOR_FFN_DOWN_SHEXP, "weight", i), { hparams.n_ff_shexp, n_embd }, 0);
66496666
}
66506667
} break;
6668+
case LLM_ARCH_MIMO2:
6669+
{
6670+
tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0);
6671+
6672+
// output
6673+
output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0);
6674+
output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, 0);
6675+
6676+
for (int i = 0; i < n_layer; ++i) {
6677+
auto & layer = layers[i];
6678+
uint32_t n_embd_k_gqa = hparams.n_embd_k_gqa(i);
6679+
uint32_t n_embd_v_gqa = hparams.n_embd_v_gqa(i);
6680+
uint32_t n_head = hparams.n_head(i);
6681+
6682+
layer.wq = create_tensor(tn(LLM_TENSOR_ATTN_Q, "weight", i), { n_embd, n_embd_head_k * n_head }, 0);
6683+
layer.wk = create_tensor(tn(LLM_TENSOR_ATTN_K, "weight", i), { n_embd, n_embd_k_gqa }, 0);
6684+
layer.wv = create_tensor(tn(LLM_TENSOR_ATTN_V, "weight", i), { n_embd, n_embd_v_gqa }, 0);
6685+
layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), { n_embd_head_v * n_head, n_embd }, 0);
6686+
6687+
layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), {n_embd}, 0);
6688+
layer.attn_sinks = create_tensor(tn(LLM_TENSOR_ATTN_SINKS, "weight", i), {n_head}, TENSOR_NOT_REQUIRED);
6689+
6690+
layer.ffn_norm = create_tensor(tn(LLM_TENSOR_FFN_NORM, "weight", i), {n_embd}, 0);
6691+
6692+
// non-MoE branch
6693+
layer.ffn_gate = create_tensor(tn(LLM_TENSOR_FFN_GATE, "weight", i), {n_embd, n_ff}, TENSOR_NOT_REQUIRED);
6694+
layer.ffn_down = create_tensor(tn(LLM_TENSOR_FFN_DOWN, "weight", i), { n_ff, n_embd}, TENSOR_NOT_REQUIRED);
6695+
layer.ffn_up = create_tensor(tn(LLM_TENSOR_FFN_UP, "weight", i), {n_embd, n_ff}, TENSOR_NOT_REQUIRED);
6696+
6697+
// MoE branch
6698+
int64_t n_ff_exp = hparams.n_ff_exp;
6699+
layer.ffn_gate_inp = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "weight", i), {n_embd, n_expert}, TENSOR_NOT_REQUIRED);
6700+
layer.ffn_gate_exps = create_tensor(tn(LLM_TENSOR_FFN_GATE_EXPS, "weight", i), {n_embd, n_ff_exp, n_expert}, TENSOR_NOT_REQUIRED);
6701+
layer.ffn_down_exps = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "weight", i), {n_ff_exp, n_embd, n_expert}, TENSOR_NOT_REQUIRED);
6702+
layer.ffn_up_exps = create_tensor(tn(LLM_TENSOR_FFN_UP_EXPS, "weight", i), {n_embd, n_ff_exp, n_expert}, TENSOR_NOT_REQUIRED);
6703+
layer.ffn_exp_probs_b = create_tensor(tn(LLM_TENSOR_FFN_EXP_PROBS_B, "bias", i), {n_expert}, TENSOR_NOT_REQUIRED);
6704+
}
6705+
} break;
66516706
default:
66526707
throw std::runtime_error("unknown architecture");
66536708
}
@@ -7710,6 +7765,10 @@ ggml_cgraph * llama_model::build_graph(const llm_graph_params & params) const {
77107765
{
77117766
llm = std::make_unique<llm_build_mistral3>(*this, params);
77127767
} break;
7768+
case LLM_ARCH_MIMO2:
7769+
{
7770+
llm = std::make_unique<llm_build_mimo2_iswa>(*this, params);
7771+
} break;
77137772
default:
77147773
GGML_ABORT("fatal error");
77157774
}
@@ -7940,6 +7999,7 @@ llama_rope_type llama_model_rope_type(const llama_model * model) {
79407999
case LLM_ARCH_PANGU_EMBED:
79418000
case LLM_ARCH_AFMOE:
79428001
case LLM_ARCH_QWEN3NEXT:
8002+
case LLM_ARCH_MIMO2:
79438003
return LLAMA_ROPE_TYPE_NEOX;
79448004

79458005
case LLM_ARCH_QWEN2VL:

src/llama-model.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ enum llm_type {
123123
LLM_TYPE_230B_A10B, // Minimax M2
124124
LLM_TYPE_235B_A22B,
125125
LLM_TYPE_300B_A47B, // Ernie MoE big
126+
LLM_TYPE_310B_A15B, // /MiMo-V2-Flash
126127
LLM_TYPE_355B_A32B, // GLM-4.5
127128
LLM_TYPE_E2B,
128129
LLM_TYPE_E4B,

0 commit comments

Comments
 (0)