はじめに
Anomaly Detector API の概要と、電力の「過去実績データ」を使った異常判定のチュートリアル紹介です。
検証データには、中部電力ホームページの「電力需給状況のお知らせ」にある「過去実績データ」を使います。
- Anomaly Detector API に興味を持っている
- Cognitive Service を勉強中である
- 気軽に機械学習や、異常検知を試してみたい
上記のような方向けの記事です。
ご安心ください、機械学習の前提知識はいりません。
Anomaly Detector API の特徴
「異常検知」に特化したAPIで、時系列数値データに含まれる
異常な挙動の検出を、簡単なREST APIで利用できます。
- 時系列データを監視し、その中の異常を検出
- 機械学習の知識はなくてOK
- アルゴリズムは、産業、シナリオ、データ量に関係なく、データに最適なモデルが自動的に特定され、適用されます。
各仕様について
APIメソッド
Anomaly Detector APIでは、以下2つのメソッドが使用できます。
- Detect anomaly status of the latest point in time series.
- Find anomalies for the entire series in batch.
違いは、
最終データポイントの判定結果を返すか
すべてのデータポイントの判定結果を返すか
というもので、リクエスト形式は同じものとなっています。
実利用においては、
「Find anomalies for the entire series in batch.」→パラメータ調整
「Detect anomaly status of the latest point in time series.」→リアルタイムでの異常検知
といった方法で使用するようです。
リクエスト形式
APIのリクエスト本文は、JSON形式でデータの粒度を示す「granularity」と時系列データ「series」が含まれる必要があります。
「granularity」
時系列の間隔を指定するパラメータ
「daily」、「minutely」、「hourly」、「weekly」、「monthly」、「yearly」の6パターンが指定できます。
「series」
「timestamp」と「value」をペアにした、オブジェクトの配列を指定。
データ数は最小で12、最大で8640とし、時系列でソートされている必要があります。
また、「timestamp」はISO 8601のUTCタイムスタンプ、
「value」は数値型とし、1つでも型の異なる値が入るとリクエストエラーとなります。
その他パラメータ
「sensitivity」、「period」、「maxAnomalyRatio」、「customInterval」が使用できるようです。
四半期ごとの時系列データを使用する場合は、以下のようなリクエスト本文を使ってみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "series": [ { "timestamp": "1972-01-01T00:00:00Z", "value": 826 }, { "timestamp": "1972-04-01T00:00:00Z", "value": 902 }, ... ], "granularity": "monthly", "customInterval": 3, "maxAnomalyRatio": 0.25, "sensitivity": 95, "period": 4 } |
データ欠損
時系列データセットでは、データポイントの欠落が生じることがあります。
データ欠損は期間全体の10%まで許容され
10%を超える欠損が含まれる場合は、リクエストエラーが発生します。
欠損は特に細分化されたデータ (数分ごとなどの短いサンプリング期間に、サンプリングされたデータなど) でよく見られます。
- 前の期間のデータポイントとの置き換え
- 線状補間、または移動平均など
その特徴に基づいて、データの不足部分を埋めることを検討します。
レスポンス
Anomaly Detector APIの成功時のレスポンスは、以下のようなJSON形式となります。
「expectedValues」
学習モデルから得られる予測値で、異常判定のマージン「upperMargins」、「lowerMargins」と組み合わせて閾値を求めることができます。
「isAnomaly」、「isNegativeAnomaly」、「isPositiveAnomaly」はそれぞれ異常判定の真偽値。
異常判定閾値から外れた場合は「true」となりますが、「isNegativeAnomaly」は下限閾値、「isPositiveAnomaly」は上限閾値から外れた場合のみの判定となっています。
「period」
データの周期性を示す値で、特定のパターンが何データポイントごとに現れるかの判定結果となります。
利用料金
Anomaly Detector では、多変量の異常検出がサポートされています。
この多変量機能は個別の API として提供され、プレビューでは無料で利用できます。
チュートリアルの開始
今回の検証には、電力需要データのうちの2018年4月~5月を使用します。
2018年データを可視化すると、電力データは気象条件に大きく影響を受け、夏季、冬季は特に冷暖房の使用等に伴って電力需要が増大することがわかります。
電力需要は1日、1週間単位で周期的なパターンを示しています。
人や業務機器が稼働する平日日中帯は電力需要が大きく、土日等の休日は相対的に電力需要が小さくなります。
4月~5月は冷暖房の使用が少なく電力需要は比較的安定しますが、期間にゴールデンウィークがあり、前後の週と比較して電力需要の落ち込みが発生します。
もちろんこれを「異常」とは言えませんが、「通常とは異なる」という点から異常検知のサンプルとして使ってみます。
1.Anomaly Detectorの作成
Azureポータルの「リソースの作成」から「Anomaly Detector」を選択して作成を開始します
必要な内容を入力して、作成を行います。
作成が完了したらAPIキーを取得します。
リソースに移動し、メニューの「キー」からAPIキーをコピーして控えておきます。
2.スクリプトの作成
今回は、Python で Anomaly Detector API にリクエストしています。
Anomaly Detector API は、 Face API といった Cognitive Services の API とリクエスト方法が変わらないため、簡単に利用できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
detect-anomaly.py # coding: utf-8 import pandas as pd import json import requests def main(): # 対象期間 begin = '2018-04-01 00:00:00' end = '2018-06-01 00:00:00' # 電力需要データの読み込み df = pd.read_csv("./areajuyo_current.csv", encoding="shift-jis") # データ加工 df["timestamp"] = df["DATE"] + " " + df["TIME"] df.loc[:, ["timestamp"]] = pd.to_datetime(df["timestamp"], format="%Y/%m/%d %H:%M") df = df[["timestamp", "実績(万kW)"]] df = df.rename(columns={"実績(万kW)": "value"}) # 検出対象データの抽出 df_req = df[(df.timestamp >= begin) & (df.timestamp < end)].reset_index() df_req # リクエスト用時系列データに変換 df_req.loc[:, ["timestamp"]] = df_req["timestamp"].dt.strftime("%Y-%m-%dT%H:%M:%SZ") series = json.loads(df_req.to_json(orient="records")) # リクエスト設定 api_key = "{APIキー}" # 取得したAPIキー endpoint = "https://westus2.api.cognitive.microsoft.com/anomalydetector/v1.0/timeseries/entire/detect" headers = { "Ocp-Apim-Subscription-Key": api_key, "Content-Type": "application/json" } body = { "series": series[:8640], "granularity": "hourly" } # Anomaly Detector APIにリクエスト res = requests.post(endpoint, headers=headers, json=body) results = res.json() # レスポンスの保存 json.dump(results, open("./response.json", "w"), indent=4) # 判定結果の統合、保存 del results["period"] df_res = pd.concat([df_req, pd.DataFrame(results)], axis=1) df_res.loc[:, ["timestamp"]] = pd.to_datetime(df_res["timestamp"], format="%Y-%m-%dT%H:%M:%SZ") df_res.to_csv("./detect_results.csv") if __name__ == "__main__": main() |
3.実行結果を見る
スクリプトを実行して得られた結果を可視化しましょう。
ゴールデンウィーク期間の、平日の電力需要を「異常」として検知できていました。
レスポンスの「period」を見ると168 (= 7×24)となっており、電力需要のパターンが1週間単位で現れることを判定できていました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
{ "expectedValues": [ 1077.67, ... ], "isAnomaly": [ false, ... ], "isNegativeAnomaly": [ false, ... ], "isPositiveAnomaly": [ false, ... ], "lowerMargins": [ 10.7767, ... ], "period": 168, "upperMargins": [ 10.7767, ... ] } |
異常判定の調整
リクエスト本文に「sensitivity」オプションを付加すると、異常検知の感度調整をすることができます。
感度が高すぎると判定マージンが狭くなり、誤検知も増えるため、「sensitivity」で本当の異常値のみを検知するよう調整します。
とはいえ、今回のデータは「異常」があるわけではないので「sensitivity」の調整による閾値、異常判定の変化を確認します。
スクリプトでは、リクエスト本文の設定を以下のように変更します。
1 2 3 4 5 6 |
body = { "series": series[:8640], "granularity": "hourly", "sensitivity": 99 # => 95, 90, 85 } |
「sensitivity」を99、95、90、85と下げていくと、判定マージンが広がり、異常と判定されるポイントが減少していくことがわかります。
「sensitivity」が99の場合は、設定しない場合と結果が同じであり、「sensitivity」のデフォルト値は99となっているようです。
まとめ
Anomaly Detector APIは、非常に簡単なリクエストでデータの時系列パターンを判定し、異常を検知できることがわかりました。
決まったパターンを持つ時系列データに対しては、モデル作成といった面倒な作業無しに、異常検知の仕組みを導入できそうです。
実行のたびに過去データを送信する必要があるなど、やや使い勝手の悪い面もあります。
Face APIのように、カスタマイズができるように変更が加えられ、
より使いやすいAPIになっていくことに期待したいところです。