はじめに
Kerasなどでニューラル ネットワークを用いると直面する問題の一つに過学習があると思います。過学習に直面した時、どのように回避するのでしょうか?今回は、その回避策を3つ紹介します。

そもそも過学習って何?
過学習とは、訓練データに対して学習されているが、未知データに対しては適合できていない状態を指します。これは、受験勉強に例えるとわかりやすかと思います。例えば、過去問だけを異常に勉強して100点で臨んだいざ本番の受験では過去と傾向が若干変わり、30点だったというケースです。(泣けますね><)
その原因の一つとして統計モデルへの適合に対し、変数が多すぎる等により、訓練データの個数に比べて、モデルが複雑で自由度が高すぎることがあるためです。本当は線形(直線)で分離が可能な自称をより高次元な関数(曲線)で分離するがゆえに、汎化できないケースです。


対策
過学習の対策を3つ紹介します。
- ニューラルネットワークのサイズを小さくする
- K分割交差検証
- ドロップアウト
ニューラルネットワークのサイズを小さくする
ニューラルネットワークのサイズは、ノード数と層数の二点で決定されます。過学習が疑われるときは、基本指針としてノード数と層数を減少させることが必要です。層数とは、以下の例だと4層です。一番初めの入力層と最後の出力層は、削ることができないので、中間層を削ります。
・改善前のコード
model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(784,)))
model.add(Dense(128, activation='relu'))
model.add(Dense(128, activation='relu'))
model.add(Dense(5, activation='softmax'))
・層数改善後のコード
model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(784,)))
model.add(Dense(128, activation='relu'))
model.add(Dense(5, activation='softmax'))
・ノード数改善後のコード
model = Sequential()
model.add(Dense(128, activation='relu', input_shape=(784,)))
model.add(Dense(64, activation='relu'))
model.add(Dense(5, activation='softmax'))
ただし、ニューラルネットワークの適切なサイズを事前に把握することは困難です。よって、実際に学習結果を把握しながらパラメータを調整するのが良いかと思います。その時の評価指標としては、k−分割交差検証による複数のモデルの各ネットワークの平均二乗誤差の平均や標準偏差とするのが一般的です。k−分割交差検証については、次節に記載します。
k−分割交差検証
k-分割交差検証(クロスバリデーション)では、教師データをk分割し、 そのうちの1つをテストデータ、残りのk-1個を学習データとして使用します。そして、学習と評価を繰り返して得られるk個のモデルと性能評価から平均性能を算出するという手法です。これにより、データ数が少ない場合で、一部のデータで学習することによる過剰な適合が起きていないかを把握することができます。
例えばK=4の場合、4分割されたデータのうち3つを教師データ、残りの一つを検証データとし、平均二乗誤差Eを算出します。これを検証データを被ることがないように変更し、合計4ループ回します。このEの総和をとり、その平均値を

・k=4のコードの例
from sklearn.model_selection import KFold
def cross_validate(data, split_size=4):
results = []
kf = KFold(n_splits=split_size)
for train_idx, val_idx in kf.split(train_x_all, train_y_all):
train_x = train_x_all[train_idx]
train_y = train_y_all[train_idx]
val_x = train_x_all[val_idx]
val_y = train_y_all[val_idx]
run_train(data, train_x, train_y)
results.append(data.run(accuracy, feed_dict={x: val_x, y: val_y}))
return results
ドロップアウト
Dropoutとは、学習時の更新においてランダムに入力ユニットを0とする手法です。これにより学習時にネットワークの自由度を小さくすることで汎化性能を上げ、過学習を回避することができます。Dropout(数値)の数値のところに0-1の値を入力することができます。1は100%の確率で、ランダムに0に置き換えるという意味になります。従いまして、過学習の度合いが強い場合は、こちらの数値を大きくすると改善する可能性があります。隠れ層においては、一般的には、50%(0.5)程度と良いかと思います。
・コードの例
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.25))
まとめ
過学習の対策3種類を紹介しました。機会学習のコツは、とにかくパラメータをいじりながら損失関数の値をトラッキング、そしてさらなるパラメータ調整というループを回すことです。そうすることで、徐々にコツを覚えらえます。あまり頭でっかちにならず文献を沢山調べるというよりは、手を動かすことが大切です。すると見えてくる課題があると思いますので、その課題にあった論文をサーベイするなどが必要かと思います。
より詳細な対策を知りたい方は、こちらがオススメです。

コメント