Vertex AIでハイパーパラメータチューニングをする

おはこんハロチャオ~!なにもんなんじゃ?じょんすみすです。 みなのもの~! 『クラスメソッド 機械学習チーム アドベントカレンダー 2022』の時間だぞ~!

はい、というわけで気が付けば2022年も残すところあと1ヶ月となりましたね。 一年最後の月である12月の始まりです。 そんな12月と言えば...今年もやってまいりましたアドベントカレンダーの時期です。

当エントリー『クラスメソッド 機械学習チーム アドベントカレンダー 2022』は1日目となります。 Vertex AIのカスタムコンテナを使った学習でハイパーパラメータチューニングを行う方法を見ていきたいと思います。

カスタムコンテナのハイパーパラメータ

AutoMLを使う場合には完全に内部の処理に任せられますが、カスタムコンテナやプレビルドコンテナを利用する場合には、ハイパーパラメータをどのように利用するかも実装に含める必要があります。

パラメータチューニングが必要な場合には対象とする範囲や探索の方法まで含めて考える必要があり、処理の並列化まで考えると全てをコンテナ内の処理として実装するのはなかなかに大変ですね。 Vertex AIでは学習用コンテナに一工夫加えるだけでチューニングは自動で行ってくれる仕組みが存在しています。 加える一工夫は以下の2つのみになります。

  • 外部からパラメータを渡せるようにする
  • モデルの性能をVertex AIに渡すようにする

あとは、チューニングする際に対象となるパラメータとその探索範囲などを設定します。

これで、どんなパラメータを渡したときにどのような結果になったかを見ながら探索をする処理はVertex AI側で自動的にやってくれる仕組みの完成です。

チューニングに必要な実装

では、ここからはどのような一工夫を実装するのか見ていきましょう。

パラメータを受け取れるようにする

まずは対象となるパラメータを受け取る部分です。 この部分は非常にシンプルで、学習時に実行するコマンドの引数として受け取るようにしておくだけです。

例えば実行する処理がpythonであればそのまま sys.argv を使うか、 以下のように argparse で引数を処理します。

import argparse

import lightgbm as lgb

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--learning_rate', default=0.1, type=float)
    parser.add_argument('--num_leaves', default=31, type=int)
    ... # 他のパラメータも必要に応じて任意の数追加可能

    model = lgb.LGBMClassifier(
        lerning_late=args.learning_rate, 
        num_leaves=args.num_leaves,
        ... # 他のパラメータ
    )

    # データの読み込みと学習の実行を記述

これは train.py という名前としたときに以下のように実行できる状態になってますね。

python train.py --learning_late 0.01 --num_leaves 100

この状態にしておくことでVertex AIが実際に渡すパラメータを決定して引数として付与してくれるわけです。

性能評価をVertex AIに伝える

ハイパーパラメータチューニングでやりたいことは「一番いいモデルを頼む」なので、モデルの良し悪しを判断する指標が必要になります。 どのような指標でモデルの性能を評価するかは解きたい問題にもよりますが、 チューニングで利用する際には数値で評価できる定量的な指標である必要があります。

数値化さえ可能であれば、指標は回帰であればRMESやR^2など、分類であれば正解率やPrecision-Recall、AUCなどといった一般的なものでも問題ありませんし、独自の指標を使っても問題ありません。

性能評価自体は任意の方法で計算していただいて問題ありませんが、 その後、評価した結果をVertex AIに伝える必要がります。 結果を伝えるにはcloudml-hypertuneというライブラリを利用します。

インストールはpipで行います。

pip install cloudml-hypertune

使い方はUsageにも記載されていますが、非常に簡単です。

先ほどのパラメータ受け取りと併せて使っていくと以下のようになります。

import argparse

import hypertune
import lightgbm as lgb
from sklearn.metrics import accuracy_score

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--learning_rate', default=0.1, type=float)
    parser.add_argument('--num_leaves', default=31, type=int)
    ... # 他のパラメータも必要に応じて任意の数追加可能

    model = lgb.LGBMClassifier(
        lerning_late=args.learning_rate, 
        num_leaves=args.num_leaves,
        ... # 他のパラメータ
    )

    # ここから追加
    X_train = ... # データ読み込み
    y_train = ... # データ読み込
    X_valid = ... # データ読み込み
    y_valid = ... # データ読み込み

    model.fit(X_train, y_train, ...) # 他の引数は省略
    y_pred = model.predict(X_valid)

    # 指標の取得
    # ここでは正解率を設定
    accuracy = accuracy_score(y_valid, y_pred)

    # Vertex AIに指標を報告するための処理
    hpt = hypertune.HyperTune()
    hpt.report_hyperparameter_tuning_metric(
        hyperparameter_metric_tag='accuracy',
        metric_value=accuracy,
        global_step=10
    )

これでデフォルトでは /tmp/hypertune/outout.metric にこの指標がJSON形式で出力され、それがVertex AI側にも伝わります。 実際に動かした際には、コンソール上からは以下のように確認できる状態となります。

ジョブを実行する

必要な実装を行った処理を含むコンテナを作成したら、これを使ってハイパーパラメータチューニングジョブを実行します。

ここでは、コンソール上から設定する方法を見てみましょう。 通常と同様に、学習ジョブを作成します。この際「4. ハイパーパラメータ(省略可)」を設定することでハイパーパラメータチューニングジョブとなります。

ここで以下の2つを設定します。

  • チューニング対象のパラメータと探索範囲
  • 利用する評価指標
  • 最大探索回数と並列数
  • 探索アリゴリズム

チューニング対象のパラメータと探索範囲の指定は以下のように設定します。

Parameter nameは引数の名前を指定してます。 「--」は入れていない点にご注意ください。

Typeではパラメータとして渡す変数の種類を指定します。 これは以下の中から選択できます。

  • Categorical : いくつかの値のいずれかを取る文字列
  • Discrete : 離散値を取るパラメータを昇順で並べる
  • Double : 実数値
  • Integer : 整数値

CategoricalとDiscreteは取りうる値をカンマ区切りで複数並べます。 DoubleとIntgerは最小値と最大値を設定して、Scalingに値の取り方のスケールを設定します。

複数のパラメータをチューニング対象にする場合は「新しいパラメータを追加」を選択肢て項目を増やしたうえで同様の設定を行っていきます。

利用する評価指標の設定は以下のようになっています。

Metric to optiizeでは対象とする指標を設定します。 この値は、cloudml-hypertuneにて報告した指標のうちどれを利用するかの設定で、hyperparameter_metric_tagに指定したもの指定します。 複数の指標を報告している場合はその中のどれを利用するかを1つ選んでここに設定します。

目標はこの指標を最大化したいのか最小化したいのかを選択します。 ここでは正解率なので最大化を選択しています。

最大探索回数と並列数の設定は以下になります。

それぞれ整数値で指定します。

最後に探索アリゴリズムの指定をします。

デフォルトではベイズ最適化を使った探索が行われますが、グリッドサーチとランダムサーチを指定することも可能です。

これでハイパーパラメータチューニングに関する設定は完了です。 この先の設定も学習ジョブと同様のものとなりますのであとは必要な設定をして実行するのみです。

先ほども掲載した以下のような表示でジョブの確認が可能です。

探索するパラメータとしないパラメータがある場合は?

最後にちょっとした補足です。

利用時の状況に応じて、チューニングしたいパラメータとしなくてもいいかな?ってパラメータがあるケースもあります。 こういったケースでは、チューニングしないものに関しては最初からコードの中に書いてしまってもいいのですが、その時々で対象に含めたくなることあるかもしれません。

そういった時には、

  • 対象となりそうなもの全てをコマンドライン引数で受け取るようにする
  • 対象としないものは学習ジョブの設定でコマンドに渡す
  • 対象とするものはハイパーパラメータチューニングで設定する

という使い方も可能です。

おわりに

今回はVertex AIでカスタムコンテナを利用する際にハイパーパラメータチューニングをする方法を紹介しました。

どのような設定項目があるのかを説明するために、コンソール上からのジョブの実行方法を紹介しましたが、もちろん google-cloud-aiplatform パッケージに含まれるHyperparameterTuningJobを利用してコードで実装することも可能です。