はじめに
前回は指数平滑法を使った異常検知について説明しましたが、今回はARIMAモデルを用いた異常検知の実装方法を紹介します。ARIMAモデルは移動平均や指数平滑法と比べて以下のような特徴があります:
- 自己相関を考慮した予測が可能
- トレンドや季節性を持つデータに対応
- 差分をとることで非定常なデータを扱える
- モデルの柔軟性が高く、複雑な時系列パターンを捉えられる
これらの特徴により、ARIMAモデルは複雑な時系列データの異常検知に適しています。本記事では、ARIMAモデルを用いた異常検知の方法を解説し、特に動的閾値の概念を導入して、より効果的な異常検知の実現方法を紹介します。
想定シナリオ
ある工場の製造ラインで機器の温度を1分ごとに測定しているとします。今回は、トレンドや季節性を持つデータにおいて、突発的な温度上昇などの異常を検出することを目指します。
必要なライブラリ
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA
サンプルデータの生成
1日分(1440分)の温度データを生成します。トレンドや季節性、そして異常な温度変化を含めます。
np.random.seed(42)
time = np.arange(1440)
trend = 0.005 * time # 緩やかな上昇トレンド
seasonality = 2 * np.sin(2 * np.pi * time / 1440 * 4) # 4サイクルの季節性
noise = np.random.normal(0, 0.5, 1440)
temp = 25 + trend + seasonality + noise
# 800分付近で異常な温度上昇を追加(10分間)
temp[800:810] += 10 # 異常を明確にするため10度上昇
# データをDataFrameに変換
dates = pd.date_range(start='2021-01-01', periods=1440, freq='T')
data = pd.DataFrame({'Temperature': temp}, index=dates)
ARIMAモデルの実装と動的閾値を用いた異常検知
ARIMAモデルを適用し、動的閾値を用いて異常を検出します。
# ARIMAモデルの適用
model = ARIMA(data['Temperature'], order=(5, 1, 2))
model_fit = model.fit()
# 予測値と信頼区間の取得
predictions = model_fit.get_forecast(steps=len(data))
mean_forecast = predictions.predicted_mean
confidence_intervals = predictions.conf_int()
# 動的閾値の計算
lower_bound = confidence_intervals.iloc[:, 0]
upper_bound = confidence_intervals.iloc[:, 1]
# 異常検知
anomalies = data[(data['Temperature'] < lower_bound) | (data['Temperature'] > upper_bound)]
結果の可視化
検出した異常をグラフで可視化します。
plt.figure(figsize=(12, 6))
plt.plot(data.index, data['Temperature'], label='観測値', alpha=0.7)
plt.plot(data.index, mean_forecast, label='予測値', color='orange')
plt.fill_between(data.index, lower_bound, upper_bound, color='orange', alpha=0.2, label='信頼区間')
plt.scatter(anomalies.index, anomalies['Temperature'], color='red', label='異常値')
plt.xlabel('時間')
plt.ylabel('温度 (°C)')
plt.title('ARIMAモデルと動的閾値による温度の異常検知')
plt.legend()
plt.show()
異常の数と時間の出力
print(f"検出された異常の数: {len(anomalies)}")
print("異常が検出された時間:")
for time in anomalies.index:
print(time)
解析と考察
動的閾値を用いた手法では、データの変動に応じて閾値が自動的に調整されるため、より適切な異常検知が可能になります。
動的閾値のメリット
- データの変動に適応: 時系列データの特性(季節性、トレンドなど)に応じて閾値が調整されます。
- 誤検知の低減: データの変動が大きい箇所では閾値が広がり、小さい箇所では狭まることで、より適切な異常検知が可能になります。
- モデルの不確実性の反映: 予測値の信頼区間に基づいて閾値が設定されるため、モデルの予測精度を考慮した検知が行えます。
検出された異常の特徴
- 突発的な変化: 急激な温度上昇や下降は、動的閾値を超えやすく、異常として検出されます。
- 持続的な逸脱: 通常のパターンから長時間逸脱し続ける場合も、動的閾値によって検出可能です。
改善の余地
- モデルの選択: データの特性に応じて、ARIMAモデルのパラメータ(p, d, q)を最適化することで、より精度の高い予測と異常検知が可能になります。
- 前処理の重要性: 季節性の除去やトレンドの調整など、適切な前処理を行うことで、ARIMAモデルの性能を向上させることができます。
- 他の手法との併用: 移動平均や指数平滑法など、他の異常検知手法と組み合わせることで、検出精度をさらに向上させることができます。
まとめ
ARIMAモデルと動的閾値を組み合わせた異常検知手法は、複雑な時系列データに対して効果的です。以下が重要なポイントです:
- モデルの特性理解: ARIMAモデルの特性を理解し、適切にパラメータを設定することが重要です。
- 動的閾値の利用: データの変動に応じて閾値を調整することで、より柔軟な異常検知が可能になります。
- 継続的な改善: モデルの性能を定期的に評価し、必要に応じて再学習や調整を行うことが重要です。
異常検知においては、データの特性や異常の種類に応じて最適な手法を選択し、継続的に改善を行うことが成功の鍵となります。
参考文献
(付録)コード
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA
# サンプルデータの生成
np.random.seed(42)
time = np.arange(1440)
trend = 0.005 * time
seasonality = 2 * np.sin(2 * np.pi * time / 1440 * 4)
noise = np.random.normal(0, 0.5, 1440)
temp = 25 + trend + seasonality + noise
# 異常の追加(10分間)
temp[800:810] += 10
# データをDataFrameに変換
dates = pd.date_range(start='2021-01-01', periods=1440, freq='T')
data = pd.DataFrame({'Temperature': temp}, index=dates)
# ARIMAモデルの適用
model = ARIMA(data['Temperature'], order=(5, 1, 2))
model_fit = model.fit()
# 予測値の取得
predictions = model_fit.predict(start=1, end=len(data)-1, typ='levels')
predictions.index = data.index[1:]
# 残差の計算
residuals = data['Temperature'][1:] - predictions
# 異常検知
threshold = 3 * residuals.std()
anomalies = residuals[np.abs(residuals) > threshold]
# 結果の可視化
plt.figure(figsize=(12, 6))
plt.plot(data.index, data['Temperature'], label='観測値', alpha=0.7)
plt.plot(predictions.index, predictions, label='予測値', color='orange')
plt.scatter(anomalies.index, data.loc[anomalies.index, 'Temperature'], color='red', label='異常値')
plt.xlabel('時間')
plt.ylabel('温度 (°C)')
plt.title('ARIMAモデルによる温度の異常検知')
plt.legend()
plt.show()
# 異常の数と時間の出力
print(f"検出された異常の数: {len(anomalies)}")
print("異常が検出された時間:")
for time in anomalies.index:
print(time)
結果
$ python arima_anomaly_detection.py
検出された異常の数: 5
異常が検出された時間:
2021-01-01 13:20:00
2021-01-01 13:21:00
2021-01-01 13:22:00
2021-01-01 13:30:00
2021-01-01 13:31:00
閾値が変動することが面白いですね。