CourseraのDeep Learning SpecializationのImproving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimizationコースを修了した
Fri, Jan 12, 2018
coursera machine learning deep learning neural network tensorflowCourseraのDeep Learning SpecializationのNeural Networks and Deep Learningコースを修了したのに続き、Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimizationコースを修了した。
このコースは、ディープニューラルネットワークのチューニングなどについて学べる3週間のコース。 今のところ全部英語。
2018/1/5に始めて、1/12に完了。 8日間かかった。 修了したらまたCertifacateもらえた。
以下、3週分の内容をメモ程度に書いておく。
1週目
OverfittingやUnderfittingを防ぐテクニックについて。
動画
データ分割
深層学習のモデル構築は、検討(Idea)、実装(Code)、検証(Experiment)というサイクルの繰り返し(Iterative process)。 取り組む課題、課題のドメイン、データ量、マシンの構成などにより、ハイパーパラメータは変わるので、経験をもって事前に予測することは無理なので、サイクルをどれだけ速く回せるかが鍵。
データは、訓練データ(Training set)、Devデータ(Development set))(a.k.a. Cross-validation set)、テストデータ(Test set)に分割する。 訓練データで学習し、Devデータでハイパーパラメータを評価し、テストデータで最終的な評価と性能見積をする。 テストデータは無くてもいい。
サンプル数が1万くらいだった時代は、6:2:2くらいで分割してたけど、近年は数百万とかのデータを扱い、交差検証データやテストデータの割合はもっと小さくするのがトレンド。 98:1:1など。
Devデータとテストデータは同じようなものを使うべき。 訓練データは必ずしも同様でなくてもいい。訓練データは沢山要るので、別のデータソースからとることもある。
バイアス vs バリアンス
でかいネットワークで正則化して大量データで学習させるのが吉。
正則化
過学習(Overfitting)を防ぐため、コスト関数を正則化(Regularization)すべし。
ロジスティック回帰ではL2ノルム(L2 norm)を使ったL2正則化が一般的。 L1正則化はあまり使われない。 L1正則化をすると、wがスパース行列になってモデルを圧縮できると言われることがあるが、経験上その効果はほとんどない。
正則化パラメータλはハイパーパラメータで、Devデータで評価する。
ニューラルネットワークでは正則化項にフロベニウスノルム(Frobenius norm)を使う。
Dropout(Inverted Dropout)
ランダムにノードを無効化しながら学習することで過学習を防ぐ。 画像処理の分野では、特徴量の数が多く学習データが少ない傾向があるので、ほぼ常に使われる。
コスト関数が計算できなくなるのが欠点。 計算する必要があるときにはDropoutを無効化する。
データ拡張(Data augmentation)
データを加工して増やせば、高バリアンス対策になる。
早期終了(Early stopping)
過学習するまえに学習を止めるテクニック。 訓練データとDevデータについてコストをプロットして、Devデータのものが上がる前に止める。
これは、直交化(Orthogonalization)の原則、つまり一度に一つのことを考慮すべきという原則に反していて、コストを最小化するという問題と、過学習を避けるという問題に同時に対処することになるので微妙。
普通は代わりにL2正則化使えばいいけど、λを最適化する手間を省きたいときには選択肢になりうる、というか実現場ではちょくちょく選択肢になる。
訓練データの正規化(Normalization)
訓練データの各特徴量について平均を0にして分散を1にすると学習が速くなる。
訓練データを正規化したらテストデータも正規化する。 その際、正規化パラメータは訓練データのものを使う。
勾配消失(Vanishing gradient)、勾配爆発(Exploding gradient)
ニューラルネットワークの層が深くなると、層の出力や勾配が指数関数的に大きくなったり小さくなったりして、学習が難しくなる問題。 長年ディープニューラルネットワークの発展を妨げてきた問題。
パラメータのランダム初期化をすると防げる。 ガウス分布で作ったパラメータに特定の値を掛けてを分散が1/n(ReLUの時は2/n)になるように調整して、活性化関数に入力する値を約1に抑える。 掛ける値は活性化関数ごとにだいたい決まっていて((e.g. Xavier Initialization)、その値をハイパーパラメータとして調整するのはそれほど優先度は高くない。
Gradient checking
順伝播・逆伝播が正確に実装できているかを、数値計算手法で概算した勾配と逆伝播で出した勾配を比べてチェックするテクニック。 計算コストが高くなるので、デバッグ時にのみ使う。
Dropoutしてるときには使えない。
プログラミング課題
初期化
ゼロ初期化、大きい値でのランダム初期化、He初期化(Xavier初期化っぽいやつ)を実装して性能を比べる。
正則化
正則化無し、L2正則化、Dropoutの実装と比較。
Gradient checking
Gradient checkingの実装と、その結果を利用した逆伝播のデバッグ。
Yoshua Bengioへのインタビュー
2週目
学習を速くするテクニックについて。
動画
ミニバッチ勾配降下法(Mini-batch gradient descent)
普通の勾配降下法(i.e. バッチ勾配降下法)よりかなり速いので、大規模データでよく使われるテクニック。 学習回数に対するコストのプロットはノイジーになる。
ミニバッチサイズというハイパーパラメータが増える。 ミニバッチサイズがmならバッチ勾配降下法、1なら確率的勾配降下法(Stochastic gradient descent)になる。
ミニバッチ勾配降下法と確率的勾配降下法は収束しない。
バッチ勾配降下法は遅すぎる。 確率的勾配降下法はベクトル化の恩恵がなくなるという欠点がある。 ので、適当なミニバッチサイズにするのがよく、それが一番速い。
2000個くらいのデータならバッチ勾配降下法。 それより多ければ、64~512位のミニバッチサイズがいい。 メモリ効率を考えると2の累乗数がいいし、CPU/GPUメモリサイズに乗るサイズにすべし。
指数加重移動平均 (EWMA: Exponentially Weighted Moving Average)
ノイズのあるデータから、よりスムーズなプロットを書く手法。 過去数日の平均をもとにプロットする。
この手法だと、最初の方のデータが不当に小さくなってしまう。 これが問題になるなら、バイアス補正(Bias correction)をかける。
モーメンタム(Momentum)付き勾配降下法
パラメータを更新するときに、勾配そのままではなく、勾配の指数加重移動平均を使う手法。 勾配降下を滑らかに速くできる。 慣性(勢い)をつけて走り抜ける感じ。
指数加重移動平均を計算するときのβが新たなハイパーパラメータになる。 普通0.9。この場合バイアス補正はそんなに効果ないので普通かけない。
RMSprop
パラメータを更新するときに、勾配そのままではなく、勾配の二乗平均平方根(RMS: Root Mean Square)を使う手法。 学習率を上げつつ、勾配降下を滑らかに速くできる。
二乗平均平方根を計算するときのβと、ゼロ除算を防ぐためのεが新たなハイパーパラメータになる。 提唱者は、
β=0.999
、ε=10^-8
を推奨しているし、これらをチューニングすることはあまりない。Couseraが広めたことで、よく使われるようになった。
Adam(Adaptive Moment Estimation)
モーメンタムとRMSpropとバイアス補正を組み合わせた最適化アルゴリズム。 これをミニバッチ勾配降下法と一緒に使えばだいたい上手くいく。
学習率減衰(Learning rate decay)
ミニバッチ勾配降下を収束させるために、学習率を徐々に小さくする手法。 エポックごとに学習率を下げる。
学習率αが、α0と 減衰率(Decay rate)とエポック番号から計算されるようになるので、α0と減衰率がハイパーパラメータ。
学習率の計算方法にはいくつかある。 指数関数的に下げたり、階段状に下げたり。
Andrew先生はあまり使わない手法。 学習率をよくチューニングすれば十分。
局所最適解(Local optima)
かつて、勾配が0になる点は、コストの谷、つまり局所最適解だと考えられていて、そこに嵌ることが問題だった。
けど、ディープニューラルネットワークでは多くは尾根的なもの。 鞍の上みたいな部分なので鞍点(Saddle point)と呼ばれる。 特徴量が沢山あるので、ちょっと動かすとどれかの勾配は負になる。
よって局所最適解はあまり恐れなくていい。 代わりに、鞍点の台地みたいな部分では勾配が小さいので学習効率が悪くなる。 ここを勢いよく抜けたいので、モーメンタムやRMSpropやAdamが有効になる。
プログラミング課題
ミニバッチ勾配降下法実装
訓練データをシャッフルして、ミニバッチサイズに分割して(余りは余りでミニバッチにする)、forループで回す。
モーメンタムとAdam実装
単なるミニバッチ勾配降下法とモーメンタム付きとAdamを比較。
Yuanqing Linへのインタビュー
中国の国立深層学習研究所のトップ。
2週目
ハイパーパラメータのチューニング方法、バッチ正規化、ソフトマックス回帰、TensorFlow。
動画
ハイパーパラメータのチューニング
一番重要なのは学習率。 次はモーメンタムのβとかミニバッチサイズとか隠れ層のノード数。 その次がレイヤ数とか学習率減衰率。
チューニングの際は、かつてはグリッドサーチ(Grid search)がよく使われたけど、これはハイパーパラメータが多くなるとつらい。 ランダムサーチ(Randomized search)がより効率的。
グリッドサーチだとあるパラメータを固定して別のパラメータを変化させるけど、変化させたパラメータがどうでもいいものだった場合、その試行がほとんど無駄になるので。
粗くランダムサーチして当たりをつけ、範囲を絞って細かいランダムサーチする。
ランダムといってもいろいろあって、ユニット数なんかは一様にランダムでいいけど、学習率なんかはlogスケールの上でランダムにしたほうがいい。
実運用では、計算リソースが少ない場合に採るパンダアプローチと、潤沢なリソースで複数のモデルを同時に訓練するキャビアアプローチがある。
バッチ正規化(Batch normalization)
深層学習の実用化において最も重要なアルゴリズムの一つ。 ハイパーパラメータの選定を簡単にして、ディープニューラルネットワークの訓練を簡単にする。
バッチ正規化では、各層の入力を正規化する。 ミニバッチごとに平均を0、分散を1に正規化した後、βとγというパラメータでそれぞれを調整する。 aよりz(i.e. 活性化関数適用前)を正規化するのが普通。
(ハイパーではない)パラメータとしてβとγが層ごとに増える。 これらもWとともに学習する。 βがbの役割をするので、bはいらなくなる。
バッチ正規化は共変量シフト(Covariate shift)という問題に対応するもの。 共変量シフトは、訓練した後で入力の分散が変わると、また訓練しなおさないといけないという問題。 ニューラルネットワークの内部では、前のほうの層のWが学習を進めるたびに変わり、その層の出力が変わる。 つまり後のほうの層への入力が変わるので、後のほうの層の学習が進みにくい。 バッチ正規化は、この後のほうの層への入力の分散を一定範囲に抑えることで、後のほうの層の学習を効率化する。
Dropoutと同様な論理(ノードへの依存が分散される)で正則化の効果もややある。
訓練が終わったら、最後のミニバッチの平均μと分散σ^2を保存しておいて、予測時に使う。 μとσ^2は訓練データ全体から再計算してもよさそうだけど、普通はやらない。
ソフトマックス回帰(Softmax regression)
ニューラルネットワークで多値分類(Multi-class classification)するアルゴリズム。 出力層(ソフトマックス層)のノード数をクラス数にして、活性化関数にソフトマックス関数を使う。 出力層の各ノードは、サンプルが各クラスに属する確率を出力する。
TensorFlow
ディープラーニングフレームワークはいろいろある: Caffe/Caffe2、CNTK、DL2J、Keras、Lasagne、mxnet、PaddlePaddle、TensorFlow、Theano、Torch。 プログラミングしやすいこと、訓練性能がいいこと、オープンであることが重要。
プログラミング課題
TensorFlowの基本
TensorFlowでのプログラムはだいたい以下のような手順で書く。
- テンソル(tensor)をつくる。これはまだ評価されない。
- テンソル間の計算式(計算グラフ)を書く。
- テンソルを初期化する。
- セッションを作る。
- セッションを実行する。ここで計算が実行される。
後で(セッション実行時に)値を入れたい場合はプレースホルダ(placeholder)を使う。
TensorFlowでのニューラルネットワーク実装
画像を読み込んで多クラス分類するNNを作る。 以下、今回使った関数の一部。
- シグモイド関数:
tf.sigmoid(x)
- エントロピーコスト:
tf.nn.sigmoid_cross_entropy_with_logits(logits, labels)
- One-hotエンコーディング:
tf.one_hot(labels, depth, axis)
- 最急降下法:
tf.train.GradientDescentOptimizer(learning_rate = learning_rate).minimize(cost)
- シグモイド関数: