Databricks にて Hyperopt を利用した際に特定の MLflow の experiment へトラッキングする方法

## 概要
Databricks にて Hyperopt を利用した際に特定の MLflow の experiment へトラッキングする方法を共有します。Hyperopt 利用時には、MLflow へトラッキングする際に`set_experiment`により experiment を指定することで想定通りにトラッキングできました。

```python
mlflow.set_experiment(experiment_id="1420974677739169")

with mlflow.start_run():
  best_result = fmin(
    fn=objective, 
    space=search_space,
    algo=algo,
    max_evals=32,
    trials=spark_trials)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/11c4734c-6539-c1a4-066f-fd709c2835df.png)

Hyperopt 利用時には親子関係でトラッキングが実施されるため、次のリンク先にて記載されている MLflow の仕様により、experiment_id の指定方法によっては想定通りに動作しない場合があります。

- [NLested runs should inherit experiment ID from parent · Issue #1109 · mlflow/mlflow · GitHub](https://github.com/mlflow/mlflow/issues/1109) 

本記事では、次のリンク先にあるサンプルノートブックに基づいた検証結果を共有します。サンプルノートブックの一部を修正し、その実行結果を確認しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/bcbc984e-53bf-16ec-4afa-8bafc3839217.png)

引用元： [hyperopt-sklearn-model-selection - Databricks (microsoft.com)](https://learn.microsoft.com/ja-jp/azure/databricks/_extras/notebooks/source/hyperopt-sklearn-model-selection.html)

Hyperopt の詳細を知りたい場合には、次のドキュメントが参考になります。

- [Hyperopt の概念 - Azure Databricks | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/databricks/machine-learning/automl-hyperparam-tuning/hyperopt-concepts)
- [ハイパーパラメーターの調整 - Azure Databricks | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/databricks/machine-learning/automl-hyperparam-tuning/)
- [scikit-learn と MLflow を使用したハイパーパラメーター チューニングの並列化 - Azure Databricks | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/databricks/machine-learning/automl-hyperparam-tuning/hyperopt-spark-mlflow-integration)
- [モデルの型を Hyperopt と MLflow と比較する - Azure Databricks | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/databricks/machine-learning/automl-hyperparam-tuning/hyperopt-model-selection)

## 基本的な Hyperopt 利用時の MLflow へのトラッキング動作の確認

サンプルノートブックの実行結果から基本的な Hyperopt による MLflow へのトラッキングに関する動作を確認します。

サンプルノートブックでは、`fmin`メソッドを実行する際に、`mlflow.start_run`によりトラッキングが実施されるようになっております。experiment_id を指定していないため、現在のノートブックにトラッキングされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/7282f76f-039c-5d69-0240-2b5d608d5ee8.png)

Hyperopt で、チューニング対象の関数を`object`パラメータに指定するのですが、サンプルノートブックでは次のようなコードとなっております。MLflow へのトラッキングが明示されておりませんが、`fmin`メソッドがトラッキング対象となっているため ML モデルの学習時に MLflow へトラッキングするようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/af91e77e-8416-b9c4-e37f-aad5a715bfb1.png)

サンプルノートブックの実行後、現在のノートブック上の experiment を確認すると、MLflow に対して親子階層でトラッキングされることが確認できました。`dashing-sheep-743`が親の experiment であり、`spiffy-rat-555`や`orderly-finch-409`などが子の experiment です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/eb854d2b-7a68-dbdb-6615-0f5114bf12ff.png)

親の experiment では、`fmin`メソッド実行時のパラメータや実行時のソースであるノートブックへのリンクが記載されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/c18b843d-a9fc-87f6-4ed7-b2d0242c7357.png)

子の experiment では、モデル学習時のハイパーパラメータやメトリックなどの ML モデルの情報が記載されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/a2c8b881-278b-bac5-fb16-63a08b399dd9.png)

## 指定した experiment にトラッキングする方法

### C1. `set_experiment`メソッドで experiment_id を指定する方法

`set_experiment`メソッドで experiment_id を指定するため、トラッキング先の experiment を作成後、サンプルノートブックのコードを次のように修正しました。

**修正前**

```python
with mlflow.start_run():
  best_result = fmin(
    fn=objective, 
    space=search_space,
    algo=algo,
    max_evals=32,
    trials=spark_trials)
```

**修正後**

```python
mlflow.set_experiment(experiment_id="1420974677739169")

with mlflow.start_run():
  best_result = fmin(
    fn=objective, 
    space=search_space,
    algo=algo,
    max_evals=32,
    trials=spark_trials)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/13248d0d-bb5e-ab30-b301-121d23c889e1.png)

ノートブックを実行すると、指定した experiment  に親子階層でトラッキングされました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/dac7084f-5d34-de03-6821-41333d1956e4.png)

## 想定通りに experiment にトラッキングできない例

### E1. `fmin`メソッド実行時に experiment_id を指定する方法

`fmin`メソッド実行時に experiment_id を指定するため、サンプルノートブックのコードを次のように修正しました。

**修正前**

```python
with mlflow.start_run():
  best_result = fmin(
    fn=objective, 
    space=search_space,
    algo=algo,
    max_evals=32,
    trials=spark_trials)
```

**修正後**

```python
with mlflow.start_run(experiment_id="1420974677739169"):
  best_result = fmin(
    fn=objective, 
    space=search_space,
    algo=algo,
    max_evals=32,
    trials=spark_trials)
```

修正したノートブックを実行すると、`fmin`メソッド実行に関するトラッキングのみ指定した experiment に実施されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/4e1a7c26-489f-0271-5a07-aacffedc0c8f.png)

ML モデルの学習に関するトラッキングは、実行したノートブックの experiment に記載されていました。そのため、親子階層でトラッキングされていないため、想定通りにトラッキングできていないと判断しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/3ea2fac2-68ec-e794-3687-e60331a8ee38.png)


### E2. `fmin`メソッドと`object`指定の関数実行時に experiment_id を指定する方法

`fmin`メソッドと`object`指定の関数実行時に experiment_id を指定するため、サンプルノートブックのコードを次のように修正しました。

**修正前**

```python
with mlflow.start_run():
  best_result = fmin(
    fn=objective, 
    space=search_space,
    algo=algo,
    max_evals=32,
    trials=spark_trials)
```

```python
def objective(params):
    classifier_type = params['type']
    del params['type']
    if classifier_type == 'svm':
        clf = SVC(**params)
    elif classifier_type == 'rf':
        clf = RandomForestClassifier(**params)
    elif classifier_type == 'logreg':
        clf = LogisticRegression(**params)
    else:
        return 0
    accuracy = cross_val_score(clf, X, y_discrete).mean()
    
    # Because fmin() tries to minimize the objective, this function must return the negative accuracy. 
    return {'loss': -accuracy, 'status': STATUS_OK}
```

**修正後**

```python
with mlflow.start_run(experiment_id="1420974677739169"):
  best_result = fmin(
    fn=objective, 
    space=search_space,
    algo=algo,
    max_evals=32,
    trials=spark_trials)
```

```python
def objective(params):
  with mlflow.start_run(experiment_id="1420974677739169"):
    classifier_type = params['type']
    del params['type']
    if classifier_type == 'svm':
        clf = SVC(**params)
    elif classifier_type == 'rf':
        clf = RandomForestClassifier(**params)
    elif classifier_type == 'logreg':
        clf = LogisticRegression(**params)
    else:
        return 0
    accuracy = cross_val_score(clf, X, y_discrete).mean()
    
    # Because fmin() tries to minimize the objective, this function must return the negative accuracy. 
    return {'loss': -accuracy, 'status': STATUS_OK}
```

修正したノートブックを実行すると、`fmin`メソッド実行に関するトラッキングのみ指定した experiment に実施されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/f9786a44-19fd-8508-d6df-76148619792f.png)

ML モデルの学習に関するトラッキングは、実行したノートブックの experiment に記載されていました。そのため、親子階層でトラッキングされていないため、想定通りにトラッキングできていないと判断しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0e0acd35-7740-d78b-5faa-a5503d2602d9.png)