Databricks ではじめる機械学習：データ取り込みからモデル登録まで

みなさん、こんにちは！

Databricks は通常のデータ処理やデータ分析に加え、MLflow の機能が統合されていたり、機械学習まわりに強みがあることが特徴です。

「Databricks で機械学習を扱ってみたいけれどどうすればいいかわからない」「これからはじめて使ってみる」という方を対象に、機械学習のデータ取り込みからモデル登録までをシンプルな例をもとに解説していきます。
※本記事では Azure Databricks を使用しています。

## はじめに
今回は例として、有名なアヤメの分類データである iris データセットを読み込み、分類モデルを作成したのち Unity Catalog にモデルの登録を行います。
さっそく、以下で具体的な手順について見ていきましょう。

## 手順
### 1. データ取り込み
DBFS の sample フォルダに[Iris データセット](https://archive.ics.uci.edu/dataset/53/iris)をアップロードした後、ノートブックで以下のプログラムを実行します。

```ruby:qiita.rb
from pyspark.sql.types import *

# 入力ファイルパス
path = "dbfs:/FileStore/tables/sample/iris.csv"

# スキーマ指定
schema = StructType([
    StructField("sepal_length", DoubleType(), False),
    StructField("sepal_width", DoubleType(), False),
    StructField("petal_length", DoubleType(), False),
    StructField("petal_width", DoubleType(), False),
    StructField("species", StringType(), False)
])

# CSVデータ読み込み
iris_sdf = spark.read\
    .format("csv")\
    .options(header="false")\
    .load(path, schema=schema)

display(iris_sdf)
```

iris データセットのCSVファイルを読み込み、pyspark DataFrame として格納します。
格納されたデータは以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/b80ba09f-ad10-4978-8891-086a951cc20b.png)

### 2. データ変換
以下のコマンドにより、pyspark DataFrame から pandas DataFrame への変換、species の表記修正を行い、`` describe() ``で統計情報を表示します。

```ruby:qiita.rb
iris_df = iris_sdf.toPandas()

iris_df.loc[iris_df["species"] == "Iris-setosa", "species"] = "setosa"
iris_df.loc[iris_df["species"] == "Iris-versicolor", "species"] = "versicolor"
iris_df.loc[iris_df["species"] == "Iris-virginica", "species"] = "virginica"

iris_df.describe()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/1e4c079e-bca3-4620-82fb-3fe2eb641176.png)

また、以下のコマンドを実行し特徴量の分布を確認してみます。
```ruby:qiita.rb
import seaborn as sns

%matplotlib inline
sns.pairplot(iris_df, hue="species")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/244755f0-1226-4bee-9f94-05b66260b84a.png)

Databricks の実行環境にはデータ分析や機械学習で使用する主要なライブラリ（searborn, scikit-learn, mflow等）が標準でインストールされているため、余計な手間なく分析作業を行うことができます。

また、下記コマンドを実行し、モデルの学習用と検証用に分割したデータを準備しておきます。
```ruby:qiita.rb
from sklearn.model_selection import train_test_split

iris_df_list = iris_df.values.tolist()
X = [row[:-1] for row in iris_df_list]
y = [row[-1] for row in iris_df_list]

# 学習用と検証用にデータを分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
```
今回は species を予測対象にしたいので、species が y、それ以外は X とします。
取得した X, y をもとに学習用と検証用のデータを作成しておきます。

### 3. モデル作成
以下を実行し、モデルの作成と記録を行います。

```ruby:qiita.rb
import mlflow
from mlflow.models.signature import infer_signature
from sklearn.ensemble import RandomForestClassifier

def train_model(n_estimators, max_depth):
    # モデルの定義
    model = RandomForestClassifier(
        n_estimators=n_estimators,
        max_depth=max_depth,
        criterion="gini",
        random_state=0,
    )

    # 訓練
    model.fit(X_train, y_train)

    # 評価
    score = model.score(X_test, y_test)

    return model, score

# MLflow の記録開始
with mlflow.start_run(run_name="iris_untuned_random_forest"):
    # モデルの学習
    n_estimators = 10
    max_depth = 5
    model, score = train_model(n_estimators, max_depth)
    print(f"n_estimators: {n_estimators}, max_depth: {max_depth}, score: {score}")

    # シグネチャ取得
    signature = infer_signature(X_train, model.predict_proba(X_train)[:,1])

    # 記録保存
    mlflow.log_param("n_estimators", n_estimators)
    mlflow.log_param("max_depth", max_depth)
    mlflow.log_metric("score", score)
    mlflow.sklearn.log_model(model, "model", signature=signature)
```
コードについて解説します。

まず、上記コードではモデル学習用の関数`train_model`を定義しています。与えられた`n_estimators`と`max_depth`をもとに機械学習モデル（今回はランダムフォレスト分類器）の学習と評価を行い、パラメータとスコアを返す関数です。

`mlflow.start_run()`のwithブロックが処理の本体となっており、パラメータ`n_estimators`と`max_depth`を`train_model`関数に与え、結果を取得します。

シグネチャはモデルの入力と出力の仕様であり、モデル登録用に取得しています。
ブロックの最後ではパラメータやメトリクスをまとめて記録しています。

Databricks には MLflow の機能が統合されており、メニューの「エクスペリメント」から上記を実行した結果の詳細を確認することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/166b9f0a-309c-4d6a-9a4c-fd4c81ef4070.png)

### 4. モデル登録
以下のコマンドでMLパイプラインの run_id を取得し、Unity Catalog にモデルを登録することができます。

※Unity Catalog にモデルを登録するためには、アタッチしたクラスターで Unity Catalog が有効化されている必要があるためご注意ください。
```ruby:qiita.rb
run_id = mlflow.search_runs(filter_string='tags.mlflow.runName = "iris_untuned_random_forest"').iloc[0].run_id
```

```ruby:qiita.rb
import time

# Unity Catalog にモデルを登録
mlflow.set_registry_uri("databricks-uc")
model_name = f"main.default.iris_classifier"
model_version = mlflow.register_model(f"runs:/{run_id}/model", model_name)

time.sleep(15)
```

登録したモデルはメニューの「モデル」から確認することができます。
モデルの管理画面で表示されたリンクをクリックすると、下記のようにカタログ画面が開きモデルの詳細情報を確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/e664567c-c487-412c-8fcf-a118657fc8fe.png)


## さいごに
機械学習に関して、データ取り込みからモデル登録までの一連の流れをシンプルな例とともに見てきました。

今回ご紹介した内容以外にも、Databricks では機械学習まわりの様々な機能をサポートしています。下記の公式チュートリアルも参考に、いろいろと試してみてください。

[チュートリアル: AI と機械学習の概要 | Databricks Documentation](https://docs.databricks.com/aws/ja/machine-learning/ml-tutorials)

本記事が Databricks における機械学習の理解の一助となれば幸いです。

## 参考記事
 * [MLflowの使い方 - 機械学習初心者にもできる実験記録の管理 - - DATAFLUCT Tech Blog](https://tech.datafluct.com/entry/20220603/1654251480)
 * [チュートリアル：Databricksでの総合的な機械学習モデル | Databricks on AWS](https://docs.databricks.com/ja/mlflow/end-to-end-example.html)