概要
Week 5 ではニューラルネットワークを誤差逆伝搬法を使って学習することを学びました。
誤差逆伝搬法
- 誤差逆伝搬法は、コスト関数の微分を得る方法
- 微分が分かれば今までと同様に最適化手法を適用できる
誤差逆伝播法のアルゴリズム
各データに対して、
- 入力層に入力値をセット
- forward propagation を普通に計算
- さっきのδを入力層に向かって計算する. 最初は単に δ(L) = (出力層 - データの答え)
- 中間層は各エッジのウェイトと接続先のエラーをかけて足し合わせたものをエラーとする
- 各ノードの出力とそのエラーをかけたものを配列として集めて Δ とし、前回の Δ と足す
- 最後に、エラーの重みに関する偏微分と等しくなる D を計算する. D は Δ / m + λΘ だが、証明は難しいとのこと
λ は正規化の係数
δ (デルタの小文字) は各レイヤーの各ノードのエラー。以下のように計算する
- 最後のレイヤーの各ノードの出力と答えの差
- 中間のレイヤーは
δ^{(l)} = (Θ^{(l)})^Tδ^{(l+1)} .* g'(z^{(l)})
簡単に言うと、重みとその出力のエラーをかけて合計しただけのものと同じようなもの
直感的な理解は難しい
- 誤差逆伝播法が直感的に分かってなくてもちゃんと使える
- 線形回帰やロジスティック回帰と比べて誤差逆伝搬法は数学的にクリーンというわけではない
- 可視化するのも難しい。
誤差逆伝搬法を理解するための工夫
わかりやすさのために変えること
- 正規化を無視
- ロジスティック回帰の代わりに誤差の二乗を使う
Backpropagation in Practice
今まで使ってきたアルゴリズムはベクトルを受け取ってベクトルを返すものだったが、ニューラルネットワークは重みなどが行列になった。行列からどうベクトルを取り出す (Unroll する) のかという話。
行列を長いベクトルにする。(:) を使ってベクトルとして取り出して結合する
thetaVec = [Theta1(:); Theta2(:); Theta3(:)];
戻すときは reshape を使う
Theta1 = reshape(thetaVec(1: 100), 10, 11);
Theta1 は 10x11 の行列
パラメータが行列になっているほうが扱いやすいが、最適化アルゴリズムはベクトルを受け取るように作られていることが多いので unroll する必要がある
Gradient Checking
- Backpropagation はバグを作りやすい
- 最急降下法がちゃんと実装できているか確かめる方法
Θを少し増やした値と少し減らした値の J の傾きをもって Θ の傾きの近似とする
\frac{d}{dΘ}J(Θ) ≒ \frac{J(Θ+ε) - J(Θ-ε)}{2ε}
εはとても小さな値で、
ε = 10^{-4}
だいたいこれくらいの値を使う。小さすぎると問題があるそう。
Θがベクトルの場合
- 各パラメータに関して上と同じことを行う
- 得られた数値が誤差逆伝搬法で得られた数値に近いか確かめる (小数点以下数桁が同じとか)
- gradient checking はとても遅い処理なので実際の学習の際には使わないようにする。
Random Initialization
Θの初期値
- ゼロで初期化するのはロジスティック回帰ではよいがではうまく行かない
- 各エッジの重みが同じだと偏微分も等しくなるので、どのエッジも同じ値になってしまう
- ランダムに初期化する
Putting It Together
- コスト関数を作る
- 誤差逆伝播法によってコスト関数の微分の式が与えられる
- Gradient Checking でも近似した微分の式が得られる
- Gradient Decent や他の最適化手法によって微分が0の場所に近づくようにしていく
- パラメータの微分をベクトルとして渡すように作られているので unroll する
- コストが最小になる重みが見つかる
課題メモ
Pause 問題
macOS で octave を実行すると pause がうまく動かない問題があり、いままでは pause があるところを消して対処していた。
ここに載っている pause 関数を pause.m として ex4 ディレクトリに入れておけばうまく動いた。
function pause
input('','s');
end
Octave-gui
課題の実装時に行列のサイズが合わずに内積ができなかったりするミスが頻発して、そろそろ実行、デバッグのルーチンが大変になってきた。
Octave-gui を使うとブレークポイントを張ったところで止めて現在の変数の中身を GUI から見ることができるので便利だった。
しかし、1つ目のブレークポイントで止めた時にコンソールでコマンドを入れるとなぜかプログラムが続行してしまう不具合があった。止めたいところの前の適当な場所にブレークポイントを張って一度止めておくと、二度目の停止では問題なく動いた。
課題内容について
- Feedforward NN は前作ったのと同じコードが使える
- ラベル出力と数値を変換するのは対角行列を作る diag を使うと便利だった
- Backpropagation を実装するのはえらい大変だった。数式通りに作ればよいが、内積でつまづいた。行列のサイズと縦横を気をつけること。また例によって縦横の違いで数式と内積の順番が異なることがある。コード量は多くない
- Regularization した結果は、してない結果と同じ J や Theta1_grad に入れるが、テストがうまく作られてて Regularization してなくても通る設問としてないと通らない設問になっていて submit できる