Databricks でワインの品質を予測するモデルを構築 2

#はじめに
[前回の記事](https://qiita.com/JJ33/items/659c8173ff457aed977c)の続編、Azure Databricks で機械学習モデルを構築する例を試していきます。


#データの前処理
モデルをトレーニングする前に、欠損値をチェックし、データをトレーニングセットと検証セットに分割します。

```python
data.isna().any()
```
![2020-09-01_16h59_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543340/f410ef02-22ec-ac52-cc38-67e93ce8a098.png)

欠損値はありません。

```python
from ​sklearn.model_selection import train_test_split

train, test = train_test_split(data, random_state=123)
X_train ​= train.drop(["quality"], axis=1)
X_test ​= test.drop(["quality"], axis=1)
y_train ​= train.quality
y_test = test.qual
```

#ML flow を使う準備
Workspace → ユーザー名 → プルダウンで Createと進み、MLflow Experiment をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543340/78bb858d-00a0-1df3-1255-4becc8381a43.png)
任意で名前をつけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543340/66a7a675-c10b-94f5-ad9d-2b2d7d089eb4.png)
この画面に遷移します。Experiment ID は Notebook の中で使うのでメモしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543340/ca9e0599-8756-6850-a7ff-242b608531eb.png)

mlflow をインストールするために、以下のコマンドを実行します。

```python
dbutils.library.installPyPI("mlflow")

#= の後に、先ほど作成した mlflow experiment の id を入力、同じく実行します。

mlflow_experiment_id = {ここにID}
```

#ベースラインモデルの構築
出力はバイナリであり、複数の変数間に相互作用がある可能性があるため、このタスクはランダムフォレスト分類器に適しているようです。

次のコードは、scikit-learnを使用して単純な分類子を作成します。
MLflowを使用してモデルの精度を追跡し、後で使用するためにモデルを保存します。

```python
import mlflow
import mlflow.pyfunc
import mlflow.sklearn
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import roc_auc_score

# sklearn の RandomForestClassifier の predictメソッドは、バイナリ分類（0または1）を返します。 
# 次のコードは、関数 SklearnModelWrapper を作成します。この関数は、predict_proba メソッドを使用して、観測値が各クラスに属する確率を返します。
# mlflow.start_run は、このモデルのパフォーマンスを追跡するために、新しいMLflowの実行を作成します。

class SklearnModelWrapper(mlflow.pyfunc.PythonModel):
  def __init__(self, model):
    self.model = model
    
  def predict(self, context, model_input):
    return self.model.predict_proba(model_input)[:,1]

# コンテキスト内で、mlflow.log_paramを呼び出して、使用されたパラメーターを追跡 
# mlflow.log_metricを呼び出して、精度などのメトリックを記録します。

with mlflow.start_run(run_name='untuned_random_forest'):
  n_estimators = 10
  model = RandomForestClassifier(n_estimators=n_estimators, random_state=np.random.RandomState(123))
  model.fit(X_train, y_train)

  # predict_proba returns [prob_negative, prob_positive], so slice the output with [:, 1]
  predictions_test = model.predict_proba(X_test)[:,1]
  auc_score = roc_auc_score(y_test, predictions_test)
  mlflow.log_param('n_estimators', n_estimators)

  # ROC 曲線の下の領域をメトリックとして使用します。
  mlflow.log_metric('auc', auc_score)

  wrappedModel = SklearnModelWrapper(model)
  mlflow.pyfunc.log_model("random_forest_model", python_model=wrappedModel)
```

モデルによって出力された、学習された機能の重要性を調べます。

```python
feature_importances = pd.DataFrame(model.feature_importances_, index=X_train.columns.tolist(), columns=['importance'])
feature_importances.sort_values('importance', ascending=False)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543340/afc63480-5beb-b88b-8563-57a1728ae969.png)

前に示した箱ひげ図に示されているように、品質を予測するにはアルコールと密度の両方が重要です。

ROC曲線下の面積（AUC）をMLflowに記録しました。
右上の「実験」をクリックして、実験実行サイドバーを表示します。

モデルは0.89のAUCを達成しました。

ランダム分類子のAUCは0.5であり、AUC値が高いほど優れています。

#MLflowモデルレジストリへのモデルの登録
このモデルをモデルレジストリに登録すると、Databricks内のどこからでも簡単にモデルを参照できます。

次のセクションでは、これをプログラムで行う方法を示しますが、「モデルレジストリにモデルを登録する」 の手順に従って、UIを使用してモデルを登録することもできます。

```python
run_id = mlflow.search_runs(filter_string='tags.mlflow.runName = "untuned_random_forest"').iloc[0].run_id

model_version = mlflow.register_model(f"runs:/{run_id}/random_forest_model", "wine_quality")
```

```python
model_name = "wine_quality"
model_version = mlflow.register_model(f"runs:/{run_id}/random_forest_model", model_name)
```

[モデル]ページにワイン品質のモデルが表示されます。
「モデル」ページを表示するには、左側のサイドバーの「モデル」アイコンをクリックします。

次に、このモデルを本番環境に移行し、モデルレジストリからこのノートブックにロードします。

```python
from ​mlflow.tracking import MlflowClient

client = MlflowClient()
client.transition_model_version_stage(
 ​name="wine_quality",
 ​version=model_version.version,
 ​stage="Production"
)
```
「モデル」ページに、ステージ「生産」のモデルバージョンが表示されます。

パス「models：/ wine-quality / production」を使用してモデルを参照できるようになりました。

```python
model = mlflow.pyfunc.load_model("models:/wine_quality/production")

# Sanity-check: This should match the AUC logged by MLflow
print(f'AUC: {roc_auc_score(y_test, ​model.predict(X_test))}')
AUC: 0.888902759745664
```

#新しいモデルを試す
ランダムフォレストモデルは、ハイパーパラメーター調整なしでもうまく機能しました。

次のコードは、xgboost ライブラリを使用して、より正確なモデルをトレーニングします。
Hyperopt と SparkTrials を使用して、並列ハイパーパラメータースイープを実行し、複数のモデルを並列にトレーニングします。
以前と同様に、コードはMLflowを使用して各パラメーター構成のパフォーマンスを追跡します。

```python
from ​hyperopt import fmin, tpe, hp, SparkTrials, Trials, STATUS_OK
from ​hyperopt.pyll import scope
from ​math import exp
import ​mlflow.xgboost
import ​numpy as np
import ​xgboost as xgb

search_space = {
  ​'max_depth': scope.int(hp.quniform('max_depth', 4, 100, )),
  ​'learning_rate': hp.loguniform('learning_rate', -3,0),
  ​'reg_alpha': hp.loguniform('reg_alpha', -5, 1),
  ​'reg_lambda': hp.loguniform('reg_lambda', -6, 1),
  ​'min_child_weight': hp.loguniform('min_child_weight', -1,3),
  ​'objective': 'binary:logistc',
  ​'seed': 123, #トレーニングのためのシード
}

def ​train_model(parms):
  ​#トレーニングされたモデルは自動的にMLflowに記録されます。
  ​mlflow.xgboost.autoog()
  ​with mlflow.start_run(nested=Tru):
    ​train = xgb.DMatrix(data=X_train, label=y_tran)
    ​test = xgb.DMatrix(data=X_test, label=y_tet)
    ​#テストセットを渡して、xgbが評価指標を追跡できるようにします。
    ​# XGBoostは、評価指標が改善されなくなったときにトレーニングを終了します。
    ​booster = xgb.train(params=params, dtrain=train, num_boost_round=1000,\
                       ​evals=[(test, "test")], early_stopping_rounds=0)
    ​predictions_test = booster.predict(tet)
    ​auc_score = roc_auc_score(y_test, predictions_tet)
    ​mlflow.log_metric('auc', auc_scor)

    ​# 損失を-1 * auc_scoreに設定して、fmin が auc_score の戻り値を最大化するようにします
    ​return {'status': STATUS_OK, 'loss': -1*auc_score, 'booster': booster.attributes()}

# 並列処理を大きくするとスピードアップにつながりますが、ハイパーパラメータースイープの最適性は低下します
# 並列処理の妥当な値は、max_evalsの平方根です。
spark_trials ​= SparkTrials(parallelism=10)

# MLflow実行コンテキスト内でfminを実行し、各ハイパーパラメーター構成が「xgboost_models」と呼ばれる、メインのサブとして記録されるようにします。
with mlflow.start_run(run_name='xgboost_modls'):
  ​best_params = fin(
    ​fn=train_modl, 
    ​space=search_spae, 
    ​algo=tpe.sugget, 
    ​max_evals96,
    ​trials=spark_trias, 
    ​rstate=np.random.RandomStat(1
```

次回は結果を表示していきます。３へ続きます。

#公式リンク
[Databricks](https://databricks.com/jp/)