はじめに:MoE は「省コストのモデル」から「通信トポロジーを変える手法」へ変わった

もし混合エキスパートモデル(Mixture of Experts, MoE)を「パラメータが多いのに、その都度一小部分しか使わない」という理解にとどめているなら、その見方は 2020 年より前ならおおむね通用していたものの、今日の現実から見るとすでに決定的に古くなっている。今日 MoE がメインストリームの大モデルのデフォルト架構になったのは、安いからではない。偶作でもなく、もともと一つに絡み合っていた三つの問題を切り離せたからである。総パラメータ数はtoken ごとの激活計算量から独立して積み上げられ、エキスパート重みは別々のデバイスに置いて all-to-all 通信によってほぼ線形の容量拡張を得られ、共有部分(attention、embedding、shared expert)は密のまま保って汎用能力の安定性を残せる。この三つが揃って、DeepSeek-V3 の 671B 総パラメータ / 37B 激活、Qwen3 シリーズの A3B と A22B、Mixtral 8x7B のような、公開設定を読めばそのまま確認できる構造が現れたわけである。

ただし硬貨の裏側も同じくらいはっきりしている。MoE は「純粋な計算の問題」を「通信 + スケジューリング + ルーティング安定性」という複合問題に変えてしまった。訓練時には all-to-all のブロック、容量オーバーフロー、token dropping、補助損失と主タスク損失との勾配の衝突を処理せねばならず、推論時にはルーティングの揺らぎ、バッチサイズがルーティング分布に与える攪乱、量子化誤差がゲート logits を拡大すること、そして同じテキストが訓練段階と推論段階で別のエキスパートを選ぶことによる不整合を処理せねばならない。これらのどれか一つ处理好しなければ、MoE の利得の大半は食られてしまう。

そこで本稿では MoE の歴史をなぞることも、特定のモデルの架構解説を繰り返すことも止めておく。したいのは、MoE を工学上実際に突き当たる六つの判断ポイントに分解することである。つまり、疎な激活の本質的な利得はどこから来るのか、top-k routing の数学的な細部と、やっておくべき小さな工夫、capacity factor と token dropping が持ってくる見えざる税、load balancing 損失と主タスクとの勾配の衝突、そして三つの代替ルート、shared expert がなぜますます採用されるようになったのか、expert parallelism と all-to-all の通信の勘定——最後に推論側の安定性の問題と一連の工学的トレードオフの判断に落ちる。各セクションでは選択肢を並べるのではなく、明快な判断を一つ示す。

先に境界を一点だけ述べておく。本稿でいう「MoE」は Transformer における疎な MoE のフォワード層(Sparse MoE FFN)、つまり各ブロックの FFN を複数の並列 FFN と一つのルーターに置き換えたものを指す。attention の部分は密のまま、token 次元での疎化は行わない——この疎化のルート(動的ヘッド選択、ヘッド剪定、ブロック級疎化)は工学的には別の問題にもっと近く、本稿では触れない。

密と疎:本質的な違いは「パラメータが少ない」ことではなく、勾配更新の密度にある

まず最も混同されやすい区別から。密モデルと疎な激活モデルの違いは、「パラメータ総量がどちらが大きいか」ではない。各パラメータが毎ステップどれだけの勾配信号を受け取れるか、これが本質的な違いだ。密モデルではすべてのパラメータがすべての token の順方向と逆方向に参加する。パラメータ数 P は token あたりおよそ 2P 程度の浮動小数演算に対応し、メモリには P 個の重みが常駐しなければならない。MoE では総パラメータ数 P が激活パラメータ数 A よりはるかに大きく、token あたり k 個のエキスパートしか通らない。パラメータ数 P に対応するのは重みの保存だけであり、計算量は A で勘定する。

これがもたらす最初の直接的な帰結がある。MoE のコスト性能の良さは、容量拡張と計算拡張の切り離しに由来する。同じ token あたり FLOPs の制約のもとで能力を上げたい場合、密のルートは幅と深さを増やすしかない。パラメータと計算が同時に増える。MoE のルートはエキスパート数だけを増やせるので、総パラメータは膨らんでも激活計算はほとんど動かない。これが、2024 年以降「大きなパラメータ、低遅延」を追うモデルがほぼすべて MoE を選んだ理由である。

第二の帰結はよく忘れられる。MoE の演算節約は本物だが、メモリと通信のオーバーヘッドは増える。つまり「MoE は安い」という言葉は、訓練と推論の両段階でまったく違う意味になる。訓練時の MoE の目標はスループット(tokens/s)と通信効率である。総計算量はほぼ激活パラメータに従うので、MoE の勝ち筋は、より多くのパラメータを使ってより良い収束に到達できることに宿る。推論時の MoE の目標は遅延とメモリに変わり、MoE はこの両方で追加で払うことになる。エキスパート重みはすべてメモリに常駐させ、実行時に all-to-all 通信を行わなければならないからである。結果としてよくあるのは、MoE モデルの単一リクエスト遅延が同激活量の密モデルより良くならないという点だ。利点が現れるのは、大スループット、長コンテキスト、バッチ処理のシーンである。

三本の勘定は分けて考える

観点密モデルMoE モデル
重みのメモリおよそ総パラメータ数に等しい総パラメータ数に等しい(未激活エキスパートを含む)
token ごとの激活パラメータ数総パラメータ数に等しいattention パラメータ + ルーティングエキスパート k 個分 + shared expert
token ごとの計算量総パラメータ数に線形して増える激活パラメータ数に比例して増え、独立に拡張できる
オプティマイザ状態のメモリ総パラメータ数に比例して増える同じく総パラメータ数に比例して増え、MoE のほうがむしろ高くつく
カード間通信テンソル並列が中心でパターンが固定expert parallelism が all-to-all を持ち込むためトポロジーに依存する

この表で最も過小評価されがちなのが、オプティマイザ状態の行である。AdamW を使う場合、各パラメータについて fp32 のマスター重みと一階および二階モーメントが追加で常駐する。パラメータあたり概算十几バイトとして、671B の MoE は訓練段階でオプティマイザ状態だけで数十 TB の規模になる。これが MoE の訓練では expert parallelism + オプティマイザ状態のシャーディング + 必要なら CPU offload がセットで必要になり、逃げ道がない理由である。それとは対照的に、推論段階にはオプティマイザがない。必要なのは bf16 の重みと KV cache だけで、メモリの勘定はずっとすっきりしたことになる。

もう一つ、直感的な違いがある。MoE の容量増加には利得の逓減点がある。エキスパート数が 8 から 64 へ増えるとき、エキスパートあたりの入力 token 数は急速に減り、エキスパート内部のデータ分布が疎になり、ルーターの学習信号が弱くなる。一方で総パラメータは同時に膨らみ、通信コストは線形に上がる。だから「エキスパートを増やす」のは、思いつくままに続けられるアクションではない。これが後で改めて触れていく粒度の問題になる。

ルーティングの数学的な形:softmax、top-k、そして必ずやっておくべき小さな工夫

MoE 層の順方向は四つのステップに分解できる。ルーターが logits を計算し、softmax でゲートの確率を得て、top-k でエキスパートを選び、選ばれた確率を再正規化してエキスパート出力の重み係数にする。この四つのステップのそれぞれに落とし穴はあるが、実際に成否を分けるのは二つの、書き間違いやすい細部である。top-k のあとは必ず再正規化すること、そしてルーティング logits のスケール制御である。

まず正規化の話である。softmax のあとで上位 k 個の確率を正規化せずにそのまま重みにすると、token がエキスパートに渡す実際の信号強度が token ごとに激しく揺れる。k=2 のとき、二つの確率が (0.6, 0.25) であることもあれば (0.31, 0.28) でもある。後者ではどちらのエキスパートも三割以下に弱められてしまう。訓練初期のこの揺れは各エキスパートの実効学習率を不安定にし、訓練後期には一部のエキスパートが「学習不足」に陷る。標準的な対策は、この k 個の確率をもう一度正規化して合計を 1 にすることである。そうすれば各 token は全体で必ずひとかけのサイズの信号を寄与する。

次に logits のスケールである。ルーターは一つの線形層で、その出力がそのまま softmax に入る。訓練が進むとゲートの logits の絶対値は大きくなりやすく、モデルは自発的にルーティングの判断をより確定的にする。softmax はそれに応じて one-hot に近づき、勾配は「どのエキスパートが選ばれやすいか」から「選ばれたそのエキスパートしかルーティングの勾配を受け取らない」に変わってしまう。これには二つの結果がある。ひとつは訓練初期の探索不足、もうひとつは選ばれたエキスパートが一度選ばれるとその後も選ばれやすくなり、自己強化的なマタイ効果が生じる。だからこそ、多くの実装は訓練時に logits にガウスノイズ(jitter)を少し足して、探索を明示的に開けておく。

import torch
import torch.nn as nn
import torch.nn.functional as F


class TopKRouter(nn.Module):
    """top-k 路由器:门控 -> 加噪 -> softmax -> 选专家 -> 重归一化 -> 容量裁剪。"""

    def __init__(self, d_model, n_experts, top_k=2, capacity_factor=1.25,
                 jitter_noise=0.0):
        super().__init__()
        self.n_experts = n_experts
        self.top_k = top_k
        self.capacity_factor = capacity_factor
        self.jitter_noise = jitter_noise
        # 刻意不加 bias:常数偏置会让少数专家凭空拿到系统性优势
        self.gate = nn.Linear(d_model, n_experts, bias=False)

    def forward(self, x, drop_tokens=True):
        # x 形状 [T, D],T 是 batch 与 seq_len 展平后的 token 总数,
        # 这样容量、派发、回收三步都只跟 T 有关,与 batch 切分无关
        logits = self.gate(x)                                     # [T, E]

        # 训练期噪声,压制 softmax 过早饱和,保证早期能探索到冷门专家
        if self.training and self.jitter_noise > 0.0:
            logits = logits + torch.randn_like(logits) * self.jitter_noise

        probs = logits.softmax(dim=-1)                            # [T, E]
        topk_w, topk_i = probs.topk(self.top_k, dim=-1)           # [T, k]

        # 最关键的一步。不做重归一化,不同 token 送进专家的总信号
        # 会在 0.2 到 1.0 之间乱跳,每个专家的有效学习率跟着抖动
        topk_w = topk_w / topk_w.sum(dim=-1, keepdim=True)

        # 容量按期望负载定,而不是按实际最大负载定,
        # 这样相邻两批之间容量保持确定,不会因运气好坏而变
        expected = self.top_k * x.shape[0] / float(self.n_experts)
        capacity = max(int(expected * self.capacity_factor), self.top_k)

        # rank[i, e] 表示 token i 是第几个把专家 e 选进来的,
        # 只有 rank 为 0 的那条记录拿到处理权,其余进入丢弃集合
        one_hot = F.one_hot(topk_i, num_classes=self.n_experts)    # [T, k, E]
        rank = one_hot.cumsum(dim=1) - 1                          # [T, k, E]
        keep = (rank.squeeze(1) == 0).gather(1, topk_i)           # [T, k]

        return topk_i, topk_w, keep, capacity

このコードには三つの工学的な細部があり、それぞれ触れておく価値がある。第一に、ルーターが bias を持たないのは、思いつきではない。bias は各エキスパートに定数の logit バイアスを与える。訓練初期に、bias が偶然大きくなったエキスパートがいれば、そのエキスパートは継続して選ばれ、継続して更新され、bias 自身も更新される。正のフィードバックとなり、ルーティング分布を平坦化するのは難しい。

top-k の選択について、k=1 と k=2 は別のものだ。k=1 は Switch Transformer のやり方で、ルーティングは純粋な argmax だ。重みの混合はなく、各エキスパート出力をそのまま総和する。構造は最も単純で、ルーティングも最も均衡しやすい。ただし各 token が得られるのはひとつのエキスパート表現だけで、表現の多様性は限られる。k=2 は二つのエキスパートがそれぞれ重み付きの出力を出すことを許すので、表現力はより強い。Mixtral 8x7B はこの設定を使っており、現在ではより一般的なデフォルトでもある。k=4 以上では利得が明らかに逓減する。使える容量が希釈され、各エキスパートが見るデータも少なすぎるからだ。工学的に Expert Choice Routing という妥協もある。token にエキスパートを選ばせるのではなく、各エキスパートが自分の容量に応じて top-C 個の token を選ぶ方式だ。利点は天然に完全な均衡になり、token dropping がまったくないこと。欠点はルーティングが因果性を満たさなくなること——token の割り当ては batch 全体に依存するので、自回帰推論やストリーミングのシーンではそのまま使えない。だから実際には encoder 系か、訓練期の探索的な研究にしか現れない。

もっと攻撃的な変種も知っておくとよい。ルーターを元の位置に置くのではなく、一段下の層に沈めるというものである。DeepSeekMoE は、標準の FFN を中間次元で複数のセグメントに切り、各セグメントに入力を共有するルーターと細粒度のエキスパートを一つずつ置くという設計を提案した。DeepSeek-V3 はこの設計の上で、ルーティングエキスパート数を 256 にし、shared expert を一つ加え、各エキスパートが routed token を 8 個だけ受け取り、同時に系列レベルのバイアス項で補助損失を置き換えている。これは公開設定に載っている構造上の事実であり、ベンチマークのスコアとは一切関係ない。工学的な含意はこうだ。エキスパート数を大きくしても各エキスパートを小さくできる。ルーティングの均衡が損失項で押し戻すのではなく、バイアス項が毎ステップの訓練後に勾配なしの均衡修正を行うからである。

capacity factor、token dropping、そして計算しにくい隠れた税金

capacity factor が存在するのは、一つの統計的な事実を扱うためだ。ルーターは完璧にバランスしていない。訓練目的が明確に全エキスパートの一致した負荷を求めているとしても、実際の token 分布には必ず揺らぎが残る。人気エキスパート、あるバッチでは上限を超えて token を受け取る。工学的な対処として、各エキスパートにハード上限 C = capacity_factor * k * T / n_experts を設定し、上限を超えた token はそのまま捨てる。これが token dropping だ。

token dropping の代償はよく「少しだけ計算を失う」と書かれるが、この言い方は過小評価している。まず、捨てられた token は単純に「FFN を一つ減らした」わけではない。ルーターを一通り抜けるので、all-to-all の送信枠も使い、embedding と attention のすべての計算にも参加する。FFN 出力だけを生成しないだけだ。つまり毎ステップの訓練で drop_ratio の割合の token は純粋な無駄になっている。ルーティングと通信のコストは全額払っているのに、FFN の寄与は何も得ていない。その token の損失への寄与も単純には無視できない。Switch Transformer の実装では、捨てられた token の FFN 枝の出力はゼロにされる。この token の教師信号は弱まったのと同じで、主タスクの勾配も一緒に縮む。

capacity factor を大きくしすぎないという直感は正しい。よく語られる説明は、しかし、十分に正確ではない。本当の理由はこうだ。capacity factor が大きいほど、システムは訓練の中でルーター自身をうまく訓練させることに依存する。もし capacity factor を 1.5 まで上げるなら、50% の過剰割り当てを許容しても token を捨てないということになる。すると、あるエキスパートが勾配の異常やデータ分布の偏りによって突然人気になったとき、あなたはより多くのメモリと計算力を持ってそのエキスパートを受け止めさせ、均衡損失には訂正させない。表面上は capacity factor が大きいほど「無害」に見える。しかし実際には、負荷の不均衡を「強制的に修正される」状態から「計算力で覆い隠される」状態へ変えているだけだ。あるバッチのルーティング分布の偏りが余量を超えたとき、token dropping は突然ゼロ近傍から目に見える比率まで跳躍する。訓練曲線には極めて説明しにくい振動が出る。

実際の工学的な取值範囲はだいたいこうなる。k=1 のシーン(Switch スタイル)は受け取るエキスパートが一つしかないぶん揺らぎが大きく、capacity factor はしばしば 1.25 から 1.5 を要する。k=2 のシーンなら 1.0 から 1.25 で十分だ。細粒度エキスパートかつ各エキスパート容量が小さいシーン(たとえば一エキスパートあたり 8 token しか受けない)は、capacity factor が 1.0 に近く,甚至 1.0 でなければならない場合もある。低カウントでは単一エキスパート自体の統計的揺らぎが大きいので、捨てないために容量を広げる必要があるが、同時にエキスパート数が十分多いので全体としては均衡が保たれるからだ。これらの一連の数値は定理ではなく、よく使われる設定の帯域にすぎない。正しいやり方は、自分の訓練ログで drop 比率を監視して、drop を 1% 以下に安定的に抑えてから、capacity factor をさらに下げていくことである。そうしてこそ真の最適値に近づく。

見落とされがちな利得がもう一つある。一部の token を捨てるのは、実は正則化になっている。これによってルーターは、すべての token をただ一つの最適エキスパートに集めるのではなく、ある token を「次点だが許容できる」エキスパートへ回すことを学ぶ。過学習とエキスパート崩壊は一定程度は緩和される。訓練の軌跡を観察するとき、drop 比率がほぼゼロまで抑えられ、同時にエキスパート負荷分布の分散もほぼゼロになっているなら、それが 좋いこととは限らない。タスク分布が極端に単一であるか、あるいは均衡制約が強すぎてルーターがすでに意味のある判別を一切しなくなっている可能性がある。

ロードバランシング損失:主タスクとの間に本物の勾配の衝突がある

ロードバランシングの考え方は素朴だ。主損失に加えて補助項を一つ置き、各エキスパートが受け取る token 数が平均からどれだけズレているかを罰する。Switch Transformer が使っているのは load balancing loss である。各エキスパート i について P_i を現在のバッチ内で割り当てられた token の割合、f_i を実際にそのエキスパートへルーティングされた token の割合と定義し、補助損失は全エキスパート n_experts * sum(P_i * f_i) となる。理想的に均分されていれば各項は 1/n_experts、総和は 1 になるので、これを最小化すればよいだけだ。利点は単純であること、追加の通信が要らないこと、ほぼすべての実装に初めから入っていることである。

問題は、この項が主タスクの目的関数とまったく別の空間で最適化していることにある。主タスクが求めているのは「今の入力にちゃんと当てはまる」ことで、補助損失が求めているのは「ルーターが token を全エキスパートに均等に分ける」ことである。この二つの要求は多くの場面で衝突する。あるエキスパートが特定の意味領域のエキスパートとして特定種類の token を専門的に扱うということは、まさにそのエキスパートが受け取る token が不均衡であることを意味する。そしてそれこそが、求める専門化そのものだ。補助損失は絶えず、休みも取らずにこの専門化を手前へ引き戻そうとする。収束が遅くなり、エキスパート間の入力分布が似ていき、最終的に全エキスパートが一つへ収束していく、という形で表れる。

もう少し細かく言うと、衝突は二つの階層に現れる。第一の階層は方向の衝突だ。補助損失の勾配はルーターの gate パラメータにしか作用しないが、主タスクの FFN 勾配は gate とエキスパートウェイトの両方に作用する。両者が逆向きを向いているとき、gate は勾配のレベルで綱引きになり、エキスパートウェイトも一緒に振動する。第二の階層は大きさの衝突だ。補助損失の係数 alpha は手作業で調整せねばならず、しかも主タスクの均衡要求を押し戻せるほど大きく、かつ専門化を壊さないほど小さくなければならない。alpha を大きくしすぎるとエキスパートが同質化して、MoE は「まったく同じ FFN の複数コピー」に退化する。alpha を小さくすると負荷分散が大きくなり、drop 比率が上昇して実効スループットが落ちる。調整コストが高く再現性が悪い。これが、このルートにおけるいちばん現実的な痛点である。

import torch
import torch.nn as nn
import torch.nn.functional as F


def load_balance_loss(probs, indices, top_k, n_experts):
    """Switch 风格的均衡损失,理想均分时返回值恰好等于 1。"""
    # P 是期望份额:全体 token 的门控概率在专家维度上的均值
    P = probs.mean(dim=0)                        # [E]
    # f 是实际份额:top-k 硬分配之后每个专家真正收到的比例
    mask = torch.zeros_like(probs)
    mask.scatter_(1, indices, 1.0)
    f = mask.mean(dim=0)                         # [E]

    lb = n_experts * torch.sum(P * f)
    # 额外加一份零方差项,防止少数专家被彻底饿死而不报警
    lb = lb + n_experts * torch.sum(P * P)
    return lb


class BiasBalancedRouter(nn.Module):
    """辅助损失-free 的均衡:给每个专家一个不参与反向的标量偏置。

    核心洞察是:偏置只改变选中谁,不改变选中时的概率与权重,
    因此它能修正负载分布,却不会污染主任务在门控上的梯度方向。
    """

    def __init__(self, d_model, n_experts, top_k):
        super().__init__()
        self.gate = nn.Linear(d_model, n_experts, bias=False)
        self.correction_bias = nn.Parameter(
            torch.zeros(n_experts), requires_grad=False)
        self.top_k = top_k

    @torch.no_grad()
    def update_bias(self, per_seq_counts):
        """按序列统计的负载来修正偏置,避免长序列主导整个 batch。"""
        score = per_seq_counts.float().mean(dim=0)          # [E]
        delta = score.mean() - score                        # 超载压低,欠载抬高
        self.correction_bias += torch.sign(delta) * 1e-3
        self.correction_bias.clamp_(-1e-2, 1e-2)

    def forward(self, x):
        logits = self.gate(x)
        probs = logits.softmax(dim=-1)                      # [T, E]
        # 选专家看加偏置的分布,定权重仍看原始分布,两者解耦
        biased = probs + self.correction_bias.view(1, -1)
        topk_i = biased.topk(self.top_k, dim=-1).indices   # [T, k]
        topk_w = probs.gather(1, topk_i)                   # [T, k]
        topk_w = topk_w / topk_w.sum(dim=-1, keepdim=True)
        return topk_i, topk_w

このコードが示しているのは、いま最も代表的な代替ルート、すなわち勾配なしのバイアス項で補助損失を置き換えるという新しい手法である。その核心の洞察は、ルーティングの判断とルーティングの重み付けという二つのことを分離できるという点にある。バイアスは「誰を選ぶか」の一歩にだけ加え、softmax の確率と出力の重み付けは元の logits のままを使う。そうすれば均衡の仕組みが、主タスクの勾配の方向を汚さずに効くようにできる。DeepSeek-V3 が公開している説明はまさにこの仕組みだ。さらに系列の次元でもバイアスを更新し、バッチ内で長い文章が統計を支配しないようにしている。

バイアス法のほかに、知っておく価値のある代替ルートがいくつかある。z-loss が罰するのはルーティング logits の分布ではなく、その絶対値の大きさだ。その働きは softmax の飽和を抑えることで、均衡目的とは直交している。たいてい補助損失と併用され、低コストな安定化の手段になっている。エントロピー正則化は、ゲート分布が十分なエントロピーを保つことを直接報酬として与える。その本質は「不要太早めに決めてしまうな」ということで、訓練の初期フェーズに向いている。Expert Choice Routingは確率的な均衡を硬い割り当てに取り替える、最も徹底的な方式だが因果性の問題があり、非自回帰のシーンでしか使えない。ルーティング範囲の制限はトポロジーのレイヤーのテクニックである。ノードの中で先に局所的な均衡を一度行い、ノードの間では均衡済みの token 分布だけを受け渡す。all-to-all の大域的な均衡問題を、ノード内の局所的な問題へと次元を落とすのだ——DeepSeek-V3 が公開しているノード制限ルーティングもこの考え方である。どのルートを選ぶかは、工学的には主として自回帰・ストリーミングの制約で決まる。encoder 系のモデルなら Expert Choice を検討できるが、自回帰モデルではほぼバイアス法か、分布統計を変えない他のテクニックを選ぶしかない。

shared expert:「不均衡を許す」逃生装置

shared expert の考え方は非常に直接的である。n_routed 個のルーティングエキスパートに加えて、常に激活してすべての token を受ける通常の FFN を一つ(あるいは少数個)取り付ける。その出力はルーティングエキスパート側の重み付き出力と加算されるか、連結される。DeepSeekMoE がこの構造を初めて体系的に論証し、DeepSeek-V2 や V3 が採用した。公開設定によく見られるのは、ルーティングエキスパート 256 個に対して shared expert を 1 個置く組み合わせである。

shared expert が解いてくれるのは、これまで繰り返し出てきたあの矛盾である。shared expert がなければ、ルーターの負荷均衡の圧力のすべてを「専門化を弱める」ことで返済しなければならない。ある領域の token が特定エキスパートへ全量集中することは許されず、補助損失が絶えず分配を平坦に拘束する。ところが、専門化には不均衡が必要なのだ。shared expert は「すべての token が通る必要がある汎用の変換」を切り出して外側へ出す。こうするとルーティングエキスパート同士は安心して分化できる。汎用能力は shared expert が受け止めているので、ルーティングエキスパートが万能になる必要はなく、それぞれがコードや数学や自然言語といった専門表現を個別に学べばよい。均衡損失がルーティングエキスパートに加わる圧力は、こうしてかなり小さくなる。

勾配の角度来看、shared expert にはもう一つ、より隠れた役割がある。崩壊しない勾配の集積点を提供しているのだ。ルーティングエキスパートは負荷が著しく不均衡だと、半分近いエキスパートが長い間 token を受け取らず、パラメータがほとんど更新されないことがある。shared expert は毎ステップすべての token から訓練されるので、最適化器に安定した降下方向を与えるのと同じことになる。あるフェーズでルーティング分布が異常なドリフトを起こしても、shared expert があるおかげで、モデルには少なくとも退化しない道が一本残されている。だから多くの MoE モデルで、shared expert がオプションではなく固定の設定として残されているのである。

代償ももちろんかかる。shared expert は密なので、その激活計算は疎化で削れない。token 一つにつき完全な FFN をもう一つ計算するのとまったく同じである。だから shared expert のサイズを通常のエキスパートより小さくしておくのが一般的な選択になる。細粒度エキスパートのアーキテクチャの下では、shared expert はたいてい中程度のサイズに設計され、ルーティングエキスパートと同サイズになることは少ない。判断基準は単純である。shared expert を取り除いて短い区間だけ対照実験を走らせてみて、各指標に明らかな低下がなければ、この密な計算分は丸ごと無駄に払ったことになる。DeepSeekMoE が報告したアブレーションの結論はこうだ。同じパラメータ予算のもとで shared expert は一貫したわずかな向上をもたらし、しかもその向上はエキスパート数が多いほど大きくなる。

もう一つ、はっきりさせておきたい境界がある。shared expert は均衡の代わりにはならない。均衡の圧力を軽減はしても、なくしてはくれない。ルーティングエキスパート間の負荷分散が依然として大きいなら、結局はバイアス法か補助損失で管理せねばならない。だからこれは「均衡の難度を下げる構造的な手段」と考えるべきで、「均衡を丸ごと同免してしまうスイッチ」ではない。

ルーター自体の設計の変種:ゲートは必ずしも線形層とは限らない

ここまでの議論はすべて一つの前提の上に立っている。ルーターは線形層一層と softmax だけからなるものだという前提だ。この前提で十分使えるが、変種を知っておくことで、いつ変更すべきか、そして変更の結果どのようなコストを払うのかを判断しやすくなる。

ノイズ付き top-kはきわめて直感的な改造法である。ガウスノイズを足すのではなく、各エキスパートへのゲートスコアに、対数正規分布からサンプリングした乗性ノイズを掛ける。ノイズが大きいほど探索は十分になる。利点は、ノイズが確率スケールと掛け合わさって作用するため、ノイズが一部のエキスパートスコアを無理な領域へ押し出すことを自然に避けられる。加法ノイズと比べて、訓練の安定性は通常よい。ただし代償として毎ステップのサンプリングが必要になる。検証や推論のときにはサンプリングを切っていなければならず、異なるノイズ強度同士をそのまま比較することもできない。

ハッシュルーティングは反対側の極端を行く。学習をまったく使わず、token のハッシュ値を固定のエキスパートへ割り当てるだけだ。意味するのは効果ではなく、決定性とコストである。ゲートパラメータがなく、均衡損失がなく、ルーティングのドリフトもない。対照のベースラインとして非常に適し、特定の構造の中でエキスパートを均一に分配するためによく使われる。欠点も明確で、エキスパート間に意味的な専門分化が形成されない。学習式ルーティングよりも効果の上限が明らかに低い。

低ランクゲートは「エキスパート数が多いとゲートのパラメータ数が大きすぎる」という問題への対処である。まず隠れ状態を低次元の空間へ射影し、低次元の空間の中でゲートを計算してから、エキスパート次元へ射影し直す。DeepSeekMoE でも低ランク射影を使うゲートが用いられている。得られる利得は、ゲートのパラメータ数が「エキスパート数×隠れ次元」から「二つの小さな行列のコスト」へ下がることだ。エキスパート数の多い設定では、この削減は相当大きい。代償は、ゲートの表現力が低ランクボトルネックに制限される点にある。エキスパート数が増え続けて低ランクが足りなくなった場合、ルーティング品質は劣化する。

より深いゲートネットワーク、たとえば 2 層の小さな MLP のようなものは、より複雑なルーティング戦略を表現でき、理論上はエキスパート同士の分担をより構造的にできる。ただし実際には二つの問題が生じる。ゲートの計算オーバーヘッドが上がる。そして訓練データの分布に過学習しやすくなる。実践上の判断はこうだ。線形ゲートでもエキスパート出力の類似度がすでに高いなら、エキスパートが同じ作業を繰り返しているので、より深いゲートネットワークに置き換えるのは実際に効くことが多い。類似度が正常で、エキスパート分기가すでに明確な場合は、ゲートを深めるのはほぼパラメータの無駄になる。

shared expert のルーティングも変えられる。これまで shared expert を、すべての token を常に受ける密な分岐として説明してきた。これが最も一般的なやり方だ。しかし shared expert 自体をゲートに参加させたり、異なる大きさの shared expert を複数置いて粒度の異なる入力を別々に受け持たせるような設計もある。前者は shared expert の「偏りのない勾配の集積点を提供する」という役割を弱めてしまう。後者はまた均衡問題を呼び戻す。shared expert の価値は、恰恰それが不均衡であることに宿る。それを均衡のスケジューリングに参加させた瞬間、その役割は消えてしまう。したがってここでは、「構造を統一する」ために余計な変更をするべきではない。

ルーターの変種を選ぶときの一つだけの汎用原則がある。まず診断し、そのあとに構造を変える。エキスパート分担が混乱しているとき、問題の多くはゲートの表現力が足りないとか、探索が不足していることに由来する。負荷が不均衡なときは、より深いゲートに替えても、まず大きな効果とはならない。触るべきは均衡の仕組みのほうだ。この二つを分けて考えることが、無効なパラメータ探索を避ける鍵になる。

expert parallelism と all-to-all:本当の壁は通信にある

このあたりまで来ると、MoE の工学的な重心はアルゴリズムからシステムへ移ったと明確になる。密モデルのテンソル並列パターンは固定されている。各層を行または列に分割し、順方向と逆方向で all-reduce か reduce-scatter を通す。通信量と形状は静的に導出できる。MoE の expert parallelism は違う。各 token がどのエキスパートへ行くかはデータ依存で、実行時にしか決まらない。だから all-to-all で token をそのエキスパートを持つデバイスへ送り、計算が終わったら all-to-all で返さないといけない。MoE の層 1 つにつき all-to-all は最低 2 回、順方向で 2 回、逆方向で 2 回(実際の実装では勾配もルーティング情報を必要とするので回数はさらに増える)。

import torch
import torch.distributed as dist


class ExpertParallelDispatch:
    """把 [T, D] 的 token 按专家分片送到对应设备,算完再收回来。

    设备 r 持有 experts_per_rank 个专家。all-to-all 之后本地收到的是
    别的设备上、属于本地专家的那些 token,顺序与分桶索引一一对应。
    """

    def __init__(self, n_ranks, n_experts):
        assert n_experts % n_ranks == 0, "专家数需要被并行度整除"
        self.n_experts = n_experts
        self.experts_per_rank = n_experts // n_ranks

    def dispatch(self, x, topk_i, topk_w):
        T, D = x.shape
        k = topk_i.shape[1]

        # 每个 token 有 k 条路由记录,先展平成 [T*k] 这一条长序列
        flat_i = topk_i.reshape(-1)
        flat_w = topk_w.reshape(-1)

        # 目标设备 = 专家编号 // 每设备专家数
        dst_rank = flat_i // self.experts_per_rank

        # 按目标设备分桶,桶内保持稳定序;这一步的排布直接决定
        # 后面能不能用 grouped GEMM 一次算完整层的专家
        order = torch.argsort(dst_rank, stable=True)
        counts = torch.bincount(dst_rank, minlength=dist.get_world_size())
        send = x[order // k] * flat_w[order].unsqueeze(-1)

        # 关键前置步骤:接收端必须先知道要收多少字节,
        # 因此要把每张卡的负载计数交换一次,这是一次额外同步
        input_splits = counts.cpu().tolist()
        recv_splits = self._exchange_counts(counts)

        recv = torch.empty(sum(recv_splits), D,
                           dtype=x.dtype, device=x.device)
        dist.all_to_all_single(recv, send.contiguous(),
                               output_split_sizes=recv_splits,
                               input_split_sizes=input_splits)
        return recv, recv_splits, order

    @staticmethod
    def _exchange_counts(counts):
        buf = [torch.zeros_like(counts) for _ in range(dist.get_world_size())]
        dist.all_gather(buf, counts.to(dist.get_backend()))
        return buf[dist.get_rank()].cpu().tolist()

    def combine(self, out, order, T, k, D):
        """把专家输出按原 token 顺序加权求和,送回它原本所在的设备。"""
        recv = torch.empty(T * k, D, dtype=out.dtype, device=out.device)
        dist.all_to_all_single(recv, out.contiguous(),
                               output_split_sizes=[T * k],
                               input_split_sizes=None)
        x = torch.zeros(T, D, dtype=out.dtype, device=out.device)
        x.index_add_(0, order // k, recv)   # 权重在派发时已经乘过
        return x

このコードで最も注目に値するのは、計算ではない二つの部分である。第一に、負荷カウンタは先に all-gather を一度やっておく必要がある。受信側は all-to-all の前に、自分が何バイト受け取るのか并不知道だからだ。これは追加の同期になる。小さなメッセージでは、そのオーブッドの比率が相当高くなるので、多くのフレームワークは最初の all-to-all と融合させ、あるいは推定と検証の方式で回避している。第二に、並び順がカーネルの性能を決める。同じエキスパートに属する token を連続した一区間に並べてはじめて、grouped GEMM でエキスパートをまとめて計算できる。グループ化できなければ N 回の独立した小さな GEMM に退化するだけで、GPU の利用率は直接半減する。データレイアウトの細部の細部は、数学的な部分よりも実際のスループットを左右することが多い。

通信トポロジーについて、MoE のインターペースへの感度は密モデルより圧倒的に高い。expert parallelism の all-to-all は、ノードをまたぐ、全カードを結んだ、不規則な交換である。各カードは他のすべてのカードに異なる数の token を同時に送る必要がある。しかもその数は毎ステップ変化する。NVLink ドメイン内ならまだ許容できるが、ノードをまたいでネットワークに出ると容易にボトルネックになる。ネットワーク帯域はふつう NVLink の十几分の一だからである。EP の並列度を単純に GPU 総数に等しくしてはいけない。工学的には EP を NVLink ドメインの中に閉じる方が一般的だ。たとえば 8 カードを 1 ドメインにして、その中で EP を行い、ドメイン間では激活層と attention 層に対して DP か TP を掛ける。あるいは DeepEP のような MoE の通信最適化に特化したライブラリを使い、小メッセージとクロノード転送のオーバーヘッドを下げる手もある。DeepEP は token の転送と低精度の KV・激活の転送を別のパスに分けて帯域を組み合わせ、各 token の RDMA 書き込みをよりコンパクトにパッキングしている。

もう一つ、見落とされがちな事実がある。MoE のバッチが小さいほど、通信効率は悪くなる。all-to-all に参加している各カードの送信データ量は、そのバッチの token 数に比例する。token が少なすぎるとメッセージが細かく分裂し、遅延の比率が跳ね上がる。だから MoE の訓練は通常、グローバルバッチを大きく取る。动不动百万 token スケールの系列数×系列長のような規模になる。推論でも、大量のバッチでサービスする方式のほうが、低並行のオンライン場面には向いている。

順方向と逆方向の非対称性という問題も記録に留めておく価値がある。順方向の all-to-all のデータ量は、逆方向の何倍もある。逆方向では勾配を関係するすべてのデバイスへ戻し、さらに順方向で保存したルーティングインデックスとゲートウェイトをブロードキャストし直さなければならないからだ。DeepSeek-V3 が公開している取り組みは、、逆方向で激活の勾配、ゲートウェイト(ゲートの再計算に使う)、そしてルーティングインデックスを同時に返す,この工夫で逆方向の 2 回から 3 回の通信を 1 回に圧縮する。Tutel や MegaBlocks といったライブラリも同様の通信融合を進めている。MegaBlocks の考え方は block-sparse GEMM によって容量次元と drop 次元を疎な構造の中に取り込むことだ。そうすれば層全体の capacity factor が計算量に影響しなくなる。

エキスパートを細かくしすぎるとむしろ悪くなる:直感に反するが非常に安定した法則

「エキスパートが多いほど強い」は、MoE について最も広く流通している誤解の一つだ。実際の状況はこうである。エキスパート粒度にはスイートスポットがあり、それを超えると利得は急速に逓減し、無視 に転じることもある。理由は四つあり、いずれも具体的だ。

第一に、各エキスパートが見る token 数が少なすぎる。全体の token 数が固定でエキスパート数が倍になれば、エキスパートあたりの token シェアは半分になる。FFN のパラメータを訓練するには十分なデータが必要で、サンプルが少ないとエキスパートは何も学べない。ごく狭い局所パターンをかろうじてフィットするだけで、容量利用率は極端に低い。工学的には、エキスパート間の出力分布がしだいに似てくる。どのエキスパートも極端に狭い入力分布に閉じ込められているので、狭い分布をいくつかつなぎ合わせた全体の能力は、むしろ少し広めの分布を一つと招かるものよりも劣る。

第二に、通信とスケジューリングのオーバーヘッドがエキスパート数とともに増える。エキスパート数が倍になれば、all-to-all のバケット分けはもっと細かくなり、メッセージは小さくなり、grouped GEMM のグループ数は増える一方で、各グループの計算量は減る。GPU の効率はそれに比例して落ちていく。この部分のコストは硬いもので、より大きなモデル規模で薄まることはない。

第三に、ルーターの学習が難しくなる。出力次元がエキスパート数に線形に増え、ゲートのパラメータ数もエキスパート数とともに増え、softmax の空間が大きくなり、最適なルーティングの探索が難しくなり、初期の収束が遅くなる。観察される現象はこうだ。エキスパート数の多い設定は前期の loss 下降が遅く、粗粒度の設定の最終的な水準に追いつくまでに、より多くの token を必要とする。

第四に、細粒度の利得はルーター自体が十分に強いことに依存している。細粒度エキスパートという前提は、ルーターが十分に精密な判別ができ、各 token が最も適した小さなエキスパートを見つけられるという点にある。ルーターの判別力が追いついていないなら、細粒度は単に能力の足りない分配問題をもっと細かく刻んだだけで、新しい情報はまったく生まれない。DeepSeekMoE の実験設計には重要な対照がある。総パラメータ数を固定して、エキスパート粒度だけを変えることで「細粒度」そのものの寄与を切り離している。この点は多くの再現実験で無視され、結果としてパラメータ数の変化を粒度の利得と取り違えてしまう。

では、スイートスポットはおおよそどこか。実践上の経験はこうである。粗粒度(エキスパートが少なく各々が大きい)は中小規模に、細粒度(エキスパートが多く各々が小さい)は大規模に対応する。粗粒度の典型的な設定は Mixtral の 8 エキスパート、そして DBRX の大規模・少数エキスパート路線である。細粒度の典型的な設定は、DeepSeek-V3 が公開している 256 個のルーティングエキスパートや、各エキスパートが routed token を 8 個だけ受け取るといった形だ。粒度が適切かどうかを判断する最も直接的な指標は、各エキスパートの実効 token 利用率と、エキスパート出力同士のペア解散度である。類似度が全体として高ければ、エキスパートが同じ作業を繰り返している。利用率が極端に低ければ、大量のエキスパートのパラメータが遊んでいる。この二つの指標は、loss 曲線を見るよりも早くシグナルを与えてくれる。

もう一つの妥協ルートが混合粒度である。層と層のあいだで異なるエキスパート数を使う、あるいは同じ層のなかで、一方の粗粒度エキスパートが汎用的な特徴を扱い、他方の細粒度エキスパートが専門的な特徴を扱う、という設計である。原理的には筋が通っている。しかし実装の上では all-to-all のバケットロジックが複雑になり、工学的には割に合わない。成熟した EP フレームワークをすでに持っていて、しかもデータ分布が実際に多層的な異質性を示す場合でない限り、その利得は出にくい。

エキスパート間のスケールドリフト:エラーにならないが、とても致命的な問題

MoE には、密モデルには存在しない隠れた問題がある。異なるエキスパートごとの出力スケールが、訓練とともにそれぞれ別々にドリフトする。密モデルではすべての token が同じパラメータを通るので、活性化値の統計分布は全体がまとまって収束する。MoE では各エキスパートは一小部分の token しか扱わない。あるエキスパートへ入る入力の分布が狭ければ、その出力分布も狭くなる。そして異なる狭い分布をひとつにまとめて残差を加算するとき、スケールの不一致が拡大される。

スケールドリフトの直接的な帰結は三つある。ひとつめは訓練が不安定になることである。あるステップでどのエキスパートかが出力として異常に大きな振幅を出し、残差加算のあとで層全体の活性化値の範囲を大きく押し広げられる。以降の層の正規化が歪んで、loss のスパイクが起きうる。ふたつめはルーティングがより縮退しやすいことである。あるエキスパートが出力振幅が大きいせいで残差ストリームにおける発言権が強くなり、より多くの token を引きつけたくなり、不均衡がさらに助長される。みっつめは量子化精度が引きずられることである。低精度量子化のスケール係数は通常、全体統計またはテンソル統計から計算される。あるエキスパートだけ振幅が他の十倍なら、テンソル全体の量子化ステップがそれに合わせるしかなく、他のエキスパート精度が無駄に犠牲になる。

対処の方法はいくつかある。コストの低い順に並べるとこうなる。最も軽いのは、各エキスパート内部の FFN で正規化の構造を保つことである。全エキスパートのパラメータ化の方式をそろえることで、出力スケールが自然に同じ桁に並び、構造の違いによるドリフトが起きなくなる。多くの MoE 実装でエキスパート FFN の内部に残差接続と正規化が残されているのはこのためである。やや重いのは、エキスパート出力のスケールを監視することである。各エキスパート出力テンソルの平均、標準偏差、最大値を訓練ログに記録して、どのエキスパートがドリフトし、どこまでドリフトしているかを直接見る。ほとんどのチームはこのことをしないので、この種の問題は loss のスパイクが出てから初めて調べ始めることになり、大量の時間を無駄にする。より重いのは、エキスパート単位の数値保護を導入することで、極端な振幅の切り詰めや検出などである。これらの手段は通常、低精度訓練においてオーバーフロー保護としてだけ使われるのであって、通常の設定ではない。

もう一つ、見落とされがちな関連がある。エキスパート間のスケール差は、ルーティングの数値感度を直接拡大する。前に述べたように、top-k の選択は logits の相対的な大きさに依存する。異なるエキスパートが受け取る token 数が大きく異なると、受け取った token 数そのものが次の層のゲート入力の統計量に影響する。結果として「負荷の不均衡がスケール差を生み、スケール差がルーティング判断をずらし、ルーティングのずれが負荷の不均衡をさらに強める」という正のフィードバックが生まれる。従ってエキスパート尺度のドリフトを監視することは、本質的に均衡の健康状態を監視することでもある。

実務的にまとめると、こうなる。MoE の可観測性の要求は、密モデルより圧倒的に高い。密モデルなら loss 曲線と勾配ノルムはほぼ十分だ。MoE では少なくとも、drop 比率、エキスパート別の負荷分布、エキスパート別の出力スケール、この三グループの指標に加えて、バイアス項の大きさやゲートのエントロピーまでないと、基本的なデバッグ能力が備わっているとはいえない。このグループの指標を実装するコストはきわめて小さい。しかしこれを用意しないと、MoE が異常な挙動をしたときにできることは、原因の当て推量だけになる。

メモリと演算の勘定:MoE の本当のコストを掰き明かす

MoE で最も誤って計算されやすいのはメモリである。正しいやり方は、訓練と推論の二つに分けて勘定することだ。そしてこの二つの結論は、しばしば真逆になる。

訓練段階では、重みは bf16 で保存し(パラメータあたり 2 バイト)、オプティマイザの状態は AdamW で見積もる。fp32 のマスターウェイトに一階・二階モーメントを加えて、パラメータあたりおよそ 12 バイト、合計およそ 14 バイト。さらに勾配そのもの 2 バイトを加えて、概算でパラメータあたり 16 バイトの桁になる。これに通信バッファ、活性化値、そして MoE 特有の dispatch と combine の中間結果を上乗せする。総パラメータ 671B の規模で言えば、オプティマイザ状態だけで TB 級になる。つまり MoE の訓練ではオプティマイザ状態も expert parallelism でシャーディングし、CPU offload や ZeRO 型のページング最適化も必要になることが多い。加えて活性化値のメモリは capacity factor のために膨らむ。各エキスパートは容量ぶんスロットを予約するので、実際に drop が起きても空のスロットが残る。この勘定から得られる結論はこうだ。MoE の訓練が節約するのは演算であって、メモリではない。ある構成では、メモリコストの方が同一の激活量を持つ密モデルより高くなることもある。

推論段階では、bf16 の重みと KV cache だけで、パラメータあたり 2 バイト、671B ならおよそ 1.3TB 級の重みになる。KV cache のコストに比較すると、MoE に特有のコストは次の二点にある。すべてのエキスパート重みを常駐させなければならないという点が一つ。激活層のように必要に応じて出し入れはできない。ただし莫大な入れ替え遅延を許容できる場合を除く。もう一点は、all-to-all 通信が毎回のフォワードで発生する。推論は必ずバッチを束ねて初めて価値が出る。大きなバッチがなければ、MoE の二つの利点(容量が大きい、メモリに収まる)はどちらも通信遅延に食われてしまう。そして密モデルは小バッチのほうが速く、また単純でもある。

演算の勘定はもっと微妙だ。激活パラメータ数 A は token ごとの実際の FLOPs と等しくない。ルーターの計算、top-k の選択、容量パディング、token dropping による空転、all-to-all の搬送オーバーヘッドが、いずれも余計なコストになる。k=2 で、エキスパート数が多く、capacity factor が 1.25 のような設定では、これらの追加項の合計によって実際の FLOPs が A から見積もった値を明らかに上回りうる。粗く言えば、ルーティングとスケジューリングのオーバーヘッドは、激活パラメータの比率が过分高くない小さいモデルほど目だって効いてくる。工学的に正しいやり方は実測することであり、見積ることではない。ハードウェアと並列設定を固定し、tokens/s とエンドツエンド遅延を実測し、激活パラメータ数から実効スループット・パラメータを逆算する。この数字こそが、MoE 同士を比較できる指標になる。

もう一つ、見落とされがちなコストの軸がある。量子化によるダメージが MoE にの被害は均一ではない。下文で触れる。

推論側のルーティング安定性:訓練と推論の不整合が持つ本当の代償

このセクションは MoE の工学の中でもっとも落とし穴が多く、もっとも系統的に語られていない部分である。中心的な問題は一行でまとめると、こうなる。ルーティング判断はデータ依存である。推論時の数値条件は訓練時とは違うので、同じ入力が推論では別のエキスパートへルーティングされ、モデルの挙動がドリフトする。

バッチサイズはどうやってルーティング分布を変えるか

top-k ルーティング自体は各 token について独立に計算されるので、理論上はバッチの影響を受けない。しかし実際の実装ではバッチの影響を受ける。ルーターの行列積が異なる GEMM カーネルを通る。バッチが小さいと別のタイル戦略や低精度の経路が起動する。数値誤差の大きさが変わり、drop のロジックはバッチが十分大きいとほとんど発動しないが、小さいときは drop 比率が明らかに上がる。落ちた token の FFN 出力はゼロになるので、モデル構造が一時的に変わったのと同じになる。この「バッチサイズでモデルが変わる」効果は密モデルにはほとんど存在しないのに、MoE ではきわめて目立つ。

ここから生じる実践的なチェック項目がある。同じテキストで batch=1 と batch=64 のとき、同じ貪欲デコードの結果が得られるか。得られないなら、実装に数値的な過敏さがあるか、drop による構造変化があることを意味する。このチェックは回帰テストに書き込む価値がある。本番化するまでしか現れない奇妙な問題(同じ問題でも並行度が違えば違う答えを返す)を、先に炙り出せるからだ。

量子化がルーティングに与える被害は、FFN に対してよりもっと致命的だ

重み量子化が MoE に与える影響は均一ではない。密モデルの量子化の経験をそのまま MoE へ持ち込むと、ここでつまずく人は多い。ルーターは薄い線形層が 1 層あるだけで、その出力 logits がそのまま top-k の選択を決める。そして top-k は階段状の操作である。2 つのエキスパートサーバの logit の差が 0.01 であるか 0.11 であるかで、量子化誤差の面前では選択が反転しうる。logits の差が小さいとき、つまりルーティングが最も重要で決定力が最大の瞬間、に、量子化が持ち込む相対誤差の割合が最大になる。

そこで実践上のコンセンサスはこうだ。ルーターの重みとバイアスは高精度に保ち、量子化しない。そのパラメータ数は全エキスパートを合計しても微々たるもので(E × D に対して E × 3D²)、fp16 か bf16 のままにするコストは無視できる。得られるものは、追跡の難しい出力不安定性のクラス全体が消えることだ。関連する経験則として、訓練の終盤で一度「ルーティング温度キャリブレーション」を行う。logits の分布を調べるのだ。大量の token で top-1 と top-2 の差が極端に小さければ、ルーティングが曖昧な領域にいるということで、量子化誤差に特に敏感だ。この場合はルーティング温度を上げるか、推論時に選んだ k 個のエキスパートをハードに top-k で取るのではなく、確率でブレンドする。

推論側で現実として向き合う制約

考慮しなければならない点がいくつかある。活性化値の量子化は通常、重みの量子化より安全だ。ルーティング判断を直接乱さないからである。ただし capacity padding の空きスロットにはすべてゼロが入っている。量子化のスケールを全体統計で計算するとこれらのゼロに引っ張られるので、有効 token で統計を取ることを推奨する。エキスパート間の統合や枝刈りは推論段階で実行可能だ。これは MoE の推論最適化として実用的な道筋だ。各エキスパート間の入出力の類似度や重要度を計測し、冗長なエキスパートを統合するか、そのまま切り落とす。これで常駐メモリと all-to-all の参加側を大幅に減らせる。前提は、わずかな精度低下を受け入れられることと、この枝刈りがオフラインで完了して重みに固定されていることである。

複数カードへの分割戦略は EP に追随させる。推論サービスで EP の並列度はハードウェアのインターペースの物理境界に合わせるべきであり、分割の粒度はエキスパート数を割り切らなければならない。あるドメイン内の GPU 数がエキスパート数を割り切れないと、そのドメインは一部のエキスパートだけを持つことになり、一部の token はドメインを往復しなければならない。遅延は直接 2 倍になる。異なる推論フレームワークの MoE サポートの成熟度には大きな差がある。vLLM と SGLang のどちらにも MoE 向けの融合カーネルがある。ルーティングと gather/scatter と grouped GEMM を 1 個か 2 個の kernel にまとめているのだ。サポートの状況は、エキスパート数、top-k、shared expert の有無、MLA のような直交構造の有無などに依存する。デプロイ前に狙いのフレームワークで実測してスループットを測るべきだ。「このモデルは MoE をサポートしている」という粗い結論で判断してはいけない。

自分で MoE レイヤーを実装するときのチェックリスト

ここまでの内容を読み終えて、自分で実装することにしたなら、ここに順番どおりに実行する自己点検リストを挙げる。それぞれの項目は、それまでの章で挙げた落とし穴の一つに対応し、どれも小規模な実験で独立に検証できる。完全な訓練を回す必要はない。

このリストの前半 6 項目はアルゴリズムの正当性、後半 6 項目はシステムの正当性であり、両者の切り分け方法はまったく違う。前者は数百ステップの小規模な訓練とログで検証できる。後者は実際の並列設定で動かして初めて意味を持つ。もっとも飛ばされやすいのは最後の 2 項目だ。エラーにならず、本番環境の特定の並行度でのみ露出するからである。バッチをまたぐ一貫性の確認を CI に書き込むのは、通常、費用対効果がもっとも高いアクションになる。

ロングコンテキストとマルチモーダルにおける MoE:token の種類がもたらす新しい問題

ここまで議論してきたルーティングの問題は、すべての token が同質だという前提に立っていた。しかしロングコンテキストやマルチモーダルのシーンでは、この前提は成立しない。そしてそこから、別個に扱う価値のある新しい工学的な問題がいくつか生じる。

第一に token の種類による負荷の不均衡である。マルチモーダル入力は射影を通ったあと、視覚 token、テキスト token、そして音声 token が混ざる。隠れ状態の空間におけるこれらの分布の違いは大きく、ルーターの偏好もまったく違ってくる。視覚 token は少数のエキスパートに極端に集中してルーティングされる可能性がある。視覚特徴の分布は本来就テキストより集中しているからだ。結果として、混在したバッチの中でテキスト側のエキスパート負荷は容量を大きく下回り、視覚側のエキスパートは頻繁に容量上限に触れて drop を生じる。対処方法は、全体の平均ではなく token の種類ごとに drop 比率と負荷分散を別々に統計することだ。容量を種類ごとに設定する必要があるなら通常、二つの容量ロジックを維持することになり複雑性が上がる。見合うかどうかの損益計算が必要になる。

第二に、コンテキスト長と capacity factor の相互作用である。容量は平均負荷から計算される。負荷の分散はエキスパート数だけでなく、各系列の token 数にも依存する。系列長が数千から数十万に増えると、大バッチ上のルーティング分布はより安定する(分散が下がる)。しかし分布の偏った段落が現れると、あるエキスパートが大量の token を一気に飲み込む。そしてその一部が容量を超えた時点で捨てられる。ロングコンテキストで捨てられるのは、ふつうランダムな周辺サンプルではなく、連続したある区切りである。つまり drop がモデルの挙動に与える影響は、短いコンテキストの場合とは比べものにならないほど重い。失われるのは散発した token ではなく、意味のまとまった一区切り全体なのだ。緩和の考え方としては、ロング系列のために容量を別枠で大きめに確保する、由来の異なる token に異なる容量クォータを設定する、あるいはロング系列の区間では drop を緩めて演算の損失を受け入れる、などが考えられる。

第三に、系列レベルの均衡と系列レベルのバイアスのトレードオフである。 앞에서、バイアス項の更新を系列ごとの統計で行うという話をした。利点は、長い系列が全体の統計を支配しないことだ。しかしあるバッチの中で系列長の差が非常に大きい場合(ロングコンテキストと短いテキストを混ぜて訓練)、系列ごとの統計にすると短い系列のノイズがバイアスの振動として増幅される。妥協的な手法として token 数で重み付けした統計を使う、あるいは更新の窓を長く取ってノイズを平滑化する。DeepSeek-V3 が公開しているのは、系列レベルの補助均衡係数と系列レベルのバイアスを組み合わせる方法であり、これによってルーティングが特定の系列にも、特定の token にも偏らないようにしている。これは見習うべき方向の考え方である。均衡を単一の階層に押し込まず、複数の階層に分解してそれぞれ処理する。

第四に、位置と構造の直交性である。モデルの attention 層そのものが疎な構造や低ランクな構造を持ち込む場合(低ランク KV 圧縮や multi-head latent attention のような attention を圧縮する手法)、両方の疎パターンが積み重なる。attention は系列次元で圧縮し、MoE は特徴次元で疎にする。工学的な影響は、メモリのピークと通信パターンをやり直す必要があることだ。この二種類の構造で最適な並列設定はしばしば異なるので、attention のみモデルの設定の結論をそのまま持ち越すことはできない。

これらの問題の共通点はこうだ。いずれも「ルーティングはデータ依存であり、現実のデータの分布は訓練時の想定よりもずっと広い」ということに根ざしている。したがってマルチモーダルやロングコンテキストで MoE を使うときは、監視指標の粒度を純テキストのシーンより細かくし、容量と均衡の設計も「全体で一組」から「種類別・長さ別の段階分け」へ向けて変えていかなければならない。

1 回の MoE 訓練における並列度と精度の設定チェックリスト

アルゴリズムの問題をここまで議論してきたが、最後には設定表に落とし込む必要がある。以下のチェックリストは「まず並列度を決め、次に精度を決め、最後に通信の最適化を決める」という順序で 구성され、それぞれの項目についてそれを選んだ理由と、適用されない境界を示す。

並列戦略はどうやって切り分けるか

attention 層とエキスパート層で別の並列方式を使う。これが MoE の訓練設定の基本的な形である。attention 層はテンソル並列とパイプライン並列を使う。エキスパート層は expert parallelism を使う。理由は両者の通信パターンがまったく異なるからだ。attention 層の通信は固定形の reduction なのでテンソル並列に向いている。エキスパート層の通信はデータ依存の不規則な分散なので、expert parallelism にしか向かない。これらを混ぜてしまう(エキスパートにもテンソル並列をかける)と、通信効率が極端に悪くなる。

並列度の配分に、よくある誤りが一つある。expert parallelism の並列度を大きくしすぎることだ。expert parallelism の all-to-all は全カードを結んだ通信であり、高速インターペースのドメインを抜けると帯域が急激に落ちる。通信に参加するデバイスが増えるほどバケット分けは細かく、メッセージは小さくなり、効率はさらに悪化する。実践上の経験では、expert parallelism の並列度をひとつの高速インターペースドメインの大きさに揃えるべきだ。ドメインの間はデータ並列か、激活層へのテンソル並列で拡張する。このドメインの大きさをハードな制約として設計するほうが、事後のチューニングよりもはるかに有効だ。

エキスパート数は並列度で割り切れる値でなければならない。割り切れないときには 2 つの対処がある。あるデバイスにエキスパートを 1 つ多く持たせる(負荷が本質的に不均衡になるので、通常は割に合わない)、あるいはエキスパート層に追加で 1 層の非 reduction なスケジューリングを入れる。前者は単純だが、もっとも忙しいカードがスループットを決めることになる。後者は柔軟だが実装が複雑になる。たいていの場合は、エキスパート数を並列度の整数倍に設計するのがもっとも楽である。これが、多くのモデルのエキスパート数が 8 や 16 や 64 や 128 や 256 のような整数になっている理由でもある。

精度はどう決めるか

マスターウェイトと順逆方向の計算は bf16、オプティマイザの状態は fp32。これが現在の大規模訓練の主流の選択である。bf16 のダイナミックレンジは、MoE のように数値分布の広い活性化(とくにエキスパート間で出力スケールの差が大きい場合)を収めるのに十分であり、fp32 のマスターウェイトは長時間訓練の数値的な安定性を保証する。MoE が精度に対して敏感なのは、ルーティング logits のスケールが訓練とともにドリフトし、logits がまた階段的なエキスパート選択を直接決めるからである。

より攻めた低精度訓練には追加のルーティング保護が要る。fp8 のようなさらに低い精度で順方向と行列積を行い、訓練スループットをさらに押し上げるのは実現可能な方向である。ただしルーティング経路の保護が必ず要る。ゲートの GEMM をより高い精度に保つ、あるいは fp8 のスケール係数の計算からルーティング層だけを除外する。DeepSeek-V3 が公開しているのは、全体を fp8 で訓練しつつ細粒度の精度配分と、いくつかのオーバーフロー保護を組み合わせたものである。この道筋の適用境界ははっきりしている。大規模で、エキスパートが多く、演算が制約されている事前訓練のシーンでのみ、複雑度の増加が勘定に合う。中規模な訓練で使うなら、たいてい過剰な設計になる。

通信の最適化はどの順で進めるべきか

通信がボトルネックなら、最適化には明確な優先順位がある。第一優先は通信回数を減らすこと。逆方向の伝播で激活勾配・ゲート重み・ルーティングインデックスを一度にまとめて返すようにし、複数回に分けて送ることをやめる。この項目は利得が最大で、しかも数値結果を変えない。第二優先は通信と計算の重ね合わせ。あるエキスパートをディスパッチしている間に別のエキスパートを計算し、通信と計算のダブルバッファによって両者の時間を加算するのではなく重ね合わせる。第三優先で、ようやく通信量そのものの圧縮に入る。激活をより低い精度で送る、発生源の異なるトラフィックを経路で分けて送る、小さなメッセージのパッキング方式を改善する——ここまでだ。

この順序の背後にある判断はこうである。回数の削減は純粋にアルゴリズム上の利得、重畳はシステム上の利得、圧縮は工学上の利得である。前二者は上限が高く、さらに精度リスクを持ち込まない。したがって先に着手すべきである。現実には、最初からパッキング形式の最適化に取りかかる人が多く、得られるものは期待に及ばないことが多い。

MoE の評価の罠:オフライン指標はなぜ人を騙しやすいか

MoE の評価には、密モデルよりも二層の不確実性が加わる。構造由来のものと実行環境由来のものである。この二層の不確実性を制御できなければ、評価から得られる結論はまったくノイズである可能性すらある。

構造という層の中心にある問題は、モデルの出力がバッチサイズとデプロイ設定に依存することだ。すでに議論したとおり、容量トリミングと低精度経路によって、同じ入力でも並行度が違えば別のエキスパートを通り、異なる出力になる。つまり評価時に単一の固定バッチ設定しか使わないなら、測りやすいのは「ある一つのスライス上のモデル」であって、モデルそのものではない。正しいやり方は、評価時にバッチとスケジューリングのパラメータを固定して記録し、最低でも一度、バッチをまたぐ一貫性の検査を実行することである。一貫性が成立しないなら、その一回の評価におけるすべての比較に打折を適用しなければならない。

量子化という層は別に警戒が必要である。MoE における量子化誤差はモジュールごとに不均一に落ちる。エキスパート FFN の重み数が絶対的な多数を占めるため、量子化誤差の大部分はそちらで生じるが、ルーターのパラメータ数は微々たるものなのに、決定への影響は段差的である。「全体のビット数が同じ」量子化方式は MoE ではむしろ悪い結果を出しやすい。多くの方式はパラメータ数に応じて精度を割り当てるため、本来なら高精度を保やすべきルーターに余計な精度を割いてしまう。全体のバイト数が同じでも有効な情報が同じとは限らない——これは密モデルから量子化の経験をそのまま持ち込んだときに最も踏みやすい罠である。

もう一つ、構造の層にある評価上の罠がある。エキスパートのアンバランスは評価セットの分布カバレッジに影響する。ある種の評価サンプルが同じエキスパートに大量にルーティングされると、それらの誤りはそのエキスパート得意の短板によって拡大され、評価結果はモデルの平均能力ではなくそのエキスパートの一方の偏りを強く反映してしまう。評価セットをルーティング結果のエントロピーごとに層別して見るのは、この偏りを明らかにできる実用的な手法である——高エントロピーのサンプルと低エントロピーのサンプルでの指標の差を見れば、総平均だけを見るよりモデルの本当のロバスト性を反映する。

実行環境という層は通常の工学的規律そのものである。推論フレームワークのバージョン、並列設定、カーネル実装を固定してから比較する。異なる MoE モデル同士を比較するときは、同じデータ処理と評価スクリプトを使っていることを確認する。結果を報告するときは、capacity factor、ドロップ方針、量子化方式など、モデル挙動を変える設定を一緒に書き出す。最後の一条が特に重要である——capacity factor と量子化方式を明示していない MoE の評価レポートの数字は、比較可能性を持たない。

ルーティングとデータ配分:エキスパートが何を学ぶかは、何を与えるかで決まる

ここまでの均衡と容量に関する議論は、すべてデータを外生変数として扱っていた。しかしデータ配分こそ、実は MoE で最も過小評価されている設計変数である。理由は単純で、エキスパート同士の分業はデータから学ぶものであり、データ分布の不均衡はそのまま負荷の不均衡に翻訳されるからである。

最も典型的なシナリオは、データ配分の言語や領域の比率が極端に偏っている場合である。ある種のデータが訓練コーパスの大半を占めると、ルーターはすぐにその部分の token を特定のエキスパートへ大量に送ることを学ぶ——これはルーターのバグではなく、正しく最適化しているからである。あるエキスパートが繰り返しその種のデータにフィットするように訓練されれば、その種類の token に対する性能が高まり、さらに多くのその種類の token がそこへルーティングされて強化される。結果としてそのエキスパートは過負荷になり、データ比率の小さい領域は長い間十分な訓練を得られず、モデルはマイナーな領域で明らかに能力が落ちる。そしてこれらはすべて負荷ヒストグラムの上では、その数本のエキスパートだけ柱が異常に高くなるという形で現れる。

ここから逆説的だが重要な判断が導かれる。MoE の負荷均衡目標は、ある程度までデータ多様性の目標と衝突する。均衡損失は全エキスパートが同じ負荷を持つことを望み、token がどの領域由来かは区別しない。一方、本当に欲しいのは「各領域を扱えるエキスパートが一つずつあり、そのエキスパートが十分に訓練されていること」である。この二つの目標は、データ配分自体が均衡しているときにしか一致しない。したがって MoE 訓練における均衡ハイパーパラメータとデータ配分は必ず同時に調整しなければならず、どちらか一方だけを調整すると大概は最適以下の結果になる。

逆に言えば、これは shared expert の価値に新たな注釈を足すものでもある。shared expert はすべての token を受けるので本質的にデータの実比率で訓練され、人工的な均衡で歪められたことのない汎用能力を提供できる。一方、ルーティングエキスパートは均衡の制約によって平準化され、その専門化の程度は逆に均衡圧力によって制限される。両者が組み合わさることで、モデルは「汎用能力の保真度」と「少数領域における専門化」の間にかなり妥当な妥協を見いだしている。

実践上の助言をもう一つ。負荷ヒストグラムを見るときは、必ずデータ源ごとにバケット分けて見るべきである。全体で見ればすでにかなり均一に見えるかもしれない(大きな領域が押さえられているため)だが、領域ごとに分解すると、ある小領域の token が少数のエキスパートに極端に集中しており、しかもそのエキスパート全体の負荷は高くない——つまり遊休している容量があるのに使われていない、ただルーティングの判断がそれらを選んでいないだけだ。この場合、capacity factor を上げても均衡の強度を上げても解決しない。すべきはルーターの判別力と、その領域が訓練データの中でどれだけサンプルを持つかを点検することである。

ついでに、これは推論側とも共鳴する話である。実際のサービスのトラフィック分布が訓練時のデータ配分と大きく異なる場合——訓練時は中国語主体で、実サービスは多言語の混合であるような場合——MoE のエキスパート分布は実サービス時に、十分に訓練されていない状態へ押しやられることになる。負荷分布は明確にずれる。この問題は密モデルには存在しないが、MoE では必ず専門的に対処しなければならない。よくあるやり方は、データ配分の中で意図的に分布外だが構造化されたデータを一部残し、ルーターがその種のタイプに対して「崩れない」フォールバック領域を持てるようにしておくことである。

工学的トレードオフのチェックリスト:MoE を導入すべきでないとき

MoE はデフォルトでより良い架構ではない。その利得には明確な前提条件がある。その条件を書き出せば、逆に自分のプロジェクトが MoE を導入すべきかどうかを判断するための一組の問いになる。

規模は十分大きいか。MoE の中核的な利得は「容量と計算の解耦」であり、この利得は十分な大きさの激活計算量がなければ現れない。目標とする激活パラメータが 1B から 3B 程度にとどまるなら、エキスパートを追加して得られる容量増はルーティングのオーバーヘッドと通信のオーバーヘッドに食いつぶされる可能性が高い。粗粒度 MoE の最小有効規模はおおよそ 7B 激活パラメータ以上であり(これが Mixtral 8x7B という構成が何度も模倣されてきた理由でもある)、細粒度のルートはさらに規模を求める。

カード間インターペースはあるか。デプロイ先が単一カードか、少数ノードでしかもインターペースが弱いなら、MoE の all-to-all はそれを死刑判決を下す。MoE を適用できる前提は、少なくともノード内に複数カード的高速インターペース(NVLink ドメイン)があるか、あるいは演算利用率の低下を受け入れる代わりに容量を買う気持ちがあるかのどちらかである。

サービス形態はスループット型かレイテンシ型か。MoE は大批量・高スループット・ロングコンテキストの場面に向いている。オフライン処理、データ合成、バッチ API などがそれだ。低レイテンシで小バッチのオンライン単一リクエストの場面には向かない——まさにそこが密モデルの最も服务水平のよい領域である。

運用能力は足りるか。MoE を動かすとは、drop 比率、エキスパート負荷の分散、エキスパート出力の類似度、all-to-all 通信の占比、ルーティングバイアスの大きさという複数の指標を同時に監視し、それらが異常なときに容量なのか均衡なのか通信なのかを切り分けられる力がある、という意味である。相比すると、密モデルのチューニング経路はずっと短い。チームの規模が限られるなら、密モデルを確実に仕上げること(より良いデータ、より長いコンテキスト、より安定した推論スタック)のほうが通常はリターンが大きい。

逆に、MoE を導入する価値のあるシグナルはこうである。密モデルが同じ演算量で能力の天井にすでに明显にぶつかっている、導入したいメモリ量が激活量をはるかに超えるパラメータを収められる、負荷が大バッチ処理である、しかも訓練クラスタに高速インターペースがあり、そして複雑度の高い分散デバッグを一度やり抜く覚悟がある。

チューニングの順序:この三つのノブは根本から分けては調整できない

MoE のチューニングが苦しいのは、補助損失係数、capacity factor、エキスパート数、top-k の四つが強く結び付いているからである。一つを変えると、残りの三つの最適値も跟着動く。実践で最も起こりやすい誤りは、すべてのノブを一度に変えてから最終的な loss を見て「どのパラメータがおかしいか」を判断することである——この判断にはほとんど情報量がない。最終的な loss は四つの変数が共同で作用した結果だからである。

比較的安全な順序はこうである。まず構造を固定し、次に均衡を調整し、最後に容量を詰める。具体的には、エキスパート数と top-k は構造の決定であり、訓練を始めたら変更しない。変更すれば訓練のリセットに等しくなるからである。均衡の方法(補助損失法かバイアス法か)は訓練開始前に決めておく,因为它が訓練曲線全体の形を決めるので、後から入れ替えるにはやり直しが必要になる。補助損失係数 alpha は大まかな規模で走査する。たとえば 0.01 から 0.1 の間で数点を取り、各点で数千ステップ走らせて、エキスパート負荷の分散が落ちる速度と主タスク loss が上がる速度を観察し、分散を抑えられる一方で主タスクの loss が明らかに遅くならない点を見つけたら、そこを固定して二度と動かさない。capacity factor は最後に回し、分布が安定したあとで徐々に下げる。詰める過程でドロップ比率を監視する。

「均衡が過度かどうか」を判断するには、実用的な間接指標がある。エキスパート出力のペア間類似度である。ある入力の組に対して、任意の二つのエキスパート出力がコサイン類似度で普遍的に高いなら、エキスパート同士が作業を重複していることを意味している。均衡の強度をさらに上げても利得はない。問題は負荷の不均衡ではなく、ルーターの判別能力かエキスパート構造にある。負荷ヒストグラムを見るよりも、この指標のほうが真の退行を見つけやすい。

逆に、負荷はすでに相当均一であるのにルーティング分布が明らかに偏っている場合(少数のエキスパートが大部分の token を獲得している)、バイアス項の修正強度が足りない、あるいは修正の統計ウィンドウが粗すぎることを意味する。更新頻度を上げてやれば通常は改善する。

よくある障害とその切り分けの思路

現象最も考えられる原因対処の方向
訓練初期の loss 下降が異常に遅く、後に追いつくルーティングの探索が不足し、エキスパートが十分に訪問されていない訓練期のノイズを強め、冷たいエキスパートが長期にゼロ負荷でないかを確認
負荷がすでに十分均等なのに、指標が改善せずむしろ悪化する均衡が過度で、エキスパートが同質化している補助損失係数を下げるか、勾配なしのバイアス項による均衡に切り替える
訓練の途中で loss が突然跳ね上がるあるエキスパートに負荷が一時的に集中し、token dropping の比率が上昇したcapacity factor の余裕とバイアス項の修正ステップ幅を確認する
スループットが訓練ステップとともに緩やかに低下するルーティング分布が次第に鋭くなり、all-to-all のメッセージが益々不均衡になるlogits に温度制約を課し、ゲートのエントロピーが持続的に低下していないか観察
推論出力が並行度によって異なる容量トリミングと低精度経路が持ち込む数値的な感度推論時はトリミングを行わない、ルーターを高精度に保ち、バッチをまたぐ一貫性の回帰テストを行う
メモリ使用量が予想を大きく上回るオプティマイザ状態がシャードされておらず、容量確保の空きスロットが多すぎるオプティマイザをシャード化し、capacity factor を詰め、冗長なエキスパートを統合する

この表の中で単独で強調すべきは四行目の項目である。スループットが訓練ステップとともに緩やかに低下するという現象はきわめて見つかりにくい。エラーを出さず、落ちもしない。ただ遅くなるだけなので、「クラスタが混み始めた」と誤解されやすい。真の原因はしばしば、ルーティング分布が訓練とともに次第に鋭くなることにある。初期の段階ではエキスパート間の配分は比較的ランダムで、all-to-all のメッセージ量は各カードの間で比較的均等である。しかし訓練が進むにつれてルーティングは決定性を増し、人気のエキスパートが載るカードの送受信データ量は他のカードをはるかに上回り、残りのカードは待つだけになる。逆方向の all-to-all はさらに顕著である。勾配を戻すデータ量が順方向より大きくなるからである。結果は、クラスタ内で最も忙しいカードが全体のスループットを決めることになり、残りの演算は空転する。緩和手段には、ルーティング logits に温度の上限を課すこと、周期的に負荷の再配分を行うこと、あるいは物理的に最も人気のエキスパートを別々のノードに分散させることが含まれる。

エキスパート冗長と後訓練:MoE を密に変えていく現実的なルート

ここまで繰り返し登場してきた現象がエキスパート冗長である。エキスパート数は多いのに、実際に効いているのは一小部分で、残りは作業を重複するか長期に遊んでいる。これによって生じるコストは直接的だ——メモリには大量の使われない重みが眠り、all-to-all は依然としてそのためにバケット分けと通信のオーバーヘッドを払い、ルーターの出力次元も大きくなって学びにくくなる。

冗長を見分けるには、互いに補い合ういくつかの手段がある。最も直接的なのは入力分布の統計である。エキスパートごとに受け取った隠れ状態をクラスタリングし、入力クラスタが高度に重なるエキスパートがいくつあるかを調べる。次が出力の等価性テストである。一組のプローブ入力を用意し、エキスパート A の出力とエキスパート B の出力を二人ずつ突き合わせ、差が極めて小さければこの二つは統合できる。さらにもう一つ、刈り込み後の検証がある。価値の低いエキスパートを一挙に切り落とし、短時間の評価を走らせて指標がどれだけ落ちるを見る。これは最も正直だが、最も高くつく方法でもある。试すたびに検証をやり直す必要があるからだ。

これらの判断を土台に、疎なモデルの密造化と呼ばれるルートが注目に値する。基本操作はこうである。訓練済みの密モデルの中で、各 FFN を複数の小さな FFN に分割する(これが MoE の最も原始的な形である)。そして元のモデルで大量のサンプルを生成して蒸留の目標とし、小さなエキスパートに元の FFN の出力をフィットさせる。この手順は主干を変更する必要も、事前訓練データを用意する必要もない。コストはゼロから MoE を訓練するより遥かに安く、「既存の密モデルを素早く疎な形に変えたい」という場面にはきわめて実用的である。DeepSeekMoE の小規模エキスパート+shared expert 構造や、後発の疎なアップサンプリング系の研究も、似た発想のファミリーに属する。訓練済みの密モデルを初期値として、インクリメンタルな訓練によって密 FFN の能力を複数の小さなエキスパートに分割していくというものである。

このルートがエンジニアリングチームにとって持つ現実的な意味はこうである。「MoE を入れるかどうか」という架構の賭けを、巻き戻せる工学的な選択肢へ格下げしてくれる。まず既存の密モデルに密造化の手を加えて第一版の利得を得る。効果が微妙なら、コストはインクリメンタルな訓練の時間だけで、完全な事前訓練をやり直すわけではない。逆に利得が立つのなら、密モデルは最初から手元にある。

ただしその境界も明確にしておくべきである。密造化で得られるのは「重みが分割された密モデル」であり、そのルーターは事後にフィットさせたもので、元の FFN の分業を再現することに傾いたものであって、学び出された専門化ルーティングではない。この種の構造は通常、エキスパート分業が整い、負荷均衡も取りやすいが、エキスパートごとの表現の多様性は限られ、上限もゼロから訓練してエキスパートが本当に専門化を許した MoE より低い。終点の形というより、過渡的な形とベースラインに適している。

関連するが方向が逆のルートとして、推論期のエキスパート統合と構造化枝刈りがある。訓練には触れず、デプロイ時に冗長なエキスパートを等価なより大きな FFN へ統合し、それによって常駐メモリを減らし、all-to-all に参加するデバイス数を減らし、場合によってはより密な推論形態まで退化しうる。レイテンシには敏感だがメモリがほかの手段では足りないデプロイ場面では、この道はずっと安上がりである。

この二つのことを並べて考えると、MoE のエンジニアリング上の現実的な一本の軸線が浮かび上がる。MoE のデプロイ形態は固定されるべきではなく、密から疎へ、そして必要に応じて密に戻るという連続スペクトル上の位置にある。訓練時は疎な形態で能力の上限を買い、デプロイ時は実際の負荷とレイテンシ予算に応じて疎さを選び、その途中のどの段階でも調整できる。MoE を固定された架構の選定結果としてではなく、長期的に保守すべき形態可変のシステムとして扱うことは、長期的にはリターンが最も大きくなる。

まとめ:MoE を工学に落地させる上で覚えておきたいこと

ここまでの内容を、そのまま意思決定に使える形に圧縮する。疎な激活の本質的な利得は容量と計算の解耦であり、これによって総パラメータ数と token ごとの計算量が独立に調整できる二つのノブになる。ただしその代償として、問題の重心は演算から通信とスケジューリングへと移動する。ルーターの実装の細部は数学の式よりも成否を左右する。top-k の後で必ず再正規化すること、訓練期のノイズで softmax の早期飽和を抑えること、ルーターに bias を持たせないこと、容量は実際の負荷ではなく期待負荷で決めること——これである。capacity factor は一種の保険料であり、大きすぎると演算でルーティングの問題を覆い隠すことになり、覆い隠しすぎれば突然崩壊する。一方小さすぎると drop 比率が上がり、スループットを直接引きずる。

load balancing 損失は MoE のうち最も有名な勾配の衝突点である。ゲートパラメータに与える方向が、主タスクが専門化を求める方向と正反対であり、係数を手で調整する必要があり、再現性も悪い。勾配なしのバイアス項による均衡、z-loss、エントロピー正則化、ノード制限ルーティングはより「外科手術的」な四つの代替ルートであり、その中でバイアス法は「誰を選ぶか」と「選んだときの重み」を切り離すため、現行の自己回帰モデルにおいて最も実用的な一本になっている。shared expert は選択肢ではない。汎用能力をルーティングエキスパートの専門化の圧力から引き出し、決して潰れない勾配の集積点を提供するが、代償として疎化できない一式の密演算が発生する。粒度の混合が利得を示すには総パラメータ数の厳密な整合が必要であり、粒度が細かすぎるとデータ不足、通信の細分化、ルーターの困難さによって裏返る。

推論側で警戒すべきはルーティングの数値感度である。ルーターを高精度に保つこと、batch サイズが変わるとルーティング分布が変わること、量子化誤差が top-1 と top-2 の差が小さいときに拡大される。「同じテキストが異なる batch で一貫して生成されるか」を回帰テストに書き込むのは、コストが最も低く、得られるものが最も大きい防御手段である。最後に、あるプロジェクトが MoE を導入すべきかどうかを判断するには五つを見る。モデル規模、インターペース条件、サービス形態、運用能力、データ多様性だ。そのうち一つでも満たさなければ、密モデルがより良い工学的な選択である可能性がある——これは保守ではなく、MoE の複雑度のコストを意思決定に織り込んでいるということである。

公開設定から設計意図を逆算して読むことも、押さえておく価値のある読み方である。「大量のルーティングエキスパート+少数の激活」という組み合わせを見たら、細粒度のルートを辿っていると判断でき、このとき capacity factor の余裕は必ず小さく、負荷分散は本質的に高めになる。「ルーティングエキスパート数は少なく、各エキスパートが大きい」という組み合わせなら粗粒度のルートであり、容量の余裕への要求はより高い。shared expert があれば通常は均衡の圧力が大きく、モデル規模も大きいことを示唆している。これらの設定を一度読み通するだけで、その MoE の均衡戦略、並列分割、メモリの勘定についてかなり近い見立てがつく。訓練ログを一切見る必要はない。

2026 年のこの方向におけるコンセンサスは相当鮮明で、四文にまとめられる。ルーティングエキスパートはより細粒度になり、shared expert は標準装備になった。均衡の仕組みは勾配を持つ補助損失から、主タスクの勾配を汚さない統計的な補正へと移行しつつある。expert parallelism の通信最適化は通信回数の削減から、より細かい帯域マッチングへと移っている。推論側の量子化戦略は、ルーターを精度の禁区として扱う必要がある。この四文の背後にあるのは同じ一つの判断である——MoE の主要な矛盾は「訓練できるかどうか」から「効率的、安定的、予測可能に使えるか」へと移行した。

この方向に進んでいるチームにとって、この文はほぼ「どこにリソースを投入すべきか」を決めるものである。

最後に最初の判断へ戻る。MoE はもう「より安いモデル」ではなく、「より難しいモデル」である。通信とスケジューリングの複雑さと引き換えに、容量と計算の解耦を得た。規模が十分大きく、負荷が十分重く、インターペースが十分速いときにはこの交換は非常に割に合するが、それ以外では本来簡潔だったはずの方策を脆いものにしてしまう。自分が何を交換しているのかを明確に把握することは、MoE をやるという行為そのものの前提である。