【はじめての Databricks】金融取引データから異常検知 #5 リモデル/モデル比較

## はじめに
2つ目のモデルを作成、mlflow で1つ目モデルと比較するところまでを行います。


#### 連絡目次
1. [導入/環境設定](https://qiita.com/Catetin0310/items/28706cf7e6ae86b55ba1)
2. [Collaborative Notebook でデータ可視化](https://qiita.com/Catetin0310/items/b5bda50982cfc132f75c)
3. [Anomaly Detector をデータ探索ツールとして使ってみる](https://qiita.com/Catetin0310/items/d29b5fb00e047a7c107e)
4. [1つ目のモデル構築 (データの偏り 未考慮)](https://qiita.com/Catetin0310/items/a46ba6b1ee6e6290512a)
5. [2つ目のモデル構築 (データの偏り 考慮)] (https://qiita.com/Catetin0310/items/0780b4e0f1ba07509930)  → 本稿

## データセットの分割
1つ目のモデルでは学習に用いるデータセットに偏りがありました。今回はこの課題を解消できるように Train データセットをサンプリングします。

```python
# Train データセットをラベルごとに抽出
dfn = train.filter(train.label == 0)
dfy = train.filter(train.label == 1)

# 要素数
N = train.count()
y = dfy.count()
p = y/N

# 通常取引dfの一部を抜粋、不正取引の df に union
train_b = dfn.sample(False, p, seed = 92285).union(dfy)

# データ全体の要素数、不正取引要素数、構成日比率、train データセットの件数表示
print("Total count: %s \nFraud cases count: %s \nProportion of fraud cases: %s" % (N, y, p))
print("Balanced training dataset count: %s" % train_b.count())
```
> Total count: 5090311 
Fraud cases count: 6610 
Proportion of fraud cases: 0.0012985454130405784
Balanced training dataset count: 13252

中身を見てみます。通常取引/不正取引でおおよそ半々になっていますね。

```python
display(train_b.groupBy("label").count())
```
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/5320f741-e541-ffc0-d956-3a7b72118cfc.png)




## パイプライン修正
バランスが取れたデータセットをモデル学習に利用できるので、今回の評価指標は `evaluatorAUC` を用います。前記事で作成したパイプラインをそのまま利用します。

```python
# 作成したパイプラインに修正を加えて定義
crossval_b = CrossValidator(estimator = dt,
                          estimatorParamMaps = paramGrid,
                          evaluator = evaluatorAUC,
                          numFolds = 3)  
pipelineCV_b = Pipeline(stages=[indexer, va, crossval_b])

# 新たに作成したデータセットで学習
cvModel_b = pipelineCV_b.fit(train_b)
```


## モデルの作成と分類木の可視化
モデルを構築し、樹形図を見てみます。

```python
dt_model_b = pipeline.fit(train_b)
display(dt_model_b.stages[-1])
```

> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/dbc45bc6-6b60-3497-4bd6-c7d8e4f0846a.png)

続いて特徴量の重要度を出力します。

```python
ExtractFeatureImp(dt_model_b.stages[-1].featureImportances, train_pred_b, "features").head(10)
```
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/f9ff7436-3aa7-58d3-7ad3-48df02854240.png)

orgDiff (取引実行者の口座残高差分) が重要と判断しているようです。樹形図・利用している特徴量ともに、前回作成したモデルとは全く違いますね。


## 評価・メトリクスの保存
前回と同様、評価のために必要な情報を導出していきます。

#### 精度指標

```python
# パイプラインの中で一番いいモデルを構築
train_pred_b = cvModel_b.transform(train_b)
test_pred_b = cvModel_b.transform(test)

# Training データで評価指標を算出
pr_train_b = evaluatorPR.evaluate(train_pred_b)
auc_train_b = evaluatorAUC.evaluate(train_pred_b)

# Test データで評価指標算出
pr_test_b = evaluatorPR.evaluate(test_pred_b)
auc_test_b = evaluatorAUC.evaluate(test_pred_b)

# 評価指標の出力
print("PR train:", pr_train_b)
print("AUC train:", auc_train_b)
print("PR test:", pr_test_b)
print("AUC test:", auc_test_b)
```
> PR train: 0.99069584320536
AUC train: 0.9948781444445811
PR test: 0.09312146366332584
AUC test: 0.9929393129548542


#### 混合行列

```python
# 予測結果の一時ビュー作成
test_pred_b.createOrReplaceTempView("test_pred_b")

# テスト結果の混合行列 spark df の作成
test_pred_b_cmdf = spark.sql("select a.label, a.prediction, coalesce(b.count, 0) as count from cmt a left outer join (select label, prediction, count(1) as count from test_pred_b group by label, prediction) b on b.label = a.label and b.prediction = a.prediction order by a.label desc, a.prediction desc")

# Pandas df に変換
cm_b_pdf = test_pred_b_cmdf.toPandas()

# 混合行列の配列生成
cm_b_1d = cm_b_pdf.iloc[:, 2]
TP, FN, FP, TN = cm_b_1d

# 2次元にする
cm_b = np.array([[TP, FN], [FP, TN]])

# 混合行列描画 
plot_confusion_matrix(cm_b, "Confusion Matrix (balanced Test)")
```
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/0ca82ef6-bc8d-99b8-e60f-70a1bdc58954.png)

#### メトリクス保存

```python
with mlflow.start_run(experiment_id = mlflow_experiment_id) as run:

  mlflow.log_param("balanced", "yes")
  mlflow.log_metric("AUC train", auc_train_b)
  mlflow.log_metric("AUC test", auc_test_b)
  mlflow.log_metric("PR train", pr_train_b)
  mlflow.log_metric("PR test", pr_test_b)
    
  # モデルのロギング
  mlflow.spark.log_model(dt_model_b, "model")
  
  # 混合行列もログとして残す
  mlflow.log_artifact("confusion-matrix.png")
```

これで一通りの処理が完了しました。

## モデルの比較
前回のモデルと今回のモデルを比較していきます。
数種の評価指標であれば以下のように Notebook で出力・比較可能ですが、この方法では複数のモデルを比較しづらいです。

```python
print("---model #1---")
print("PR train:", pr_train)
print("AUC train:", auc_train)
print("PR test:", pr_test)
print("AUC test:", auc_test)

print("---model #2---")
print("PR train:", pr_train_b)
print("AUC train:", auc_train_b)
print("PR test:", pr_test_b)
print("AUC test:", auc_test_b)
```
> ---model #1---
PR train: 0.847168047612724
AUC train: 0.8648916320839469
PR test: 0.8432895226140703
AUC test: 0.8633701428270321

> ---model #2---
PR train: 0.99069584320536
AUC train: 0.9948781444445811
PR test: 0.09312146366332584
AUC test: 0.9929393129548542


#### mlflow で比較
mlflow を用いると比較が容易です。workspace > users と進み、フラスコのアイコンをクリック。

![2020-08-28_14h23_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/14e752ea-9257-3a73-89ec-b299bbb8c079.png)

こちらの画面に遷移します。モデルを選択し、Compare をクリック。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/9bf2272d-c76a-3d56-46e6-45eeed8eb411.png)

X軸とY軸にそれぞれ PR Test と AUC Test を設定、散布図で比較します。
![2020-08-28_14h46_37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/c7d89c9c-0ce5-24aa-40f3-d534da4f1db9.png)

各モデルの ID をクリックすると、モデルそのものや、パイプラインの実行パラメータ、保存しておいた図などにアクセス可能。試行錯誤していると情報が散逸しがちなのでとても便利です。

![2020-08-28_15h09_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/8575c5cc-4452-d83b-42c8-d50a478d302f.png)

今回のモデルは「疑わしきはすべて罰する」ようなロジックです。実際に不正検知をはじくフローに組み込むのであれば初回モデルが、当初の要件に沿うためには今回のモデルが合うかもしれません。(いずれも改良必須ですが)



## まとめ
5回にわたって金融取引データを可視化、考察、モデル構築、リモデル、比較するところまでをご紹介しました。

本番環境では Azure Datafactory などのデータ関連サービス群を組み入れ、よりシンプルかつ柔軟性の高いアーキテクチャを設計すべきですが、アドホックに機械学習プロジェクトを始めたいという方は、まず Databricks を動かしながらデータ分析の流れを掴んでみてはいかがでしょうか？




# 参考リンク
[datarbicks Resources (公式参考記事集)](https://databricks.com/resources)
[Detecting Financial Fraud at Scale with Decision Trees and MLflow on Databricks](https://databricks.com/blog/2019/05/02/detecting-financial-fraud-at-scale-with-decision-trees-and-mlflow-on-databricks.html)
[mlflow - track machine learning training runs](https://docs.databricks.com/applications/mlflow/tracking.html)
[Synthetic Financial Datasets For Fraud Detection](https://www.kaggle.com/ntnu-testimon/paysim1)
[Binary Classifier Evaluation made easy with HandySpark](https://towardsdatascience.com/binary-classifier-evaluation-made-easy-with-handyspark-3b1e69c12b4f)
[notebook](https://pages.databricks.com/rs/094-YMS-629/images/financial-fraud-detection-decision-tree.html?_gl=1*iti1sv*_gcl_aw*R0NMLjE1OTI1MzQ1NDUuQ2owS0NRandvYXozQlJEbkFSSXNBRjFSZkxjR1M0by12YkVQVXlMX0w4cEgtQmhULXhQZkNnb1hOZWkyNlpIcC1BZXJ0ME9RU0JUVmFzOGFBbTJVRUFMd193Y0I.&_ga=2.140112691.145739234.1594189054-892260679.1590545229&_gac=1.124776952.1592534547.Cj0KCQjwoaz3BRDnARIsAF1RfLcGS4o-vbEPUyL_L8pH-BhT-xPfCgoXNei26ZHp-Aert0OQSBTVas8aAm2UEALw_wcB)