MoE朴素.h// MoE朴素.h —— MoE混合专家层计算声明// 用途实现每层 FFN 部分——256 个专家选 top-8 激活 共享专家// 说明MoE 是 Qwen3.6-35B-A3B 的核心稀疏架构每 token 只计算 81 个专家// 激活参数仅 ~3B总参数 35B这就是「小显存跑大模型」的根本原因#pragmaonce// 引入基础类型浮点/向量#include公共/基础定义.h// 引入权重加载权重视图#include模型/权重加载.h// 引入路由路由条目/路由选择#include内核/MoE/路由.h// MoE层参数一个 MoE 层所需的全部权重与维度常量// 说明指向权重视图由调用方从 权重存储 绑定本结构不持有权重数据structMoE层参数{// 路由权重二维const权重视图*路由门控nullptr;// [输入维 2048 → 专家数 256]ffn_gate_inp// 专家权重三维[输入维, 中间维, 专家数]const权重视图*专家上投影nullptr;// [2048 → 512 × 256]ffn_up_expsconst权重视图*专家门控投影nullptr;// [2048 → 512 × 256]ffn_gate_expsconst权重视图*专家下投影nullptr;// [512 → 2048 × 256]ffn_down_exps// 共享专家权重二维const权重视图*共享上投影nullptr;// [2048 → 512]ffn_up_shexpconst权重视图*共享门控投影nullptr;// [2048 → 512]ffn_gate_shexpconst权重视图*共享下投影nullptr;// [512 → 2048]ffn_down_shexpconst权重视图*共享门控标量nullptr;// [2048 → 1]ffn_gate_inp_shexp// 维度常量size_t 输入维0;// 2048size_t 中间维0;// 512专家 FFN 中间维size_t 专家数0;// 256size_t 激活专家数0;// 8top-k};// MoE朴素单 token 的 MoE 前向计算// 参数输入 f32 输入向量长度 输入维参数 层权重与维度// 路由结果 预先算好的专家选择可传 路由选择 的输出// 输出 f32 输出向量长度 输入维// 说明计算链// 1. 对每个激活专家 eh SiLU(W_up_e·x) ⊙ (W_gate_e·x)out_e W_down_e·h// 2. 累加输出 权重_e × out_e// 3. 共享专家h_s SiLU(W_up_s·x) ⊙ (W_gate_s·x)out_s W_down_s·h_s// 4. 门控门 σ(W_门控标量·x)输出 门 × out_svoidMoE朴素(const浮点*输入,constMoE层参数参数,const向量路由条目路由结果,浮点*输出);MoE朴素.cpp// MoE朴素.cpp —— MoE混合专家层计算实现// 用途每层 FFN 部分——256 个专家选 top-8 激活 共享专家// 数学// 专家 eSwiGLUh_e SiLU(W_up,e · x) ⊙ (W_gate,e · x)// out_e W_down,e · h_e// 路由p softmax(W_gate_inp · x)选 top-8权重归一化// 输出 Σ_{e∈top8} 权重_e · out_e 门 · out_s共享专家带 sigmoid 门控// 说明专家权重是三维量化张量 [内维, 中间维, 专家数]q4_k/q5_k/q6_k// 第 e 个专家的矩阵起点 数据 e × 每专家字节数#include内核/MoE/MoE朴素.h// 引入反量化块级反量化#include内核/反量化/反量化基础.h// 引入张量表块尺寸#include模型/张量表.h// 引入 GEMV共享专家二维矩阵乘#include内核/GEMV/GEMV朴素.h// 引入激活函数SiLU/Sigmoid#include内核/归一化/激活函数.h// 引入错误处理参数校验#include公共/错误处理.h// 专家切片 GEMV对三维量化专家权重取第 e 个专家做矩阵乘// 说明三维形状 [行, 列, 专家数]dimension[0]行 是连续轴// 第 e 个专家矩阵 [行, 列]行×列 个元素数据起点 数据 e×每专家字节数// 输出[行] Σ_列 专家[行][列] × 输入[列]// 参数行 专家矩阵的行数输出维列 列数输入维staticvoid专家GEMV(const权重视图权重,size_t 专家索引,size_t 行,size_t 列,constfloat*输入,float*输出){// 每元素字节数按量化类型constsize_t 每元素字节static_castsize_t(每元素字节数(权重.信息-类型));// 每专家字节数 行 × 列 × 每元素字节constsize_t 每专家字节数行*列*每元素字节;// 专家偏移constsize_t 专家偏移专家索引*每专家字节数;// 专家数据起点constuint8_t*专家数据权重.数据专家偏移;// 块尺寸constsize_t 每块元素块元素数(权重.信息-类型);constsize_t 每块字节块字节数(权重.信息-类型);// 每行字节数行内连续存储constsize_t 每行字节(列/每块元素)*每块字节;// GEMV外层行、内层列连续访问for(size_t 行号0;行号行;行号){constuint8_t*行数据专家数据行号*每行字节;长浮点 累加0.0;for(size_t 块号0;块号列/每块元素;块号){// 反量化当前块到临时缓冲浮点 缓冲[256];反量化块到浮点(权重.信息-类型,行数据块号*每块字节,缓冲,每块元素);for(size_t i0;i每块元素;i){累加static_cast长浮点(缓冲[i])*static_cast长浮点(输入[块号*每块元素i]);}}输出[行号]static_cast浮点(累加);}}// MoE朴素单 token 的 MoE 前向计算voidMoE朴素(const浮点*输入,constMoE层参数参数,const向量路由条目路由结果,浮点*输出){// 防御关键权重必须绑定if(!参数.路由门控||!参数.专家上投影||!参数.专家门控投影||!参数.专家下投影){抛出运行错误(MoE朴素路由/专家权重未绑定);}// 输出清零累加起点std::fill(输出,输出参数.输入维,0.0f);// 临时缓冲向量浮点中间(参数.中间维);向量浮点门控(参数.中间维);向量浮点专家输出(参数.输入维);// —— 第 1 步对每个激活专家做 SwiGLU 并累加 ——for(const路由条目条目:路由结果){constsize_t e条目.专家索引;const浮点 权重条目.权重;// h SiLU(W_up_e·x) ⊙ (W_gate_e·x)// up 投影输入 2048 → 中间 512专家矩阵 [512, 2048]专家GEMV(*参数.专家上投影,e,参数.中间维,参数.输入维,输入,中间.data());// gate 投影输入 2048 → 中间 512专家GEMV(*参数.专家门控投影,e,参数.中间维,参数.输入维,输入,门控.data());// SiLU(up) ⊙ gatefor(size_t i0;i参数.中间维;i){中间[i]激活SiLU(中间[i])*门控[i];}// out_e W_down_e·h中间 512 → 输入 2048专家矩阵 [2048, 512]专家GEMV(*参数.专家下投影,e,参数.输入维,参数.中间维,中间.data(),专家输出.data());// 累加输出 权重_e × out_efor(size_t i0;i参数.输入维;i){输出[i]权重*专家输出[i];}}// —— 第 2 步共享专家每 token 都算——if(参数.共享上投影参数.共享门控投影参数.共享下投影){// 共享专家是二维权重 [内维, 中间维]用 GEMV朴素// h_s SiLU(W_up_s·x) ⊙ (W_gate_s·x)GEMV朴素(*参数.共享上投影,输入,中间.data());GEMV朴素(*参数.共享门控投影,输入,门控.data());for(size_t i0;i参数.中间维;i){中间[i]激活SiLU(中间[i])*门控[i];}// out_s W_down_s·h_s中间 512 → 输入 2048GEMV朴素(*参数.共享下投影,中间.data(),专家输出.data());// 门 σ(W_门控标量·x)标量// 说明共享门控标量是 一维 [输入维] 张量不是二维直接点积浮点 门1.0f;if(参数.共享门控标量){// 一维 f32 权重ffn_gate_inp_shexp.weight 实测类型 f32const浮点*门控权重reinterpret_castconst浮点*(参数.共享门控标量-数据);长浮点 门值0.0;for(size_t i0;i参数.输入维;i){门值static_cast长浮点(门控权重[i])*static_cast长浮点(输入[i]);}门激活Sigmoid(static_cast浮点(门值));}// 输出 门 × out_sfor(size_t i0;i参数.输入维;i){输出[i]门*专家输出[i];}}}