optunaしくじり備忘録

## はじめに
Google Colaboratory上でCNNを使って画像認識を試しています。
ディープラーニングではハイパーパラメータの設定値によって学習結果がかなり異なる結果になることがあります。ハイパーパラメータの最適化を行うことができるoptunaを使っていろいろと試行錯誤してみましたので、その備忘録として記録を残したいと思います。

### optunaについて
まず、optunaについておさらいします。
optunaとはPrefferdNetworks社が作成したディープラーニングの学習で使用するハイパーパラメータを最適化するフレームワークのことです。optunaを使ってハイパーパラメータの最適化を実施するためには、[公式サイト](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.create_study.html#optuna.create_study)に記載されているようにoptuna.create_study()で学習を開始し、optimize()で最適化を実施します。
これを実施することにより最適なハイパーパラメータを算出することができます。

### trial.suggest_discrete_uniform() APIについて
まず、全結合層のユニット数を設定するため、以下のような設定としました。
```python:Warningが発生するコード
def objective(trial):
    mid_units = int(trial.suggest_discrete_uniform("mid_units", 128, 512, 128))

    model = create_model(mid_units)

study.optimize(objective, n_trials=20)
```
このコードで実行すると以下のようなWarningが発生しました。

**FutureWarning: suggest_discrete_uniform has been deprecated in v3.0.0. This feature will be removed in v6.0.0. See https://github.com/optuna/optuna/releases/tag/v3.0.0. Use suggest_float(..., step=...) instead.**

suggest_discrete_uniform() APIは、v3.0.0で非推奨となり、v6.0.0で削除されるとのこと。代わりにsuggest_float() APIを使ってくれ～と記載されていますね。

私が使用している環境のoptunaはv4.1.0でしたので、上記のWarningが出力されたということですね。Warningの指摘通り、suggest_float() APIを使うようにします。

### trial.suggest_float() APIについて
suggest_discrete_uniform() APIではなくsuggest_float() APIを使ってくれ～とのことでしたので、単純に置き換えます。
[公式Web](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html)では、suggest_float() APIは以下のように説明されています。
　suggest_float(name, low, high, *[, step, log])
lowからhighまでの小数点の値から任意の値を提案するためのAPIのようですね。オプションとしてstepで値の間隔を指定できるのだろうと思います。
128～512の範囲を128間隔で指定したかったので、以下のように記述してみました。
```python:Errorが発生するコード
def objective(trial):
    mid_units = int(trial.suggest_float("mid_units", 128, 512, 128))

    model = create_model(mid_units)

study.optimize(objective, n_trials=20)
```
このコードで実行すると以下のようなErrorが発生しました。

**TypeError: Trial.suggest_float() takes 4 positional arguments but 5 were given**

引数の数が異なっている？？
いろいろと試行錯誤したところ、正しくは以下のようにstep=で数値の指定が必要なようです。
```python:Errorが発生しないコード
def objective(trial):
    mid_units = int(trial.suggest_float("mid_units", 128, 512, step=128))

    model = create_model(mid_units)

study.optimize(objective, n_trials=20)
```

### Sequentialモデルについて
Sequentialモデルを使ってCNNのモデルを以下のように作成しました。

```python:Warningが発生するコード
IMG_HEIGHT = 100
IMG_WIDTH = 100

def create_model(mid_units):
    model = models.Sequential()
    model.add(keras.layers.Conv2D(filters=32, kernel_size=3,
                 activation="relu", padding="same", input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)))
    model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), padding="same"))
    model.add(keras.layers.Dropout(0.2))
    model.add(keras.layers.Conv2D(filters=64, kernel_size=3,
                 activation="relu", padding="same"))
    model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), padding="same"))
    model.add(keras.layers.Dropout(0.2))
    model.add(keras.layers.Conv2D(filters=128, kernel_size=3,
                 activation="relu", padding="same"))
    model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), padding="same"))
    model.add(keras.layers.Dropout(0.2))
    model.add(keras.layers.Flatten())
    model.add(keras.layers.Dense(mid_units))
    model.add(keras.layers.Dense(2, activation="softmax"))
    return model

def objective(trial):
    mid_units = int(trial.suggest_float("mid_units", 128, 512, step=128))

    model = create_model(mid_units)

study.optimize(objective, n_trials=20)
```
これを実行すると、以下のようなWariningが発生しました。

**UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.**

Sequentialモデルの場合は、最初のレイヤとしてConv2Dをいきなり配置するのではなくInputレイヤを配置してくれ～ということのようです。
そこで以下のように最初のレイヤとしてInputLayerを追加しました。

```python:Warningが発生するコード2
IMG_HEIGHT = 100
IMG_WIDTH = 100

def create_model(mid_units):
    model = models.Sequential()
    model.add(keras.layers.InputLayer(input_shape=(IMG_HEIGHT, IMG_WIDTH, 3)))
    model.add(keras.layers.Conv2D(filters=32, kernel_size=3,
                 activation="relu", padding="same"))
    model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), padding="same"))
    model.add(keras.layers.Dropout(0.2))
    model.add(keras.layers.Conv2D(filters=64, kernel_size=3,
                 activation="relu", padding="same"))
    model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), padding="same"))
    model.add(keras.layers.Dropout(0.2))
    model.add(keras.layers.Conv2D(filters=128, kernel_size=3,
                 activation="relu", padding="same"))
    model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), padding="same"))
    model.add(keras.layers.Dropout(0.2))
    model.add(keras.layers.Flatten())
    model.add(keras.layers.Dense(mid_units))
    model.add(keras.layers.Dense(2, activation="softmax"))
    return model

def objective(trial):
    mid_units = int(trial.suggest_float("mid_units", 128, 512, step=128))

    model = create_model(mid_units)

study.optimize(objective, n_trials=20)
```

これを実行すると、以下のようなWariningが発生しました。

**UserWarning: Argument `input_shape` is deprecated. Use `shape` instead.**

どうもInputLayerではinput_shapeは非推奨のようですね。
以下のように設定することでWarningは解消しました。

```python:Warningが発生しないコード
IMG_HEIGHT = 100
IMG_WIDTH = 100

def create_model(mid_units):
    model = models.Sequential()
    model.add(keras.layers.InputLayer(shape=(IMG_HEIGHT, IMG_WIDTH, 3)))
    model.add(keras.layers.Conv2D(filters=32, kernel_size=3,
                 activation="relu", padding="same"))
    model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), padding="same"))
    model.add(keras.layers.Dropout(0.2))
    model.add(keras.layers.Conv2D(filters=64, kernel_size=3,
                 activation="relu", padding="same"))
    model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), padding="same"))
    model.add(keras.layers.Dropout(0.2))
    model.add(keras.layers.Conv2D(filters=128, kernel_size=3,
                 activation="relu", padding="same"))
    model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), padding="same"))
    model.add(keras.layers.Dropout(0.2))
    model.add(keras.layers.Flatten())
    model.add(keras.layers.Dense(mid_units))
    model.add(keras.layers.Dense(2, activation="softmax"))
    return model

def objective(trial):
    mid_units = int(trial.suggest_float("mid_units", 128, 512, step=128))

    model = create_model(mid_units)

study.optimize(objective, n_trials=20)
```

### おわりに
今回はoptuna実施時に発生したWarningやErrorについて備忘録として残しました。
optunaを使ってハイパーパラメータの最適化を実施していきたいと思います。