저전력 자율 배치를 위한 위성 이상 대응 작업을 위한 확률적 그래프 신경망 추론
Source: Dev.to
Introduction: A Noisy Signal from the Edge of Space
3시 정각이었고, 나는 실험실에서 CubeSat 프로토타입의 텔레메트리 데이터 스트림을 바라보고 있었다. 위성 시뮬레이션은 라즈베리 Pi 4에서 실행되고 있었으며, 궤도 환경을 모방하기 위해 의도적으로 전력 제한이 걸려 있었다. 갑자기 추진 모듈의 온도 센서 값이 크게 진동하기 시작했는데, 전통적인 임계값 알람을 울릴 정도는 아니었지만 뭔가 잘못됐다는 신호는 충분히 전달했다. 온보드 규칙 기반 시스템은 여전히 침묵했다.
시스템 간 의존성을 수동으로 추적하면서 나는 내가 보고 있는 것이 무엇인지 깨달았다: 위성의 상호 연결된 하위 시스템을 통해 전파되는 연쇄적인 이상 현상. 이 순간은 근본적인 통찰을 확고히 했다: 복잡한 자율 시스템에서 이상은 고립된 사건이 아니라 네트워크 현상이다.
분산 위성 별자리와 그 고장 모드를 연구하면서 나는 전통적인 이상 탐지 접근법이 센서를 독립적인 존재로 취급하기 때문에 실패한다는 것을 알게 되었다. 실제로 전력 시스템의 열 이상은 통신 지연으로 나타날 수 있고, 이는 다시 자세 제어에 영향을 미친다. 그래프 신경망(GNN)은 이러한 관계를 포착할 수 있지만, 표준 GNN은 낮은 전력 환경에서 자율적인 의사결정에 필수적인 불확실성 정량화가 부족하다.
Technical Background: From Deterministic Graphs to Probabilistic Inference
The Graph Representation Problem
가장 어려운 부분은 이상을 탐지하는 것이 아니라 구조적·시간적 의존성을 모두 포착할 수 있는 방식으로 시스템을 표현하는 것이었다. 위성은 단순히 센서들의 집합이 아니라 다음과 같은 동적 네트워크이다:
- 물리적 연결(전원 버스, 데이터 버스, 열 경로)은 강한 의존성을 만든다.
- 기능적 의존성(자세 제어가 전력을 필요로 하고, 통신이 열 안정성을 필요로 함)은 부드러운 제약을 만든다.
- 시간 패턴(궤도 주기, 열 사이클, 통신 윈도우)은 시간에 따라 변하는 관계를 만든다.
전통적인 베이지안 네트워크는 텔레메트리 데이터의 고차원·시계열 특성을 다루는 데 한계가 있었다. 마코프 논리 네트워크는 유연성을 제공했지만, 엣지 하드웨어에서 실시간 추론을 수행하기엔 계산적으로 불가능해졌다.
Enter Probabilistic Graph Neural Networks
Probabilistic Graph Neural Networks(PGNNs)는 GNN의 표현력과 확률 모델의 불확실성 정량화를 결합한다. 여기서 중요한 두 종류의 불확실성이 있다:
- Aleatoric 불확실성 – 관측 자체에 내재된 잡음(센서 노이즈, 우주 방사선 효과).
- Epistemic 불확실성 – 제한된 학습 데이터로 인한 모델 불확실성(특히 희귀 이상 상황에서 중요).
Monte Carlo Dropout은 계산 효율적이지만, 우주 작전에서 흔히 발생하는 분포 외 상황에서는 불확실성을 과소평가하는 경향이 있다. 이는 그래프 구조에 맞게 조정된 베이지안 신경망 접근법을 탐구하게 만든 동기가 되었다.
Implementation Details: Building a PGNN for Satellite Operations
Graph Construction from Telemetry Data
각 하위 시스템은 현재 센서 값(정규화), 슬라이딩 윈도우 기반 평균·분산 등 과거 통계, 운영 모드 플래그, 마지막 정비 이후 경과 시간과 같은 특징을 갖는 노드가 된다. 엣지는 물리적 연결 행렬, 과거 데이터에서 학습된 상관 패턴, 알려진 기능적 의존성을 기반으로 만든다.
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch_geometric.nn import GCNConv, MessagePassing
from torch_geometric.data import Data
import numpy as np
class SatelliteGraphBuilder:
"""Constructs dynamic graph from satellite telemetry"""
def __init__(self, n_subsystems, history_window=100):
self.n_subsystems = n_subsystems
self.history_window = history_window
self.physical_adjacency = self.load_physical_connectivity()
def telemetry_to_graph(self, telemetry_batch):
"""Convert time‑series telemetry to graph representation"""
# Node features: [current_value, mean_10s, std_10s, trend]
node_features = []
for subsystem_id in range(self.n_subsystems):
current = telemetry_batch[subsystem_id, -1]
history = telemetry_batch[subsystem_id, -self.history_window:]
features = [
current,
np.mean(history),
np.std(history),
self.calculate_trend(history)
]
node_features.append(features)
# Dynamic edge weights based on correlation
edge_index, edge_attr = self.compute_dynamic_edges(telemetry_batch)
return Data(
x=torch.tensor(node_features, dtype=torch.float32),
edge_index=edge_index,
edge_attr=edge_attr
)
def compute_dynamic_edges(self, telemetry_batch):
"""Compute edges based on recent correlations"""
correlations = np.corrcoef(telemetry_batch)
edge_index = []
edge_weights = []
for i in range(self.n_subsystems):
for j in range(i + 1, self.n_subsystems):
if abs(correlations[i, j]) > 0.7: # Strong correlation threshold
edge_index.append([i, j])
edge_index.append([j, i]) # Undirected graph
weight = (self.physical_adjacency[i, j] * 0.3 +
abs(correlations[i, j]) * 0.7)
edge_weights.extend([weight, weight])
return (torch.tensor(edge_index, dtype=torch.long).t().contiguous(),
torch.tensor(edge_weights, dtype=torch.float32))
Probabilistic Graph Neural Network Architecture
PGNN은 결정론적 특징 추출과 확률적 불확실성 추정을 분리하여, 저전력 배치에 필요한 표현력과 계산 효율성 사이의 균형을 맞춘다.
class ProbabilisticGNNLayer(MessagePassing):
"""Single layer of probabilistic graph neural network"""
def __init__(self, in_channels, out_channels, dropout_rate=0.1):
super().__init__(aggr='mean')
# Deterministic transformation
self.deterministic_lin = nn.Linear(in_channels, out_channels)
# Probabilistic components
self.mu_lin = nn.Linear(in_channels, out_channels)
self.log_var_lin = nn.Linear(in_channels, out_channels)
self.dropout = nn.Dropout(dropout_rate)
self.activation = nn.ReLU()
def forward(self, x, edge_index, edge_weight=None):
# Message passing (aggregation)
aggregated = self.propagate(edge_index, x=x, edge_weight=edge_weight)
# Deterministic path
det_out = self.deterministic_lin(aggregated)
# Probabilistic path (mean and variance)
mu = self.mu_lin(aggregated)
log_var = self.log_var_lin(aggregated)
# Apply dropout and activation
det_out = self.dropout(self.activation(det_out))
mu = self.dropout(self.activation(mu))
log_var = self.dropout(self.activation(log_var))
return det_out, mu, log_var
이러한 구성 요소들은 엣지 하드웨어에서 실시간 이상 추론이 가능한 PGNN의 핵심을 이루며, 안전한 자율 위성 운영을 위해 예측값과 함께 보정된 불확실성 추정치를 제공한다.