内容
15stepで踏破 自然言語処理アプリケーション入門 を読み進めていくにあたっての自分用のメモです。
今回は3章Step10で、自分なりのポイントをメモります。
準備
- 個人用MacPC:MacOS Mojave バージョン10.14.6
- docker version:Client, Server共にバージョン19.03.2
章の概要
8章では単純パーセプトロンから発展して多層パーセプトロンを導入し、9章では多クラス識別器を実装してみた。
10章ではニューラルネットワークの改善を目指す。
- ニューラルネットワークの層を深くする難しさ
- 重みの最適化手法
- ニューラルネットワークのチューニング方法
10.1 Deep Neural Networks
もともとは3層以上のニューラルネットワークをdeepと呼んでいた。
層を増やすのは model.add
で層を増やすだけで良い。
ニューラルネットワークの層を深くする難しさ
項目 | 解決策 |
---|---|
過学習しやすくなる | ・Early stopping ・層が多いとニューラルネットワークの表現力が高いため、学習データへ過剰に適合しやすい ・エポック単位で学習を繰り返すが、テストデータに対する精度が下がる前に学習を切り上げること ・Dropout ・学習時に一部のユニットを一定の割合でランダムに無視し、予測時には全ユニットを使う ・1回の学習で有効になるユニットの数が少ないので過学習しづらい ・複数のニューラルネットワークの足し合わせに似た予測の仕方が、アンサンブル学習と同様の効果を発揮する |
学習がうまく進まない | ・Batch normalization ・内部共変量シフトが起きる ・手前の層の重みの更新のせいで、後ろの層の重みの更新が妨げられる ・データの分布が平均0で分散1になるよう正規化する ・新たな層として追加したり、層中の活性化関数の前で実行したりする |
計算量の増大 | 高速に並列処理ができるGPUを利用してニューラルネットワークを学習する |
EarlyStopping
model.fit(X, y,
epochs = 100,
validation_split = 0.1,
callbacks = [EarlyStopping(min_delta = 0.0, patience = 1)])
# epochs:ある程度大きくして、EarlyStoppingで切り上げるより前に終わらないようにする
# validation_split:入力した学習データのうち、学習データとバリデーションデータの割合を指定できる
# callbacks:listで指定したコールバックが学習中に逐次呼び出される
Dropout
model = Sequential()
model.add(Dense(..))
model.add(Dropout(0.5))
model.add(Dense(..))
model.add(Dropout(0.5))
model.add(Dense(..))
model.add(Dropout(0.5))
model.add(Dense(.., activation = 'softmax')
model.compile(..)
# Dropoutのコンストラクタ引数はユニットを無視する割合
BatchNormalization
model = Sequential()
# 新たな層として追加
model.add(Dense(.., activation = 'relu'))
model.add(BatchNormalization(0.5))
# 活性化関数の前に追加
model.add(Dense(..))
model.add(BatchNormalization(0.5))
model.add(Activation('relu')
model.add(Dense(.., activation = 'softmax')
model.compile(..)
10.2 ニューラルネットワークの学習
勾配降下法
誤差関数を重みで微分することで傾きを求め、傾きの逆方向に重みの値を更新し、ニューラルネットワークの学習を進める。
大域最適解と局所最適解
- 大域最適解:求めたい最適解。取りうる重みの中で最も誤差が最小となる重み(最適解)
- 局所最適解:部分的に見れば最適解だが、最も適した解が他にも存在する
確率的勾配降下法とミニバッチ法
- 勾配降下法(バッチ法)
- 一度に全データを投入して全データの平均に対して重みを更新する
- 学習が進みづらい
- 局所最適解に陥りやすい
- 確率的勾配降下法(SGD)
- 一度に一個だけランダムに選んだ学習データを投入し、それに対して重みを更新する
- ノイズに対して弱く、重みの更新が悪い方向にブレると学習が安定しない
- ミニバッチ法
- バッチ法とSGDの中間
- 一度に数個だけランダムに選んだ学習データを投入し、それらのデータの平均に対して重みを更新する
10.3 ニューラルネットワークのチューニング
項目 | 内容 |
---|---|
バッチサイズ | ・学習時のバッチサイズで、Kerasのデフォルトは32 ・2のべき乗が多いがこれはただの慣習だが、小さい値は密に大きい値は疎に探索することに意味がある |
Optimizer | ・よく使われるのは後発のAdamだが、問題によってはシンプルなSGDが一番いい場合もある ・下の学習率とともにチューニングする |
学習率 | ・一度に更新する重みの比率で、KerasのAdamのデフォルトは0.001 |
活性化関数 | ・広く使われるのはReLU(Rectified Linear Unit)だが、改良版のやLeaky ReLUやSeLU(Scaled Exponential Unit)も検討の余地あり。 ・Leaky ReLU:入力が0以下の場合、傾きが小さな線形関数で変換 ・ELU:入力が0以下の場合、指数関数から1引いた値で変換 |
正則化/荷重減衰 | ・過学習を避けるために重みが大きくなりすぎないよう制約をかけ、下記のようなノルムを損失関数に加える ・l1ノルム:重みの各要素の絶対値の和 ・l2ノルム:重みの各要素の2乗和 ・l∞ノルム:重みの各要素の最大絶対値 |
重みの初期化 | ・Kerasのデフォルトは乱数で初期化される ・分布を指定することも可能 |
活性化関数
model = Sequential()
model.add(Dense(.., activation = 'selu'))
model.add(Dense(.., activation = LeakyReLU(0.3)))
model.add(Dense(.., activation = 'softmax')
model.compile(..)
正則化
model = Sequential()
model.add(Dense(.., activation = 'relu',
kernel_regularizer = regularizers.l2(0.1)))
model.add(Dense(.., activation = 'softmax',
kernel_regularizer = regularizers.l2(0.1)))
model.compile(..)
重みの初期化
model = Sequential()
model.add(Dense(.., activation = 'relu',
kernel_initializer = initializers.glorot_normal()))
model.add(Dense(.., activation = 'softmax',
kernel_initializer = initializers.glorot_normal()))
model.compile(..)
glorot_normal():Glolotの正規分布のことで、Xavierの正規分布とも呼ばれる(こっちの方が聞いたことがあった)。
Xavierの初期値はsigmoid関数やtanh関数に適しているが、活性化関数にReLUを用いる場合はReLUに特化したHeの初期値の方が良さそう。