逆誤伝播法 - 逆誤差伝播法
ディープラーニングで最も理解するのが難しいアルゴリズム、逆誤伝播法(逆誤差伝播法)について解説します。
逆誤伝播法とは何か
逆誤伝播法は、各層の重みとバイアスのパラメーターに対する損失関数の傾きを求めるアルゴリズムの一つです。
各層の重みとバイアスのパラメーターに対する損失関数の傾きを求めるアルゴリズムには、逆誤伝播法よりも理解するのにより簡単な方法もあります。
ディープラーニングでは、パフォーマンスが重要なので、サンプルコードで最初から、逆誤伝播法が採用されていると考えてください。
逆誤伝播法をいくつかの要素に分けて考えていきましょう。
最終層の個々の重みとバイアスのパラメーターに対する損失関数の傾きを求める
逆誤伝播法は、後ろから進んでいきます。最終出力は損失関数です。損失関数は、誤差の指標になります。
まず最初に、最後の層のバイアスパラメータと損失関数の関係を考えましょう。重みとバイアスは定義されていると考えてください。vec_addはベクトルの積、mat_mulは行列積、vec_subは、ベクトルの差を求める関数だと考えてください。
# 重み my $weights = [..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ...,]; # バイアス my $biases = [..., ..., ...]; # 期待される出力 my $desired_outputs = [1, 0, 0]; # 入力 my $inputs = [0.3, 0.2, 0, 0.5]; # 出力 my $outputs = vec_add(mat_mul($wieghts, $inputs), $biases); # 活性化された出力 my $activate_outputs = activate($outputs); # 損失関数の結果(誤差の指標) my $cost = cost(vec_sub($desired_outputs, $activate_outputs);
ここで、バイアスの一つの値を動かしたときに、損失関数がどう動くかを見たいと考えてください。たとえば、バイアス1を微小値0.001増加させたときに、損失関数が0.002減少したとします。
この時、バイアス1に対する損失関数の傾きは「-2 = -0.002/0.001」です。そして順番に、すべてのバイアスと、すべての重みについて、求めていきます。逆誤伝播法は、この値を速く求めるためのアルゴリズムです。
ここからは、難しいことはもう考えません。ソフトウェアエンジニアとしては、こうすれば、それが、求まるという考え方をしましょう。数学的知見のあるAIの研究者が導き出した方法を実装します。
バイアスに対する損失関数の傾き
まず、最初に、最初のバイアスに対する損失関数の傾き求めます。
# ひとつのバイアスの損失関数に関する傾き # = 活性化関数の導関数(ひとつの出力) * 損失関数の導関数(ひとつの期待される出力, ひとつの活性化された出力); my $biase_grads0 = activate_derivative($outputs->[0]) * cost_derivative($desired_outputs->[0], $activate_outputs->[0]);
すべてのバイアスに対する損失関数の傾きを求めるので、forループします。
my $biase_grads = []; for (my $i = 0; $i < @$biases; $i++) { $biase_grads->[$i] = activate_derivative($outputs->[0]) * cost_derivative($desired_outputs->[0], $activate_outputs->[0]); }
重みに対する損失関数の傾き
重みに対する損失関数の傾きを求めます。
# ひとつの重みに対する損失関数の傾き0行0列 = 上記で求めたひとつのバイアスの傾き0 * ひとつの入力0 my $weight_grads0 = $biase_grads->[0] * $inputs->[0];
重みは、列優先の行列として表現されているので、forループは以下のようになります。
my $weights_grads = []; for (my $input_index = 0; $input_index < $inputs_length; $input_index++) { for (my $biase_index = 0; $biase_index < $biases_length; $biase_index++) { my $weight_grad_index = $biase_index + ($input_index * $biases_length); $weight_grads->[$weight_grad_index] = $biase_grads->[$biase_index] * $inputs->[$input_index]; } }
これは、バイアスを縦ベクトルとみて、入力を横ベクトルとみた行列の掛け算としても表現できます。
# バイアスの傾き 1 2 3 # 入力 4 5 6 7 # 重みに対する損失関数の傾き 4 5 6 7 8 10 12 14 12 15 18 21
このページの計算内容を現在試行錯誤中です。
中間層の個々の重みとバイアスのパラメーターに対する損失関数の傾きを求める
逆誤伝播法では、重みとバイアスの傾きを求めるために層を後ろ向きにたどっていきますが、上記で求めた、バイアスの傾きが利用されます。
# 重み my $weights = [..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ...,]; # バイアス my $biases = [..., ..., ...]; # 入力 my $inputs = [0.3, 0.2, 0, 0.5]; # 出力 my $outputs = vec_add(mat_mul($wieghts, $inputs), $biases); # 活性化された出力 my $activate_outputs = activate($outputs);
バイアスに対する損失関数の傾き
バイアスに関する損失関数の傾き0 =「次の層のバイアスの傾き0~n」と「次の層の重み0列0~n行」の内積 バイアスに関する損失関数の傾き1 =「次の層のバイアスの傾き0~n」と「次の層の重み1列0~n行」の内積
重みに対する損失関数の傾き
重みに対する損失関数の傾きの計算は、最後の層で求めた方法と同じです。
(この記事の計算やコードは、試行錯誤中のものです。)