ElasticsearchのData Frame Analyticsを使ってClassificationによる予測分類を実装する

# Data Frame Analyticsとは

ElasticsearchをPlatinum以上のライセンスで使うと、機械学習の機能が使えるようになります。もっともよく使われる機能は異常検知（Anomaly Detection）だと思いますが、そのほかにData Frame Analyticsという機能もあります。

PythonではPandasなどのライブラリを使って大きなデータセットに対してさまざまなデータの加工や分析を行うことができるデータフレーム分析の手法が一般化しています。これと同様の手法を使い、Data Frame Analyticsではインデックスに保存されているデータを使って機械学習モデルをElasticsearch上で作成することができます。

データフレーム分析では、説明変数と目的変数という概念が重要な役割を果たします。説明変数（または特徴量）は、予測モデルの入力として使用されるデータの属性を指します。これらは、モデルが予測を行うために使用する情報を提供します。例えば、顧客の年齢、性別、所得などが説明変数となることがあります。Data Frame Analysticsの機能上では「Included fields」で指定します。

一方、目的変数（または応答変数）は、モデルが予測または説明しようとする結果を指します。例えば、顧客が特定の製品を購入するかどうか、あるいは特定の病気を持っているかどうかなどが目的変数となることがあります。Data Frame Analysticsの機能上では「Dependent variable」で指定します。

Elasticsearchのデータフレーム分析では、これらの概念を使用して、大量のデータから説明変数と目的変数の間の関係を探索し、その関係を基に予測モデルを構築します。そしてそのモデルを使い、新しくElasticsearchに投入されるドキュメントに対してIngest Pipeline上で未知の情報を予測させ、元のドキュメントに付加させることを実現します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/1e6fa496-c437-a00c-d2e5-332bc2b4f026.png)

この記事ではKibanaに同梱されているサンプルデータ「Sample Flight Data」を使って、あるフライトがキャンセルされるかどうかを予測するモデルを作成してみようと思います。

この記事で利用するElasticsearchのバージョンは8.12.2です。


# できることとできないこと

上にある図を見てもらうとわかる通り、Data Frame Analyticsでは大きく分けて以下の二つのフェーズがあります。

 - データから学習してモデルを作成するフェーズ
 - 作成されたモデルを利用して、入力されたドキュメントに対して何らかの予測を行うフェーズ

ここで一つ重要なポイントとして、Anomaly Detectionと違ってData Frame Analyticsは時系列データを対象にしているわけではないということが挙げられます。したがって、何らかの時系列的な傾向から未来を予測するような挙動はData Frame Analyticsには適していません。例えばCPU利用率の傾向が増減を繰り返しながらも全体として増えてきている、というような特徴を掴みたいようなユースケースはAnomaly Detectionを利用しましょう。

逆にData Frame Analyticsでは入力されたデータ（ドキュメント）一つを見て、そのドキュメントに含まれている情報から、未知の情報を引き出す用途で利用できます。例えばこれまでにN回コールセンターに電話をしてきた人に対して、退会の可能性を予測するようなケースです。このような予測を実現するためには、どのような入力パターンの時に実際に求める結果になったのか（退会したのか）を学習（教師）データとして事前に学習をする必要があります。そのためData Frame Analytics（回帰/Reggressionと分類/Classification）は教師あり（Supervised）学習と分類されています。

また、当然のことながらそもそも入力されるデータに結果へ影響のある情報が含まれていない場合は正しく予測しようがありませんので、適切な学習データを用意することは何よりも重要です。






# キャンセルを予測する

ここではElasticsearchのData Frame Analyticsを使ってどのようなことが実現できるのかを知ってもらうために、サンプルデータを使った具体的な分析方法について紹介しようと思います。Kibanaに同梱されているフライト情報を元に、そのフライトがキャンセルするのかを予測するモデルを作っていきます。

予測には以下の情報を使うこととしました。

- 出発地
- 出発地の天候
- 目的地
- 目的地の天候
- 距離


## サンプルデータのインポート

まずKibanaトップページの「Try sample data」のリンクを開き、「Other sample data sets」を展開して「Sample flight data」をインポートしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/ec3713a4-c6c9-db8f-d9e1-0906abdc2f21.png)

インポートできると[Flights] Global Flight Dashboardというダッシュボードもインストールされます。このダッシュボードを見て、どのようなデータが取り込まれたのかの概要を掴んでおきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/5eee10f2-60fb-590f-a197-e263a01f99b2.png)

画面右上を見ると、遅れやキャンセルの情報が含まれているらしいことがわかりますね。


## Data Frame Analytics: Classification Jobの作成

まずData Frame Analyticsのジョブを作成していきます。Machine Learning > Data Frame Analytics JobsからCreate data frame analytics jobをクリックし、「Kibana Sample Data Flights」 Data Viewを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/91be7628-159e-d502-9e0d-e52f211150a9.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/0c7c8cbc-d5d7-b402-89d1-1cc6eba6a65c.png)

今回、予測したいのは「キャンセルする」「キャンセルしない」の2つの分類のどちらになるかを予測するモデルです。このような分類を行うモデルを作成するにはClassificationを選択します。もしも例えば遅延する時間が何分なのか、というような数値を予測するモデルを作成したい場合はRegressionを選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/ea49e500-f48e-b5bc-de2e-ceae9c77564b.png)

Dependent variableに、今回予測したい結果が入っているフィールドを選択します。キャンセルしたかどうかはCancelledフィールドにBoolean型で格納されているので、これを選択します。

Included fieldsには、予測に利用するフィールドを選択します。ここは結果に大きく関わる部分なので、結果に影響を及ぼすと思われるフィールドを適切に選択する必要があります。分析は一回で性能の良いモデルを作ろうと思わず、まずは広めに選択してそこから絞り込む形で試行錯誤していくのが良いでしょう。

今回は以下のフィールドを対象としました。

- Origin
- OriginWeather
- Dest
- DestWeather
- DistanceMiles

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/63bc554f-56d8-02e3-b6cb-20e3d458bc37.png)

Scatterplot Matrixには、現在ソースインデックスにあるデータの分布が可視化されています。これを使って、どのフィールドがどのフィールドと相関があるのかを掴んで、Included fieldsを選択する指標にすることができます。ここはあくまでデータの内容を素早く理解するために表示されているもので、実際の機械学習の何らかの結果が表示されているわけではありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/22a0c463-57b2-c4b2-5ea9-77471d31f214.png)

またこのグラフ表示の下にあるTrainig percentは目立ちませんが重要な設定です。これまでに機械学習の実装経験のある方ならお分かりの通り、機械学習を実行するとき、手元にあるデータの全てを学習に使うことはしません。一部を検証用に未学習の状態で残しておきます。こうすることで、作成されたモデルの性能が学習済みデータだけにフィットしてしまう過学習を起こしていないかを検証することができるようになります。このTraining percentでは入力されたデータの何％を学習に利用するのかを設定することができます。ここではデフォルトの80%をそのまま使うことにします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/5f6b77d2-e15c-a89e-ea61-522a2caa5f5e.png)

Additional optionsではその他データフレーム分析を実行する際のパラメータを指定することができます。ここでは細かい説明はしきれないので、別途[公式ドキュメント](https://www.elastic.co/guide/en/elasticsearch/reference/current/put-dfanalytics.html)を当たってください。またElasticsearchのData Frame Analyticsではアルゴリズムとして[XGBoost](https://xgboost.readthedocs.io/en/stable/)を利用しています。HyperparametersのチューニングはXGBoostの情報を参考にすると良いでしょう。

https://www.kaggle.com/code/prashant111/a-guide-on-xgboost-hyperparameters-tuning

今回は「Feature importance values」に5を指定しました。ここの値を設定することで、分析後にどのフィールドがどれだけ予測結果に影響があったのかを知ることができるようになります。

入力したらContinueをクリックして次に進みます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/65e03796-8082-282a-d674-a5ae0a87af1b.png)

ジョブIDやdescriptionは適切に入力して次に進みましょう。Validataionがかかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/cf144743-d7b5-b4b0-30f1-158d71453a39.png)

今回Feature importance valuesに5を指定したため、負荷がかかりそうだという警告が出ていますが、意図したものなのでこのまま進みます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/dd9a50a2-9673-fad8-39a0-903bf368c408.png)

Start immediatelyをチェックした状態のままCreateをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/921ae158-91ec-3166-f529-1aaa4e8ed262.png)

これで無事ジョブが作成されてそのまま実行されます。多少時間がかかりますが、完了するとこのような画面が表示されるのでView Resultsをクリックして結果を確認しましょう。

## 結果の確認

View ResultsをクリックするとData Frame Analyticsの実行結果を確認する画面が表示されます。この画面はメインメニューからMachine Learning > Data Frame Analytics Jobsの画面から、作成したジョブのViewをクリックすることによっても辿ることができます。

### Analysis

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/642eab85-fa4b-98c1-d12b-e71e3931f61b.png)

画面上部のAnalysisを展開すると、実行時の学習データおよびテストデータの数、実行日やメモリー使用量、各種ハイパーパラメータなど、どのような定義でこのジョブが実行されたのかを知ることができます。

### Model evaluation

Model evaluationでは作成したモデルの性能が評価できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/311790e8-7156-c5fe-ae9b-a759ea9644d0.png)

最初のテーブル表示を確認することで、実績としてCancelledの値に対して予測した結果と実績の比較ができます。

 - falseだったもののうち、falseと予測されたものが73%(正答), trueが2７%
 - trueだったもののうち、falseと予測されたものが28%, falseが72%(正答)
 
また、その下のグラフ表示では、中央の斜め線のbaseline(全く出鱈目に予測した場合の正答率)に比べ、今回のモデルの正答率がどうだったかを示すラインがグリーンで表示されています。

### Total feature importance

Total feature importanceも作成されたモデルを理解するのに非常に有用な情報です。このモデルでは入力されるどのフィールドが結果にどれだけ影響を及ぼしているのかを知ることができます。ジョブ作成時、「Feature importance values」に5を設定していたため、影響のあったトップ５のフィールドが表示されています。

今回の結果では、目的地の天候(DestWeather)、ついで出発地の天候(OriginWeather)が特に強い影響を及ぼしていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/38d29a62-319f-ba09-b39e-cce2f7b28c85.png)

### Results

Resultsには、具体的に個々のデータについて、それぞれのデータが実際にどのように予測されたかを確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/7e66b73d-83e4-8962-4fcf-5685f84e12f0.png)

ml.で始まるフィールドは、Data Frame Analyticsのよって作成されたフィールドです。したがってml.CancelledがMLによる予測値、Cancelledは元データにあった実績値になります。

またml.is_trainingは学習に使われたかどうかを表しています。

## 結果の評価

おおよそのモデルの評価はすでに見てきました。約73%程度の精度で結果を予測するモデルができているようです。しかし、この数値は元データ全体に対しての分析結果です。

しかし今回Training percentを80%に指定していました。そこで学習データとテストデータ（未学習データ）で性能がどの程度違うかを確認してみましょう。

画面の右上にTrainingとTestingという二つのタブ表示があります。ここでTrainingを押すと学習に使われたデータのみ、Testingを押すと未学習データのみに分析結果を絞り込むことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/5a06e560-7cf3-cd90-0e3a-01c26a05b76e.png)

Trainingをクリックして学習済みデータに対しての性能を確認しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/4a206ea5-84f6-3e37-bfa2-9f08e7e1c0bc.png)

おおよそ全体の場合と性能は変わっていないようです。

次にTestingをクリックして未学習データでの性能を確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/5b5d6533-9ee7-f510-72da-8f1999aec482.png)

Cancelledがtrueの場合の性能が低下していることがわかります。つまりいわゆる過学習を多少なりとも起こしているということですね。キャンセルされた実績データの少ないことが原因でしょうか。今回Elasticが作成しているサンプルデータに対して分析しているのでこれ以上の原因分析は意味をなさないのでしませんが、このようにテストデータでの性能がどうなっているかについては十分注意してください。

ジョブ作成のところでも書いたように、１回の学習で高性能なモデルを作ることは難しいと思います。選択するフィールドやパラメータなど、いくつかのパターンを試しながら精度の高い結果を出すモデルができるまでチューニングしていきましょう。

## 作成したモデルを使って予測する

さて、精度はどうであれとにかくData Frame Analyticsのジョブを実行してある予測をするモデルが作成できました。学習だけしても何らかの予測を実際に行わなければ作成した意味がありません。この記事の最初に説明したように、通常Elasticsearchでは新たに投入されるドキュメントに対してIngest Pipelineを利用してこのモデルを使った予測結果を追加することでこのモデルを利用します。

先ほどはジョブの実行結果を確認していましたが、ジョブと作成されたモデルは別のリソースです。作成済みのモデルはMachine Learning > Trained Modelsに表示されます。ここにはData Frame Analyticsで作成したモデル以外にも、ベクトル検索などで利用するテキストのエンべディング用のモデルなども表示されます。（日本語のベクトル検索については[こちらの記事](https://qiita.com/daixque/items/931b8be343075b835097)を参照して下さい。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/25f24afe-a7d9-6746-c718-d4803da26e25.png)

ここで先ほどのジョブで作成したモデルflight_cancel_prediction_job-1709359438780が（番号が末尾についた形で）表示されていることがわかります。マウスオーバーすると「Test model」ボタンが表示されるのでクリックしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/ac4fba87-8369-3ad3-05ba-c13f9dc0b49d.png)

すると画面上に具体的なドキュメントのサンプルが表示されます。「Simulate pipeline」ボタンをクリックすると、そのドキュメントに対してこのモデルで予測を行った場合の出力結果がResult欄に表示されます。このResultのmlオブジェクト以下に予測結果の情報が入ってくるので確認してみましょう。

Pipelineの定義についてはView request bodyを展開すると具体的な実装が確認できます。モデルの利用はInference Processorによって実現しますので、詳細は[Inference Processorのドキュメント](https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-processor.html)を確認してください。

もしデータ投入契機ではなく任意のデータに対してこのモデルを利用した予測を実施したい場合は、[Infer trained model API](https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-trained-model.html)が利用できます。以下はこのAPIを使って二つのフライトに対してキャンセルされるかどうかを予測する例です。

```
POST _ml/trained_models/flight_cancel_prediction_job-1709359438780/_infer
{
  "inference_config": {
    "classification": {
      "num_top_feature_importance_values": 0,
      "num_top_classes": 0
    }
  },
  "docs": [
    {
      "Dest": "Cagliari Elmas Airport",
      "Origin": "El Dorado International Airport",
      "OriginWeather": "Sunny",
      "DestWeather": "Rain",
      "DistanceMiles": 1000
    },
    {
      "Dest": "Dubai International Airport",
      "Origin": "Al Maktoum International Airport",
      "OriginWeather": "Thunder & Lightning",
      "DestWeather": "Rain",
      "DistanceMiles": 1000
    }
  ]
}
```

結果は以下のようになりました。

- 晴天のEl Dorado International Airportから雨のCagliari Elmas Airportに向かう場合はキャンセルしない
- 雷のAl Maktoum International Airportから雨のDubai International Airportに向かう場合はキャンセルする（ただ確度はかなり低い）

```json
{
  "inference_results": [
    {
      "Cancelled_prediction": false,
      "prediction_probability": 0.9733273011591903,
      "prediction_score": 0.21760108071993553
    },
    {
      "Cancelled_prediction": true,
      "prediction_probability": 0.2662477697132039,
      "prediction_score": 0.2662477697132039
    }
  ]
}
```

# その他注意事項など

## データ準備

一般に機械学習では学習のために適切なデータを揃えることが最も重要です。そのためには多くの場合、たとえばログ情報やRDBにある取引情報などの一次データソースからデータを抽出して、データフレーム分析に適した形へ変形する必要があります。

この前処理をElasticsearchで実装する場合、TransformやEnrich Processor、あるいはRuntime Fieldなどの要素技術を組み合わせることになるでしょう。

例えばElasticsearchに集約されているログをユーザー単位にTransformし、ここのユーザーのその後の退会状況をEnrichやRuntime Fieldによって付与したインデックスを使ってジョブを作成するような方針が考えられます。それぞれのユースケースに応じてデータの準備についてはよく検討してください。

## Pandasとの連携

データ準備でより複雑な前処理が必要になってくる場合は、そういった処理はPythonなどElasticsearchの外部で実装する方がわかりやすく柔軟に対応できるかもしれません。

Elasticが提供しているPythonのライブラリElandを使うと、ElasticsearchのIndexに保存されているデータをPandasのデータフレームとして読み込んだり、逆にPandasのデータフレームをElasticsearchのIndexとして保存したりすることができるようになります。この方法については以下の記事で説明しているので参照してください。

https://qiita.com/daixque/items/a9708a6e438a8f84fac4

さらに、Python側でScikit Learnなどで学習してモデルを作成してElasticsearchに学習済みモデルをアップロードすることも可能です。この辺りについては別の記事を用意したいと思います。取り急ぎ以下のドキュメントを紹介しておきます。

https://www.elastic.co/guide/en/elasticsearch/client/eland/current/machine-learning.html

# まとめ

このように、ElasticsearchのData Frame Analyticsを使うことでElasticsearchにあるデータを学習してモデルを作成し、そのモデルを使って何らかの未知の情報を予測することができるようになります。予測に必要な情報が十分に与えられるようなケースで効果を発揮するため、そのようなユースケースを見つけること自体が難しいところではあるのですが、上手く使うことができれば強力なツールになるのではないでしょうか。


# 参考資料

Data Frame Analyticsについては、以下の資料も参考にしてください。

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

https://www.elastic.co/blog/using-elastic-supervised-machine-learning-for-binary-classification

https://opster.com/guides/elasticsearch/how-tos/elasticsearch-data-frame-analytics/