3層ニューラルネットワークを計算式を見てみる

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、小澤です。

当エントリは「Machine Learning Advent Calendar 2017」の25日目のエントリです。

今回は、最終日ということで少しはDeep Learningにつながるネタも書きたいと思います。 (すみません、実装が間に合いませんでした...)

単純パーセプロン

さて、まずはすべての基礎になる単純パーセプロンについて解説します。

脳の構造を真似して・・・みたいなのはそんなに関係ないので飛ばさせていただいて、 単純パーセプトロンの構造は以下のようになります。

入力が3つの特徴(x1, x2, x3)で表され、y(0か1)を予測するような問題と考えてください。 それぞれに内部で持っているwをかけて足し合わせた値が一定以上であれば1, そうでなければ0を返すようなステップ関数をかませます。

[latex] y = step(x_1 w_1 + x_2 w_2 + x_3 w_3, \alpha) [/latex]

step(a, b)はa >= bであれば1, それ以外は0を返す関数とします。 この時にyの値をいい感じに予測できる各wを求められれば勝ち、というものです。

さて、この形は線形回帰やロジスティック回帰でも似たような形式を見ましたね? そこからもわかるかと思いますが、これは線形の分類しかできません。

多層への拡張とシグモイド関数

ニューラルネットではこのパーセプトロンをたくさん組み合わせます。

一番左が入力層、真ん中が隠れ層、一番右が出力層となります。

入力層から隠れ層への流れは

[latex] u_1 = x_1 w_{11} + x_2 w_{12} + x_3 w_{13} + b_1 \\ u_2 = x_1 w_{21} + x_2 w_{22} + x_3 w_{23} + b_2 [/latex]

のように計算されたのち、活性化関数(単純パーセプトロンではstep関数) f(x)を通して以下のような出力を得ます。 [latex] z_i = f(u_i) [/latex]

隠れ層のユニット数や層の数を増やしてもどうようの計算を順にしていきます。 この流れを順伝播(フィードフォワード)と呼びます。

活性化関数では、以下のようなものがよく使われます。

ロズスティック・シグモイド関数

[latex] f(x) = \frac{1}{1 + \exp(-x)} [/latex]

ReLU

[latex] f(x) = max(0, x) [/latex]

ニューラルネットでは層の数を増やしていくと、よりいい感じに学習していくのですが、深くなるとうまく学習できない問題がありました。 その問題を解決して層を深くできるようになったら今まで機械学習手法よりも圧倒的にいい結果が出せるようになったというのが、ディープラーニングブールの始まりです。 現在では、それだけではなく様々な工夫や手法が取り入れられていますが、3層の全結合ニューラルネットワークはもっとも基本的な部分となります。

最終的な出力y_iについてですが、こちらは1ユニットで回帰、2ユニットでどちらか一方を0, もう一方を1になるようなweightにすることで二値分類、同じ考え方他で3つ以上のユニットで多値分類などの使い方が可能です。

多値分類の問題では、ソフトマックス関数というものがよく利用されます。

[latex] softmax({\bf x}_i) = \frac{\exp({\bf x}_i)}{\sum_{j=0}^N \exp({\bf x}_j)} [/latex]

出力層のユニット数がN、i番目のユニットの入力をx_iとした時のy_iとなります。

バックプロパゲーション

さて、ニューラルネットでのweightの更新はどのように行うのでしょうか? 順に微分していくとなると、計算が非常に大変です。 そのため、誤差逆伝播(バックプロバゲーション)という方法を使います。

バックプロバゲーションでは、

  • まず、出力層の結果から中間層-出力層間のweightを更新
  • 次にその値を使って入力層-中間層間のweigthを更新

という計算を行っていきます。 これは、層の数が増えても順に計算できる仕組みになっています。

どの層の計算かを明確にするため、

[latex] 入力層-中間層間のweight : w_{ji}^{(2)} \\ 中間層-出力層間のweight : w_{ji}%{(3)} [/latex]

と表記します。

クロスエントロピーの誤差関数

[latex] E({\bf w}) = -\sum_{k=0}^{K} d_k \log(y_k) [/latex]

ここで、Kは分類対象のクラス数、dは d = [0, 1 ,0, 0]のような正解ラベルは1、それいがいは0が入る配列です。

これを微分するために、微分の連鎖ルールを使います。

[latex] \frac{\partial E}{\partial w_{ij}^{(3)}} = \frac{\partial E}{\partial u_i^{(3)}} \frac{\partial u_i^{(3)}}{\partial w_{ij}^{(3)}} [/latex]

途中計算は割愛しますが、これを計算すると

[latex] \frac{\partial E}{\partial w_{ij}^{(3)}} = (y_k -d_k)z_j^{(2)} [/latex]

となります。

続いて、中間層の誤差関数を計算します。

中間層も同様に微分の連鎖ルールを適用すると

[latex] \frac{\partial E}{\partial w_{ij}^{(2)}} = \frac{\partial E}{\partial u_j^{(2)}} \frac{\partial u_j^{(2)}}{\partial w_{ij}^{(2)}} [/latex]

まず、

[latex] u_j^{(2)} = \sum_i w_{ij}^{(2)} z_j^{(1)} [/latex]

より、

[latex] \frac{\partial u_j^{(2)}}{\partial w_{ij}^{(2)}} = z_j^{(1)} [/latex]

となります。 もう片方は、微分の連鎖ルールにより、出力層とのつながりから

[latex] \frac{\partial E}{\partial u_j^{(2)}} = \sum_k \frac{\partial E}{\partial u_k^{(3)}} \frac{\partial u_k^{(3)}}{\partial u_j^{(2)}} [/latex]

となります。

[latex] \frac{\partial u_k^{(3)}}{\partial u_j^{(2)}} [/latex]

は、

[latex] u_k^{(3)} = \sum_j w_{kj}^{(3)} f(u_j^{(2)}) [/latex]

から、

[latex] \frac{\partial u_k^{(3)}}{\partial u_j^{(2)}} = w_{kj}^{(3)} f'(u_j^{(2)}) [/latex]

と計算することができます。

[latex] \frac{\partial E}{\partial u_k^{(3)}} [/latex]

は出力層で計算しているのと同様に計算可能です。 ここでこの値をδとおくことで、任意の階層のニューラルネットに対して、

[latex] \delta_j^{(l)} = \sum_k \delta_k^{(l+1)}(w_{kj}^{(l+1)} f'(u_j^{(l)})) [/latex]

とあわらすことができます。

このようにして計算した値をいつものように

[latex] w = w - \alpha \frac{\partial E}{\partial w} [/latex]

で出力層から順に計算してくことが可能になっています。

おわりに

今回は、Deep Learningにもつながるニューラルネットの基本的な部分を見ていきました。

現在はDeep Learningのためのライブラリが非常に充実しており、その中で3層全結合のニューラルネットも組むことが可能なのでほとんど意識することがないかもしれませんが、このような形で実現されています。

参考文献