HELLO CYBERNETICS

深層学習、機械学習、強化学習、信号処理、制御工学、量子計算などをテーマに扱っていきます

フィードフォワードニューラルネットワークの基本

 

 

follow us in feedly

今回はニューラルネットワークの話をします。

僕のブログに行き着く人の多くは「ニューラルネットワーク」とか「ディープラーニング」とかで検索している人が多いみたいなので、きっと最も需要がある項目なのでしょう。

ディープラーニングをやるにしても、元々の出発点になっているニューラルネットワークについての勉強は欠かせません。なぜなら、積層自己符号化器のようなディープラーニングには、最終的に普通のニューラルネットワークを学習させるためのバックプロパゲーションを用いることが多いからです。

 

 

 

ニューラルネットワークの基本

ニューラルネットワークは様々な種類がありますが、最も単純なフィードフォワードニューラルネットについて解説します。フィードフォワードニューラルネットは、データの流れが一方向であって、データが行ったり来たり、あるいはループしたりしないような構造になっています。以後ニューラルネットと行ったらフィードフォワードニューラルネットのことになります。

基本と用語

取り敢えず具体例があった方がいいと思うので、3入力1出力のニューラルネットで説明します。一番左側は入力層と言います。例えば3次元データの回帰問題を解かせたい場合は、3次元の各成分が一番左側の入力層に与えられます。入力層は、入力されたデータをそのまま出力するだけです。

真ん中が中間層と言います。中間層には、入力されたデータの各成分に適当な重みを付けて和を取ったものが入力されます。中間層は、入力されたデータに対して何らかの変換を行ったあとに、その値を出力します。

一番右が出力層です。ここは、中間層の値を受け取って何らかの変換を行い値を出力します。応用上はその値が上手い回帰曲線になっていれば嬉しいといった具合です。

 

 

f:id:s0sem0y:20160521233549p:plain

 

次に図の各青い丸は、ノードと言います。この図では、入力層のノード数が3、中間層のノード数が3、出力層のノード数が1といった具合です。ノードのことをユニットという場合もあります(Bishop本ではユニットになっています)。

ノード同士を繋いでる辺のことをエッジと言います。しかしこれはニューラルネットではあまり使わないです。「ノード同士の接続を〜」などと言い回されることのほうが多いです。

 

層から層へ値が渡されるときには、値に対してとある重みが付加されます。

図の入力層と中間層を例に取れば、中間層の一番上のノードに対しては入力層の値が

 

(x_1,x_2,x_3)^T

 

であれば、

 

w_1x_1+w_2x_2+w_3x_3

 

が入力されます。このときのw重みと言います。

通常は、入力層の第iノードから中間層の第jノードへの重みをw_{ji}と書きます(こうやって書いておかないと、中間層の一番上のノードと真ん中のノードと一番下のノードそれぞれに対して違う文字を使わなければいけなくて大変!)。

改めて、中間層の一番上へのノードへの入力を書きなおせば

 

w_{11}x_1+w_{12}x_2+w_{13}x_3

 

となります。

 

次に中間層のノードは、入力層から受け取った値と、とある値bを比較します。

つまり中間層の一番上のノードは

 

a_1=w_{11}x_1+w_{12}x_2+w_{13}x_3-b_1

 

という値について検討をするのです。このときのbを閾値とかバイアスとか言います。

次に中間層は、入力された値に対して何らかの変換h(a_j)を施します。

例えば中間層の一番上のノードは

 

h(a_1)=h(w_{11}x_1+w_{12}x_2+w_{13}x_3-b_1)

 

を計算します。このときの変換h(・)を活性化関数と言います。

中間層はこのようして変換を行った値を、出力層に渡します。

 

出力層は中間層から受け取る値に対して重み付き和を得て、その値からバイアスを引き、活性化関数によって変換をして何らかの値を得ます(要するに中間層と一緒)。

 

今回の図のケースの場合は、層が3つあるので、3層ニューラルネットと言います。

場合によっては中間層が1つのニューラルネットと言います。

しかし、Bishop本は、今回の図のニューラルネットを2層ニューラルネットと呼ぶことを推奨しています。なぜなら、実際にニューラルネットが仕事をしているのは中間層と出力層のみだからです。入力層は一切何もしておらず、ただただデータの値を表しています。つまり下図のように見ることもできるからです。

 

f:id:s0sem0y:20160522001904p:plain

 

まあ勉強する上では自分が分かればそれでいいです。

今何を計算しているのかというのが理解できていれば大丈夫でしょう。他の人とニューラルネットについて会話するときにはこのへんには注意してください。

今回はこの図以上に多層のニューラルネットを扱いませんので、あまり気にしないことにします。

 

ニューラルネットでは活性関数を問題に応じて予め設定しておき、学習によって重みwとバイアスbを獲得します。

 

ニューラルネットワークの数式的表現

以上でニューラルネットの基本的なことを知ったものとして話します。

まず中間層のj番目のノードに対して

 

a_j=w_{j1}x_1+w_{j2}x_2+w_{j3}x_3-b_j

 

という値が入り、これを活性化関数で変換するというのを見てきました。

もっと一般的に入力層がD個のノード、中間層がM個のノード、出力層がK個のノードで構成されている場合を見ていきます。

 

f:id:s0sem0y:20160522004821p:plain

 

中間層j番目のノードへの入力は

 

a_j= \sum_{i=1}^{D} w_{ji}x_i -b_j

 

となります。\sumの中に-b_jを含んでしまいたいので、コレをw_j0と置いて

 

a_j= \sum_{i=0}^{D} w_{ji}x_i

x_0=1

 

としてしまいます。こんなことして良いのかと思うかもしれませんが、結局のところ重みwもバイアスbも学習によって獲得するもので、区別する必要がないので構いません。

だからもうバイアスbのことは忘れてください。

これをベクトルを使って表現すれば、

 

a_j=w_j^Tx

w_j=(w_{j0},w_{j1},...,w{jD})^T , x=(x_0,x_1,...,x_D)^T

 

と出来て非常に見やすいですね。ベクトルを使って表記された教科書も多いので、どちらでも読めるようにしておいてください。

 

中間層のj番目のノードは活性化関数によって変換したz_j=h(a_j)を出力層に渡します。出力層も中間層と同様な計算をします。出力層j番目のノードは重みrを使って

 

b_k=\sum_{j=0}^{M} r_{kj}z_j

z_0=1

 

を計算し、y_j=h(z_j)を出力することになります。

 

 

重み(weight)にrって文字使うのは気持ち悪いので、入力層から中間層への重みをw^{(1)}、中間層から出力層への重みをw^{(2)}と表現することにします。

 

まとめると、ニューラルネットワークは以下の計算をします。

 

・中間層j番目のノード

 

a_j= \sum_{i=0}^{D} w_{ji}^{(1)}x_i

x_0=1

 

が入力され

 

z_j = h(a_j)

 

を出力する。

 

・出力層k番目のノード

 

b_k=\sum_{j=0}^{M} w_{kj}^{(2)}z_j

z_0=1

 

が入力され

 

y_k=h(b_k)

 

を出力する。

 

 

こうしてネットワーク全体では、

入力ベクトルxに対して出力ベクトルyk番目の成分は

 

y_k

\begin{array}{llll}  = h(b_k) \\  = h \left( \sum_{j=0}^{M} w_{kj}^{(2)}z_j  \right) \\  = h \left( \sum_{j=0}^{M} w_{kj}^{(2)}h(a_j) \right) \\ = h \left( \sum_{j=0}^{M} w_{kj}^{(2)} h{\left( \sum_{i=0}^{D} w_{ji}^{(1)}x_i \right)} \right) \end{array}

 

場合によっては、中間層と出力層の活性関数に異なったものを用いる場合もあります。

それぞれの活性化関数をh_1,h_2と置くと、より一般的には

 

y_k=h_2 \left( \sum_{j=0}^{M} w_{kj}^{(2)} h_1{\left( \sum_{i=0}^{D} w_{ji}^{(1)}x_i \right)} \right)

 

となります。

 

まとめ:ニューラルネットワークという合成関数

普通の回帰問題では、データxに対して回帰曲線y

 

y=f(x)

 

として獲得すべく、都合の良い関数fを探します。

例えば最も単純な最小二乗直線を得る場合には、

 

y = \sum_{i} a_ix_i

 

となるようなa_iを探すわけです。より一般の回帰問題では

 

y = \sum_{i} a_iΦ_i(x)

 

という係数a_iと基底関数Φ_iの組を探します。

もしも入力データが1次元でΦ_i(x) = x^iと置けば、この回帰問題は多項式フィッティングということになります。

 

ともかく、一般的な回帰問題に対して複数個の基底関数なるものを準備し、それに対する係数を探すという手続きを行います。一方で、ニューラルネットの場合は、多次元入力データの各成分を適当な重み付け和として扱い、ある1つの活性化関数によってまとめて変換してしまいます。

データの扱いがずいぶん乱暴に見えますが、代わりにこれを複数回行うことによって全体では、活性化関数の出力の和を更に活性化関数によって変換するという合成関数になっています。

十分にたくさんのノードを準備することで、任意の関数が表現されることが知られており、たくさんの基底関数を準備しなくても良いということです。

しかし、合成関数によって表現力を得たことにより困難も生じてきます。

通常、回帰問題は評価関数の最適化を行いますが、このときに頻繁に使われるのが勾配法です。評価関数を微分して0となる点が極値を与えますから、とにかく微分を計算したくなります。

ニューラルネットで回帰をするならば、真の曲線tに対して出力yがどれだけ近いかを評価したいところですが、合成関数の微分はかなり厄介ですね。しかもパラメータの数が膨大なので、全てのパラメータにより偏微分が0になる場所を求めるのはかなり困難を極めそうです。

 

そこで登場したのがバックプロパゲーションになります。

今回はこの辺で閉じます。またいずれ復習のためにバックプロパゲーションについての記事を書くかもしれません。(でもこれは計算だけで、とくに解釈を説明する部分もないのでやらないかも…)

 

 

 

 

 

 

 

 

s0sem0y.hatenablog.com