mllm icon indicating copy to clipboard operation
mllm copied to clipboard

转换模型 转换词汇表是否支持Mixtral-8x7B OpenMoE Qwen1.5-MoE等MoE模型

Open Lucyliu1234 opened this issue 2 months ago • 6 comments

使用官方提供的MiniCPM MoE模型的mllm文件和词汇表进行推理结果正常,使用官方工具自己转换得到MiniCPM MoE模型的mllm文件和词汇表进行推理结果也正常,但使用官方工具自己转换得到Qwen1.5-MoE模型的mllm文件和词汇表进行推理得到无意义的字符。感谢!

Lucyliu1234 avatar Oct 11 '25 02:10 Lucyliu1234

  1. 请问您的运行环境是什么?是 X86 / Arm, Linux / Android / Mac
  2. 请问您使用的 MLLM 版本是 V1 还是 V2?
  3. 请问您的 Qwen1.5-MoE 的 modeling 文件是怎么书写的?

chenghuaWang avatar Oct 11 '25 06:10 chenghuaWang

运行环境为linux 使用的版本为V1 我检查了一下 modeling 文件,发现有一些问题,现在正在参考官方的 modeling 文件进行修改。官方文件是 Python (.py) 形式,所以不能直接使用。请问在将其改写成 .hpp形式 时,有哪些实用的技巧吗?我刚开始学习这方面的知识,所以感觉有点困难,总是容易出错,非常感谢!

  1. 请问您的运行环境是什么?是 X86 / Arm, Linux / Android / Mac
  2. 请问您使用的 MLLM 版本是 V1 还是 V2?
  3. 请问您的 Qwen1.5-MoE 的 modeling 文件是怎么书写的?

Lucyliu1234 avatar Oct 12 '25 10:10 Lucyliu1234

  1. 建议对照着 Py 的 modeling 文件编写,可以和 torch 逐层对精度。
  2. 如果您不是很依赖于 v1 的功能,如 MoE 的优化。建议用 v2 来写,体验会好不少。

chenghuaWang avatar Oct 12 '25 12:10 chenghuaWang

  1. 建议对照着 Py 的 modeling 文件编写,可以和 torch 逐层对精度。
  2. 如果您不是很依赖于 v1 的功能,如 MoE 的优化。建议用 v2 来写,体验会好不少。

由于我想要使用edgemoe的功能,使用edgemoe推理Qwen1.5 MoE 所以我仍采用了v1版本进行了代码的编写,编写过程中参照推理的相关文件。由于想要快速完成一个可正常运行的版本,所以暂时省去了MBM的相关代码,按需加载专家。但是经详细对照后,MiniCPM MoE推理结果仍是无意义的字符。 具体改动如下: mllm-main/examples/demo_qwen_moe_mbm.cpp 与MiniCPM MoE版本类似 改了对应文件的路径和这个语句 QWenMoEConfig config(tokens_limit, "2.7B"); 由于省去了MBM相关代码所以setting文件在后续没有使用。configuration文件是参照官方文件写的参数。modeling文件的主要改动如下: 1.专家类增加了共享专家机制,使用norm_topk_prob(官方文件默认为false)判断路由部分是否需要归一化

class Qwen2Moe final : public Module {
public:
    Qwen2Moe() = default;
    Qwen2Moe(const QWenMoEConfig &config, const QWenMoENameConfig &names, const std::string &base_name) {
        ...
        norm_topk_prob = config.norm_topk_prob;

        // 添加共享专家机制
        shared_expert = Qwen2MoeMLP(config.hidden_size, config.shared_expert_intermediate_size, names,
                                    base_name + ".shared_expert.");
        shared_expert_gate = Linear(config.hidden_size, 1, false, base_name + ".shared_expert_gate");
        sigmoid = Sigmoid(base_name + "sigmoid");
    }

    std::vector<Tensor> Forward(std::vector<Tensor> inputs, std::vector<std::any> args) override {
        auto hidden_states = inputs[0];
        // 处理batch
        ...

        // ===== 共享专家部分 =====
        // 检查共享专家是否已加载
        if (!shared_expert.loaded()) {
            shared_expert.load();
        }
        auto shared_expert_output = shared_expert({hidden_states})[0];
        auto shared_gate_logits = shared_expert_gate(hidden_states);
        // 使用sigmoid激活
        shared_gate_logits = sigmoid(shared_gate_logits);
        shared_expert_output = shared_expert_output * shared_gate_logits;

        // ===== 路由部分 =====
        auto scores = gate(hidden_states); //  1, batch*seq, 1, num_experts
        scores = softmax(scores);
        auto experts_w_i = Tensor::topk(scores, num_experts_per_tok, DIMENSION);
        auto expert_weights = experts_w_i[0];                            //  1, batch*seq, 1, k
        auto expert_indices = experts_w_i[1];                            //  1, batch*seq, 1, k
        expert_indices = expert_indices.view(-1, 1, 1, -1);              // 1, 1, 1, k* batch*seq
        // 按照Python版本的归一化逻辑
        if (norm_topk_prob) {
            expert_weights = expert_weights / expert_weights.sum(DIMENSION); //  1, batch*seq, 1, k
        }
        expert_weights = expert_weights.view(-1, -1, 1, 1);              // 1, k* batch*seq, 1, 1
        auto idxs = expert_indices.argsort();                            // 1, 1, 1, k* batch*seq
        auto tokens_per_expert = expert_indices.bincount();

        // ==== 专家推理 ====
        auto expert_cache = moe_infer(hidden_states, tokens_per_expert, expert_weights, idxs);

        // ==== 合并 ====
        expert_cache = expert_cache + shared_expert_output;
        
        return {expert_cache};
    }

    Tensor moe_infer(Tensor &hidden_states, Tensor &tokens_per_expert, Tensor &expert_weights, Tensor &idxs) {
      ...
    }
    ...
};

2.decoder类中未使用深度缩放

class Qwen2MoeDecoderLayer final : public Module {
...
    std::vector<Tensor> Forward(std::vector<Tensor> inputs, std::vector<std::any> args) override {
        auto hidden_states = inputs[0];
        
        // Self Attention
        auto residual = hidden_states;
        hidden_states = input_layernorm(hidden_states);
        hidden_states = self_atten({hidden_states, hidden_states, hidden_states}, args)[0];
        hidden_states = residual + hidden_states;
        
        // MLP/MoE
        residual = hidden_states;
        hidden_states = post_attention_layernorm(hidden_states);
        hidden_states = moe({hidden_states}, args)[0];
        hidden_states = residual + hidden_states;
        
        return {hidden_states};
    }
    ...
};

3.Qwen2MoeForCausalLM类中也没有使用深度缩放

class Qwen2MoeForCausalLM final : public Module {
...
    std::vector<Tensor> Forward(std::vector<Tensor> inputs, std::vector<std::any> args) override {
        return do_forward(inputs, args);
    }

    std::vector<Tensor> do_forward(std::vector<Tensor> inputs, std::vector<std::any>) {
        auto x = embedding(inputs[0]);
        auto outputs = model({x})[0];
        auto last_hidden = outputs.clip({}, {}, {outputs.sequence() - 1, outputs.sequence()}, {});
        auto lm_head_tensor = lm_head();
        auto logits = Tensor::mm(last_hidden, lm_head_tensor.transpose(Chl::SEQUENCE, Chl::DIMENSION));
        return {logits};
    }
...
};

请问有什么问题吗?https://github.com/huggingface/transformers/blob/main/src/transformers/models/qwen2_moe/modeling_qwen2_moe.py

这个是官方的modeling文件链接。 如果有需要,我也可以上传相关代码供参考。非常感谢!

Lucyliu1234 avatar Oct 16 '25 01:10 Lucyliu1234

cc @yirongjie

chenghuaWang avatar Oct 16 '25 07:10 chenghuaWang

cc @yirongjie 你们过去有遇到过这种问题吗?如果遇到过原因是哪些呢?我可以再排查一下

Lucyliu1234 avatar Oct 16 '25 07:10 Lucyliu1234