MOE路由:路由(logits: top-k/8)

路由.h

cpp 复制代码
// 路由.h ------ MoE 专家路由选择声明
// 用途:从路由门控输出(256 维 logits)选择 top-8 专家并计算权重
// 说明:路由是 MoE 的核心决策------每个 token 只激活一小部分专家(8/256),
//       权重 = softmax 后 top-k 的重新归一化

#pragma once

// 引入基础类型(浮点/无符号/向量)
#include "公共/基础定义.h"

// 路由条目:一个被选中的专家及其权重
struct 路由条目 {
    // 专家索引:被选中的专家编号(0..专家数-1)
    无符号 专家索引 = 0;
    // 权重:该专家输出的加权系数(top-k 归一化后,总和为 1)
    浮点 权重 = 0.0f;
};

// Softmax朴素:数值稳定的 softmax(先减最大值再指数)
// 公式:softmax(z)_i = e^{z_i - max(z)} / Σ_j e^{z_j - max(z)}
// 参数:输入 = logits 数组;输出 = 概率数组(可与输入同一缓冲);数量 = 元素个数
void Softmax朴素(const float* 输入, float* 输出, size_t 数量);

// 路由选择:从门控输出选 top-k 专家并归一化权重
// 参数:门控输出 = 路由 logits(长度 = 专家数);专家数 = 全部专家个数(256);
//       激活数 = 要选择的专家个数(8)
// 返回:按权重降序的路由条目列表(长度 = 激活数),权重总和为 1
// 步骤:1. softmax → 2. 选 top-k(部分选择)→ 3. top-k 权重重新归一化(和为 1)
向量<路由条目> 路由选择(const float* 门控输出, size_t 专家数, size_t 激活数);

路由.cpp

cpp 复制代码
// 路由.cpp ------ MoE 专家路由选择实现
// 用途:从路由门控输出(256 维 logits)选择 top-8 专家并计算归一化权重
// 说明:路由决策是 MoE 的核心------每个 token 只激活一小部分专家(8/256),
//       其余专家权重不参与计算(稀疏计算,这是 MoE 效率的关键)
// 数学:
//   p = softmax(W_gate · x)          ------ 路由概率分布(256 维)
//   选 top-k(k=8):保留概率最大的 8 个专家
//   权重重新归一化:w_e = p_e / Σ_{e∈top8} p_e  ------ 保证权重和为 1

#include "内核/MoE/路由.h"

// 引入算法库(部分排序)
#include <algorithm>
// 引入标准头:数学函数(指数/最大)
#include <cmath>
// 引入错误处理(参数校验)
#include "公共/错误处理.h"

// Softmax朴素:数值稳定的 softmax(先减最大值再指数)
// 说明:减最大值保证 exp 不溢出(e^{大数} 会变 inf,减 max 后指数 ≤1)
void Softmax朴素(const float* 输入, float* 输出, size_t 数量) {
    // 防御:数量必须 ≥1
    if (数量 == 0) {
        抛出运行错误("Softmax朴素:数量必须大于 0");
    }
    // 找最大值(数值稳定:减最大值后再指数)
    float 最大值 = 输入[0];
    for (size_t i = 1; i < 数量; ++i) {
        if (输入[i] > 最大值) {
            最大值 = 输入[i];
        }
    }
    // 指数并求和
    float 总和 = 0.0f;
    for (size_t i = 0; i < 数量; ++i) {
        输出[i] = std::exp(输入[i] - 最大值);
        总和 += 输出[i];
    }
    // 归一化(总和可能为 0 仅当全部输入为 -inf,防御除零)
    if (总和 > 0.0f) {
        for (size_t i = 0; i < 数量; ++i) {
            输出[i] /= 总和;
        }
    } else {
        // 全部 -inf 的退化情况:均匀分布(防御)
        for (size_t i = 0; i < 数量; ++i) {
            输出[i] = 1.0f / static_cast<float>(数量);
        }
    }
}

// 路由选择:从门控输出选 top-k 专家并归一化权重
// 步骤:1. softmax → 2. 选 top-k(部分选择)→ 3. top-k 权重重新归一化(和为 1)
向量<路由条目> 路由选择(const float* 门控输出, size_t 专家数, size_t 激活数) {
    // 防御:参数合法
    if (专家数 == 0 || 激活数 == 0) {
        抛出运行错误("路由选择:专家数与激活数必须大于 0");
    }
    // 激活数不能超过专家总数
    const size_t 有效激活数 = std::min(激活数, 专家数);

    // 步骤 1:softmax 得到路由概率
    向量<浮点> 概率(专家数);
    Softmax朴素(门控输出, 概率.data(), 专家数);

    // 步骤 2:选 top-k(按概率降序取前 k 个专家的索引)
    // 用部分排序:构造 (概率, 索引) 对,排序后取前 k
    向量<std::pair<浮点, size_t>> 候选(专家数);
    for (size_t i = 0; i < 专家数; ++i) {
        候选[i] = {概率[i], i};
    }
    // 降序部分排序(最大的 有效激活数 个在前)
    std::partial_sort(候选.begin(), 候选.begin() + 有效激活数, 候选.end(),
                      [](const auto& a, const auto& b) { return a.first > b.first; });

    // 步骤 3:top-k 权重重新归一化(和为 1)
    浮点 权重总和 = 0.0f;
    for (size_t i = 0; i < 有效激活数; ++i) {
        权重总和 += 候选[i].first;
    }
    // 防御:权重总和为 0(全 -inf 退化)时均匀分配
    const 浮点 归一化系数 = (权重总和 > 0.0f) ? 1.0f / 权重总和 : 1.0f / static_cast<浮点>(有效激活数);

    // 构建返回列表(按权重降序)
    向量<路由条目> 结果(有效激活数);
    for (size_t i = 0; i < 有效激活数; ++i) {
        结果[i].专家索引 = static_cast<无符号>(候选[i].second);
        结果[i].权重 = 候选[i].first * 归一化系数;
    }
    return 结果;
}
相关推荐
Shockang2 小时前
AI Slop 治理实战
人工智能
Mr数据杨3 小时前
医学影像分类实战复盘 从 Kaggle 竞赛到可落地建模流程
人工智能·数据分析·kaggle竞赛
顶点多余4 小时前
那些在算法中适合巩固的知识点---1
java·前端·算法
AI情绪识别开源5 小时前
检信 ALLEMOTION OS 加密打包可执行程序 — 全面测试报告版本: v1.3功能测试 / 性能测试 /
开发语言·数据结构·人工智能·功能测试
ZGIAI5 小时前
ZGI 迭代节点:批量资料的逐项处理
人工智能·架构
ZGIAI5 小时前
ZGI 知识检索:让业务回答有据可查
人工智能·架构
Asize5 小时前
框架的说明书是写给 AI 看的:我用 Next.js 搭了个博客
人工智能·代码规范·next.js
2601_955662465 小时前
AI 配音工具 7 款实测:短视频、影视解说、小说推文音质横向对比
人工智能·音视频·语音识别·视频
AI创界者5 小时前
PinkCherry-MiniMax-H3 全能AI视频整合包:8G显存开箱即用,支持首尾帧/超分补帧/自动提示词
人工智能·aigc