ハイパーパラメータ最適化ツールのHyperactiveを試す
この記事では,ハイパーパラメータ最適化ツールのHyperactiveについてまとめています.
- ハイパーパラメータ最適化について
- Hyperactive について
- Random ForestのハイパーパラメータをPSOで最適化
- CNNのハイパーパラメータをRandom Searchで最適化
- 終わりに
ハイパーパラメータ最適化について
機械学習では人が手作業で調整する必要があるハイパーパラメータが存在します.ハイパーパラメータを調整することは高い精度を求める上で重要となりますが,手作業では限界があるため,多くの自動化手法が提案されています.
提案されていたり利用されている自動化手法やツールとして,以下のようなものがあります.
- Grid Search
- Optuna (TPE)
- Meta-Heuristics Algorithm (e.g. PSO, ES)
Hyperactiveは,これまであまり提案されてこなかったMeta-Heuristics Algorithmやその他の手法を用いて最適化を行うことができます.
Hyperactive について
概要
Hyperactiveは機械学習や深層学習のハイパーパラメータを自動で最適化するAPIです.このAPIを使うことで,様々な機械学習手法のハイパーパラメータに対して進化戦略 (ES) や粒子群最適化 (PSO) などの手法を用いて最適化を行うことができます.
この記事を書いている時点 (2019/08/21) でver. 0.4.1.5であり,現在も開発が行われています.
使うことの出来る機械学習パッケージと最適化手法
Hyperactiveでは,以下の機械学習パッケージに対応しています.
- scikit-learn
- LightGBM
- CatBoost
- Keras
また,Hyperactiveは以下の通り多くの最適化手法を使うことができます.
- Hill Climbing
- Stochastic Hill Climbing
- Tabu Search
- Random Search
- Random Restart Hill Climbing
- Random Annealing
- Simulated Annealing
- Stochastic Tunneling
- Parallel Tempering
- Particle Swarm Optimization
- Evolution Strategy
- Bayesian Optimization
インストール
READMEにある通り,次のようにインストールします.
pip install hyperactive
Random ForestのハイパーパラメータをPSOで最適化
今回は例としてRandom Forestのハイパーパラメータ最適化を行いました.
使うデータセット
今回はscikit-learnにあるIrisデータセットを用いました.Irisは4次元の特徴量,3種類のラベルを持つアヤメのデータセットです.
最適化なしと最適化を行った上での分類結果
まずRandom Forestを用いてハイパーパラメータ最適化を行わずに結果を求め,その後HyperactiveのPSOを用いて最適化を行った上で結果を求めました.
最適化なし
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier import time iris_data = load_iris() X = iris_data.data y = iris_data.target X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3) t1 = time.time() forest = RandomForestClassifier() forest.fit(X_train, y_train) t2 = time.time() print('Train score: {}'.format(forest.score(X_train, y_train))) print('Test score: {}'.format(forest.score(X_test, y_test))) print("time: {}".format(t2-t1))
結果
Train score: 0.9809523809523809 Test score: 0.9111111111111111 time: 0.009917736053466797
PSOで最適化
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from hyperactive import SimulatedAnnealingOptimizer from hyperactive import ParticleSwarmOptimizer import time iris_data = load_iris() X = iris_data.data y = iris_data.target X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3) # this defines the model and hyperparameter search space search_config = { "sklearn.ensemble.RandomForestClassifier": { "n_estimators": range(10, 100, 10), "max_depth": [3, 4, 5, 6], "criterion": ["gini", "entropy"], "min_samples_split": range(2, 21), "min_samples_leaf": range(2, 21), } } # start point start_point = { "sklearn.ensemble.RandomForestClassifier.0": { "n_estimators": [30], "max_depth": [6], "criterion": ["entropy"], "min_samples_split": [12], "min_samples_leaf": [16], } } #Optimizer = SimulatedAnnealingOptimizer(search_config, n_iter=100, n_jobs=4) Optimizer = ParticleSwarmOptimizer(search_config, n_iter=10, # number of iterations to perform metric="accuracy", n_jobs=1, cv=3, verbosity=1, random_state=None, warm_start=start_point, # Hyperparameter configuration to start from memory=True, # Stores explored evaluations in a dictionary to save computing time scatter_init=False, # Chooses better initial position by training on multiple random positions with smaller training dataset n_part=10, # number of particles w=0.5, # interia factor c_k=0.5, # cognitive factor c_s=0.9) # social factor # search best hyperparameter for given data t1 = time.time() Optimizer.fit(X_train, y_train) t2 = time.time() print("time: {}".format(t2-t1)) # predict from test data prediction = Optimizer.predict(X_test) # calculate accuracy score score = Optimizer.score(X_test, y_test) print("test accracy: {}".format(score))
結果と最適化されたパラメータ
start_point = {'sklearn.ensemble.RandomForestClassifier.0': {'n_estimators': [70], 'max_depth': [4], 'criterion': ['gini'], 'min_samples_split': [16], 'min_samples_leaf': [13]}} train score: 0.9721780604133546 test score: 0.9555555555555556 time: 18.719361782073975
Hyperactiveにも各手法ごとハイパーパラメータが存在しますが,今回は初期値のままで行いました.最適化されたパラメータはstart_pointに格納されるようです.
CNNのハイパーパラメータをRandom Searchで最適化
次に,深層学習の例としてCNNのハイパーパラメータ最適化を行いました.今回はGPUを積んでいないノートPCで実験を行っており,CNNはそれ自体の計算時間が長いため,今回は最適化手法の中で比較的計算コストの低いRandom Searchを用いています.また,いずれもepoch=5,batch size=500とし,損失関数にはcategorical clossentropyを用いています.
使うデータセット
ここではKerasにあるMNISTデータセットを用いました.MNISTは784 (28×28) 次元の特徴量,10種類のラベルを持つ手書き数字の画像データセットです.
最適化なしと最適化を行った上での分類結果
最適化なし
import time import numpy as np from keras.datasets import mnist from keras.utils import to_categorical from keras.layers import Input, Dense, Activation, BatchNormalization, Flatten, Conv2D, MaxPool2D, Dropout from keras.models import Model from keras.models import Sequential (X_train, y_train), (X_test, y_test) = mnist.load_data() X_train = X_train.reshape(60000, 28, 28, 1) X_test = X_test.reshape(10000, 28, 28, 1) y_train = to_categorical(y_train) y_test = to_categorical(y_test) model = Sequential() model.add(Conv2D(filters=32, kernel_size=(3,3), activation='relu', input_shape=(28,28,1))) model.add(MaxPool2D(pool_size=(2,2))) model.add(Conv2D(filters=(32), kernel_size=(3,3), activation='relu')) model.add(MaxPool2D(pool_size=(2,2))) model.add(Flatten()) model.add(Dense(30, activation='relu')) model.add(Dropout(0.4)) model.add(Dense(10, activation='softmax')) model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy']) t1 = time.time() history = model.fit(X_train, y_train, epochs=5, batch_size=500, verbose=1, validation_data=(X_test, y_test)) t2 = time.time() print("time: {}".format(t2-t1)) score = model.evaluate(X_test, y_test, batch_size=32, verbose=0) print('validation loss:{0[0]}\nvalidation accuracy:{0[1]}'.format(score))
結果
train score: 0.2430 test score: 0.3045 time: 146.26237177848816
Random Searchで最適化を行った結果
import time import numpy as np from keras.datasets import mnist from keras.utils import to_categorical from hyperactive import RandomSearchOptimizer, ParticleSwarmOptimizer (X_train, y_train), (X_test, y_test) = mnist.load_data() X_train = X_train.reshape(60000, 28, 28, 1) X_test = X_test.reshape(10000, 28, 28, 1) y_train = to_categorical(y_train) y_test = to_categorical(y_test) # this defines the structure of the model and print("time: {}".format(t2-t1))the search space in each layer search_config = { "keras.compile.0": {"loss": ["categorical_crossentropy"], "optimizer": ["SGD"]}, "keras.fit.0": {"epochs": [5], "batch_size": [500], "verbose": [2]}, "keras.layers.Conv2D.1": { "filters": [32, 64], "kernel_size": [(3, 3)], "activation": ["relu"], "input_shape": [(28, 28, 1)], }, "keras.layers.MaxPooling2D.2": {"pool_size": [(2, 2)]}, "keras.layers.Conv2D.3": { "filters": [16, 32], "kernel_size": [(3, 3)], "activation": ["relu"], }, "keras.layers.MaxPooling2D.4": {"pool_size": [(2, 2)]}, "keras.layers.Flatten.5": {}, "keras.layers.Dense.6": {"units": [30], "activation": ["relu"]}, "keras.layers.Dropout.7": {"rate": list(np.arange(0.4, 0.8, 0.2))}, "keras.layers.Dense.8": {"units": [10], "activation": ["softmax"]}, } Optimizer = RandomSearchOptimizer(search_config, n_iter=10) # search best hyperparameter for given data t1 = time.time() Optimizer.fit(X_train, y_train) t2 = time.time() print("time: {}".format(t2-t1)) # predict from test data prediction = Optimizer.predict(X_test) # calculate accuracy score score = Optimizer.score(X_test, y_test) print("test accracy: {}".format(score))
結果と最適化されたパラメータ
start_point = {'keras.compile.0': {'loss': ['categorical_crossentropy'], 'optimizer': ['SGD']}, 'keras.fit.0': {'epochs': [5], 'batch_size': [500], 'verbose': [2]}, 'keras.layers.Conv2D.1': {'filters': [32], 'kernel_size': [(3, 3)], 'activation': ['relu'], 'input_shape': [(28, 28, 1)]}, 'keras.layers.MaxPooling2D.2': {'pool_size': [(2, 2)]}, 'keras.layers.Conv2D.3': {'filters': [16], 'kernel_size': [(3, 3)], 'activation': ['relu']}, 'keras.layers.MaxPooling2D.4': {'pool_size': [(2, 2)]}, 'keras.layers.Flatten.5': {}, 'keras.layers.Dense.6': {'units': [30], 'activation': ['relu']}, 'keras.layers.Dropout.7': {'rate': [0.4]}, 'keras.layers.Dense.8': {'units': [10], 'activation': ['softmax']}} train score: 0.93845 test score: 0.9614 time: 1330.1183042526245
たった5epochですが,最適化を行った結果は最適化なしの場合よりかなり良い結果を得ることができていることが分かります.
終わりに
最近は最適化ツールとしてOptunaが人気ですが,様々な最適化手法を試すことのできるツールとして非常に興味深いツールだと思います.
一般にハイパーパラメータ最適化を行うにあたってはOptunaでも使われているTPEが優秀ですが,Meta-Heuristics Algorithmの深層学習のハイパーパラメータ最適化への適用はまだあまり研究が行われていない分野であり,研究を行いやすくするツールとしても役に立つのではないかと思います.