【TensorFlow Keras】 Tunerを用いたハイパーパラメータ最適化

## ハイパーパラメータとは

深層学習モデルの性能は、ネットワークアーキテクチャや学習アルゴリズムの設定によって大きく左右されます。
これらの設定値を「ハイパーパラメータ」と呼びます。
適切なハイパーパラメータの選択は、モデルの性能向上に不可欠です。

### Deep Learningにおける2つのハイパーパラメータ

1. **モデルハイパーパラメータ**: 非表示レイヤーの数と幅などのモデルの選択に影響します。
    - レイヤー数
    - ユニット数（ニューロン数）
    - 活性化関数の種類
    - ドロップアウト率
    - カーネルサイズ（CNNの場合）
2. **アルゴリズムハイパーパラメータ**: 確率的勾配降下法 (SGD) の学習率や k 最近傍 (KNN) 分類器の最近傍の数など、学習アルゴリズムの速度と質に影響します。
    - 学習率
    - バッチサイズ
    - エポック数
    - オプティマイザの種類
    - 正則化パラメータ（L1/L2）

## ハイパーパラメータ最適化の手法

ハイパーパラメータを最適化するための主な手法には、以下のものがあります：

1. **グリッドサーチ**: 指定した値の組み合わせをすべて試す方法
2. **ランダムサーチ**: ランダムに値の組み合わせを試す方法
3. **ベイズ最適化**: 過去の試行結果を利用して次の試行を決定する手法
4. **Hyperband**: 早期終了を活用して効率的に探索する手法

## Keras Tunerの導入

TensorFlowのKeras Tunerは、こうしたハイパーパラメータの最適化を自動化するためのライブラリです。まずはインポートします：

```jsx
import keras_tuner as kt
```

## ハイパーモデルの定義

ハイパーモデルとは、ハイパーパラメーターの探索のために「モデル定義」+「探索するハイパーパラメータの設定」を1つのまとまりにしたものです。`model_builder`関数を定義して、探索したいハイパーパラメータを指定します。

```Python
def model_builder(hp):
  model = keras.Sequential()
  model.add(keras.layers.Flatten(input_shape=(28, 28)))

  # 最初の Dense レイヤのユニット数をチューニングする
  # 32〜512 の範囲で最適な値を選択
  hp_units = hp.Int('units', min_value=32, max_value=512, step=32)
  model.add(keras.layers.Dense(units=hp_units, activation='relu'))
  model.add(keras.layers.Dense(10))

  # オプティマイザの学習率をチューニングする
  # 0.01、0.001、0.0001 の中から最適な値を選択
  hp_learning_rate = hp.Choice('learning_rate', values=[1e-2, 1e-3, 1e-4])

  model.compile(optimizer=keras.optimizers.Adam(learning_rate=hp_learning_rate),
                loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                metrics=['accuracy'])

  return model

```

### ハイパーパラメータの種類

Keras Tunerでは、以下のような方法でハイパーパラメータを定義できます：

- **Int()**: 整数値のハイパーパラメータ（例：レイヤーのユニット数）
- **Float()**: 浮動小数点値のハイパーパラメータ（例：ドロップアウト率）
- **Choice()**: 指定した複数の値から選択するハイパーパラメータ（例：活性化関数の種類）
- **Boolean()**: TrueまたはFalseのバイナリ選択（例：バッチ正規化の使用有無）
- **Fixed()**: 固定値のパラメータ（探索対象外）

## Hyperbandを用いたハイパーパラメータチューニング

Hyperbandは、初期の性能が低いモデルを早期に破棄し、有望なモデルにより多くの計算リソースを割り当てる効率的な手法です。

```Python
tuner = kt.Hyperband(model_builder,
                     objective='val_accuracy',
                     max_epochs=10,
                     factor=3,
                     directory='my_dir',
                     project_name='intro_to_kt')

```

### Hyperbandの主要パラメータ

- **objective**: 最適化する指標（例：'val_accuracy'）
- **max_epochs**: 学習する最大エポック数
- **factor**: 各ステージで残すモデルの割合を決めるパラメータ（例：3は各ステージで1/3のモデルのみが残る）
- **directory**: 結果を保存するディレクトリ
- **project_name**: プロジェクト名

## その他のチューナー

Keras Tunerには、Hyperband以外にも以下のようなチューナーが用意されています：

1. **RandomSearch**: ランダムにハイパーパラメータを探索します
    
    ```python
    tuner = kt.RandomSearch(
        model_builder,
        objective='val_accuracy',
        max_trials=10,
        directory='my_dir',
        project_name='random_search'
    )
    
    ```
    
2. **BayesianOptimization**: ベイズ最適化に基づいてハイパーパラメータを探索します
    
    ```python
    tuner = kt.BayesianOptimization(
        model_builder,
        objective='val_accuracy',
        max_trials=10,
        directory='my_dir',
        project_name='bayesian_opt'
    )
    
    ```
    

## アーリーストッピングの設定

探索中の各モデルに対してアーリーストッピングを適用することで、不要な計算を避けられます：

```jsx
stop_early = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5)

```

## 探索の開始

`search()`メソッドを呼び出して、ハイパーパラメータの探索を開始します：

```jsx
tuner.search(img_train, label_train, epochs=50, validation_split=0.2, callbacks=[stop_early])

# 最高のハイパーパラメータを取得
best_hps = tuner.get_best_hyperparameters(num_trials=1)[0]

```

探索中、Keras Tunerは様々なハイパーパラメータの組み合わせを試し、最も良い結果を出した設定を記録します。

## 最適なハイパーパラメータの確認と活用

探索後は、最適なハイパーパラメータの値を確認できます：

```python
print(f"最適なユニット数: {best_hps.get('units')}")
print(f"最適な学習率: {best_hps.get('learning_rate')}")

```

## 最適なモデルの学習

最適なハイパーパラメータを用いてモデルを構築し、学習を行います：

```Python
# 最適なハイパーパラメータでモデルを構築し、50 エポックで学習
model = tuner.hypermodel.build(best_hps)
history = model.fit(img_train, label_train, epochs=50, validation_split=0.2)

val_acc_per_epoch = history.history['val_accuracy']
best_epoch = val_acc_per_epoch.index(max(val_acc_per_epoch)) + 1

```

## 最適なエポック数での再学習

検証精度が最も高かったエポック数を特定し、そのエポック数でモデルを再学習します：

```jsx
hypermodel = tuner.hypermodel.build(best_hps)

# モデルを再学習
hypermodel.fit(img_train, label_train, epochs=best_epoch, validation_split=0.2)

eval_result = hypermodel.evaluate(img_test, label_test)

```

## ハイパーパラメータチューニングのベストプラクティス

1. **探索範囲を適切に設定する**: 広すぎると探索に時間がかかり、狭すぎると最適値を見逃す可能性があります
2. **計算コストと精度のトレードオフを考慮する**: max_trialsやmax_epochsを調整して探索の範囲を制御します
3. **複数の指標をモニターする**: 主要な指標だけでなく、複数の評価指標を確認することで、モデルの総合的な性能を評価します
4. **Cross-Validationを併用する**: データセットが小さい場合は、クロスバリデーションを用いて評価の信頼性を高めます

### 参考文献

Tensorflowの公式チュートリアルです。

https://www.tensorflow.org/tutorials/keras/keras_tuner?hl=ja