Elasticsearchの異常検知ジョブにおける検知器/Detectorについて

# はじめに

 Elasticsearchの異常検知(Anomaly Detection)ジョブを作成するとき、作成するルールが一定の条件にマッチする内容であれば、Kibanaが用意しているウィザードを使って案内通りフィールド等を設定するだけで簡単に作成することができます。

 ウィザードを使ってどのようなジョブを作成できるのかについては以下の記事で詳しく説明しましたのでそちらも参照してください。

 https://qiita.com/daixque/items/7358726f5a6955476c94

 一方、特定機能向けのウィザードで作成できないような条件がある場合は、「高度な設定 / Advanced」のウィザードを使うか、[Create anomaly detection jobs API](https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-job.html)を利用するかすることで、細かなパラメータ調整、設定した上でジョブを作成することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/762d3e65-1bbc-7812-cab2-bad6f6ce3f4d.png)


 このように細かくパラメータを設定してジョブを作成するとき、最も重要になるのが検知器/Detectorの構成方法です。この記事では実装したい内容に対して適切な検知器/Detectorを設定するためのヒントを紹介したいと思います。


 # 検知器/Detectorとは

 Elasticsearchの異常検知/Anomaly DetectionはBucket Span単位での集計/Aggregationをベースに動いています。例えばBucket　Spanが15分でアクセスログを使ったアクセス回数に対するシングルメトリック分析を実施する場合、高度な設定に変更すると、検知器の設定は以下のようにcountというFunctionを使っていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/a6406657-0999-05d2-d54d-83ab5eb55250.png)

これはElasticsearchに対して以下のようなアグリゲーションを実行して得られるのと同等の集計をしています。

 ```json
"aggregations": {
    "buckets": {
        "date_histogram": {
            "field": "timestamp",
            "fixed_interval": "900000ms"
        }
    }
}
```

シングルメトリックから変換した場合、実際にこのようなaggregationをdatafeedで実行していますが、ジョブの作成方法によってはdatafeedではなく、Anomaly Detectionのジョブ側で集計している場合もあります。この辺りの仕組みはデータフィードの説明として別途解説したいと思います。

`count`で集計する場合には集計対象はドキュメント自体の数なのでこれ以上のパラメータの指定はなくとも期待通り動作しますが、例えば最大値や平均値を取得したい場合には、求める対象の数値をもつフィールドを設定する必要があります。それ以外にも「高度な設定/Advanced」でジョブを作成すると、以下のような画面で検知器/ディテクターを構成することができます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/965d4621-c8b0-dee1-21f8-4a527359f212.png)

以下ではここで指定できるそれぞれの項目の意味について紹介していきたいと思います。


## 関数 / Function

集計するときに使われる関数で、Elasticsearchの検索時に行われるaggregationに相当します。プルダウンから以下のように選択できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/472efc7c-3e7d-bd8a-f225-cece59e49470.png)

ここで例えばcountは単にバケット内にあるドキュメントの数を数えるfunctionです。そしてその下にあるhigh_count, low_countにも注目してください。これらはcount関数を適用した結果ドキュメントの件数をカウントした結果、通常よりもhigh_countならば高い時のみ、low_countならば低い時のみ異常として反応する関数です。count以外も多くの関数はhighまたはlowの接頭辞付きのものが選べますが、同様に動作します。

そのほか、それぞれの関数の動作については以下のドキュメントを参照してください。

https://www.elastic.co/guide/en/machine-learning/current/ml-functions.html


## フィールド　 / Field

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/05b266ca-43c2-f2e5-9657-6393284e23ee.png)

関数と対になる設定で、関数が集計する対象となるフィールドを設定します。これは常に定義するわけではなく、例えばcountのようにフィールドに依存しない集計の場合は設定する必要はありません。max/meanのようにフィールドに格納されている数値データに対して最大値・平均値のような集計を行う場合にその対象フィールドを指定します。


## フィールド別 / By field および  パーティションフィールド / Partition field

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/aeb4dd3c-9b54-61e0-3982-0390e9267e21.png)

例えばホストごとにCPUの利用率の推移の異常を検知したいような場合、他のホストとデータを分離してそれぞれのエンティティ単位で分析したいことがあります。この時に利用するのがこの「フィールド別 / By field」あるいは「パーティションフィールド / Partition field」です。

「フィールド別 / By field」はそれぞれのエンティティーを含んだ全体として学習を行うのに対し、「パーティションフィールド / Partition field」はエンティティーごとにデータを完全に分離した上で学習を行います。

ではどちらを使えば良いのか、ということになるわけですが、ここが少しややこしいところです。大雑把にいうと、分割した際のカーディナリティーが多い場合（利用しているマシンのスペックにもよりますが、10,000以上程度）の場合は「フィールド別 / By field」を利用した方が良いでしょう。

そのほか、新しいエンティティが新たに登場した時の挙動が異なります。「フィールド別 / By field」では、全体として一つのモデルを作成して傾向を学習しているため、新しいエンティティーが新規に登場した場合、それを異常と判定することができます。逆に「パーティションフィールド / Partition field」では、それぞれのデータが完全に分離されているため、そのエンティティーが（他に比べてめずらしく、あるいは全く新しく）登場した、ということは検知しません。このような場合の期待する動作が明確な場合は、上記の特徴を考慮して適切なフィールドを設定しましょう。

以下のdiscussも参考にしてください。

https://discuss.elastic.co/t/ml-kibana-difference-between-by-field-name-and-partition-field-name/193385


## オーバーフィールド / Over field

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/2da61ad1-aaf0-cf70-5f5e-007a87a12f91.png)


集団　/ Populationウィザードで利用されているフィールドです。この値を設定すると、ジョブの挙動が大きく変わります。通常のAnomaly detectionでは自分自身の時系列の中で、今検査しているバケットの値が通常時に比べて異常かどうかを判定します。

それに比べ、ここで「オーバーフィールド / Over field」を設定すると、対象としているフィールドの値（エンティティー）が他の値（エンティティー）に比べて異常かどうかをスコアリングするようになります。例えば他のホストに比べてCPUの利用率が高い、などを検知したい場合はホストIDを指定します。


## 頻繁なものを除外 / Exclude frequent

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/4809567a-8ee9-8f26-86ed-d60b9ce652b1.png)

「フィールド別 / By field」あるいは「オーバーフィールド / Over field」を指定している場合、そこに頻繁に出現する値については異常からは除外することができます。

- all : byおよびoverのフィールドに頻繁に出現するエンティティーは除外
- none : 除外しない
- by : byに頻繁に出現するエンティティーのみ除外
- over : overに頻繁に出現するエンティティーのみ除外


# まとめ

この記事では、「高度な設定 / Advanced」のウィザードを使うか、[Create anomaly detection jobs API](https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-job.html)を利用する場合にキーとなる検知器/Detectorの設定について説明しました。ElasticsearchにおけるAnomaly detectionの振る舞いを決定づける設定ですので、本番向けのジョブを作成する際などにはよく理解して適切な設定をし、効率的な異常検知を実装してください。