2021年6月30日水曜日

Optuna でハイパーパラメーターを最適化する

前回、サンプルを元に適当にハイパーパラメーターを設定した。

今回は Optuna | 株式会社Preferred Networks を使って、いくつかのハイパーパラメーターの組み合わせを試して、一番良かった結果を報告してくれるやつを試す。

環境構築

前回 の環境に optuna を追加。

ゼロから構築する場合には、以下コマンド群を叩く。

apt-get update

apt-get install -y python3-dev python3-pip python3-venv

python3 -m venv --system-site-packages ./tensorflow
source ./tensorflow/bin/activate

pip install --upgrade pip
pip install --upgrade optuna
pip install --upgrade keras
pip install --upgrade tensorflow

プログラム作成

ざっくり手順は以下の感じ。

  1. パラメーター候補の組み合わせを試して、学習結果の評価を返却する関数
    • 以下コードの objective がその関数
  2. optuna#create_study で Study オブジェクトを作る
  3. Study#optimize に「1.」で作成した関数を渡す
    • Study クラスのオブジェクトが、「1.」で指定された組み合わせを色々試して、「試したパラメーターの組み合わせとその評価結果」を記録してくれる
  4. 「3.」の結果から、一番評価の良かったモデルを特定して、そのモデルで分類を行う
from keras.layers import Dense, Dropout, Activation
from keras.models import Sequential
from keras.optimizers import SGD, Adam
from keras.utils import np_utils
from sklearn import datasets
from sklearn.model_selection import train_test_split
from tensorflow import keras
import numpy as np
import optuna
import tensorflow as tf

# パラメーター候補の組み合わせを試して、学習結果の評価を返却する関数
def objective(trial):
    # dropout_rate を 0.25 から 0.5 の間で試す
    dropout_rate = trial.suggest_uniform('dropout_rate', 0.0, 0.5)

    # 試す損失関数の種類を列挙。今回は `categorical_crossentropy` のみ。
    loss = trial.suggest_categorical('loss', ['categorical_crossentropy'])

    # 試す最適化の種類を列挙。今回は `sgd` と `adam` を試す。
    optimizer = trial.suggest_categorical('optimizer', ['sgd', 'adam'])

    # 試す活性化関数の種類を列挙。今回は `tanh` と `relu` を試す。
    activation = trial.suggest_categorical('activation', ['tanh', 'relu'])

    # 試す metrics の種類を列挙。今回は `accuracy` のみ。
    metrics = trial.suggest_categorical('metrics', ['accuracy'])


    # 試すレイヤー数を指定。 1 から 5 層を試す。
    n_layer = trial.suggest_int('n_layer', 1, 5)

    # Sequential モデル生成
    model = Sequential()

    # 入力パラメーターが以下 4 つなので input_dim=4
    # - sepal length: がくの長さ
    # - sepal width: がくの幅
    # - petal length: 花弁の長さ
    # - petal width: 花弁の幅
    input_dim=4

    # 出力は以下 3 種なので units=3
    # - 0: Iris-Setosa(セトナ)
    # - 1: Iris-Versicolour(バーシクル)
    # - 2: Iris-Virginica(バージニカ)
    units=3

    # 入力層
    model.add(Dense(input_dim))

    # 隠れ層
    # dropout_rate が一様というのはダメな気がするがとりあえず optuna の使い方の練習ということで。
    for i in range(n_layer):
        model.add(Dense(units, activation=activation))
        model.add(Dropout(dropout_rate))

    # 出力層
    model.add(Activation('softmax'))

    # モデルのコンパイル
    model.compile(loss=loss, optimizer=optimizer, metrics=metrics)

    # 学習
    history = model.fit(train_x, train_t, batch_size=32, epochs=1000)

    # モデルの保存
    model.save("./trials/{}.model".format(trial.number))

    # 学習結果の評価を返却
    return 1 - history.history[metrics][-1]


## データ準備

# 乱数を固定値で初期化し再現性を持たせる
np.random.seed(0)

# pip パッケージ scikit-learn の datasets から、 iris データをロード
# X: インプットデータ
# T: 正解データ
iris = datasets.load_iris()
X = iris.data
T = iris.target

# 数値を、位置に変換 [0,1,2] ==> [ [1,0,0],[0,1,0],[0,0,1] ]
T = np_utils.to_categorical(T)

# データを訓練用とテスト用に分割
train_x, test_x, train_t, test_t = train_test_split(X, T, train_size=0.8, test_size=0.2)


# パラメーター最適化。 `n_traials` の数だけ試す。
study = optuna.create_study()
study.optimize(objective, n_trials=100)

# 最適化結果表示
print("study.best_trial: {}".format(study.best_trial))
print("study.best_params: {}".format(study.best_params))
#print("study.best_trials: {}".format(study.best_trials))
#print("study.best_value: {}".format(study.best_value))
#print("study.trials: {}".format(study.trials))

# 一番成績の良かったモデルをロード
model = keras.models.load_model("./trials/{}.model".format(study.best_trial.number))

# 学習済みモデルでテストデータを分類する
Y = np.argmax(model.predict(test_x), axis=-1)

## 結果検証確認

# to_categorical の逆変換
_, T_index = np.where(test_t > 0)

# 結果出力
print()
print('RESULT')
print(Y == T_index)

実行結果省略、以上。

参考資料

0 件のコメント:

コメントを投稿