2017年12月26日火曜日

Batch Normalizationとスケーリングの影響を可視化して遊んでみた

Batch Normalizationとスケーリングが性能に与える影響を可視化してみました。 たぶん大多数の人にとっては誰得感のある情報ですが、 世の中のデータはスケーリングが簡単なデータばかりではないので、私は一度で良いから可視化してみたかった。

全体のコードは出すまでもないかなと思ったので省略しますが、Kerasの一番わかりやすいサンプルであるmnist_mlp.pyをベースにして調査してみました。 まずはBatch Normalizationを付けない状態で、スケーリングを行っている部分を以下のように変化させてみました。
# x_train /= 255  # 1e1 (baseline)
# x_test /= 255  # 1e1 (baseline)
# x_train = x_train / 255 / 100   # 1e-2
# x_test = x_test / 255 / 100  # 1e-2
x_train = x_train / 255 * 10000   # 1e4
x_test = x_test / 255 * 10000  # 1e4
結果は以下。mnistのコードをそのまま利用した場合、適切なスケーリングである1e1から離れると顕著に性能が下がる事がわかります。 特に特徴量の平均が大きくなるとまったく学習できていません。 いかにスケーリングが重要かよくわかる結果と思います。




次にBatch Normalization (BN)を付けた場合も検証してみます。 モデルに以下のようにBNを追加し、同じようにスケーリングを変化させてみました。
model = Sequential()
model.add(Dense(512, input_shape=(784,)))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Dropout(0.2))
model.add(Dense(512))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Dropout(0.2))
model.add(Dense(num_classes, activation='softmax'))
特徴量の平均を小さくすると性能低下しています。 [0,1e-2]のスケーリングでも若干の収束遅れが見られ、[0,1e-4]では悲惨な事になる事がわかりました。 逆に平均を大きくしても性能にはまったく影響なし。BNも割とあっさり死ぬんだなあ。



特徴量の平均を小さくした時にBNが死ぬのはなぜでしょう。 BNの標準化式に含まれるepsilonの影響かと考え、epsilonをより小さな値(1e-8)にしてみました。 しかしこれは失敗で、先ほどと大差ないグラフになりました。 epochを増やしたり、epsilonをさらに小さくしたり、最適化関数を変えたりしてみましたが駄目駄目。 となると更新幅の係数の影響のほうが大きいのかも。 まあよくわからないので、BNを使う時は特徴量は小さくするよりを大きくしたほうが良さそうと、お茶を濁しておきます。 ちなみに[0,1e-4]などの分布のデータを事前に標準化すれば問題は起きません。 この特性は多層で大丈夫なのかなあとちょっと心配になる結果。



まとめ

  • 特徴量はスケーリングしないと駄目、絶対
  • スケーリングが面倒臭いならBatch Normalizationしよう
  • ただし特徴量の平均が小さ過ぎるとBatch Normalizationは死ぬ
  • Batch Normalizationが死んだ時は特徴量の平均を大きく
  • 緩募: 特徴量の平均が小さい時でもBatch Normalizationを動かす方法

誰得感のある情報ですが、Batch Normalizationは凄いものの特徴量の平均が小さい時に起きる突然の死に注意という事がわかりました。

1 件のコメント: