工場の設備は、起動・定常・高負荷といった複数の運転モードを行き来します。サーバー群も、昼のピークと夜間バッチでは「正常の顔」がまったく違います。こうした多変量時系列を1つのモデルで扱うと、モデルはすべてのモードを平均的に学ぼうとして、どのモードの正常もぼやけてしまいます。結果、正常なモード切替を異常と誤検出したり、逆にモードに紛れた本物の異常を見逃したりします。
さらに厄介なのが、変量どうしの「目標衝突」 です。流量・圧力・温度・振動・電流——これらを1つの共有ネットワークで同時に再構成しようとすると、各変量の最適化目標が競合し、どの変量もそこそこにしか学べません(Conflict-Aware Anomaly Detection が指摘した問題です)。
この2つの困難——多様な正常パターンと変量間の衝突——に効くのが、本記事のテーマ Bi-Attention × Mixture-of-Experts(MoE) です。Bi-Attentionで時間方向と変量方向の依存を捉え、MoEでパターンごとに専門家を割り当てて分担します。「全部を1人で平均的にこなす」のではなく、「適材適所で専門家に任せる」という発想です。
このアプローチを理解すると、次の場面で武器になります。
- マルチモード設備の監視: 運転モードが切り替わる産業設備で、モードごとに専門家が正常を学ぶ
- 多変量KPI監視(AIOps): 性質の異なる多数のメトリクスを、衝突させずに分担して扱う
- 大規模・多エンティティ: サーバー群・センサー群で、エンティティ差を専門家が吸収
本記事の内容
- なぜ単一モデルでは不十分か——複数モードと変量衝突という問題意識
- Bi-Attention(時間×チャネルの双方向注意)の仕組み
- Mixture-of-Experts(gating・top-k・負荷分散)の仕組み
- PyTorchでのスクラッチ実装と、専門家の特化・性能の実測
- 関連研究(Graph-MoE・Conflict-Aware・BAMoE)の位置づけ
前提・関連記事



全体像

処理の流れはシンプルです。多変量時系列の窓 $W$ を Bi-Attention で符号化して表現 $z$ を作り、router(gating) が $z$ を見て適切な専門家を選び、選ばれた専門家が窓を再構成します。再構成誤差が異常スコアです。正常データだけで学習し、未知の異常は「どの専門家でもうまく再構成できない」ため誤差が大きくなります。
カギは「窓ごとに違う専門家が担当しうる」点です。起動モードの窓は専門家A、高負荷モードの窓は専門家B——というように、正常の多様性を専門家で分担します。まず符号化の Bi-Attention から見ていきましょう。
Bi-Attention:時間とチャネル、2方向の注意
多変量時系列には2種類の依存があります。時間方向(各時刻が過去・未来とどう関係するか)と、チャネル方向(各変量が他の変量とどう関係するか)です。Bi-Attentionはこの両方を自己注意で捉えます。

左図の時間方向の注意は、窓を「$K$ 個の時刻ベクトル($m$ 次元)の列」とみて、時刻間の関係を測ります。周期性や前後関係を捉えるのが役割です。右図のチャネル方向の注意は、同じ窓を「$m$ 個のチャネル($K$ 次元)の集合」とみて、変量間の相関を測ります。「この流量ならこの圧力」という同時関係を捉えます。
自己注意の計算はどちらも同じ形です。入力 $X$ から query/key/value を作り、
$$ \begin{equation} \mathrm{Attn}(X) = \mathrm{softmax}\!\left(\frac{QK^\top}{\sqrt{d}}\right)V, \quad Q=XW_Q,\ K=XW_K,\ V=XW_V \end{equation} $$
時間方向では $X \in \mathbb{R}^{K \times d}$(時刻が並ぶ)、チャネル方向では $X \in \mathbb{R}^{m \times d}$(変量が並ぶ)として適用し、それぞれ平均プーリングで埋め込みを得て連結します。チャネル方向の注意が効くのは、相関の崩れ(ある変量だけ符号反転など) が多変量異常の典型だからです。後の実験で、チャネル注意を外すと性能が大きく落ちることを確認します。
データフローを式で追う
「双方向(bi)」とは、同じ窓を2つの軸に沿って2回エンコードし、その表現を束ねることを指します。窓 $W \in \mathbb{R}^{K \times m}$($K$ 時刻 × $m$ 変量)を起点に、流れを1段ずつ書き下すと次のようになります。
$$ \begin{align} \text{時間枝:}\quad & H_t = \mathrm{Attn}_t\!\big(W W_t^{\mathrm{in}}\big) \in \mathbb{R}^{K \times d}, \quad z_t = \frac{1}{K}\sum_{k=1}^{K} (H_t)_{k,:} \\ \text{チャネル枝:}\quad & H_c = \mathrm{Attn}_c\!\big(W^{\top} W_c^{\mathrm{in}}\big) \in \mathbb{R}^{m \times d}, \quad z_c = \frac{1}{m}\sum_{j=1}^{m} (H_c)_{j,:} \\ \text{統合:}\quad & z = \mathrm{ReLU}\!\big(W_p\, [\,z_t \,;\, z_c\,]\big) \in \mathbb{R}^{d} \end{align} $$
ポイントは枝ごとに注意のトークンが何かが違うことです。時間枝では $W$ をそのまま入れるので、各トークン=各時刻ベクトル(長さ $m$ を $d$ に射影)で、注意行列は $K \times K$ の「時刻間の関係」になります。チャネル枝では $W^{\top}$ を入れるので、各トークン=各変量の時系列(長さ $K$ を $d$ に射影)で、注意行列は $m \times m$ の「変量間の関係」になります。転置1つで注意の意味が切り替わるのが双方向注意の要点です。
最後に2つの表現を連結して線形射影 $W_p \in \mathbb{R}^{d \times 2d}$ で混ぜることで、「いつ・どの変量が・どう連動しているか」を1本の表現 $z$ に圧縮します。この $z$ がルーターと専門家の共通入力になります。
なお、この「2つの経路で別々に注意をかけ、束ねる」という設計は時系列異常検知で広く使われています。下図は Anomaly Transformer の Anomaly-Attention で、本記事の Bi-Attention とは枝の役割が異なります(片方が学習可能なガウス事前、もう片方が通常の系列注意)が、「2経路の注意を組み合わせる」という骨格は共通です。

出典: Xu et al., “Anomaly Transformer,” ICLR 2022 (arXiv:2110.02642), Fig.1。これは BAMoE の図ではなく、「2経路の注意を組み合わせる」同系統設計の代表例として引用。 上段(prior-association)と下段(series-association)という2つの注意経路が、最後に value 側で合流して1つの表現になっています。本記事の Bi-Attention も「2経路で別々に注意 → 統合」という同じ骨格で、経路を時間軸とチャネル軸に割り当てたものと捉えられます。
こうして得た表現 $z$ を、次は専門家に振り分けます。
Mixture-of-Experts:専門家を選び、分担する
Mixture-of-Experts(MoE) は、複数の小さなネットワーク(専門家)と、入力ごとにどの専門家を使うか決めるルーター(gating) からなります。

時系列異常検知での MoE の使われ方を、公開論文の全体図で押さえておきましょう。下図は Graph-MoE の全体構成で、左の符号化器(ここではGNN)が作った表現を Mixture of Experts に通し、Dynamic Expert Router が窓ごとに専門家の重みを決め、再構成/予測の誤差を異常スコアにします。本記事の Bi-Attention + MoE も、符号化器が Bi-Attention に置き換わるだけで、データの流れ(符号化 → ルーター → 専門家 → スコア)はこの図とほぼ同じです。

出典: Liu et al., “Graph Mixture of Experts and Memory-augmented Routers for Multivariate Time Series Anomaly Detection,” AAAI 2025 (arXiv:2412.19108), Fig.2。BAMoE の図ではなく、MoE を時系列異常検知に組み込んだ同系統の例。 符号化器(GNN)→ 複数の Expert → Router → スコアという骨格が、本記事の構成と対応します。
ルーターは表現 $z$ から各専門家への重みを出します。
$$ \begin{equation} g(z) = \mathrm{softmax}(W_g\, z) \in \mathbb{R}^{N} \end{equation} $$
全専門家を使うと重い・専門分化しないので、上位 $k$ 個(top-k) だけを選び、その重みを再正規化して使います。再正規化は、選ばれた集合 $\mathcal{T}=\mathrm{top\text{-}k}(g)$ の中だけで重みを足して1にする操作です。
$$ \begin{equation} \tilde{g}_e = \frac{g_e}{\sum_{e’ \in \mathcal{T}} g_{e’}} \quad (e \in \mathcal{T}), \qquad \tilde{g}_e = 0 \quad (e \notin \mathcal{T}) \end{equation} $$
最終出力は選ばれた専門家の重み付き和です。
$$ \begin{equation} \hat{W} = \sum_{e \in \mathcal{T}} \tilde{g}_e \cdot \mathrm{Expert}_e(z) \end{equation} $$
top-k には2つの狙いがあります。第一に計算量——専門家を $N$ 個用意しても、各窓で実際に走るのは $k$ 個だけなので、$N$ を増やして表現力を上げつつ推論コストを抑えられます(条件付き計算)。第二に専門分化を促す——常に全員を薄く混ぜると各専門家は「平均的な何でも屋」に収束しますが、毎回 $k$ 個に絞ると、各専門家は自分が選ばれる入力(=特定のモード)に集中して学べます。本記事の実装では $N=4,\ k=2$ を採用します。$k=1$(ハードルーティング)は最も尖りますが学習が不安定になりやすく、$k=2$ が分化と安定のバランス点でした。
ここで一つ問題があります。放っておくと、ルーターは少数の「強い」専門家ばかり選ぶようになり(rich-get-richer)、他の専門家が育たない専門家崩壊(expert collapse) が起きます。これを防ぐのが負荷分散損失(load-balancing loss) です。専門家ごとの平均使用率 $\bar{g}_e$ がなるべく均等になるよう、その散らばり(変動係数の2乗など)を罰します。
$$ \begin{equation} \mathcal{L}_{\text{lb}} = \frac{\mathrm{Var}_e(\bar{g}_e)}{(\mathrm{mean}_e\,\bar{g}_e)^2}, \qquad \bar{g}_e = \frac{1}{B}\sum_{i} g_e(z_i) \end{equation} $$
これは専門家ごとの平均使用率 $\bar{g}_e$ の変動係数の2乗(CV²) です。全員が均等(全 $\bar{g}_e$ が等しい)なら分散ゼロでペナルティ最小、誰かに偏ると分散が増えてペナルティが大きくなります。平均で割って正規化しているので、スケールに依らず「ばらつきの相対量」だけを罰せます。全体の損失は「再構成誤差 + 重み $\lambda$ × 負荷分散損失」です。
$$ \begin{equation} \mathcal{L} = \underbrace{\frac{1}{B}\sum_i \|W_i – \hat{W}_i\|^2}_{\text{再構成誤差(=異常スコア)}} + \lambda\, \mathcal{L}_{\text{lb}} \end{equation} $$
ルーターを賢くする:記憶拡張ルーター
素朴なルーター $g(z)=\mathrm{softmax}(W_g z)$ は「今の窓 $z$ だけ」で専門家を決めます。しかし運転モードの切替には時間的な文脈(直前までどのモードだったか)が効きます。Graph-MoE はこれを 記憶拡張ルーター(memory-augmented router) で実現します。

出典: Liu et al., AAAI 2025 (arXiv:2412.19108), Fig.3。BAMoE の図ではなく、ルーティングに時間的文脈を持ち込む同系統の例。 過去の記憶 $M_{t-1}$ を query、現在の表現 $\tilde{H}$ を key/value とする multi-head attention で文脈を取り込み(図中央)、Gate を経て更新された記憶 $M_t$ と専門家への重み $R$ を出します。ルーターが「点」ではなく「履歴」を見ることで、モード切替の前後でルーティングが滑らかになり、切替点での誤検出を抑えられます。本記事の実装では簡潔さを優先して素朴ルーターを使いますが、この記憶機構は実用系で効く拡張です。
変量ごとにルーターを分ける:multi-gate MoE(MMoE)
ここまでは1つのルーターが全変量分の再構成をまとめて担当しました。しかし変量ごとに最適な専門家が違うなら、ルーターも変量ごとに分けるべきです。これが multi-gate MoE(MMoE) で、変量(タスク)$k$ ごとに専用ゲート $g_k$ を持ち、共通の専門家プールを別々の配合で混ぜます。
$$ \begin{equation} y_k = \mathrm{Tower}_k\!\Big( \sum_{e=1}^{N} g_{k,e}(z)\, \mathrm{Expert}_e(z) \Big), \qquad g_k(z)=\mathrm{softmax}(W_{g_k} z) \end{equation} $$
Conflict-Aware Anomaly Detection (CAD) は、この MMoE 型で変量間の目標衝突を緩和します。下図がそのネットワークで、共有の専門家プール(expert 1…M)を、変量ごとのゲートが別々に重み付けし、各変量専用の tower が最終予測 $\hat{y}_k$ を出します。

出典: Si et al., “Beyond Sharing: Conflict-Aware Multivariate Time Series Anomaly Detection,” ESEC/FSE 2023 (arXiv:2308.08915, CC BY 4.0), Fig.2。BAMoE の図ではなく、MMoE で衝突を緩和する同系統の例。 注目点は p-gate(personalized)と s-gate(shared) の2系統ゲートです。共有ゲートが収束を安定させ、個別ゲートが専門家崩壊を防ぐ、という役割分担になっています。専門家を共有しつつゲートだけ分けることで、「共通の正常パターンは共有しながら、変量ごとの違いはゲートで吸収する」ことができます。
専門家1つの中身も見ておきましょう。CAD では各専門家が CNN → 平坦化 → 2層フィードフォワード という小さなネットワークで、畳み込みで局所的な時間変化を、共有フィルタで変量間の依存を捉えます。

出典: Si et al., ESEC/FSE 2023 (arXiv:2308.08915, CC BY 4.0), Fig.3。BAMoE の図ではなく、専門家の内部構造の一例。 RNN ではなく CNN を使うのは、並列計算できて局所的な変化に鋭いためです。本記事の実装では専門家を MLP にしていますが、畳み込み専門家に差し替える発展も自然です。
理論が揃ったので、実装して動かしましょう。
PyTorchでの実装
複数の運転モード(regime)を持つ合成多変量時系列で、Bi-Attention + MoE を実装・検証します。確かめたいのは、(1) 専門家がモードごとに特化するか、(2) 単一モデルより性能が上がるか、(3) チャネル注意・負荷分散が効くか、です。
複数モードを持つ合成データ
正常データが3つのregime(運転モード:低周波・高周波・位相ずれ)を切り替える多変量時系列を作ります。各regimeは「正常だが別物」です。テストには振幅変化・相関破壊・点異常を埋め込みます。
import numpy as np, torch, torch.nn as nn
from sklearn.metrics import roc_auc_score, average_precision_score, precision_recall_curve
np.random.seed(5); torch.manual_seed(5)
M, K, EPOCHS, R = 5, 24, 60, 3 # 変量数・窓長・エポック・regime数
def gen_series(N, anomalies=False, seed=0):
rng = np.random.default_rng(seed)
reg = np.zeros(N, int); seg = 150
for s in range(0, N, seg): reg[s:s+seg] = rng.integers(0, R) # 区間ごとにモード切替
X = np.zeros((M, N))
for i in range(N):
r = reg[i]
if r == 0: base=[np.sin(0.05*i), np.sin(0.05*i+0.2), 0.8*np.sin(0.05*i), np.sin(0.03*i), 0.5*np.sin(0.05*i)]
elif r == 1: base=[np.sin(0.18*i), -np.sin(0.18*i), np.sin(0.12*i), np.cos(0.18*i), np.sin(0.15*i)]
else: base=[np.sin(0.08*i+1.0), np.cos(0.08*i), np.sin(0.08*i-1.0), 0.6*np.cos(0.05*i), -0.7*np.sin(0.08*i)]
X[:, i] = np.array(base)
X += 0.05*rng.standard_normal((M, N)); lab = np.zeros(N, int)
if anomalies:
for s0 in [300, 900]: # 振幅変化
ln=18; X[:, s0:s0+ln] *= 1.35; lab[s0:s0+ln] = 1
for s0, ch in [(550, 2), (1250, 1)]: # 相関破壊
ln=16; X[ch, s0:s0+ln] = -X[ch, s0:s0+ln]; lab[s0:s0+ln] = 1
for c0 in [720, 1500]: # 点異常
ch = rng.integers(0, M); X[ch, c0:c0+2] += 1.3; lab[c0:c0+2] = 1
return X.T, lab, reg
Xtr, _, _ = gen_series(2600, False, 11); Xte, lab, rte = gen_series(1800, True, 29)
mu, sd = Xtr.mean(0), Xtr.std(0)+1e-8; Xtr, Xte = (Xtr-mu)/sd, (Xte-mu)/sd
def windows(X, lab=None, reg=None):
N=len(X); W=np.stack([X[i:i+K] for i in range(N-K+1)]); out=[W.astype(np.float32)]
if lab is not None: out.append(np.array([lab[i:i+K].max() for i in range(N-K+1)]))
if reg is not None: out.append(np.array([np.bincount(reg[i:i+K]).argmax() for i in range(N-K+1)]))
return out
Wtr, = windows(Xtr); Wte, yte, regw = windows(Xte, lab, rte)
Wtr_t, Wte_t = torch.tensor(Wtr), torch.tensor(Wte)
print("窓数 train=%d test=%d 異常率=%.3f" % (len(Wtr), len(Wte), yte.mean()))
各regimeは周波数・相関が異なり、単一モデルには「3つの別々の正常」を同時に学ぶ難題になります。

背景色が運転モードです。色が変わるたびに系列の振る舞い(周波数・相関)が切り替わっているのが分かります。これらはすべて「正常」です。
Bi-Attention + MoE モデル
自己注意ブロック、Bi-Attention符号化、MoEデコーダを実装します。use_channel/topk を切り替えてアブレーションもできるようにします。
import torch.nn.functional as F
class SelfAttn(nn.Module):
def __init__(self, d):
super().__init__(); self.q=nn.Linear(d,d); self.k=nn.Linear(d,d); self.v=nn.Linear(d,d); self.d=d
def forward(self, x): # x:(B,L,d)
q,k,v = self.q(x), self.k(x), self.v(x)
a = torch.softmax(q@k.transpose(1,2)/self.d**0.5, dim=-1)
return a@v
class BiAttnMoE(nn.Module):
def __init__(self, M, K, d=32, n_exp=4, topk=2, use_channel=True):
super().__init__()
self.M,self.K,self.d,self.n_exp,self.topk,self.use_channel = M,K,d,n_exp,topk,use_channel
self.t_in=nn.Linear(M,d); self.t_attn=SelfAttn(d) # 時間方向
self.c_in=nn.Linear(K,d); self.c_attn=SelfAttn(d) # チャネル方向
self.proj=nn.Sequential(nn.Linear(d*2 if use_channel else d, d), nn.ReLU())
self.router=nn.Linear(d, n_exp)
self.experts=nn.ModuleList([nn.Sequential(nn.Linear(d,d),nn.ReLU(),nn.Linear(d,M*K)) for _ in range(n_exp)])
def encode(self, W): # W:(B,K,M)
ht = self.t_attn(self.t_in(W)).mean(1) # 時間注意→平均
if self.use_channel:
hc = self.c_attn(self.c_in(W.transpose(1,2))).mean(1) # チャネル注意→平均
return self.proj(torch.cat([ht,hc], -1))
return self.proj(ht)
def forward(self, W):
B = W.shape[0]; z = self.encode(W)
gate = torch.softmax(self.router(z), -1) # (B,n_exp)
if self.topk < self.n_exp: # top-k 選択+再正規化
val, idx = gate.topk(self.topk, dim=-1)
mask = torch.zeros_like(gate).scatter(-1, idx, 1.0)
g = gate*mask; g = g/(g.sum(-1, keepdim=True)+1e-9)
else: g = gate
outs = torch.stack([e(z) for e in self.experts], 1) # (B,n_exp,M*K)
recon = (g.unsqueeze(-1)*outs).sum(1).view(B, self.K, self.M)
return recon, gate
def lb_loss(self, gate): # 負荷分散(importanceのCV^2)
if self.n_exp == 1: return gate.sum()*0.0
imp = gate.mean(0); return imp.var(unbiased=False)/(imp.mean()**2+1e-9)
エンコーダは時間注意とチャネル注意の埋め込みを連結し、ルーターがそこから専門家を選びます。各専門家は窓($K\times M$)を再構成する小さなMLPです。
学習と評価
損失は「再構成誤差 + 負荷分散損失」。スコアは再構成誤差です。
def train(model, epochs=EPOCHS, lb=1.0, seed=5):
torch.manual_seed(seed); opt=torch.optim.Adam(model.parameters(), 1e-3); hist=[]
for ep in range(epochs):
perm = torch.randperm(len(Wtr_t))
for i in range(0, len(Wtr_t), 128):
W = Wtr_t[perm[i:i+128]]; recon, gate = model(W)
loss = ((W-recon)**2).mean() + lb*model.lb_loss(gate)
opt.zero_grad(); loss.backward(); opt.step()
return model
@torch.no_grad()
def score(model, Wt):
recon, _ = model(Wt); return ((Wt-recon)**2).mean((1,2)).cpu().numpy()
def best_f1(y, sc):
p,r,_ = precision_recall_curve(y, sc); f = 2*p*r/(p+r+1e-12); return float(np.nanmax(f))
moe = train(BiAttnMoE(M,K,n_exp=4,topk=2,use_channel=True), lb=1.0) # 本命
single = train(BiAttnMoE(M,K,n_exp=1,topk=1,use_channel=True), lb=0.0) # 単一モデル(MoEなし)
sc_moe, sc_sg = score(moe, Wte_t), score(single, Wte_t)
print("MoE AUC=%.3f AP=%.3f F1=%.3f" % (roc_auc_score(yte,sc_moe), average_precision_score(yte,sc_moe), best_f1(yte,sc_moe)))
print("単一 AUC=%.3f AP=%.3f F1=%.3f" % (roc_auc_score(yte,sc_sg), average_precision_score(yte,sc_sg), best_f1(yte,sc_sg)))
実行結果は次のとおりです。
| 構成 | ROC-AUC | PR-AUC(AP) | 最良F1 |
|---|---|---|---|
| Bi-Attention + MoE | 0.934 | 0.592 | 0.634 |
| 単一モデル(MoEなし) | 0.914 | 0.512 | 0.621 |

損失は安定して収束します。次に、MoEの肝である専門家の特化を確認します。
専門家はモードごとに特化するか
各テスト窓で最も重みの大きい専門家(top-1)を調べ、「regime(モード) × 専門家」の担当割合をヒートマップにします。

左のヒートマップを見ると、regimeごとに担当する専門家が偏っているのが分かります。特にregime2は専門家4が54%を担当し、regime1は専門家1に寄っています。完全な1対1ではありませんが、「モードに応じて担当が分かれる」という専門分化が起きています。右の棒グラフは使用率で、負荷分散あり(緑)は4専門家がほぼ均等(1/N=0.25付近)に使われる一方、負荷分散なし(赤)は偏りが出やすいことを示します。負荷分散損失が「全員を活かす」役割を果たしています。
時系列で見ると、モード切替に追随して担当専門家が交代する様子がよりはっきりします。

背景色(regime)が変わるタイミングで、選ばれる専門家が切り替わっています。ルーターがモードを読み取って適切な専門家にルーティングできている証拠です。
再構成・スコア・検出性能
正常窓と異常窓で再構成を比べます。

正常窓では入力(黒)と再構成(赤破線)がよく一致し、異常窓では大きくずれて再構成誤差が跳ね上がります。この誤差をスコアにします。

赤帯(真の異常)でスコアが上昇し、閾値で検出できています。単一モデルとの比較が下図です。

Bi-Attention + MoE は単一モデルを ROC-AUC・PR-AUC ともに上回りました(AUC 0.934 vs 0.914、AP 0.592 vs 0.512)。特にPR-AUCの差が大きく、不均衡データでの実用性能で効いています。複数の正常モードを専門家で分担した効果です。
アブレーション
各要素の寄与を確認します。

- MoE vs 単一: F1 0.634 vs 0.621。MoEが上回る
- チャネル注意なし: F1 0.527 と大きく低下。チャネル方向の注意が最も効く要素で、相関破壊型の異常検出に不可欠
負荷分散損失については、このtop-2構成では性能(F1)への影響は小さく、主な役割は前述の使用率の均等化(専門家崩壊の防止) です。崩壊は特にtop-1の疎なルーティングや大規模設定で深刻になり、そこで負荷分散が効いてきます。
関連研究と BAMoE
MoEを時系列異常検知に応用する研究は活発です。
- Conflict-Aware Anomaly Detection (CAD): 変量ごとに回帰目標が衝突するという問題を指摘し、multi-gate MoE(MMoE) 的な「専門経路+共有」で緩和。本記事の問題意識の出発点です。
- Graph Mixture of Experts (Graph-MoE, AAAI 2025): GNNの階層的な多層グラフ情報をMoEで統合し、記憶拡張ルーター(memory-augmented router) が時間的な大域特徴で重み付けする、プラグアンドプレイな手法です。
- BAMoE: Bi-Attention Synergy with Expert Routing (ICASSP 2026): まさに本記事のテーマ——双方向注意とMoEルーティングを組み合わせた最新の一本です(本記事執筆時点で本文は未公開のため、ここでは構成要素の解説に留めます)。
これらに共通するのは、「1つのモデルですべてを担うのをやめ、専門家に分担させる」という発想です。多変量・マルチモード・大規模という時系列異常検知の現実的な難しさに対する、自然な回答といえます。
評価データセットについて
本記事は合成データで仕組みを検証しましたが、実手法は標準ベンチマークで評価されます。多変量時系列なら SMD(28エンティティ×38次元)・SWaT(51次元)・WADI(123次元)・SMAP・MSL・PSM が定番です。MoE型は特に、複数エンティティ・多モードを含むSMDやPSMで真価を問われます。評価指標は、point-adjust F1の水増し問題を避け、VUS-PRなど閾値非依存の指標を併用するのが望ましいでしょう。
まとめ
Bi-Attention × Mixture-of-Experts による多変量時系列異常検知を、理論から実装まで解説しました。
- 問題意識: 多様な正常モードと変量間の目標衝突を、単一モデルは平均的にしか扱えない
- Bi-Attention: 時間方向+チャネル方向の自己注意で、時間依存と変量相関を両取り
- MoE: gating+top-kで専門家を選び、負荷分散で全員を活かす
- 実測: 専門家がモードごとに特化し、単一モデルをAUC・AP・F1で上回る。チャネル注意が最重要要素
- 位置づけ: CAD・Graph-MoE・BAMoEなど、「分担」で現実の難しさに挑む潮流の一部
「全部を1人で」から「適材適所で分担」へ——MoEは、多様で大規模な時系列データに対する強力な設計思想です。



参考文献
- Y. Wang ら “Beyond Sharing: Conflict-Aware Multivariate Time Series Anomaly Detection (CAD).” (arXiv:2308.08915)
- L. Liu ら “Graph Mixture of Experts and Memory-augmented Routers for Multivariate Time Series Anomaly Detection.” AAAI, 2025 (arXiv:2412.19108)
- N. Shazeer ら “Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer.” ICLR, 2017(MoEと負荷分散の原典)