Skip to content
Published on

Mixture of Experts(MoE)アーキテクチャ論文レビューと実践スケーリング 2026

Authors
  • Name
    Twitter
Mixture of Experts(MoE)アーキテクチャ論文レビューと実践スケーリング 2026

概要

大規模言語モデルのパラメータ数が数兆規模に拡大する中、すべてのパラメータを毎トークンごとに活性化するDenseモデルの限界が明らかになった。学習と推論のコストがパラメータ数に比例して増加するためである。Mixture of Experts(MoE)はこの問題に対する最も有力な解決策として定着した。MoEは全パラメータのうち一部のエキスパート(Expert)のみを選択的に活性化し、モデル容量は大きく維持しながら実際の演算量をDenseモデルと比較して劇的に削減する。

2022年にSwitch Transformerが単一エキスパートルーティングで兆単位パラメータ拡張の可能性を開き、GShardは600Bパラメータモデルの分散学習パイプラインを提示した。ST-MoEは学習安定性と転移学習品質を同時に引き上げ、Mixtral 8x7BはオープンソースMoEモデルの実用性を証明した。DeepSeek-MoEとDeepSeek-V3は細分化されたエキスパート分割と補助損失なしのロードバランシングという新しい方向を開いた。本記事では各論文の核心的貢献を分析し、ルーティングメカニズム、学習安定性、推論最適化までMoEを実践で運用するための戦略を整理する。

MoE核心概念

Sparse Activationの原理

MoEの核心的アイデアは条件付き演算(Conditional Computation)である。入力トークンごとに全エキスパートの一部のみを活性化するため、モデルの総パラメータ数と実際の演算に使用されるパラメータ数が分離される。例えば、Mixtral 8x7Bは合計47Bのパラメータを保有するが、各トークンは13Bのパラメータのみを使用する。Dense 47Bモデルと比較して演算量が約1/3.6に削減されながらも、品質は同等かそれ以上である。

Dense vs Sparse MoE比較

項目DenseモデルSparse MoE
活性パラメータ全パラメータ = 活性パラメータ全体の10-30%のみ活性化
FLOPs効率パラメータ数に比例活性パラメータ数に比例
メモリ要求量パラメータサイズ分全パラメータ格納が必要(メモリはより多い)
学習安定性比較的安定ルーティング不安定、エキスパート崩壊リスク
スケーラビリティ線形コスト増加エキスパート追加で低コスト拡張
推論レイテンシ予測可能ルーティングオーバーヘッド、エキスパートロード遅延
代表モデルLLaMA、GPT-4(推定)Mixtral、DeepSeek-V3、Switch Transformer

基本MoEレイヤー実装

MoEレイヤーの基本構造をPyTorchで実装すると以下の通りである。ゲーティングネットワークが入力トークンに対して各エキスパートの重みを計算し、Top-Kエキスパートを選択して出力を合算する。

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

class Expert(nn.Module):
    """単一エキスパートFFNモジュール"""
    def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1):
        super().__init__()
        self.w1 = nn.Linear(d_model, d_ff)
        self.w2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self.w2(self.dropout(F.gelu(self.w1(x))))

class MoELayer(nn.Module):
    """Mixture of Expertsレイヤー"""
    def __init__(
        self,
        d_model: int,
        d_ff: int,
        num_experts: int,
        top_k: int = 2,
        dropout: float = 0.1,
    ):
        super().__init__()
        self.num_experts = num_experts
        self.top_k = top_k
        self.experts = nn.ModuleList([
            Expert(d_model, d_ff, dropout) for _ in range(num_experts)
        ])
        # ゲーティングネットワーク: 入力 -> エキスパートスコア
        self.gate = nn.Linear(d_model, num_experts, bias=False)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # x: (batch, seq_len, d_model)
        batch_size, seq_len, d_model = x.shape
        x_flat = x.view(-1, d_model)  # (B*S, d_model)

        # ゲーティングスコア計算
        gate_logits = self.gate(x_flat)  # (B*S, num_experts)
        gate_probs = F.softmax(gate_logits, dim=-1)

        # Top-Kエキスパート選択
        top_k_probs, top_k_indices = torch.topk(
            gate_probs, self.top_k, dim=-1
        )  # (B*S, top_k)
        # 選択されたエキスパート重みの再正規化
        top_k_probs = top_k_probs / top_k_probs.sum(dim=-1, keepdim=True)

        # エキスパート別出力の合算
        output = torch.zeros_like(x_flat)
        for k in range(self.top_k):
            expert_idx = top_k_indices[:, k]  # (B*S,)
            weight = top_k_probs[:, k].unsqueeze(-1)  # (B*S, 1)
            for i in range(self.num_experts):
                mask = (expert_idx == i)
                if mask.any():
                    expert_input = x_flat[mask]
                    expert_output = self.experts[i](expert_input)
                    output[mask] += weight[mask] * expert_output

        return output.view(batch_size, seq_len, d_model)

この実装は教育目的であり、実際の本番環境ではMegablocksやTutelのような最適化ライブラリを使用してエキスパート別バッチ処理とAll-to-All通信を効率的に行う。

主要論文分析

Switch Transformer(Fedus et al., 2022)

Switch Transformerの核心的貢献は、MoEルーティングを極端に簡素化したことである。従来のMoEがTop-2以上のエキスパートを組み合わせていたのに対し、Switch Transformerは各トークンにたった1つのエキスパートのみを割り当てるSwitch Routingを導入した。この決定は直感に反するが、単一エキスパートルーティングが通信コストを半減し、ルーティング演算自体のオーバーヘッドも削減するという実験結果に裏付けられた。

T5-Baseアーキテクチャベースで同一演算量対比最大7倍の事前学習速度向上を達成し、兆単位パラメータモデルまで拡張した。bfloat16混合精度学習によりメモリ効率も改善し、Expert Dropoutと補助ロードバランシング損失を併用して学習不安定性を緩和した。

Switch Transformerが導入したエキスパート容量係数(Capacity Factor)は各エキスパートが処理できる最大トークン数を決定する。容量を超えるトークンはドロップされ、学習中の情報損失を引き起こすが、演算負荷のバランスを取るのに不可欠である。

GShard(Lepikhin et al., 2021)

GShardは、MoEを大規模分散環境で実用的に学習させるためのシステム設計に焦点を当てた論文である。600Bパラメータの多言語翻訳モデルを2,048個のTPU v3アクセラレータで4日間で学習させた。核心的な技術貢献は3つある。

第一に、Expert Parallelismである。エキスパートを複数のデバイスに分散配置し、トークンを該当エキスパートが位置するデバイスにルーティングするAll-to-All通信パターンを確立した。第二に、Random Routingである。Top-1エキスパートを決定論的に選択した後、2番目のエキスパートはゲート重みに比例する確率で確率的に選択する。この方式は学習時の探索(exploration)を促進する。第三に、XLAコンパイラ拡張により最小限のコード変更でさまざまな並列化パターンを表現できる軽量APIを提供した。

GShardのグループ別容量計算式 C = 2N / (G * E)は、グループサイズG、エキスパート数E、トークン数Nを考慮して各エキスパートの処理容量を動的に決定する。

ST-MoE(Zoph et al., 2022)

ST-MoEの名前が示す通り、この論文の核心的な問いは「MoEモデルを安定的に(Stable)学習させ、効果的に転移(Transferable)できるか」である。269Bパラメータのスパースモデル(ST-MoE-32B)を32B Denseモデル相当の演算コストで学習させ、SuperGLUE、ARC、XSumなど多様なベンチマークでスパースモデル初の最高性能を達成した。

学習安定性のためにRouter Z-Lossを導入した。この損失関数はルーターロジットの大きさを制御して学習中の発散(divergence)を防止する。既存の補助損失(auxiliary loss)と併用すると学習安定性が大幅に改善される。また、Top-2ルーティング、学習時容量係数1.25、コアあたり最大1エキスパート配置など、実践的設計推奨事項を体系的に整理した。

ファインチューニング時には、エキスパートドロップアウト率を上げ、バッチサイズを減らし、学習率を保守的に設定することが転移学習品質の向上に効果的であるという実験結果を示した。

Mixtral 8x7B(Mistral AI, 2024)

Mixtral 8x7Bはオープンソースの実用性を決定的に証明したモデルである。Mistral 7Bと同じTransformerアーキテクチャで各レイヤーのFFNを8つのエキスパートに置き換え、トークンごとにTop-2エキスパートを選択する。合計47Bパラメータのうち13Bのみ活性化されるため、推論速度は13B Denseモデルと同等でありながら品質はLLaMA 2 70Bに匹敵する。

項目Mixtral 8x7BLLaMA 2 70BMistral 7B
総パラメータ46.7B70B7.3B
活性パラメータ12.9B70B7.3B
推論FLOPs~26B~140B~15B
MMLU70.669.860.1
ARC-Challenge66.464.655.5
エキスパート数8--
活性エキスパート2--

Mixtralのエキスパート特化分析の結果、エキスパートはドメインよりも構文論的パターンに基づいて特化する傾向を示した。特定のエキスパートがコードや数学にのみ反応するのではなく、文構造やトークン位置に応じてエキスパート選択が変わるのである。

DeepSeek-MoEとDeepSeek-V3

DeepSeek-MoEは「究極のエキスパート特化(Ultimate Expert Specialization)」を目標に2つの核心戦略を提示した。

第一に、細分化されたエキスパート分割(Fine-Grained Expert Segmentation)である。既存のN個のエキスパートをmN個に細分化しつつ、各エキスパートのFFN中間次元を1/mに縮小して総パラメータ数を同一に維持する。代わりにmK個のエキスパートを活性化してより柔軟なエキスパート組み合わせを可能にする。DeepSeek-MoE 16BはLLaMA 2 7Bと同等の性能を演算量40%のみで達成した。

第二に、共有エキスパート(Shared Expert)の分離である。すべてのトークンが共通で使用するエキスパートを別途分離し、ルーティングエキスパート間の重複知識保存を防止してエキスパート別特化度を高める。

DeepSeek-V3はこれを671Bパラメータに拡張しながら、革新的な補助損失なし(Auxiliary-Loss-Free)ロードバランシングを導入した。256個のルーティングエキスパートと1個の共有エキスパートを使用し、トークンあたり8個のエキスパートを活性化する。従来の補助損失方式はロードバランシングとモデル性能の間にトレードオフが存在したが、DeepSeek-V3は各エキスパートにバイアス項を追加し、学習中のエキスパート負荷をモニタリングしてバイアスを動的に調整する。この方式により学習全過程でトークンドロップなしにバランスの取れたロードを維持する。

MoEモデル比較総合

モデル総パラメータ活性パラメータエキスパート数活性エキスパートルーティング方式ロードバランシング
Switch Transformer1.6T~1/E1281Top-1補助損失
GShard 600B600B~2/E20482Top-1 + Random容量制限 + 補助損失
ST-MoE-32B269B32B相当642Top-2Router Z-Loss + 補助損失
Mixtral 8x7B46.7B12.9B82Top-2補助損失
DeepSeek-V3671B37B256+18+1Top-8Auxiliary-Loss-Free

ルーティングメカニズム深層分析

Top-Kルーティング実装

ルーティングメカニズムはMoEの核心である。入力トークンのヒドゥンステートを受け取り、どのエキスパートを活性化するか決定し、この決定の品質がモデル全体の性能を左右する。さまざまなルーティング戦略の実装を見てみよう。

import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Tuple

class TopKRouter(nn.Module):
    """Top-Kルーティング with ノイズ注入"""
    def __init__(
        self,
        d_model: int,
        num_experts: int,
        top_k: int = 2,
        noise_std: float = 1.0,
    ):
        super().__init__()
        self.top_k = top_k
        self.num_experts = num_experts
        self.noise_std = noise_std
        self.gate = nn.Linear(d_model, num_experts, bias=False)

    def forward(
        self, x: torch.Tensor
    ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
        """
        Args:
            x: (batch * seq_len, d_model)
        Returns:
            top_k_indices: (batch * seq_len, top_k)
            top_k_weights: (batch * seq_len, top_k)
            gate_logits: (batch * seq_len, num_experts)
        """
        gate_logits = self.gate(x)

        # 学習時にノイズ注入で探索を促進
        if self.training:
            noise = torch.randn_like(gate_logits) * self.noise_std
            gate_logits = gate_logits + noise

        gate_probs = F.softmax(gate_logits, dim=-1)
        top_k_probs, top_k_indices = torch.topk(
            gate_probs, self.top_k, dim=-1
        )
        # 再正規化
        top_k_weights = top_k_probs / top_k_probs.sum(dim=-1, keepdim=True)

        return top_k_indices, top_k_weights, gate_logits

ルーティング戦略比較

戦略論文活性エキスパート長所短所
Top-1Switch Transformer1最小通信コスト、単純エキスパート崩壊リスク高
Top-2ST-MoE, Mixtral2安定的、高性能Top-1対比2倍の通信
Top-1 + RandomGShard2探索促進非決定的推論
Top-K (K >= 4)DeepSeek-V38細分化エキスパート活用エキスパート数が多い場合に有効
Expert ChoiceEC Routing可変完全なバランス保証トークン別エキスパート数不均一

Expert Choice Routingは従来のルーティングと視点を逆転させる。トークンがエキスパートを選択する代わりに、各エキスパートが処理するトークンを選択する。この方式はロードバランシングを完全に保証するが、特定のトークンがどのエキスパートにも選択されない可能性があるという問題がある。

学習安定性とロードバランシング

ロードバランシング損失の実装

MoE学習で最も一般的な問題はエキスパート崩壊(Expert Collapse)である。特定のエキスパートにトークンが集中すると、そのエキスパートのみが学習され、残りのエキスパートは活用されず事実上Dead Expertとなる。これを防止するために補助ロードバランシング損失を使用する。

def load_balancing_loss(
    gate_logits: torch.Tensor,
    top_k_indices: torch.Tensor,
    num_experts: int,
    top_k: int = 2,
) -> torch.Tensor:
    """
    Switch Transformerスタイルのロードバランシング損失計算

    各エキスパートに割り当てられたトークンの比率(f_i)と
    ゲート確率の平均(p_i)の内積を最小化する。
    均等分布の時に最小値を持つ。

    Args:
        gate_logits: (num_tokens, num_experts)
        top_k_indices: (num_tokens, top_k)
        num_experts: エキスパート数
        top_k: 活性エキスパート数
    Returns:
        スカラーロードバランシング損失
    """
    num_tokens = gate_logits.shape[0]
    gate_probs = F.softmax(gate_logits, dim=-1)

    # f_i: 各エキスパートに割り当てられたトークン比率
    # one-hotエンコーディングで割り当てカウント
    expert_mask = F.one_hot(
        top_k_indices, num_classes=num_experts
    ).float()  # (num_tokens, top_k, num_experts)
    expert_mask = expert_mask.sum(dim=1)  # (num_tokens, num_experts)
    tokens_per_expert = expert_mask.sum(dim=0)  # (num_experts,)
    f = tokens_per_expert / (num_tokens * top_k)

    # p_i: 各エキスパートに対する平均ゲート確率
    p = gate_probs.mean(dim=0)  # (num_experts,)

    # バランシング損失: num_experts * sum(f_i * p_i)
    balance_loss = num_experts * (f * p).sum()

    return balance_loss

Router Z-Loss

ST-MoEで導入されたRouter Z-Lossは、ルーターロジットの大きさを制御して学習発散を防止する。ロジット値が過度に大きくなるとsoftmaxの勾配が消失し学習が不安定になるが、Z-Lossがこれを抑制する。

def router_z_loss(gate_logits: torch.Tensor) -> torch.Tensor:
    """
    ST-MoE Router Z-Loss実装

    ルーターロジットのlog-sum-exp値の二乗平均を最小化。
    ロジットが過度に大きくなることを防止して学習安定性を確保。

    Args:
        gate_logits: (num_tokens, num_experts)
    Returns:
        スカラーz-loss値
    """
    # log(sum(exp(x)))の二乗
    log_z = torch.logsumexp(gate_logits, dim=-1)  # (num_tokens,)
    z_loss = (log_z ** 2).mean()
    return z_loss


# 学習ループでの統合
def compute_moe_loss(
    task_loss: torch.Tensor,
    gate_logits: torch.Tensor,
    top_k_indices: torch.Tensor,
    num_experts: int,
    alpha_balance: float = 0.01,
    alpha_z: float = 0.001,
) -> torch.Tensor:
    """
    全体MoE損失 = タスク損失 + バランシング損失 + Z-Loss
    """
    bal_loss = load_balancing_loss(
        gate_logits, top_k_indices, num_experts
    )
    z_loss = router_z_loss(gate_logits)

    total_loss = task_loss + alpha_balance * bal_loss + alpha_z * z_loss
    return total_loss

alpha_balanceとalpha_zの設定が重要である。補助損失係数が大きすぎるとモデル性能が低下し、小さすぎるとロードバランシング効果がなくエキスパート崩壊が発生する。ST-MoEはalpha_balance = 0.01、alpha_z = 0.001を推奨した。

Auxiliary-Loss-Freeアプローチ

DeepSeek-V3が導入した補助損失なしのロードバランシングは、このトレードオフを根本的に解消する。各エキスパートに学習可能なバイアス項を追加し、学習中のエキスパート別負荷をリアルタイムでモニタリングしてバイアスを調整する。

class AuxLossFreeRouter(nn.Module):
    """DeepSeek-V3スタイル Auxiliary-Loss-Freeルーター"""
    def __init__(
        self,
        d_model: int,
        num_experts: int,
        top_k: int = 8,
        bias_update_speed: float = 0.001,
    ):
        super().__init__()
        self.num_experts = num_experts
        self.top_k = top_k
        self.gamma = bias_update_speed
        self.gate = nn.Linear(d_model, num_experts, bias=False)
        # バイアスはグラディエント更新ではなくルールベースで調整
        self.register_buffer(
            "expert_bias", torch.zeros(num_experts)
        )
        self.register_buffer(
            "target_load", torch.ones(num_experts) / num_experts
        )

    def forward(self, x: torch.Tensor):
        gate_logits = self.gate(x)
        # バイアスを加えてルーティング決定に反映
        biased_logits = gate_logits + self.expert_bias.unsqueeze(0)
        gate_probs = F.softmax(gate_logits, dim=-1)

        # Top-K選択はバイアス含有ロジットで実行
        _, top_k_indices = torch.topk(
            biased_logits, self.top_k, dim=-1
        )
        # 重みは元のゲート確率から抽出
        top_k_probs = torch.gather(gate_probs, 1, top_k_indices)
        top_k_weights = top_k_probs / top_k_probs.sum(dim=-1, keepdim=True)

        # 学習時にバイアス更新(グラディエント不要)
        if self.training:
            with torch.no_grad():
                expert_load = F.one_hot(
                    top_k_indices.view(-1), self.num_experts
                ).float().sum(0)
                expert_load = expert_load / expert_load.sum()
                # 過負荷エキスパートはバイアス減少、低負荷は増加
                self.expert_bias += self.gamma * (
                    self.target_load - expert_load
                )

        return top_k_indices, top_k_weights, gate_logits

このアプローチの核心は、ルーティング決定(Top-K選択)にはバイアスを反映するが、エキスパート出力の重み計算には元のゲート確率を使用する点にある。バイアスがモデルのグラディエントフローに干渉しないため、性能低下なしにロードバランシングを達成する。

推論最適化

Expert OffloadingとCapacity Budgeting

MoEモデルの推論で最大の課題はメモリである。すべてのエキスパートをGPUメモリに常駐させることが困難な場合、エキスパートオフローディング(Expert Offloading)が不可欠となる。活性化されないエキスパートをCPUメモリやディスクに退避させ、必要な時のみGPUにロードする戦略である。

2025年以降の研究は、投機的デコーディング(Speculative Decoding)とエキスパートオフローディングを組み合わせて性能を大幅に改善した。SpecMoEOffは投機的デコーディングでエキスパート作業量を拡大してオフローディング遅延を隠蔽し、最大2.5倍のデコード処理量向上を達成した。MoE-Specは学習なしで各レイヤーのエキスパート容量を固定し、使用頻度の低いエキスパートをドロップすることで10-30%の処理量向上を示した。

from dataclasses import dataclass
from typing import Dict, Optional
import torch

@dataclass
class ExpertCacheConfig:
    """エキスパートキャッシュ設定"""
    gpu_cache_size: int = 4   # GPUに常駐するエキスパート数
    prefetch_count: int = 2    # 先行ロードするエキスパート数
    device: str = "cuda:0"

class ExpertOffloadManager:
    """
    エキスパートオフローディング管理者。
    LRUベースでGPUキャッシュを管理し、
    次レイヤーのエキスパートを先行ロードする。
    """
    def __init__(
        self,
        experts: Dict[int, torch.nn.Module],
        config: ExpertCacheConfig,
    ):
        self.experts = experts
        self.config = config
        self.gpu_cache: Dict[int, torch.nn.Module] = {}
        self.access_order: list = []

    def get_expert(self, expert_id: int) -> torch.nn.Module:
        """エキスパートをGPUにロード(LRUキャッシュ)"""
        if expert_id in self.gpu_cache:
            self.access_order.remove(expert_id)
            self.access_order.append(expert_id)
            return self.gpu_cache[expert_id]

        # GPUキャッシュが満杯の場合、LRUエキスパートを退避
        if len(self.gpu_cache) >= self.config.gpu_cache_size:
            evict_id = self.access_order.pop(0)
            self.gpu_cache[evict_id].cpu()
            del self.gpu_cache[evict_id]

        # エキスパートをGPUに移動
        expert = self.experts[expert_id].to(self.config.device)
        self.gpu_cache[expert_id] = expert
        self.access_order.append(expert_id)
        return expert

    def prefetch(self, predicted_expert_ids: list):
        """次レイヤーで使用されるエキスパートを非同期で先行ロード"""
        for eid in predicted_expert_ids[:self.config.prefetch_count]:
            if eid not in self.gpu_cache:
                self.get_expert(eid)

量子化とExpert Pruning

MoEモデルの推論効率を高めるもう1つの軸は量子化とエキスパートプルーニングである。すべてのエキスパートを同じビットで量子化する代わりに、頻繁に活性化されるエキスパートは高精度で維持し、めったに使用されないエキスパートは積極的に量子化する適応型量子化戦略が効果的である。Expert Pruningは学習後に活性化頻度が低いエキスパートを完全に除去してメモリ使用量を削減する。Mixtral 8x7Bで2つのエキスパートをプルーニングしても性能低下が軽微であるという実験結果が報告されている。

トラブルシューティング

MoEモデルの学習と推論で頻繁に発生する問題と解決策を整理する。

エキスパート崩壊(Expert Collapse)

症状:学習中に少数のエキスパートのみが活性化され、残りのエキスパートのゲート確率が0に収束する。TensorBoardでエキスパート別トークン割り当て比率を監視すると、特定のエキスパートに90%以上のトークンが集中しているのが確認できる。

原因はほとんどの場合、補助損失係数が小さすぎるか、学習初期のルーター重み初期化が不均衡な場合である。解決策として、alpha_balanceを0.01から始めて段階的に調整し、ルーター重みを小さい値で均一に初期化し、学習初期の一定ステップ間ルーティングをランダムに実行するウォームアップを適用する。

学習発散(Training Divergence)

症状:学習中にlossが突然急増またはNaNが発生する。MoEモデルで特にbfloat16学習時に頻発する。

Router Z-Lossを適用し、ルーターロジットに対して別途float32演算を維持することが効果的である。学習率をDenseモデル対比2-5倍低くすることも推奨される。

All-to-All通信ボトルネック

症状:分散学習時にエキスパート並列化のAll-to-All通信が全学習時間の30%以上を占める。

エキスパート数をデバイス数の倍数に設定し、容量係数を減らして転送データ量を削減する。通信と演算をオーバーラップするパイプラインスケジューリングも効果的である。Megablocksライブラリはブロックスパース演算を通じてAll-to-All通信を回避するアプローチを提供する。

推論時メモリ不足

症状:エキスパートがすべてGPUメモリに搭載できずOOMが発生する。

Expert Offloadingを適用するか、エキスパートプルーニングでメモリ要求量自体を削減する。テンソル並列化(Tensor Parallelism)とエキスパート並列化を組み合わせて複数GPUに分散させるのも方法の1つである。

失敗事例

補助損失係数の過大設定

あるチームがMixtralアーキテクチャベースのカスタムMoEモデルを学習する際、エキスパート崩壊を防止するためにalpha_balanceを0.1と高く設定した。ロードバランシングは完璧だったが、モデルがトークンの意味と無関係にエキスパートを均等に分配する方向で学習され、実際のタスク性能がDenseモデルより低くなった。補助損失が主損失を圧倒し、ルーターが意味のあるエキスパート特化を学習できなかったのである。alpha_balanceを0.005に下げてRouter Z-Lossを併用した後にようやく性能が改善された。

推論時Top-1切り替えの落とし穴

学習時にTop-2ルーティングを使用したモデルの推論コストを削減するためにTop-1に切り替えた事例がある。演算量は半減したが、ベンチマークスコアが8-12%低下した。2つのエキスパートの出力が相互補完的に学習されていたため、1つのみ使用すると表現力が大幅に損失する。推論コスト削減が目的であれば、学習段階からTop-1で設計するか、量子化とプルーニングでアプローチすべきである。

共有エキスパートなしの細分化失敗

DeepSeek-MoEの細分化されたエキスパート分割を適用しながら共有エキスパート(Shared Expert)を省略した事例がある。すべてのエキスパートが基本的な言語パターン(冠詞、前置詞、句読点など)を重複して学習し、エキスパートあたりの固有知識の量が減少し、細分化のメリットが相殺された。共有エキスパートが共通知識を吸収してこそ、ルーティングエキスパートが固有のパターンに集中できる。

運用チェックリスト

MoEモデルを学習からサービングまで運用する際に確認すべき項目を整理する。

学習設定

  • ルーター重みは小さい標準偏差(0.01以下)で初期化したか
  • 補助損失係数(alpha_balance)を0.005-0.01の範囲で開始したか
  • Router Z-Lossを有効化したか
  • 学習率が同サイズDenseモデル対比保守的(2-5倍低い)か
  • bfloat16学習時にルーターロジット演算はfloat32で維持しているか
  • エキスパート数がデバイス数の倍数か
  • 容量係数(Capacity Factor)を1.0-1.5の範囲で設定したか

モニタリング

  • エキスパート別トークン割り当て比率をリアルタイムで追跡しているか
  • Dead Expert(割り当て比率1%未満)数をアラートに設定したか
  • ルーターロジットの平均サイズと分散を追跡しているか
  • All-to-All通信時間対演算時間の比率を計測しているか
  • 学習lossの急激な変動(spike)に対する自動検知があるか

推論サービング

  • エキスパートオフローディング戦略を決定したか(全量GPU / 部分オフロード / ディスク)
  • 量子化レベルをエキスパート別に差異適用したか(頻繁に使用されるエキスパートは高精度)
  • エキスパートプルーニング後の性能低下をベンチマークで確認したか
  • バッチサイズに応じたルーティングオーバーヘッドを測定したか
  • エキスパートキャッシュヒット率(Hit Rate)をモニタリングしているか
  • 投機的デコーディング適用時にDraftモデルとエキスパート予測精度を確認したか

ファインチューニング

  • エキスパートドロップアウト率を事前学習対比上げたか(ST-MoE推奨)
  • バッチサイズを事前学習対比減らしたか
  • ルーターを固定(freeze)してエキスパートのみファインチューニングするオプションを実験したか
  • ファインチューニング後にエキスパート特化分布がタスクに合わせて変化したか確認したか

参考資料