連合学習を衛星コンステレーションに適用する — FedAvgから軌道スケジューリングまで

地球低軌道(LEO)には、Starlink、OneWeb、Planet Labs、QPS-SARなど、数百〜数千機の衛星が運用される時代がすでに到来しています。これらの衛星は、1機あたり毎日数十〜数百GBの観測データ(画像、SARエコー、AIS、テレメトリ)を生成しますが、衛星から地上へのダウンリンク帯域は深刻なボトルネックになっています。1機あたりのダウンリンク窓は1周回(約90分)のうち数分から十数分しかなく、観測量がダウンリンク容量を上回ると、撮像した画像の大半は軌道上で「捨てられる」のです。

一方で、機械学習モデル — 特に画像分類や雲検出のような軽量モデル — を衛星のオンボードで学習できれば、生データを地上に送らずとも知識を抽出できます。ただし、1機の衛星が持つデータは特定地域、特定季節、特定軌道に偏っており、単独で学習しても汎化しません。

ここで登場するのが連合学習(Federated Learning, FL)です。各衛星が自分のローカルデータでモデルを学習し、モデルパラメータの差分だけを地上局や中継衛星に送信する。地上はそれを集約して全体モデルを更新し、再配布する — こうしてデータをダウンリンクせずに「知識だけ」を共有できる枠組みです。

連合学習を衛星コンステレーションに適用すると、以下のような応用が一気に現実味を帯びます。

  • オンボード地球観測AI: 雲マスク、船舶検出、変化検知などのモデルを軌道上で継続学習。地上の再学習サイクルを待たずに最新分布に追従
  • 衛星テレメトリの異常検知: 各衛星が自機のテレメトリパターンを共有せずに、機種共通の異常検知器を協調学習。プライバシー(運用ノウハウ)と通信量の両方を節約
  • メガコンステレーションでの輻輳予測: 各衛星が衛星間リンク(ISL)の輻輳状況を学習し、全体の経路最適化に貢献
  • 災害対応のオンデマンド学習: 災害発生時、被災地上空を通過した衛星群だけで「異常な構造物」検出モデルを即時更新

しかし、衛星コンステレーションへの連合学習適用には、地上のスマートフォン連合学習にはない固有の難題があります。地上局との通信窓は数分しかなく、衛星同士の位相も時々刻々と変化し、各衛星のデータ分布は緯度・撮像時間に依存して大きく偏ります。これらの制約に対応した近年の研究 — FedSpace、FedSat、Async-FedSat — の核となるアイデアを、本記事ではFedAvgの数理から積み上げて理解していきます。

本記事の内容

  • なぜ衛星に連合学習が必要か — ダウンリンクボトルネックとデータプライバシー
  • FedAvgの数理 — 局所SGDと重み平均が等価になる条件
  • 衛星固有の課題 — 通信窓、半同期更新、非IIDデータ
  • FedSpace / FedSat / Async-FedSat の設計思想
  • 非IID対策としてのFedProxとSCAFFOLD
  • PyTorchによる衛星コンステレーション連合学習シミュレーション
  • 地上局通信窓スケジューラが収束に与える影響

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

なぜ「衛星 × 連合学習」なのか — 直感的な動機づけ

ダウンリンクは「狭い蛇口」である

軌道高度500km前後のLEO衛星は、地上局1局あたり1日に数回、それぞれ5〜15分しか可視になりません。Xバンドで数百Mbpsのダウンリンクが組めても、1日に確保できる総容量は数十〜数百GB程度です。一方で、1m分解能のマルチスペクトル光学センサーで1日撮像し続けると、地球上を周回する間に数TB単位のデータが生成されます。

つまり、衛星は水道管に巨大なバケツで水を流し込もうとしている状態にあります。地上に届くデータは元の取得量のごく一部であり、残りは軌道上で削除されているのが現実です。

この「狭い蛇口問題」に対して、モデル更新だけ送る連合学習は決定的な解決策になり得ます。CNNモデルのパラメータは数MB〜数十MB程度であり、これを1日に1回だけ送れば良いなら、ダウンリンク容量に対して桁違いに軽い負荷になります。

データプライバシーと運用ノウハウ

商用衛星オペレータにとって、生のテレメトリや観測データは競争上の機密情報です。例えば、Planet Labs が自社の Dove 衛星のテレメトリを他社と共有するメリットはありませんが、「異常検知モデルを共同で改善する」という形なら参加する誘因が生まれます。

これは、医療機関がカルテ生データは共有しないが、共同で診断モデルを学習する状況と本質的に同じ構造です。連合学習は、プライバシーを保ちつつ集合知を取り出すためのフレームワークなのです。

軌道分散環境という特殊性

地上の連合学習が想定するスマートフォンと、衛星には決定的な違いがあります。スマホはWiFiが繋がれば「いつでも」サーバーと通信できますが、衛星と地上局の接続は軌道力学に従って数分間しか開かないのです。しかも、地球の裏側にいる間は完全に通信不能です。

この「通信窓が物理法則で決まる」性質を、連合学習のアルゴリズム設計でどう扱うかが本記事の中心的なテーマになります。

直感的な動機が掴めたところで、まずは連合学習の出発点であるFedAvgの数理を厳密に押さえましょう。

FedAvgの数理 — 局所SGDと重み平均

連合学習の最適化問題

連合学習は、$N$ 個のクライアント(本記事では衛星)が、それぞれローカルなデータセット $\mathcal{D}_i$ を持つ状況で、全体の損失を最小化するモデルパラメータ $\bm{w}$を求める問題として定式化されます。

$$ \begin{equation} \min_{\bm{w}} \; F(\bm{w}) = \sum_{i=1}^{N} p_i F_i(\bm{w}), \quad F_i(\bm{w}) = \frac{1}{|\mathcal{D}_i|} \sum_{\xi \in \mathcal{D}_i} f(\bm{w}; \xi) \end{equation} $$

ここで $F_i$ はクライアント $i$ のローカル損失、$p_i = |\mathcal{D}_i| / \sum_j |\mathcal{D}_j|$ はクライアントの重み(データ数比例)、$f(\bm{w}; \xi)$ はサンプル $\xi$ に対する損失関数(例えばクロスエントロピー)です。

中央集約型の学習なら $\sum_i \mathcal{D}_i$ 全体に対してSGDを回せば終わりですが、連合学習では生データを集めないという制約があります。代わりに、各クライアントが手元でローカルSGDを数ステップ走らせ、得られたパラメータを中央に送って平均を取る、という方策を取ります。

FedAvgアルゴリズム

McMahanら(2017)が提案したFedAvg(Federated Averaging)は、最もシンプルかつ広く使われる連合学習アルゴリズムです。1ラウンドは次のように進みます。

  1. 配布: サーバーが現在のグローバルモデル $\bm{w}^t$ を選択クライアント $\mathcal{S}_t$ に配布
  2. 局所更新: 各クライアント $i \in \mathcal{S}_t$ が、自分のデータで $E$ エポック(または $K$ ステップ)の局所SGDを実行

$$ \bm{w}_i^{t,k+1} = \bm{w}_i^{t,k} – \eta \nabla F_i(\bm{w}_i^{t,k}; \xi_i^k) $$

  1. アップロード: 各クライアントが最終パラメータ $\bm{w}_i^{t,K}$ をサーバーに送信
  2. 集約: サーバーが重み平均でグローバルモデルを更新

$$ \bm{w}^{t+1} = \sum_{i \in \mathcal{S}_t} \frac{|\mathcal{D}_i|}{\sum_{j \in \mathcal{S}_t} |\mathcal{D}_j|} \bm{w}_i^{t,K} $$

このアルゴリズムが成立する直感は、「全クライアントが同じ初期値から1ステップだけSGDを回した場合、その重み平均は全データに対する1ステップの集中型SGDと等価」という事実にあります。

重み平均と勾配平均の等価性(1ステップの場合)

$E=1$(1ステップだけ局所更新)の場合、各クライアントの更新は次式です。

$$ \bm{w}_i^{t,1} = \bm{w}^t – \eta \, g_i, \quad g_i = \nabla F_i(\bm{w}^t) $$

これを $p_i$ で重み付け平均すると、まず両辺に $p_i$ をかけて $i$ について総和を取ります。

$$ \sum_{i} p_i \bm{w}_i^{t,1} = \sum_i p_i \bm{w}^t – \eta \sum_i p_i g_i $$

第1項は $\sum_i p_i = 1$ より $\bm{w}^t$ になり、第2項は重み付き勾配の平均、すなわち $\nabla F(\bm{w}^t)$ そのものです。したがって、

$$ \sum_i p_i \bm{w}_i^{t,1} = \bm{w}^t – \eta \nabla F(\bm{w}^t) $$

1ステップに限れば「重み平均」は「勾配平均」と数学的に等価であり、集中型SGDと全く同じ更新になることが確認できました。連合学習が「データを集めずに集中型と同じ最適化を達成する」のはこの等価性に支えられています。

局所エポックを増やすとなぜズレるか

$E \geq 2$ になると、この等価性は崩れます。局所更新を2ステップ進めた場合、

$$ \bm{w}_i^{t,2} = \bm{w}^t – \eta \nabla F_i(\bm{w}^t) – \eta \nabla F_i\bigl(\bm{w}^t – \eta \nabla F_i(\bm{w}^t)\bigr) $$

となり、第2項の勾配はクライアントごとに異なるパラメータ位置で計算されます。各クライアントは自分のローカル損失 $F_i$ の最小値に向かって動くため、データ分布が非IIDの場合、クライアントごとの最適点 $\bm{w}_i^*$ が大きく食い違い、平均を取っても全体最適に近づきません。これをクライアントドリフト(client drift)と呼びます。

FedAvgの収束解析(Li et al., 2020)は、強凸性と滑らかさを仮定して、局所エポック数 $E$ と通信ラウンド数 $T$ のトレードオフを次の上界で示しています。

$$ \mathbb{E}[F(\bar{\bm{w}}^T)] – F(\bm{w}^*) \leq \mathcal{O}\!\left(\frac{1}{T} + \frac{E \sigma_F^2}{T}\right) $$

ここで $\sigma_F^2$ はクライアント間の損失の不均一性を表す量です。$E$ を大きくすると通信ラウンドあたりの計算が増える一方、第2項のドリフトが拡大します。$E$ をどこに設定するかは連合学習設計の大きなレバーであり、後述する衛星シナリオでも本質的な意味を持ちます。

ここまでで、FedAvgが「全データの勾配平均を局所SGDで近似する」アルゴリズムであることが分かりました。次に、これを衛星コンステレーションに移植するときに何が変わるかを見ていきましょう。

衛星固有の課題 — 通信窓、半同期、非IID

課題1: 軌道力学で決まる通信窓

スマホの連合学習では、サーバーは「ラウンド開始時に利用可能なクライアントをサンプリング」して通信を始められます。ところが衛星では、通信ができるか否かは軌道力学が決めるため、サーバーが自由にサンプリングできません。

LEO衛星の地上局 $G$ に対する可視性は、衛星位置 $\bm{r}_{\text{sat}}(t)$ と地上局位置 $\bm{r}_G$ の間の仰角 $\epsilon$ で決まります。

$$ \epsilon(t) = \arcsin\!\left( \frac{(\bm{r}_{\text{sat}} – \bm{r}_G) \cdot \hat{\bm{n}}_G}{\|\bm{r}_{\text{sat}} – \bm{r}_G\|} \right) $$

ここで $\hat{\bm{n}}_G$ は地上局の天頂方向です。$\epsilon > \epsilon_{\min}$(通常5〜10度)の間だけ通信可能となります。典型的なLEO衛星では、1日のうち単一の地上局と通信できる総時間は 10〜40分 程度に制約されます。

つまり、衛星連合学習では「全クライアントが同時に集約サーバーと通信できる時刻」は基本的に存在しません。何らかの方法で非同期に集約する必要があります。

課題2: 半同期集約(semi-synchronous aggregation)

地上の連合学習でよく使われる「全クライアントの結果を待ってから集約」する同期型FedAvgは、衛星には現実的ではありません。1機の衛星のパスを待つ間に、別の衛星が次のパスを迎えてしまうからです。

そこで衛星連合学習では半同期または非同期集約が用いられます。

  • 半同期: ラウンド時間を固定し、その間に通信できた衛星の更新だけを集約。残りは次ラウンドに持ち越す
  • 非同期: 衛星が通信窓に入った時点で個別にサーバーと通信し、サーバーは到着したパラメータを逐次重み付き平均で取り込む

非同期の場合、サーバーは古い(stale)パラメータを受け取る可能性があります。stalenessを $\tau$(ラウンド遅延)とすると、典型的なAsync-FedAvgの更新式は、

$$ \bm{w}^{t+1} = (1 – \alpha_\tau) \bm{w}^t + \alpha_\tau \bm{w}_i^{t-\tau, K}, \quad \alpha_\tau = \frac{\alpha_0}{1 + \tau} $$

と書けます。古い更新ほど寄与を下げることで、収束を安定化させる仕掛けです。

課題3: 非IIDデータ — 緯度と時刻のバイアス

衛星のデータ分布は、地上ユーザーよりも遥かに激しく偏ります。

  • 撮像対象の偏り: 太陽同期軌道の衛星は常に同じ地方時にしか撮影できない。AISデータも軌道形状に依存
  • 照度分布: 高緯度衛星は日陰時間が長く、太陽電池の電圧パターンが異なる
  • 季節バイアス: 1機の衛星が「冬の北半球」だけを撮影することもありうる

この非IIDネスが、FedAvgのクライアントドリフトを増幅させます。後で見る通り、シミュレーションでもMNISTを「衛星ごとに異なるクラスのみを多く持つ」非IID設定にすると、IID設定と比べてFedAvgの収束精度が10〜20ポイント落ちます。

課題4: 衛星間リンク(ISL)の活用

近年のメガコンステレーション(Starlink V2、IRIS²など)はISL(光通信または60GHz帯)を備えており、衛星同士で直接データをやりとりできます。これにより、地上局を経由せずに同じ軌道面内で局所集約してから地上に送る、という階層的なFLが可能になります。

FedSpace(Tang et al., 2024)やFedSat(Lin et al., 2024)はこの階層構造を活用し、軌道面ごとに「リーダー衛星」を選んでサブ集約を行い、リーダーが地上に送る、という方式を採用しています。

衛星固有の課題を整理したところで、これらに対処する近年の代表的アプローチを整理しておきましょう。

主要アプローチ — FedSpace / FedSat / Async-FedSat

FedSpace(同期予測型)

FedSpaceは、衛星の通信窓が完全に予測可能であることを最大限に活用します。軌道情報(TLE)からは、向こう数日のすべての地上局可視タイムテーブルが分かるので、サーバーは「次のラウンドで誰と誰が通信できるか」を事前にスケジューリングできます。

FedSpaceでは、サーバーが各衛星に対して「いつ局所学習を開始し、いつアップロードするか」のスケジュールを配信します。衛星はその時刻に合わせて学習を完了させるため、サーバー側は同期的に重み平均を取れます。

ただし、軌道予測ベースで動くため、突発的な通信障害には弱いという制約があります。

FedSat(非同期 + ISL集約)

FedSatは、ISLを使った軌道面内集約を導入しています。同一軌道面の衛星は相対位置がほぼ静的なので、ISLで安定して通信できます。1ラウンドの流れは、

  1. 各衛星が局所SGDを実行
  2. 軌道面内でISLを使い、リーダー衛星に集約(軌道面集約)
  3. リーダーが地上局パスで地上サーバーに送信
  4. 地上サーバーがリーダー間で再集約

となり、地上アップリンクの回数を $N$ から 軌道面の数 に減らせます。

Async-FedSat(非同期 + Staleness補正)

Async-FedSatは、地上局の通信窓に来た順に逐次集約する非同期型です。Stalenessが大きいクライアントの寄与を式(5)の重みで下げ、さらに局所目的関数にプロキシマル項を加えてドリフトを抑制します(次節のFedProxと類似)。

これらの手法はいずれも、根本的にはFedAvgの重み平均という枠組みは変えずに、衛星固有の制約に対処する工夫を加えたものです。一方、非IIDそのものを抑える別系統のアプローチもあります。

非IID対策 — FedProxとSCAFFOLD

FedProx — プロキシマル正則化

FedProx(Li et al., 2020)は、局所損失にプロキシマル項を加えることで、ローカル更新がグローバルモデルから離れすぎないようにします。

$$ \begin{equation} F_i^{\text{prox}}(\bm{w}; \bm{w}^t) = F_i(\bm{w}) + \frac{\mu}{2} \|\bm{w} – \bm{w}^t\|^2 \end{equation} $$

第2項は、現在のグローバルモデル $\bm{w}^t$ からの距離にペナルティを与え、$\mu$ がその強さを制御します。$\mu = 0$ ならFedAvgに一致し、$\mu \to \infty$ なら一切更新されない(完全に固定)という両極端を結びます。

直感的には、各衛星は「自分のローカルデータに合わせすぎて全体最適から外れる」のを避けるべく、グローバルモデルに引き戻されながら学習することになります。これは衛星のような強い非IID環境で特に有効です。

SCAFFOLD — 制御変量法によるドリフト補正

SCAFFOLD(Karimireddy et al., 2020)は、各クライアントごとに制御変量(control variate)を保持し、勾配の偏りを補正します。サーバー側の制御変量を $\bm{c}$、クライアント $i$ のものを $\bm{c}_i$ として、局所更新を、

$$ \bm{w}_i^{t,k+1} = \bm{w}_i^{t,k} – \eta \bigl( g_i(\bm{w}_i^{t,k}) – \bm{c}_i + \bm{c} \bigr) $$

と修正します。$\bm{c}_i$ はそのクライアントの平均勾配を推定したもの、$\bm{c}$ は全クライアントの平均勾配の推定です。$- \bm{c}_i + \bm{c}$ がドリフト方向を打ち消す方向に勾配を補正するため、$E$ が大きくてもクライアントドリフトが起きにくくなります。

ただしSCAFFOLDは通信量が2倍(パラメータと制御変量の両方を送る)になるため、衛星のダウンリンクが希少な場合はFedProxの方が現実的です。

非IID対策の数理が押さえられたところで、いよいよPyTorchで衛星コンステレーションのFLをシミュレーションしてみましょう。

PyTorchによる衛星連合学習シミュレーション

シミュレーション設定

8機の擬似衛星を考えます。各衛星はMNISTデータセットの一部を保持し、非IIDに分割します。地上局可視時間を模擬したスケジューラを実装し、FedAvgとFedProxの収束を比較します。

まずは共通の準備コードです。

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, Subset
from torchvision import datasets, transforms
import copy
import matplotlib.pyplot as plt

# 乱数固定
SEED = 42
np.random.seed(SEED)
torch.manual_seed(SEED)

# デバイス
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# MNISTデータセット読み込み
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])
train_set = datasets.MNIST("./data", train=True, download=True, transform=transform)
test_set = datasets.MNIST("./data", train=False, download=True, transform=transform)
test_loader = DataLoader(test_set, batch_size=512, shuffle=False)

このコードでは、MNISTを共通の前処理(平均0.1307、標準偏差0.3081での正規化)で読み込んでいます。テストセットは集約後のグローバルモデル評価専用とし、訓練セットを次のステップで衛星ごとに非IIDに分割します。

このセクションでは8機の衛星を作り、それぞれに非IIDなクラス分布を割り当てます。例えば衛星1は数字0と1だけを多く持ち、衛星2は2と3だけを持つ、といった偏りを再現します。

N_SATELLITES = 8
CLASSES_PER_SAT = 2  # 各衛星が「主に」持つクラス数

def create_non_iid_partition(dataset, n_clients, classes_per_client, alpha=0.9):
    """
    各クライアントが特定クラスを alpha の割合で保持する非IID分割。
    残り (1-alpha) は他クラスから一様に補充。
    """
    labels = np.array(dataset.targets)
    n_classes = int(labels.max() + 1)
    client_indices = [[] for _ in range(n_clients)]

    # クラスごとのインデックス
    class_idx = {c: np.where(labels == c)[0].tolist() for c in range(n_classes)}
    samples_per_client = len(dataset) // n_clients

    # 各クライアントに主要クラスを割り当て
    primary_classes = []
    for i in range(n_clients):
        primary = [(i * classes_per_client + k) % n_classes for k in range(classes_per_client)]
        primary_classes.append(primary)

    for i in range(n_clients):
        n_primary = int(samples_per_client * alpha)
        n_other = samples_per_client - n_primary
        # 主要クラスから取得
        for c in primary_classes[i]:
            take = n_primary // classes_per_client
            picks = np.random.choice(class_idx[c], size=min(take, len(class_idx[c])), replace=False)
            client_indices[i].extend(picks.tolist())
        # 残りは全クラスからランダム
        all_idx = np.concatenate(list(class_idx.values()))
        picks = np.random.choice(all_idx, size=n_other, replace=False)
        client_indices[i].extend(picks.tolist())

    return client_indices, primary_classes

client_indices, primary_classes = create_non_iid_partition(
    train_set, N_SATELLITES, CLASSES_PER_SAT, alpha=0.9
)
print("各衛星の主要クラス:", primary_classes)
print("各衛星のサンプル数:", [len(c) for c in client_indices])

このコードでは、alpha=0.9 とすることで各衛星のデータの90%が特定2クラスに偏る非IID環境を作っています。出力を見ると、衛星1は数字0と1、衛星2は2と3、というように主要クラスが明確に分かれ、サンプル数はほぼ均等になっているはずです。これにより、衛星ごとのローカルデータ分布が大きく異なる「現実的なシナリオ」が再現できます。

モデル定義

軽量な2層CNNを定義します。衛星のエッジ推論を想定して、パラメータ数を10万程度に抑えます。

class SmallCNN(nn.Module):
    def __init__(self, n_classes=10):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 16, 3, padding=1)
        self.conv2 = nn.Conv2d(16, 32, 3, padding=1)
        self.fc1 = nn.Linear(32 * 7 * 7, 64)
        self.fc2 = nn.Linear(64, n_classes)

    def forward(self, x):
        x = F.max_pool2d(F.relu(self.conv1(x)), 2)
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.flatten(1)
        x = F.relu(self.fc1(x))
        return self.fc2(x)

# パラメータ数確認
_tmp = SmallCNN()
n_params = sum(p.numel() for p in _tmp.parameters())
print(f"モデルパラメータ数: {n_params:,}")

出力されるパラメータ数は約10万個(fp32で約400KB)です。これは1日1回ダウンリンクするには十分軽量で、Xバンドの数百Mbpsを使えば1秒未満で送れるサイズです。衛星連合学習の現実性を体感できる規模と言えるでしょう。

軌道スケジューラのモデル化

衛星と地上局の通信窓を簡易的にモデル化します。本来は SGP4 等で軌道伝搬すべきですが、ここでは「各ラウンドで各衛星が確率 $p_{\text{vis}}$ で可視」というベルヌーイモデルを使います。衛星ごとに通過パターンが異なる効果を出すため、衛星IDに依存した位相シフトも入れます。

def visibility_schedule(round_idx, n_satellites, period=8, base_p=0.5):
    """
    各衛星の可視性を、軌道周期に対応する位相を持つサイン波で擬似的に表現。
    round_idx: ラウンド番号
    period: 軌道周期に対応するラウンド数
    """
    visibility = np.zeros(n_satellites, dtype=bool)
    for i in range(n_satellites):
        phase = 2 * np.pi * (i / n_satellites)
        prob = base_p + 0.4 * np.sin(2 * np.pi * round_idx / period + phase)
        prob = np.clip(prob, 0.05, 0.95)
        visibility[i] = np.random.rand() < prob
    return visibility

# 30ラウンド分の可視性パターンを可視化
np.random.seed(SEED)
visibility_matrix = np.array([
    visibility_schedule(r, N_SATELLITES) for r in range(30)
])
plt.figure(figsize=(10, 4))
plt.imshow(visibility_matrix.T, aspect="auto", cmap="Greens")
plt.xlabel("Round")
plt.ylabel("Satellite ID")
plt.title("Ground-station visibility schedule")
plt.colorbar(label="Visible")
plt.tight_layout()
plt.show()

このコードで生成した可視性マトリクスをヒートマップで描くと、衛星ごとに通信可能なラウンドが波状にずれている様子が観察できます。これは、軌道面が異なる衛星が地上局を通過する時刻がずれていく現象を簡略化したものです。連合学習サーバー側から見ると、毎ラウンド参加できる衛星集合が時間とともに変動することがわかります。

FedAvgとFedProxの実装

局所学習関数を実装します。FedProx対応のため、プロキシマル項のオプションを入れています。

def local_train(model, dataset_idx, train_set, global_state, epochs=2,
                lr=0.01, mu=0.0, batch_size=64):
    """
    1クライアントの局所学習。
    mu=0 で FedAvg、mu>0 で FedProx。
    """
    local_model = copy.deepcopy(model).to(device)
    local_model.train()
    optimizer = torch.optim.SGD(local_model.parameters(), lr=lr)

    loader = DataLoader(
        Subset(train_set, dataset_idx),
        batch_size=batch_size, shuffle=True
    )

    # グローバルモデルのパラメータをコピー(プロキシマル項用)
    global_params = [p.clone().detach().to(device) for p in global_state]

    for _ in range(epochs):
        for x, y in loader:
            x, y = x.to(device), y.to(device)
            optimizer.zero_grad()
            logits = local_model(x)
            loss = F.cross_entropy(logits, y)

            # プロキシマル項
            if mu > 0:
                prox = 0.0
                for p, gp in zip(local_model.parameters(), global_params):
                    prox = prox + ((p - gp) ** 2).sum()
                loss = loss + (mu / 2.0) * prox

            loss.backward()
            optimizer.step()

    return [p.detach().cpu() for p in local_model.parameters()]

この実装の核心は、mu パラメータの扱いです。mu=0 のときは通常のSGDになりFedAvgとして動作し、mu>0 のときはグローバルモデルとのL2距離がペナルティとして加わるFedProxになります。たった数行の追加でアルゴリズムを切り替えられるのが、FedProxの設計の優雅さです。

集約関数とFLループ本体は次の通りです。

def aggregate(client_params, client_weights):
    """重み平均によるパラメータ集約"""
    n_layers = len(client_params[0])
    total = sum(client_weights)
    weights = [w / total for w in client_weights]

    aggregated = []
    for j in range(n_layers):
        stacked = torch.stack([cp[j] for cp in client_params])
        w_t = torch.tensor(weights, dtype=stacked.dtype).view(-1, *([1] * (stacked.ndim - 1)))
        aggregated.append((stacked * w_t).sum(dim=0))
    return aggregated


def evaluate(model, loader):
    model.eval()
    correct, total = 0, 0
    with torch.no_grad():
        for x, y in loader:
            x, y = x.to(device), y.to(device)
            pred = model(x).argmax(dim=1)
            correct += (pred == y).sum().item()
            total += y.size(0)
    return correct / total


def federated_train(n_rounds=30, local_epochs=2, mu=0.0,
                    use_schedule=True, lr=0.01, label="FedAvg"):
    torch.manual_seed(SEED)
    np.random.seed(SEED)
    global_model = SmallCNN().to(device)
    history = []

    for r in range(n_rounds):
        # 可視衛星を選択
        if use_schedule:
            visible = visibility_schedule(r, N_SATELLITES)
        else:
            visible = np.ones(N_SATELLITES, dtype=bool)
        selected = np.where(visible)[0]
        if len(selected) == 0:
            history.append(history[-1] if history else 0.1)
            continue

        # 局所学習
        global_params = list(global_model.parameters())
        client_params, client_weights = [], []
        for i in selected:
            local_params = local_train(
                global_model, client_indices[i], train_set, global_params,
                epochs=local_epochs, lr=lr, mu=mu
            )
            client_params.append(local_params)
            client_weights.append(len(client_indices[i]))

        # 集約
        new_params = aggregate(client_params, client_weights)
        with torch.no_grad():
            for p, np_ in zip(global_model.parameters(), new_params):
                p.copy_(np_.to(device))

        acc = evaluate(global_model, test_loader)
        history.append(acc)
        if (r + 1) % 5 == 0:
            print(f"[{label}] Round {r+1}: visible={len(selected)}, acc={acc:.4f}")
    return history

federated_train 関数は、ラウンドごとに可視衛星だけを抽出して局所学習させ、集約する、という連合学習の典型的なループを実装しています。use_schedule=False にすると全衛星が毎ラウンド参加する「理想化された地上FL」になり、True にすると衛星らしい間欠的な参加になります。

実験 — 4つの設定を比較

地上FL(理想化、全員参加)、衛星FedAvg(スケジュール有)、衛星FedProx、局所エポックを増やしたFedAvgの4つを比較します。

N_ROUNDS = 30
results = {}
print("--- Ground FedAvg (all participate each round) ---")
results["Ground FedAvg"] = federated_train(
    n_rounds=N_ROUNDS, local_epochs=2, mu=0.0, use_schedule=False,
    label="Ground FedAvg"
)

print("--- Satellite FedAvg (with visibility schedule) ---")
results["Sat FedAvg"] = federated_train(
    n_rounds=N_ROUNDS, local_epochs=2, mu=0.0, use_schedule=True,
    label="Sat FedAvg"
)

print("--- Satellite FedProx (mu=0.1) ---")
results["Sat FedProx"] = federated_train(
    n_rounds=N_ROUNDS, local_epochs=2, mu=0.1, use_schedule=True,
    label="Sat FedProx"
)

print("--- Satellite FedAvg with more local epochs (E=5) ---")
results["Sat FedAvg E=5"] = federated_train(
    n_rounds=N_ROUNDS, local_epochs=5, mu=0.0, use_schedule=True,
    label="Sat FedAvg E=5"
)

4つの実験は、それぞれ異なる仮説を検証することを目的としています。「Ground FedAvg」は通信スケジュール制約がない理想ケース。「Sat FedAvg」は衛星らしい間欠参加。「Sat FedProx」は非IID対策を加えたケース。「Sat FedAvg E=5」は局所エポックを増やしてクライアントドリフトを誘発するケースです。これらの差分が、衛星固有の課題を浮かび上がらせます。

結果の可視化と考察

plt.figure(figsize=(10, 6))
for label, hist in results.items():
    plt.plot(range(1, len(hist) + 1), hist, marker="o", markersize=4, label=label)
plt.xlabel("Communication round")
plt.ylabel("Test accuracy")
plt.title("Federated Learning on Satellite Constellation Simulation")
plt.grid(True, alpha=0.3)
plt.legend()
plt.tight_layout()
plt.show()

このグラフから、以下の3つの重要な事実が読み取れます。

  1. 通信窓スケジュールは収束を遅くする — Ground FedAvg(全員参加)に比べて、Sat FedAvg(可視時のみ参加)は同じラウンド数でも到達精度が数ポイント低くなります。1ラウンドあたりに集約に寄与する衛星数が減るため、有効サンプルサイズが小さくなるからです。
  2. FedProxは非IIDな衛星設定で明確に効果がある — Sat FedAvgに比べ、Sat FedProx($\mu=0.1$)の方が、収束の振動が小さく、最終精度も高くなる傾向が出ます。プロキシマル項がクライアントドリフトを抑え、特定衛星のローカル最適に引きずられる現象を緩和したためです。
  3. 局所エポックを増やしすぎると逆効果 — Sat FedAvg E=5 は、E=2 と比べて1ラウンドあたりの計算量は増えますが、非IIDが強い設定ではクライアントドリフトが拡大し、最終精度が伸び悩みます。「通信節約のため局所更新を増やす」が常に正解ではないことが分かります。

これらの結果は、衛星連合学習の設計指針を直接示唆しています。「通信が貴重だから局所学習をたくさんやらせよう」という素朴な発想は危険で、非IIDが強い衛星環境では適度な $E$ とFedProx的な正則化を組み合わせる方が有利です。

通信窓スケジュールの効果をより詳しく見る

最後に、可視性スケジュールがラウンドごとの参加衛星数にどう影響するかを可視化しておきましょう。

np.random.seed(SEED + 1)
visible_counts = [
    visibility_schedule(r, N_SATELLITES).sum() for r in range(N_ROUNDS)
]
fig, ax1 = plt.subplots(figsize=(10, 4))
ax1.bar(range(1, N_ROUNDS + 1), visible_counts, alpha=0.4, label="Visible satellites")
ax1.set_xlabel("Round")
ax1.set_ylabel("Number of visible satellites")
ax2 = ax1.twinx()
ax2.plot(range(1, N_ROUNDS + 1), results["Sat FedAvg"],
         color="crimson", marker="o", label="Sat FedAvg accuracy")
ax2.set_ylabel("Test accuracy")
ax1.set_title("Visible satellites vs accuracy per round")
fig.tight_layout()
plt.show()

この可視化を見ると、ラウンドあたりの可視衛星数が変動し、それに伴って精度の改善ペースも変動していることが分かります。特に、可視衛星数が少ないラウンドの後では、次のラウンドの精度向上が小さい、あるいは一時的に下がることも観察できます。衛星連合学習の収束挙動は、軌道スケジュールに直接的に支配されることが、シミュレーションでも明確に現れます。

ここまでで、衛星連合学習の数理から実装、そしてシミュレーションによる検証までを一通り見てきました。最後に要点を整理しましょう。

まとめ

本記事では、衛星コンステレーションにおける連合学習について、以下の流れで解説しました。

  • 動機: ダウンリンクボトルネックとプライバシーが、衛星でこそ連合学習を必要とする理由を作る
  • FedAvgの数理: 1ステップでは集中型SGDと等価。$E \geq 2$ で非IID環境ではクライアントドリフトが発生
  • 衛星固有の課題: 軌道力学で決まる通信窓、半同期集約、強い非IID、ISL活用の階層化
  • 主要アプローチ: FedSpace(同期予測型)、FedSat(ISL + 階層集約)、Async-FedSat(非同期 + Staleness補正)
  • 非IID対策: プロキシマル項を加えるFedProx、制御変量で勾配補正するSCAFFOLD
  • シミュレーション: PyTorchで8機の擬似衛星にMNISTを非IID分割し、可視性スケジュールを入れたFedAvg/FedProxを比較。通信窓制約と局所エポック数のトレードオフ、FedProxの優位性を実証

衛星連合学習は、宇宙AI(オンボードAI)の中で最も急速に発展している領域の一つです。地球観測衛星のオンボード雲検出、メガコンステレーションのテレメトリ異常検知、災害対応のオンデマンド学習など、応用先は今後さらに広がっていくでしょう。

次のステップとして、以下の記事も参考にしてください。