時系列データの異常値検知を5ステップで考えてみる
〜ロバスト統計から予測残差・AI拡張まで〜
はじめに
時系列データの異常値検出は、センサー監視、防災、水文データ、設備保全、IoT、ログ監視など、さまざまな分野で重要になります。
従来は、
- 平均値から大きく外れた値
- 標準偏差で見た外れ値
- 上限値・下限値を超えた値
を検出する考え方が中心でした。
しかし、現在の時系列異常検知では、単純な外れ値だけでなく、
時系列異常検知
- 文脈を考えるとおかしい値(contextual anomaly)
- 連続区間として異常なパターン
- 複数センサー間の関係性の崩れ
- 予測値から大きく外れる挙動
- 平均や分散が変化する変化点
を検出する考え方が重要になっています。
本記事では、時系列データの異常値検出を、実装しやすい順に 5ステップ で整理して、3段階で実装を行っていきたいと考えています。
対象とする異常の種類
時系列データの異常は、大きく次の3種類に分けられます。
| 種類 | 内容 | 例 |
|---|---|---|
| 点異常 | ある時刻だけ極端に大きい・小さい | センサー値の瞬間スパイク |
| 文脈異常 | 時刻・季節・前後関係を考えるとおかしい | 夜間では正常だが昼間では低すぎる値 |
| 集合異常 | 連続した区間としておかしい | 数時間続く水位上昇、長時間の通信量低下 |
単純な外れ値検出だけでは、文脈異常や集合異常は見逃す可能性があります。
そのため、実務では データ品質チェック、ロバスト統計、周期性、予測残差、多変量関係 を段階的に組み合わせることが重要になります。
全体ロードマップ
本記事では、以下の5ステップで進めます。
| Step | 内容 | 位置づけ |
|---|---|---|
| Step 1 | データ品質チェック | 必須の前処理 |
| Step 2 | ロバスト統計による簡易異常検知 | 第1段階プロトタイプ |
| Step 3 | 周期性・季節性を考慮した異常検知 | 実務向け改善 |
| Step 4 | 予測残差ベースの異常検知 | 本格的な時系列異常検知 |
| Step 5 | 多変量・変化点・AI拡張 | 発展版 |
最初から深層学習や時系列基盤モデルを使うのではなく、説明しやすく、運用しやすい方法から段階的に進めるのが現実的です。
Step 1:データ品質チェック
目的
異常検知モデルを使う前に、まずデータそのものの品質を確認します。
時系列データでは、モデルに入れる前の段階で、すでに以下のような問題が含まれていることがあります。
- 欠測
- 重複
- 時刻抜け
- 時刻のずれ
- 負値
- 物理上限・下限を超える値
- センサー停止
- 単位ミス
- 同じ値が長時間続く状態
これらは、AIモデルで検出する以前に、ルールベースで確認すべきものです。
例
雨量データの場合:
雨量 < 0 → 異常
雨量 > 300 mm/h → 異常候補
10分間隔データなのに30分空いている → 欠測
同一時刻が複数ある → 重複
水位データの場合:
水位が物理上限を超える
水位が長時間まったく変化しない
前時刻との差が急激すぎる
このステップで行うこと
- 時刻の連続性チェック
- 欠測チェック
- 重複チェック
- 物理上限・下限チェック
- 明らかなセンサー異常チェック
- データ品質フラグの付与
出力イメージ
timestamp, value, quality_flag
2026-01-01 00:00, 12.3, OK
2026-01-01 00:10, -1.0, NEGATIVE_VALUE
2026-01-01 00:20, NaN, MISSING
この品質フラグは、後続の異常検知モデルでも利用できます。
Step 2:ロバスト統計による簡易異常検知
目的
次に、まず動く異常検知を作ります。
ここでは、平均値や標準偏差ではなく、外れ値に強い 中央値 や MAD を使います。
MAD は Median Absolute Deviation の略で、中央値からの絶対偏差の中央値です。
基本式
score(t) = |x(t) - median_window(t)| / MAD_window(t)
異常判定の例:
score(t) > threshold なら異常
使う方法
- 移動中央値
- 移動MAD
- Hampel filter
- 前時刻との差分
- 変化率
- IQR
検出できる異常
- 瞬間的なスパイク
- 急激な落ち込み
- センサー値の飛び
- 短時間の異常値
- 明らかなノイズ
なぜロバスト統計を使うのか
平均値と標準偏差は、外れ値そのものに影響されやすいです。
例えば、1つだけ極端に大きい値が入ると、平均値も標準偏差も大きく変化してしまい、異常を見逃すことがあります。
一方、中央値やMADは外れ値の影響を受けにくいため、初期プロトタイプに向いています。
Python実装イメージ
import pandas as pd
import numpy as np
def detect_hampel_anomaly(series: pd.Series, window: int = 7, n_sigmas: float = 3.0) -> pd.DataFrame:
"""
Hampel filter による簡易異常検知。
Parameters
----------
series : pd.Series
時系列データ。
window : int
移動窓サイズ。
n_sigmas : float
異常判定しきい値。
Returns
-------
pd.DataFrame
値、移動中央値、MAD、異常スコア、異常フラグを含むDataFrame。
"""
rolling_median = series.rolling(window=window, center=True, min_periods=1).median()
diff = (series - rolling_median).abs()
mad = diff.rolling(window=window, center=True, min_periods=1).median()
# MADが0の場合のゼロ割対策
mad_safe = mad.replace(0, np.nan)
# 正規分布換算の係数 1.4826 を使用
score = diff / (1.4826 * mad_safe)
is_anomaly = score > n_sigmas
return pd.DataFrame({
"value": series,
"rolling_median": rolling_median,
"mad": mad,
"score": score,
"is_anomaly": is_anomaly.fillna(False)
})
Step 3:周期性・季節性を考慮した異常検知
目的
時系列データでは、時間帯・曜日・季節によって正常値が変わることがあります。
そのため、全期間に対して同じしきい値を使うと、誤検知や見逃しが発生します。
例
- 昼間は高くて正常
- 夜間に同じ値なら異常
- 雨期なら高い水位でも自然
- 平常期なら同じ水位でも注意
- 平日は高負荷、休日は低負荷
このようなデータでは、周期性や季節性を考慮する必要があります。
使う方法
- 時刻別平均
- 曜日別平均
- 月別平均
- STL分解
- 移動平均との差分
- 季節成分を除いた残差
STL分解の考え方
時系列を次のように分解します。
観測値 = トレンド + 季節成分 + 残差
異常検知では、このうち 残差 を使います。
residual(t) = observed(t) - trend(t) - seasonal(t)
残差が大きい場合、通常の季節変動では説明できない異常と考えます。
検出できる異常
- 時間帯を考えると不自然な値
- 曜日パターンから外れる値
- 季節性では説明できない急変
- 通常周期から外れた挙動
Step 4:予測残差ベースの異常検知
目的
ここから本格的な時系列異常検知になります。
考え方はシンプルです。
正常な値を予測する
↓
実測値との差を計算する
↓
予測から大きく外れたら異常
基本式
residual(t) = actual(t) - predicted(t)
異常スコアの例:
anomaly_score(t) = |residual(t)| / expected_error(t)
使うモデル候補
| モデル | 向いている場面 |
|---|---|
| ARIMA / SARIMA | 周期性が比較的明確な単変量時系列 |
| STL + 回帰 | 季節性を分離して扱いたい場合 |
| LightGBM / XGBoost | 外部説明変数がある場合 |
| LSTM / TCN | 非線形な時間依存がある場合 |
| Transformer | 長い依存関係を扱いたい場合 |
最初におすすめのモデル
最初から深層学習を使うより、以下から始めるのが現実的です。
- STL + 残差検出
- LightGBM による予測
- SARIMA による予測
特に、雨量・水位・流量などの防災・水文データでは、説明可能性を考えると、まずは STL + 残差 または LightGBM + 残差 が扱いやすいです。
検出できる異常
- 通常パターンからのズレ
- 予測外の急変
- 継続的な異常傾向
- 雨量に対する水位反応の異常
- 水位・流量の予測外変動
Step 5:多変量・変化点・AI拡張
目的
最後に、複数系列の関係性や、長期的な状態変化を扱います。
単一系列では正常に見えても、複数データの関係を見ると異常が分かる場合があります。
例
上流雨量が増えているのに下流水位が反応しない
隣接観測所と比べて1地点だけ値が不自然
ポンプ電流と吐出量の関係が崩れている
複数センサーの相関が突然変化した
使う方法
- PCA
- Isolation Forest
- LOF
- One-Class SVM
- AutoEncoder
- 変化点検出
- Transformer
- 時系列基盤モデル
変化点検出
異常値検出と似ていますが、変化点検出は「データの性質が変わった時刻」を探します。
異常値検出:一時的におかしい点や区間を探す
変化点検出:平均・分散・傾向が変わった時刻を探す
代表的な方法:
- CUSUM
- Page-Hinkley
- PELT
- Bayesian Change Point Detection
- ruptures
AI拡張
深層学習を使う場合は、以下のような手法があります。
- LSTM AutoEncoder
- VAE
- CNN AutoEncoder
- Transformer
- Anomaly Transformer
- TranAD
- 時系列基盤モデル
ただし、AIモデルは以下の課題があります。
- 正常データが十分に必要
- しきい値設定が難しい
- 説明性が弱い
- 学習・再学習コストが高い
- 運用後のデータドリフトに注意が必要
そのため、プロトタイプ段階では、AIは最初から主役にせず、Step 1〜Step 4 の結果を補助する位置づけで導入するのが良いです。
実装方針
実装する場合は、以下のような構成にすると拡張しやすいです。
timeseries_anomaly_detection/
├─ data/
│ ├─ raw/
│ ├─ processed/
│ └─ sample/
├─ src/
│ ├─ data_quality.py
│ ├─ robust_detector.py
│ ├─ seasonal_detector.py
│ ├─ forecast_detector.py
│ ├─ multivariate_detector.py
│ ├─ change_point_detector.py
│ └─ utils.py
├─ notebooks/
├─ outputs/
│ ├─ figures/
│ └─ reports/
├─ app/
│ └─ gui_app.py
├─ requirements.txt
└─ README.md
各モジュールの役割
| ファイル | 役割 |
|---|---|
| data_quality.py | 欠測、重複、物理範囲チェック |
| robust_detector.py | 移動中央値、MAD、Hampel filter |
| seasonal_detector.py | STL分解、周期性除去 |
| forecast_detector.py | 予測残差ベース検出 |
| multivariate_detector.py | PCA、Isolation Forest、LOF |
| change_point_detector.py | 変化点検出 |
| utils.py | 共通処理、時刻処理、可視化補助 |
| gui_app.py | GUIアプリ化する場合の入口 |
Pythonライブラリ候補
| ライブラリ | 用途 |
|---|---|
| pandas | 時系列データ処理 |
| numpy | 数値計算 |
| scipy | 統計処理 |
| statsmodels | STL、ARIMA |
| scikit-learn | Isolation Forest、LOF、PCA |
| ruptures | 変化点検出 |
| PyOD | 外れ値検出アルゴリズム |
| aeon | 時系列機械学習 |
| Merlion | 時系列予測・異常検知 |
| PyTorch | AutoEncoder、LSTM、Transformer |
第1段階プロトタイプの範囲
最初の実装では、以下までを対象にするのがおすすめです。
Step 1:データ品質チェック
Step 2:ロバスト統計
Step 3:周期性・季節性対応
この範囲であれば、
- 実装が比較的簡単
- 説明しやすい
- 誤検知の確認がしやすい
- GUI化しやすい
- 実務データに適用しやすい
というメリットがあります。
第2段階で追加する内容
第2段階では、予測残差ベースを追加します。
Step 4:予測残差ベース
この段階では、以下を実装します。
- 予測モデルの学習
- 実測値との差分計算
- 残差の異常スコア化
- 予測区間からの逸脱判定
- 異常区間の抽出
第3段階で追加する内容
第3段階では、多変量・変化点・AI拡張を追加します。
Step 5:多変量・変化点・AI拡張
この段階では、以下を扱います。
- 複数観測所の関係
- 雨量と水位の関係
- 水位と流量の関係
- センサー群の相関崩れ
- 平均・分散の変化点
- AutoEncoder や Transformer の検討
運用時に重要なポイント
異常検知は、モデルを作って終わりではありません。
実務では、次の点が重要になります。
- 異常スコアを保存する
- 検出理由を表示する
- しきい値を調整できるようにする
- 誤検知を確認できるようにする
- 正常・異常のラベルを後から追加できるようにする
- 検出結果をログとして保存する
- モデルやパラメータを再現できるようにする
- 将来的に再学習できる構成にする
特に、防災・水文・センサー監視では、説明可能性が重要です。
単に「AIが異常と判断した」ではなく、
どの値が
どの時刻に
どの基準を超えて
どの程度異常だったのか
を表示できるようにする必要があります。
まとめ
時系列データの異常値検出は、最初から高度なAIモデルを使うより、段階的に進めるのが現実的です。
おすすめの進め方は次の5ステップです。
進め方
- Step 1:データ品質チェック
- Step 2:ロバスト統計による簡易異常検知
- Step 3:周期性・季節性を考慮した異常検知
- Step 4:予測残差ベースの異常検知
- Step 5:多変量・変化点・AI拡張
特に第1段階では、
第1段階
- データ品質チェック
- 移動中央値 + MAD
- Hampel filter
- STL分解 + 残差
を中心に実装すると、説明しやすく、実務にも適用しやすいプロトタイプになります。
その後、予測残差ベース、多変量異常検知、変化点検出、深層学習、時系列基盤モデルへ拡張していくのがよいと考えます。
参考リンク
-
statsmodels STL decomposition
https://www.statsmodels.org/ -
scikit-learn anomaly detection
https://scikit-learn.org/stable/modules/outlier_detection.html -
Chronos: Learning the Language of Time Series
https://arxiv.org/abs/2403.07815
