
在过去很长一段时间里大家评估 MoEMixture-of-Experts混合专家模型的安全性时注意力几乎都放在“专家是否泄露了训练数据”或“路由是否被越狱 prompt 干扰”上。很少有人认真质疑一个更基础的问题MoE 模型的稀疏激活到底是在帮助对抗鲁棒性还是在制造新的攻击面先说结论稀疏激活从来都不是天然的防御机制。路由器对输入分布的微小变化极度敏感而对抗样本恰恰是利用这种敏感性的专家。一个在干净数据上准确率很高的 MoE 模型面对 PGD 这类攻击时往往比同规模的稠密模型更容易崩溃。原因是它的决策边界不仅取决于主干网络还取决于“每个 token 被分给哪些专家”这一离散选择而这个选择是可微路径上最容易被噪声带偏的一环。本文解读的 Robust CurveMoE尝试用“模式连通性”Mode Connectivity把 MoE 的对抗防御问题重新做一遍。它听起来很学术但落地价值很直接不是只防 L∞也不是只防 L2而是在多种范数攻击下都保持可用并且不牺牲太多干净样本精度。下面我们从问题本身开始拆解它的设计思路并给出一条可落地的工程验证路径。1. 这篇文章真正要解决的问题如果只用一个词概括 CurveMoE 的出发点我会选“脆弱路由”Fragile Routing。1.1 MoE 模型的两层“攻击面”MoE 模型和稠密模型最大的区别在于它的前向计算被拆成了两个阶段路由决策门控网络读取输入通常是 token 的隐藏表示计算出每个专家Expert的得分然后取 Top-K 个专家参与计算。专家计算被选中的专家对输入做变换最后按路由权重融合输出。很多人以为“只有被选中的少数专家参与计算”意味着模型暴露给攻击者的范围更小。但从对抗攻击的角度看这反而把攻击目标变成了两个攻击主干网络这是所有深度学习模型都有的攻击面不特殊。攻击路由策略攻击者不需要彻底破坏专家只需要让噪声改变 Top-K 的选择顺序。比如让一个本该走安全专家路径的样本被路由到另一个专家或者让恶意样本在路由层获得异常高的权重。路由层的参数数量远小于主干网络但它的输出是一个离散的“选择”离散决策对输入扰动更加敏感。连续数值上的一点点偏移可能直接改变最终选择的专家集合。更麻烦的是MoE 的训练目标主要是“任务准确率 负载均衡”并没有把“路由决策的局部稳定性”纳入优化目标。这就导致了在干净样本上学的路由策略很容易在对抗样本上失灵。1.2 为什么不能用“普通对抗训练”糊弄过去最直观的做法是沿用标准对抗训练生成对抗样本然后让模型在这些样本上重新训练。这个思路在稠密模型上是有效的但到 MoE 这里会遇到三个实际问题第一路由崩塌。对抗训练会让样本分布变得更加集中和“尖刻”路由器为了保护自己倾向于把越来越多的输入路由到少数几个专家最终导致专家负载严重不均衡。你需要额外花大力气维持负载均衡损失否则训练过程中 MoE 层实际退化成稠密层。第二多范数覆盖不足。常见的攻击可以用 L∞、L2、L1 三种范数来约束扰动大小。如果只在 L∞ 球内做对抗训练模型面对 L2 或 L1 攻击时仍然可能是脆弱的。反过来也一样。论文标题里的 Multi-Norm 明确指出要防御的目标不是某一种攻击而是多种范数下的攻击。第三单一最优点过拟合。对抗训练本质上是在找“一个”能抵抗扰动的参数最优点。但 MoE 的损失地形比稠密模型复杂得多它同时包含主干参数、路由参数和专家参数。在这么高的维度上单一最优点很容易对训练时使用的攻击强度、攻击范数和生成方式过拟合。换一种攻击方式鲁棒精度立刻下降。CurveMoE 的核心判断是不要只找一个点而是找一条“低损失曲线”在这条曲线上做鲁棒优化。这个思路就是把 Mode Connectivity 从理论工具变成防御手段。1.3 什么样的读者应该关注这篇文章适合四类读者在负责 MoE 模型安全评估的算法工程师需要理解为什么路由是攻击面。想让 MoE 模型部署到真实业务如推荐、内容审核、多模态理解的工程团队需要一套可操作的鲁棒性验证方法。研究对抗防御的研究生想把 Mode Connectivity 引入自己的训练框架。对“为什么纯对抗训练在 MoE 上不够用”感到困惑的读者。2. 基础概念与核心原理2.1 Mode Connectivity两个最优解之间可能存在一条低损失路径Mode Connectivity 是深度学习理论里的一个重要现象。它说的是在神经网络的损失地形中两个独立训练得到的局部最优解往往可以通过一条“参数空间中的连续路径”连接起来并且这条路径上的每一个点都保持较低的损失值。这里需要区分两个概念线性插值直接在两个参数之间做线性加权即 $w(t) (1-t)w_1 t w_2$。通常这种简单插值会遇到高损失屏障因为神经网络的损失地形不是凸的。模式连通性说的是存在一条“曲线”路径不一定是直线连接两个最优解路径上的损失保持很低。实际使用中人们常用分段线性插值或贝塞尔曲线来参数化这条路径。类比一下两座山各有一个山谷两个山谷之间可能隔着一条陡峭的山脊。线性插值就是“直接从山脊上走过去”损失会飙高而模式连通性相当于发现了一条“穿山隧道”或“绕山路”一路走过去海拔都维持在较低水平。那这和对抗防御有什么关系对抗鲁棒性的一个经典观点是鲁棒模型倾向于落在损失地形的“平坦区域”。平坦意味着即使输入扰动造成特征偏移损失变化也不会剧烈。而 Mode Connectivity 提供了一种系统性地寻找平坦区域的方法如果两个不同的鲁棒最优点之间存在低损失连接路径那这条路径上的点尤其是路径中段的点往往具有更好的局部平滑性。CurveMoE 的思路就是把“沿曲线做优化”的思想从稠密网络推广到 MoE 架构中。2.2 多范数对抗攻击L∞、L2、L1 到底在衡量什么对抗攻击的核心是在“扰动足够小、人眼不可察觉”的前提下让模型出错。衡量扰动大小常用的三种方式范数含义攻击直觉典型攻击方法L∞所有维度中最大扰动量每个像素都能改一点点但幅度受限PGD-L∞L2扰动向量的欧氏长度总体能量受限允许个别维度扰动较大PGD-L2L1扰动向量所有维度的绝对值之和只允许少量维度被修改但可以改得明显稀疏攻击只做单一范数防御的主要问题是模型会把“鲁棒性”错误地学习成“对某种扰动模式的适应”。当攻击者切换范数后防御效果断崖式下跌。多范数防御的目标是找到一个对多种扰动几何都稳定的参数区域。2.3 CurveMoE 的命名拆解从论文标题看方法名可以拆成三部分Curve表示参数优化不是在一个点上进行而是沿着模式连通曲线进行。这里的“曲线”很可能借鉴了 Fast Geometric EnsemblingFGE或贝塞尔曲线连接的思想把鲁棒优化过程限制在低损失路径附近。MoE目标架构是混合专家模型优化对象包含路由器、共享层和多个专家网络。Multi-Norm鲁棒训练阶段同时考虑多种攻击范数而不是只对一个范数做对抗训练。需要提醒的是由于目前公开材料有限我们无法拿到论文的完整公式和实验配置。以下的介绍会区分“从标题和领域常识推导的设计逻辑”和“可确定的事实”。如果你正在做实验复现建议以官方代码和论文原文为准。3. 为什么“绕开单一最优点”对 MoE 特别重要3.1 MoE 的损失地形比稠密模型更粗糙稠密模型已经够难优化了MoE 更难。原因在于路由器的存在路由器输出权重是一个 softmax 分布但最终计算时用的是 top-k 掩码。这个“取 top-k”的操作在数学上是分段平滑的。分段意味着损失地形中存在天然的折痕和不连续梯度。专家是相对独立的计算单元。不同专家可能收敛到不同的函数子空间这让参数空间中的“最优区域”变成很多块而不是一个完整的盆地。负载均衡损失和任务损失之间需要平衡这让训练目标变成一个多目标优化问题任何一个单点的最优解都很容易被另一个损失项“惩罚”。在这种情况下找一个单一鲁棒最优点很容易陷入局部困难区域。但如果在多个最优点之间画一条低损失曲线曲线所经过的区域往往覆盖了多种“可接受的”参数配置模型的鲁棒性会更强。3.2 路由器的多范数敏感性我们用一个简单例子说明为什么路由对多范数攻击敏感。假设两个专家专家 A对 L∞ 扰动更鲁棒。专家 B对 L2 扰动更鲁棒。如果路由器在干净样本上稳定地把样本分配给 A那么面对 L2 攻击时即使 A 本身还扛得住路由器也可能因为输入扰动的变化把样本错分给 B。这就是“路由层放大器”现象即使每个专家的鲁棒性都不错路由决策的切换也会放大整体模型的脆弱性。CurveMoE 如果只更新专家参数而不给路由器做多范数鲁棒训练这个问题并不会消失。这也是为什么方法会同时覆盖整个 MoE 结构而不是只对某一层做防御。4. Robust CurveMoE 的核心设计拆解因为论文完整版本未公开我们只能从方法名和技术趋势做保守拆解。这里给出一个“最可能成立”的设计框架后续复现时可以用来对照。如果官方代码发布后细节不同应以官方为准。4.1 设计一用贝塞尔曲线参数化模式连接路径在多个模式即多个训练良好的 MoE 模型参数之间定义一条连续曲线。设两个端点参数为 $w_0$ 和 $w_m$可以构造一条贝塞尔曲线B(t) (1 - t)^2 * w0 2 * (1 - t) * t * w1 t^2 * wm ...其中 $w_1$ 是中间控制点或中间模型参数。曲线上的每一个点都可以作为模型的候选参数。训练时算法不是只更新端点而是会沿曲线采样多个点并在这些点上计算鲁棒损失。这样做的效果是让模型在整个低损失通道上都保持鲁棒而不是只在单一最优点上鲁棒。在 MoE 的复杂损失地形中曲线可以绕过路由器造成的折痕找到更平滑的鲁棒区域。4.2 设计二多范数扰动采样对抗训练的标准做法是“先攻击再防御”也就是用当前模型生成对抗样本然后最小化模型在这些对抗样本上的损失。多范数版本需要在同一个训练批次中并行生成多种范数下的对抗样本对每个样本分别生成 L∞、L2甚至 L1约束下的对抗扰动。计算它们产生的损失权重是超参数可以在训练过程中调整。优化目标是最小化所有范数下的鲁棒损失之和。这里真正容易踩坑的地方是不同范数对抗样本的“难度”不一样。L2 攻击生成对抗样本的计算量比 L∞ 大很多如果直接平均所有范数损失模型可能会被 L2 攻击带偏干净精度下降得厉害。比较稳妥的做法是先用较小的权重引入 L2再逐步增加。4.3 设计三路由感知的鲁棒约束MoE 场景下不能只对最终输出做对抗训练还要对路由决策本身做稳定性约束。一个可行的思路是计算干净样本的路由分布 $p_{clean} \text{Gate}(x)$。计算对抗样本的路由分布 $p_{adv} \text{Gate}(x \delta)$。在损失中加入路由一致性项例如 $\text{KL}(p_{clean} || p_{adv})$ 或 $\text{KL}(p_{adv} || p_{clean})$。这样做的好处是直接约束了“扰动能改变路由选择”的问题是针对 MoE 特有脆弱性的定制防御。如果只做最终输出损失路由器依然可能在内部发生大幅漂移只是输出恰好碰巧正确下一次遇到更强的攻击立刻崩盘。4.4 与标准 MoE 对抗训练的对比方面标准 MoE 对抗训练Robust CurveMoE 思路优化对象单一参数最优点一条低损失曲线上的多个点攻击范数通常只选一种多范数联合优化路由鲁棒性间接优化易被忽略显式加入路由一致约束参数空间探索在局部邻域内移动沿着模式连接路径移动主要风险对单一攻击过拟合路由崩塌计算成本高端点选择影响大5. 环境准备与实验前置条件下面开始进入工程侧。假设我们想用 PyTorch 验证 CurveMoE 的基本思路优先选择一个小规模 MoE 分类模型先跑通流程再迁移到大模型。CurveMoE 的完整实验可能需要多卡但不影响我们验证核心机制。5.1 运行环境操作系统LinuxUbuntu 20.04 或更高版本最稳妥macOS 也可但 GPU 加速效果弱。Python3.10 或以上。PyTorch2.0 或以上。版本以实际项目为准本文演示通用思路。GPU至少 1 张 24GB 显存的显卡。如果做视觉小模型实验12GB 也能跑。5.2 主要依赖pip install torch torchvision transformers adversarial-robustness-toolbox说明一下torch和torchvision用于构建模型和图像数据集。transformers用于加载 MoE 大模型比如 Mistral 系列的 MoE 模型如果只做 CNNs 分类任务模拟也可不装。adversarial-robustness-toolboxART可以用来生成 PGD 等对抗样本但也可以自己写代码更透明。5.3 一个小型 MoE 分类模型为了方便实验下面自定义一个最简单的 MoE 分类器。它由四部分组成共享特征提取层。门控网络。N 个专家网络。输出融合层。# 文件路径moe_small.py import torch import torch.nn as nn import torch.nn.functional as F class TinyMoE(nn.Module): def __init__(self, in_dim784, hidden_dim128, out_dim10, num_experts4, top_k2): super().__init__() self.encoder nn.Sequential( nn.Linear(in_dim, hidden_dim), nn.ReLU(), ) self.gate nn.Linear(hidden_dim, num_experts) self.experts nn.ModuleList([ nn.Sequential( nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, out_dim), ) for _ in range(num_experts) ]) self.top_k top_k def forward(self, x, return_routingFalse): x x.view(x.size(0), -1) h self.encoder(x) gate_logits self.gate(h) gate_probs F.softmax(gate_logits, dim-1) topk_probs, topk_indices torch.topk(gate_probs, self.top_k, dim-1) # 归一化 top-k 权重 topk_probs topk_probs / (topk_probs.sum(dim-1, keepdimTrue) 1e-8) expert_outputs [] for k in range(self.top_k): expert_idx topk_indices[:, k] weight topk_probs[:, k].unsqueeze(-1) out torch.zeros(x.size(0), out_dim, devicex.device) for e_idx in range(len(self.experts)): mask expert_idx e_idx if mask.any(): out[mask] self.experts[e_idx](h[mask]) expert_outputs.append(weight * out) fused sum(expert_outputs) if return_routing: return fused, gate_probs, topk_indices return fused这个模型设计得很直观方便盯住“路由”这一环。注意第 41 行用了循环匹配索引训练时效率不高但胜在读起来清晰适合小规模验证。5.4 数据集选择推荐从 MNIST 或 FashionMNIST 开始。它们类别清晰、维度低、单卡能快速训练适合验证 CurveMoE 的核心机制。之后可以换到 CIFAR-10 并加入更复杂的 MoE 视觉骨干。6. 多范数对抗样本生成与曲线训练骨架现在给出可运行的工程骨架。这段代码不是论文官方实现而是按标题描述的“多范数 模式连接”思路写的最小验证方案。6.1 多范数 PGD 攻击函数# 文件路径attacks.py import torch import torch.nn.functional as F def pgd_attack(model, x, y, eps, alpha, steps, normLinf): 统一的 PGD 攻击接口支持 Linf 和 L2。 x_adv x.clone().detach() if norm Linf: x_adv x_adv torch.empty_like(x_adv).uniform_(-eps, eps) else: delta torch.empty_like(x_adv).normal_() delta delta / (delta.view(x.size(0), -1).norm(dim1).view(-1, 1, 1) 1e-8) x_adv x_adv eps * delta x_adv.requires_grad_(True) for _ in range(steps): logits model(x_adv) loss F.cross_entropy(logits, y) grad torch.autograd.grad(loss, x_adv)[0] if norm Linf: grad_sign grad.sign() x_adv x_adv alpha * grad_sign # 投影到 eps 无穷范数球内 delta x_adv - x delta torch.clamp(delta, -eps, eps) x_adv x delta else: # L2 投影 delta x_adv alpha * grad delta delta - x delta_norm delta.view(x.size(0), -1).norm(dim1).view(-1, 1, 1) 1e-8 factor torch.clamp(eps / delta_norm, max1.0) delta delta * factor x_adv x delta x_adv torch.clamp(x_adv, 0.0, 1.0).detach().requires_grad_(True) return x_adv这段代码使用统一的攻击函数通过norm参数切换 L∞ 和 L2。核心逻辑是初始化扰动、迭代梯度上升、投影回允许的扰动球内。写的时候建议每一步归一化扰动和钳制到有效像素范围否则生成的对抗样本会越界。6.2 贝塞尔曲线插值Mode Connectivity 的工程实现中贝塞尔曲线是一个简洁的选择。这里演示如何通过控制点构造一条连接两个 MoE 模型的曲线# 文件路径curve.py import itertools def bezier_interpolate(models, t, steps3): 在多个模型参数之间构造贝塞尔曲线并返回 t 点对应的模型状态。 models: 按顺序排列的模型参数快照列表每个元素是 state_dict() pullback: 这里只做权重插值不处理 BN 统计量 # 简化为两段式先在下标上进行线性组合 n len(models) - 1 weights [] for k in range(n 1): binom 1 # 二项式系数 C(n,k) n_fact 1 k_fact 1 nk_fact 1 for i in range(2, n 1): n_fact * i for i in range(2, k 1): k_fact * i for i in range(2, n - k 1): nk_fact * i binom n_fact // (k_fact * nk_fact) weights.append(binom * (t ** k) * ((1 - t) ** (n - k))) # 取第一个模型的 state_dict 作为模板 base_dict models[0] new_dict {} for key in base_dict.keys(): tensor_sum None for k_idx in range(n 1): tensor models[k_idx][key] w weights[k_idx] if tensor_sum is None: tensor_sum tensor * w else: tensor_sum tensor_sum tensor * w new_dict[key] tensor_sum return new_dict这段代码的关键是对 MoE 的路由器、专家参数、共享层参数统一插值。也就是说插值后的模型既包含不同专家的混合也包含路由策略的混合。如果两个端点路由策略差异太大插值中段的模型可能会因为路由混乱而损失较高所以实际训练中需要额外的鲁棒损失来“拉平”路径。6.3 一个简化的 CurveMoE 训练循环在单机上先跑通思路训练循环可以按“双端点 曲线中段鲁棒优化”的方式组织# 文件路径train_curvemoe_simple.py # 说明这是一个最小可运行的工程骨架用于验证思路不是论文官方实现 import copy import torch import torch.nn.functional as F from moe_small import TinyMoE from attacks import pgd_attack from curve import bezier_interpolate device cuda if torch.cuda.is_available() else cpu model TinyMoE(in_dim784, out_dim10, num_experts4, top_k2).to(device) optimizer torch.optim.Adam(model.parameters(), lr1e-3) # 训练到一定精度的“初步模型”作为端点1 # 这里省略数据加载部分假设已经训练了若干轮保留 state_dict endpoint_a copy.deepcopy(model.state_dict()) # 再接着训练若干轮或换一种训练顺序得到端点2 endpoint_b copy.deepcopy(model.state_dict()) # 在主循环中训练目标是让曲线上的模型具备鲁棒性 for step in range(total_steps): # 1. 随机采样曲线参数 t torch.rand(1).item() # 2. 用端点插值得到当前参数 curve_state bezier_interpolate([endpoint_a, endpoint_b], t) model.load_state_dict(curve_state) x, y next(iter(train_loader)) x, y x.to(device), y.to(device) # 3. 多范数对抗样本生成 adv_x_linf pgd_attack(model, x, y, eps0.1, alpha0.01, steps10, normLinf) adv_x_l2 pgd_attack(model, x, y, eps2.0, alpha0.2, steps10, normL2) # 4. 同时计算干净损失和两种对抗损失 logits_clean model(x) logits_adv_linf model(adv_x_linf) logits_adv_l2 model(adv_x_l2) loss ( F.cross_entropy(logits_clean, y) 0.5 * F.cross_entropy(logits_adv_linf, y) 0.5 * F.cross_entropy(logits_adv_l2, y) ) # 5. 反向传播但只更新曲线参数对应的模型简化起见用普通优化器 optimizer.zero_grad() loss.backward() optimizer.step() # 6. 训练更新后把当前模型状态合并回两个端点 # 具体策略取决于论文设计这里略 if step % 50 0: print(fstep {step} | loss {loss.item():.4f})请你特别注意上面第 6 步的“合并回端点”没有实现因为论文并未公开具体的端点更新策略。比较合理的做法有两种周期性保存每隔若干轮把当前模型快照作为新端点重新构建曲线。软端点更新用当前模型参数对端点做 EMA指数滑动平均让曲线随训练动态演变。这两种策略都符合 “通过曲线训练提升鲁棒性” 的直觉但没有官方论文前都不算定论。实验时建议记录下来作为超参数的一部分。6.4 增加路由一致性损失前面分析过MoE 特有的问题在路由漂移。可以单独写一个路由稳定性损失def routing_consistency_loss(model, x, adv_x): 约束对抗样本与干净样本的路由分布一致。 使用 softmax 后的 gate 分布做 KL 散度约束。 with torch.no_grad(): _, gate_clean, _ model(x, return_routingTrue) _, gate_adv, _ model(adv_x, return_routingTrue) # 把 gate 分布拉向干净分布 kl_loss F.kl_div( gate_adv.log(), gate_clean, reductionbatchmean, ) return kl_loss把它加到之前训练循环的 loss 里再给一个较小的权重比如 0.1 到 0.5就能让路由对扰动更稳定。建议从 0.1 开始调避免路由被过度“锁死”而降低模型表达能力。7. 运行结果与效果验证7.1 评估命令训练完成后需要同时评估三个分数干净准确率Clean Accuracy模型在原始测试集上的准确率。鲁棒准确率Robust Accuracy模型在对抗样本上的准确率。路由稳定性Routing Stability干净样本和对抗样本的路由选择一致率。def evaluate(model, test_loader, attack_configs): model.eval() clean_correct 0 adv_correct 0 total 0 for x, y in test_loader: x, y x.to(device), y.to(device) total y.size(0) with torch.no_grad(): out model(x) pred out.argmax(dim1) clean_correct (pred y).sum().item() for cfg in attack_configs: adv_x pgd_attack( model, x, y, epscfg[eps], alphacfg[alpha], stepscfg[steps], normcfg[norm], ) with torch.no_grad(): out_adv model(adv_x) pred_adv out_adv.argmax(dim1) adv_correct (pred_adv y).sum().item() # 这里只统计单次攻击的准确率实际实验要逐个攻击分开统计 break clean_acc clean_correct / total robust_acc adv_correct / total return clean_acc, robust_acc7.2 判断成功的标准一个方法是否有效不能只看鲁棒准确率。建议按这个顺序判断干净准确率不能掉太多。如果干净准确率从 95% 掉到 80%那即使鲁棒准确率很高工程上也不可用。多种范数下的鲁棒准确率都上升。如果只提升了 L∞L2 还是老样子说明多范数目标没有真正生效。路由一致性变好。干净样本和对抗样本的路由选择一致率越高说明路由器对输入扰动越稳定。不同 seed 和不同端点初始化下结果可复现。Mode Connectivity 的性质依赖端点选择如果你换两个端点曲线完全断裂那这个方法就不够稳定。如果训练失败先看训练日志里 loss 是否稳定下降再看插值中段的模型在干净数据上是否可用。如果曲线中段模型干净准确率就极低说明两个端点之间的低损失路径不存在此时需要先调整端点模型的训练方式而不是盲目增加对抗损失。8. 常见问题与排查思路问题现象可能原因排查方式解决方案曲线中段模型干净准确率很低两个模式之间不存在低损失路径或插值方式不合适将 t 从 0 到 1 均匀采样绘制干净准确率曲线换用分段线性插值改用三个中间控制点训练端点时使用更多轮数多范数训练后干净准确率严重下降对抗损失权重过大或 L2 攻击步数太长检查训练日志中干净损失曲线单独用只含干净损失的模型做对比降低对抗损失权重先用较小 eps 和较小 alpha 做预热路由一致性损失导致 router 停滞KL 权重过大把路由过度锁死在干净分布上查看 gate 分布的熵是否显著下降调低 KL 权重或只在训练后段加入该损失多范数训练不收敛不同范数攻击难度差异大梯度方向冲突分别记录 L∞ 和 L2 的鲁棒损失先单独训练 L∞收敛后再加入 L2或对损失做梯度归一化插值后专家参数冲突专家顺序不固定相同位置的专家在两端模型中语义不同检查每个专家的激活分布是否对齐在插值前对专家做排序对齐如按专家在验证集上的激活频率重排显存不足同时生成多种范数的对抗样本观察显存瓶颈在攻击阶段还是训练阶段减少 batch size先算 L∞ 再算 L2复用中间缓存使用梯度检查点9. 最佳实践与工程建议9.1 先小后大先稠密后 MoEMode Connectivity 在稠密模型上已经有很多成熟研究。建议先在普通 ResNet 或 MLP 上验证“曲线训练 多范数对抗”的组合有效再迁移到 MoE。否则一旦模型不收敛你无法判断问题出在 MoE 的路由还是 Mode Connectivity。9.2 控制点数量不要贪多两个端点之间加一个中间控制点是性价比最高的配置。控制点越多曲线表达能力越强但训练代价和调参难度也越大。先跑 2 端点 1 中间点把流程跑通再逐步增加。9.3 对路由做独立评估MoE 安全评估不能只盯着最终准确率。建议把“路由一致性”纳入每次评估的固定指标。一个简单的做法是对 10% 的测试样本同时记录干净样本和对抗样本的 top-k 专家索引计算一致率。这个指标能直观反映模型对路由攻击的抵抗能力。9.4 日志要留足上下文对抗训练实验最容易出现“看起来鲁棒换攻击就崩”的情况。最佳实践是记录每种范数的攻击配置eps、alpha、steps、迭代次数。训练时的损失权重。曲线插值的分段方式和控制点数量。随机种子。没有这些上下文实验结果很难复现也很难定位是哪一步破坏了鲁棒性。9.5 生产环境的安全边界如果要把这种防御机制用到生产不要只依赖单一防御方法可以把 CurveMoE 训练出的模型作为第一层输入过滤和异常检测作为第二层。对未知攻击的泛化能力要单独测试。多范数训练能提升泛化上限但不代表对所有攻击都免疫。定期用新攻击方法重测路由稳定性MoE 模型的路由策略可能随数据分布漂移而改变。10. 总结与后续学习方向这篇文章从“MoE 路由是攻击面”出发拆解了 Robust CurveMoE 的三个关键设计方向用模式连通曲线替代单一最优点用多范数对抗目标覆盖不同攻击几何用路由一致性约束解决 MoE 特有的脆弱性。这三个方向单独拿出来都有研究基础组合在一起恰好对应 MoE 防御的三个短板。对想继续深入的人来说下一步可以按下面的路径走先把文中的 TinyMoE 跑通生成 L∞ 和 L2 对抗样本观察路由器在干净样本和对抗样本上的 top-k 选择差异。训练两个不同的模型端点手动检查它们之间是否存在低损失路径绘制 t-准确率曲线。再把曲线训练加进来对比单点对抗训练和曲线对抗训练的鲁棒性差距。最后再考虑迁移到大模型用 transformers 加载开源 MoE 模型并引入低秩适配器LoRA之类的参数高效微调降低显存压力。如果在实验中发现“两个端点之间根本没有低损失路径”不要急着调超参。先思考一下两个端点的差异到底是什么是数据顺序、学习率、还是专家排列的随机性Mode Connectivity 并不保证任意两个模式都能连通找到“可连通”的端点组合本身就是实验的重要结果。建议收藏这篇文章等到你真正开始做 MoE 鲁棒性实验时再取出来对照。实践出真知尤其是路由稳定性这个指标只有跑过一轮对抗样本和一批不同范数攻击后你才会理解为什么单一的普通对抗训练在这里不够用。