ニューラルネットワーク [ひとつのニューロンの学習]

Date : 2006-09-14
Author : Defolos

CONTENTS

(1.) ニューラルネットワークとは
(2.) 生体ニューロンの仕組み
- 脳の構成要素
- グリア細胞の役割
- 信号とその伝達
(3.) ニューロンモデル
- ニューロンのモデル化
- ニューロンモデルの計算練習
- ニューロンモデルの別表現
(4.) 論理演算の復習
- 論理和(OR)
- 論理積(AND)
- 否定(NOT)
- 排他的論理和(XOR)
(5.) ニューロンの学習
- 教師つき学習と教師なし学習
- 誤り訂正学習法
- 誤り訂正学習法の例
(6.) 学習プログラム
- ひとつのユニットの限界

ニューラルネットワークとは

 ニューラルネットワークとは、生物の脳に見られるような神経回路のことです。情報技術では、この生物の神経回路を手本にした人工ニューラルネットワークが様々なところで応用されています。このレポートでは生物ニューラルネットワークいついても言及しますが、人工ニューラルネットワークを主に解説して行きますので、以降は特に注意がない限り人工ニューラルネットワークをニューラルネットワークと略します。
 ニューラルネットワークは人工知能へのアプローチとして応用されており、パターン認識や制御を得意とし、組み合わせ最適化の解である非線形関数を近似する手段のひとつとして位置付けられるようになりました。また、学習できるという特性によって応用範囲は広範囲に広がっています。


生体ニューロンの仕組み

 ニューラルネットワークは先述の通り、生物の脳をモデルにした情報処理システムです。それゆえに、ニューラルネットワークを理解していくには、まず生体のニューロンの仕組みを理解することからはじめるべきでしょう。

● 脳の構成要素

 生体の脳は数百億のニューロンと多数のグリア細胞から成り立っており、ニューロンは層状に規則的に並んでいます。ニューロンは電気信号を発し、処理し、伝達して認知や推論など複雑な仕事をこなしています。ニューロンは次の図のような構造をしています。以降の説明は図を参照しながら閲覧ください。

(figure1)http://ruffnex.oc.to/defolos/text1/figure/neuron.jpg(※1)

 ニューロンは樹状突起、細胞体、軸索と呼ばれる部分に大別することができます。軸索は細胞体から一本だけ伸びており、末端で枝分かれして他の樹状突起と結びつきます。この結び目のことをシナプスと呼びます。これらのニューロンの周りを取り囲むように無数のグリア細胞が存在しています。

» 細胞体(Soma)

 ニューロンの本体といえる部分。その内部には核やミトコンドリアなどを持ち、通常の細胞と構造自体は変わらない。ニューラルネットワークにおいては信号を他のニューロンに出力するかどうかの処理装置となる。

» 樹状突起(Dendrite)

 細胞体から伸びだした多数の枝のような部分で、ニューロンの入力端子にあたる。

» 軸索(Axon)

 細胞体から伸びだしている太い軸のようなもので、途中枝分かれしながら他のニューロンと結びついている。ニューロンの出力端子にあたる。先端は樹状突起と結びつき、シナプスを形成する。

» シナプス(Synapse)

 他のニューロンをつなげる役割をする。樹状突起はシナプスを通して軸索と接続され、他のニューロンから入力信号を受け取っている。また、シナプスの伝達効率はそれぞれ異なっている。

● グリア細胞の役割

 グリア細胞はニューロンの10倍以上存在しています。ニューロンの情報伝達機能とは直接的な関係はありませんが、ニューロンがネットワークを広げていく過程、またはニューロンの存続において大きな役割を果たします。言うならばグリア細胞はニューロンを縁の下からサポートする役割を持っていると言ってよいでしょう。最近の研究で脳の情報伝達において無視できない役割を果たすことがわかってきたようです。
 グリア細胞のグリアとはギリシャ語で「膠:にかわ」を意味する言葉です。ニューロンにまとわりつくように存在しているため、このような名称となったと思われます。グリア細胞は性質の異なるいくつかの細胞を総括して使われる名称です。グリア細胞には次のような種類があります。

(figure2)http://ruffnex.oc.to/defolos/text1/figure/glia.jpg(※2)

» 星状グリア細胞(Astroglia)

 無数の突起を持った星型の形をしており、ニューロンと毛細血管の間に存在する。血管から養分や酸素などをニューロンに供給する役割を持つ。脳神経系で最も多い。

» 稀突起グリア細胞(Oligodendroglia)

 突起は少なく、軸索に巻きついて髄鞘(ミエリン鞘)を形成する。髄鞘は絶縁体の鞘であり軸索を保護する役割を持っている。この髄鞘のおかげで他の軸索の電気信号が混ざらないようになっている。電気信号の伝達速度を向上させる。

» ミクログリア(Microglia)

 血管の近くに存在する。詳しい性質は不明な点が多いが、脳におけるマクロファージであると考えられている。防御機能をもつ。

 ニューロンのネットワークを、あるニューロンが他の決まった標的となるニューロンと結合した構造の集まりであると考えると、ネットワークが作り出されるためにはニューロンの細胞体から軸策が伸びていくことと、この過程が正しい標的のニューロンに向かうことが必要となります。
 ニューロンはグリア細胞から分泌される神経栄養因子やニューロンの表面にある細胞接着分子のガイドにより、軸索を伸ばし、正しい相手に到達して結合します。このときニューロンと周囲のグリア細胞との間で情報のやり取がおこなわれます。ニューロン内では新しい遺伝子が発現し、特定の酵素が働いてニューロンの骨格が変化します。このように生体のニューラルネットワークにおいてグリア細胞は大きな役割を持っています。

● 信号とその伝達

 生体の脳は電気信号を伝達し、複雑な処理を行っています。コンピュータと動作原理は同じですが、その性質は大きく異なります。コンピュータは高速な処理素子を使い直列処理に長けていますが、一方生体の脳は処理素子の速度は非常に低速ですが超並列化された構造を持っています。これがよく言われるようなコンピュータの高速正確、脳の創造曖昧の違いを生み出していると言ってよいでしょう。

 コンピュータはトランジスタなどが信号を流す流さないというスイッチング処理を行っています。生体のニューロンにも、トランジスタのようなスイッチングを行う仕組みがあります。

○ 膜電位

 動物の細胞は細胞膜で覆われています。ニューロンの構造も通常の細胞と同じく、細胞膜を持っています。細胞は細胞膜で外部と細胞内が仕切られているので、外部と内部では電圧が異なります。細胞内部から見た外部との電圧の差(電位)を膜電位と呼び、膜電位は-70〜-90mvです。つまり通常の場合、細胞内は細胞の外側より70〜90mv電圧が低くなっています。この電位の差を静止電位(Resting Potential)と呼びます。
 他のニューロンからの出力信号を樹状突起を通して受信すると膜電位に変化がおきます。膜電位が静止電位(-70mv前後)から0のほうへ変化する脱分極(Depolarization)がおきます。脱分極があまり大きくならないうちに入力信号がストップすると元の電圧に戻ってしまい、軸索には何も出力されません。しかし、ある一定の大きさを超えると100mvほどのパルスが1m秒ほど発生し、軸索を通して他のニューロンに出力信号として伝えられます。このパルスは活動電位(Action P otential)やインパルス(Impulus)、スパイク(Spike)と呼ばれます。活動電位が発生することをニューロンが発火すると表現します。
 一定の大きさを閾値電位(Threshold Potential)と呼び、ニューロンの場合は15〜20mvに設定されています。シナプスには興奮性のものと抑制性のものとがあります。興奮性のものは膜電位を上昇させ、抑制性のものは逆に膜電位を低下させます。まとめると、膜電位が他のニューロンからの入力信号によって15〜20mvほど高くなると、100mvほどの活動電位が発生し他のニューロンへの出力信号となります。

○ 髄鞘における電気信号の伝達

 髄鞘は前述の通り、稀突起グリア細胞によって形成される絶縁体で、軸索を保護しています。稀突起グリア細胞ひとつで髄鞘ひとつを形成していますが、グリア細胞から形成される髄鞘と髄鞘の間には小さな隙間があり軸索が露出しています。この隙間をランビエ絞輪といい、ニューロンから発せられる電気信号は隙間から隙間へ絶縁体である髄鞘を飛び越えて伝わります。

○ シナプスでの伝達

 シナプスはシナプス間隔という極小さい隙間が存在しており、電気信号はこの隙間を飛び越えることはできません。ここで電気信号は一旦化学的信号に変換されます。
 電気信号がシナプスに到達すると、この電気信号によりシナプス小胞からアセチールコリンやドーパミンなどの化学物質(神経伝達物質)がシナプス間隔へ放出されます。シナプス後膜にあるチャネルや受容体がそれらの神経伝達物質を受け取ることで細胞内のイオン濃度が変化し、電気信号が引き起こされます。つまり、信号は電気信号→化学的信号→電気信号のように伝達されます。これにより100mvと高い電圧を持つ活動電位が流れても、他のニューロンに到達するときには流れたのか流れてないのかの2値(デジタル)として取り扱われることになります。

(※1)、(※2)

東京医科歯科大学 教養部 生物化学C ニューロン(http://www.tmd.ac.jp/artsci/biol/textlife/neuron.htm)より引用


ニューロンモデル

 生体のニューロンの仕組みは非常に複雑ですので、そのまま忠実に再現して情報の処理を行うのは不可能です。そこで生体ニューロンの仕組みを本質を取りこぼすことなく簡単に表現したものを利用します。このような本来複雑な仕組みを簡単に表現したものをモデルと呼びます。

● ニューロンのモデル化

 情報科学では、生体のニューロンを数理的なモデルとして単純に表現したものを用います。このようなニューロンのモデルは1943年にマッカロック(McCulloch)とピッツ(Pitts)によって最初に提案されました。ニューロンモデルは次の図のように表されます。

(figure3)http://ruffnex.oc.to/defolos/text1/figure/n_model.jpg

 このモデルは次のような数式で表現することができ、その数式に基づいて動作します。

 xは他のニューロンからの入力信号を表しており、x1からxnまでのn個の入力信号があることを意味しています。wは重み(結合荷重)を表しており、それぞれの入力信号に対する伝達効率となります。netは重み付けられた入力信号を足し合わせた値となります。このnet値が一定の値を超えることで出力信号を他のニューロンに送ります。生体のニューロンが一定の電圧にまで膜電位が高まるとインパルスを発する(発火する)のと同様の動作を行います。一定の値を閾値(しきいち)と呼び、この図ではθで表されています。
 f()は活性化関数と呼ばれ、入力されるnet値がある一定以上なら0を返し、ある一定以下なら0を返す関数です。ニューロンが発火する、しないを判定する関数ということになります。
 マッカロックとピッツがニューロンモデルを提案した当初の活性化関数はヘビサイド階段関数(Heaviside step function)または単にステップ関数と呼ばれる関数を用いていました。ステップ関数は数式では次のように表現できます。

 ヘビサイド階段関数はH(x)で表される場合が多いです。これをグラフに書き表すと次のようになります。

(figure4)http://ruffnex.oc.to/defolos/text1/figure/heaviside.jpg

 このグラフを見ていただければわかる通り、ヘビサイド階段関数は0より大きな数が入力されたときは1を返し、0以下の値が入力されたときは0を返します。活性化関数にはヘビサイド階段関数のほか、グラフの形がS字状になるシグモイド関数も多用されます。
 活性化関数で発火すると判断された入力に対してはout値が1となり、他のユニットへの入力信号を送り出すことになります。

● ニューロンモデルの計算練習

 それでは実際にニューロンモデルの動作を計算して求めてみましょう。次のようなモデルの場合に1が出力されるのか0が出力されるのかを計算してみましょう。

(figure5)http://ruffnex.oc.to/defolos/text1/figure/n_exam.jpg

 このモデルでは重みw1、w2はそれぞれ2.0と-1.0であり、閾値は1.0です。重みがマイナスになっているのは抑制性の結合係数を表しています。
 ここに入力信号x1=1.0、x2=1.0が入力されたとします。まずはじめにnet値を算出します。net値は次の式のようにして算出できます。

 これは次のようにも書き表すことができます。

 x1が1.0、w1が2.0、x2が1.0、w2が-1.0ですので、上記の式に代入してnet値を求めてみましょう。

 net値は1.0になりました。次にout値を求めてみましょう。活性化関数には前述したヘビサイド階段関数を用います。活性化関数の引数にはnet - θを渡すことになっていますので、引数を求めてみましょう。

 引数には0.0を渡すことがわかりました。それではヘビサイド階段関数に0.0を渡してみましょう。ヘビサイド階段関数では0を含む0以上の値が渡されたときには1を返します。よって0.0を引き渡した場合には1が返されます。

 以上からw1が2.0でw2が-1.0、閾値が1.0のときにx1に1.0、x2に1.0を入力したときは出力値として1.0が出力されることがわかりました。あとは各自で重みや閾値、入力信号などを適当に変更して、どのような出力結果になるのか確認してください。

● ニューロンモデルの別表現

 ニューロンモデルは前述の表現の仕方以外にも、別の表現をすることもできます。より単純に表現するために、入力信号に常に値が1.0となるx0とその重みw0 = -θを導入した表現法がよく用いられます。これを式に書き表すと次のようになります。

 このように閾値を重みと考えることで、「.: ニューロンの学習」で解説するように学習によって重みだけでなく閾値を変化させることができるようになります。ニューロンの学習について学ぶ前に、必要な前提知識となる論理演算について復習をしておきましょう。


論理演算の復習

 論理演算(Logical Operation)はブール演算(Boolean Operation)とも呼ばれ、真か偽かの2通りの元しか持たない集合における演算で、ひとつの値を出力します。演算結果も真か偽かの2通りになります。
 論理演算はコンピュータ内部での演算に使われ、コンピュータで表現する場合は真に1、偽に0を使う場合が多いです。論理演算は論理和、論理積、否定、排他的論理和がよく使われます。
 入力の全てのパターンに対する出力を表として表現したものを真理値表(Truth Table)と呼び、集合の領域を図として表現したものをベン図(Venn Diagram)またはオイラー図(Euler Diagram)と呼びます。

● 論理和(OR)

 論理和(Logical Disjunction)は入力された値のうち、どれかひとつでも真であれば真を出力します。それ以外の場合、つまり全ての入力された値が偽であった場合に限り偽を出力します。「AまたはB」の関係を表します。

○ 真理値表

+-----+-----+-----+
|  A  |  B  |  X  |
+-----+-----+-----+
|  1  |  1  |  1  |
+-----+-----+-----+
|  1  |  0  |  1  |
+-----+-----+-----+
|  0  |  1  |  1  |
+-----+-----+-----+
|  0  |  0  |  0  |
+-----+-----+-----+

○ 論理式

 論理式で表す場合、次のように書かれる場合が多いです。

○ ベン図

(figure6)http://ruffnex.oc.to/defolos/text1/figure/ld.jpg

● 論理積(AND)

 論理積(Logical Conjunction)は入力された値が全て真であるときに真を出力します。それ以外であった場合には偽を出力します。「AかつB」の関係を表します。

○ 真理値表

+-----+-----+-----+
|  A  |  B  |  X  |
+-----+-----+-----+
|  1  |  1  |  1  |
+-----+-----+-----+
|  1  |  0  |  0  |
+-----+-----+-----+
|  0  |  1  |  0  |
+-----+-----+-----+
|  0  |  0  |  0  |
+-----+-----+-----+

○ 論理式

 論理式で表す場合、次のように書かれる場合が多いです。

○ ベン図

(figure7)http://ruffnex.oc.to/defolos/text1/figure/lc.jpg

● 否定(NOT)

 否定(Logical Negation)は入力された値とは逆の値を出力します。入力値が真であれば偽を出力し、入力値が偽であれば真を出力します。反転させると覚えるといいと思います。「Aではない」という関係を表します。

○ 真理値表

+-----+-----+
|  A  |  X  |
+-----+-----+
|  1  |  0  |
+-----+-----+
|  0  |  1  |
+-----+-----+

○ 論理式

 論理式で表す場合、次のように書かれる場合が多いです。2番目の否定を表すA_は本来Aの上に線を引くことで表されるのですが、フォントの関係上このように表現しています。

○ ベン図

(figure8)http://ruffnex.oc.to/defolos/text1/figure/not.jpg

● 排他的論理和(XOR)

 排他的論理和(Exclusive Or)は入力された値が全て同じときは偽を出力し、入力された値が異なる場合には真を出力します。
 排他的論理和は論理和、論理積と否定を組み合わせることで表現できますが、使われる頻度が多いためここで取り上げました。

○ 真理値表

+-----+-----+-----+
|  A  |  B  |  X  |
+-----+-----+-----+
|  1  |  1  |  0  |
+-----+-----+-----+
|  1  |  0  |  1  |
+-----+-----+-----+
|  0  |  1  |  1  |
+-----+-----+-----+
|  0  |  0  |  0  |
+-----+-----+-----+

○ 論理式

 論理式で表す場合、次のように書かれる場合が多いです。3番目のA_、B_は否定を表していますが、本来はA、Bの上に線を引くことで表されます。フォントの関係上、このように表現しています。

○ ベン図

(figure9)http://ruffnex.oc.to/defolos/text1/figure/eo.jpg


ニューロンの学習

 ニューロンの重みや閾値を自動的に変更し、正しい解を導くことができるように修正することを学習といいます。生体の脳も入力に対する正しい出力が得られるようにニューロンとニューロンの繋がり方や伝達効率などが変化しているようです。例えば入力として犬という視覚情報が入力された場合、それを犬であると認識するという正しい出力を得られるように、脳の中のニューロンの結びつきなどが変更されています。

 学習は1949年にDonald Hebbが書いた「行動の機構」という本の中で提唱されたようです。この本でHebbは生物学的に「シナプスを経て到着した入力が直ちにパルスを発生させる時、その結合係数の効率は少し増加する」と提唱したようです。そこからHebbはニューラルネットワークの重みを変化させることで学習ができるという仮説を立てました。これがニューラルネットワークの学習の研究のはじまりだと言われています。

● 教師つき学習と教師なし学習

 入力に対する出力がこちらが定義した理想的な出力結果である教師信号通りになるように重みを変更する学習方法のことを教師つき学習と呼びます。一方、教師信号なしに学習を行う方法を教師なし学習と呼びます。ここでは教師つき学習の誤り訂正学習法を使って論理演算を学習させてみることにしましょう。
 教師信号は次のように表現されます。

» 第p番目の入力信号(x1〜xnまで)
xp1, xp2, xp3 ... xpn
» 第p番目の教師信号
yp

 x1からxnまでの入力信号をあわせてパターンひとつと考えます。パターンひとつにつき、そのパターンを入力して得られる理想的な出力結果である教師信号がひとつ与えられます。例えば第3番目の入力パターンに対する教師信号はy3となります。

● 誤り訂正学習法

 誤り訂正学習法は、適当な初期値から学習を開始し徐々に訂正していくことで学習を行います。誤り訂正学習法は入力信号に対する出力と教師信号との誤差がある場合には重みの変更を行い、出力と教師信号が一致した場合には何も行いません。重みの変更は次の式に基づいて行われます。

 wi[new]は修正後の重みを表しており、wi[old]は修正される前の重みを表しています。ηは学習率といって、1以下の小さな正の定数です。また、(yp - out)は次の式に従って出力されることになります。

 上記の式より出力と教師信号が一致していない場合に重みを修正することがわかります。つまり、ypの値が1であるのにoutの値が0になる場合には、outが1になるように重みをη・xiだけ増加させます。逆にypの値が0であるのにoutが1である場合には、outが0になるように重みをη・xiだけ減少させます。これをp個すべての入力信号に対して、重みwiが変化しなくなるまで繰り返します。

● 誤り訂正学習法の例

 それでは次の入力信号と教師信号に対して、誤り訂正学習法で学習を行う過程を見ていきましょう。また、重みの初期値はすべて0の状態からスタートします。

+---------+----+----+----+----+
|         | x0 | x1 | x2 | y  |
+---------+----+----+----+----+
|パターン1|  1 |  0 |  0 |  0 |
+---------+----+----+----+----+
|パターン2|  1 |  1 |  0 |  1 |
+---------+----+----+----+----+
|パターン3|  1 |  0 |  1 |  1 |
+---------+----+----+----+----+
|パターン4|  1 |  1 |  1 |  1 |
+---------+----+----+----+----+

 x0は常に1となる入力です。「.: ニューロンモデル」のニューロンモデルの別表記を参照してください。上記の入力信号と教師信号の関係は論理積を表しています。x1とx2を考えて、双方が1の場合にのみ1が出力されます。それにあわせて教師信号yを設定しています。
 パターン1では0と0が入力され、その時の正しい出力結果は0です。パターン2では1と0が入力され、出力は1になります。パターン3では0と1が入力され、出力は1になります。パターン4では1と1が入力され、出力は1になります。この4つのパターンを順次入力して行き、前述の式に従って重みを修正して、すべてのパターンにおいて出力と教師信号が同じになるようにします。

 重みの初期値はすべて0の状態から開始されます。ここにパターン1の入力信号を入力します。x1・w1は0でありx2・w2、x3・w3も0になるのでx1・w1 + x2・w2 + x3・w3の値であるnet値は0となります。net値0をステップ関数に入力すると1が出力されますので、out値は1となります。パターン1の教師信号は0ですのでyp - outの値は0 - 1で-1になります。yp - outは負の値ですので新しい重みはη・xpi減少させます。ここでは学習率を1と置くこととしますので、その結果wi[new] = wi[old] + η(yp - out) xpiの式はwi[new] = wi[old] - xpiとすることができ、重みの変更を行います。次のように修正されることになります。

 重みは上記のように修正されました。次にパターン2を入力します。net値は-1 + 0 + 0 = -1となり、out値は0です。out値と教師信号が異なるため、重みの修正を行います。yp - outは1 - 0であり1となりますのでxpi増加させます。

 同様にパターン3を入力します。net値は0 + 0 + 0 = 0であるためout値は1となり、教師信号と同じですので重みの修正はありません。パターン4を入力するとnet値は0 + 1 + 0 = 1となり、out値は1です。教師信号と一致するため、ここでも重みの修正は行われません。現在の重みは(0, 1, 0)です。一通り入力信号のパターンを入力し終わったため、第1回目の学習はこれで終わりです。

  一通りすべてのパターンを入力しましたが、パターン2で重みを修正しているため、新しい重みの場合にパターン1が教師信号と同じ出力をするかどうかはわかりません。そのためもう一度パターン1を入力します。再度パターン1を入力するとnet値は0 + 0 + 0 = 0となり、out値は1です。教師信号とは一致せず、yp - outの値は-1です。ですので重みは次のように修正されます。

 修正が行われたので、続いてパターン2を入力してみます。net値は-1 + 1 + 0 = 0ですので、out値は1となります。教師信号と一致しますので重みの変更は行わずに次のパターンを入力します。次のパターン3ではnet値は-1 + 0 + 0 = -1でありout値は0です。教師信号とは一致しないため重みの修正が行われます。yp - out = 1 - 0 = 1で正の値ですのでxpi増加させます。

 パターン4ではnet値は0 + 1 + 1 = 2でありout値は1です。教師信号と一致するため重みの修正は行われません。ここまでで第2回目の学習は終了しました。

 パターン3で修正が行われたため、第3回目の学習を行います。パターン1を入力するとnet値は0 + 0 + 0 = 0となりout値は1です。教師信号と違いますので修正を行います。yp - out = 0 - 1 = -1で負の値ですので次のように修正されます。

 ここで修正された重みは、この問題を解くための解となります。この後パターン2から4までを入力しても重みの修正は行われることがなく、第3回目の学習は終了します。第3回目の学習のパターン1で重みを修正しているため第4回目の学習を行いますが、第4回目の学習ではパターン1から4まで重みの修正は行われることがありません。このようにすべてのパターンで重みの修正が行われなくなったときは学習できたことを意味しており、その重みで問題を解くことができます。当然、問題を解くことのできる重みは無数に存在しており、ここで求めた重み以外でも問題を解くことは可能です。


学習プログラム

 それでは「.: ニューロンの学習」で説明した誤り訂正法を使って、ひとつのユニットが論理演算を学習できるようにプログラムを組んでみます。大変お見苦しいソースコードですが、次のようなプログラムを書きました。


/***************************************************************/
/* 12:58 2006/06/28 Defolos neuron.c
/*
/* ニューラルネットワークのユニットひとつの学習
/* 誤り訂正学習法で論理和・論理積を学習させる
/*
/***************************************************************/

#include <stdio.h>

#define INPUT_PATTERN 4 //入力パターン
#define INPUT_SIG_NUM 3 //入力信号数

/*-------------------------------------------------------------*/
/* 構造体の設定 */
/*-------------------------------------------------------------*/
struct NEURON_DATA
{
        int input[INPUT_PATTERN][INPUT_SIG_NUM];
        int weight[INPUT_SIG_NUM];
        int magister[INPUT_PATTERN];
        int net;
        int out;
};

/*=============================================================*/
/* メイン関数始まり */
/*=============================================================*/
int main (void){

        int i, j;
        int err;
        int count = 0;

/*-------------------------------------------------------------*/
/* プロトタイプ宣言  main関数内でのみ利用する */
/*-------------------------------------------------------------*/
int step (int);

/*-------------------------------------------------------------*/
/* 構造体へのデータ設定 */
/*-------------------------------------------------------------*/
struct NEURON_DATA unit;
        unit.input[0][0] = 1;
        unit.input[0][1] = 0;
        unit.input[0][2] = 0;
        unit.input[1][0] = 1;
        unit.input[1][1] = 1;
        unit.input[1][2] = 0;
        unit.input[2][0] = 1;
        unit.input[2][1] = 0;
        unit.input[2][2] = 1;
        unit.input[3][0] = 1;
        unit.input[3][1] = 1;
        unit.input[3][2] = 1;

        unit.weight[0] = 0;
        unit.weight[1] = 0;
        unit.weight[2] = 0;

        //論理和
        unit.magister[0] = 0;
        unit.magister[1] = 1;
        unit.magister[2] = 1;
        unit.magister[3] = 1;

        unit.net = 0;
        unit.out = 0;


//学習が完了するまで繰り返す
while (1){

        //エラーフラグのリセット
        err = 0;

        //カウントアップ
        count = count + 1;

        //パターン4まで繰り返し
        for (j=0; j<INPUT_PATTERN; j++){
                //net値の算出
                unit.net = 0;
                for (i=0; i<INPUT_SIG_NUM; i++)
                        unit.net = unit.net + unit.input[j][i] * unit.weight[i];

                //out値の算出
                unit.out = step(unit.net);

                //正解ではないとき、重みの変更
                if ( (unit.magister[j] - unit.out) != 0){
                        //エラーフラグ設定
                        err = 1;

                        //誤り訂正学習法のアルゴリズムに従って重みの変更
                        for (i=0; i<INPUT_SIG_NUM ;i++)
                                unit.weight[i] += (unit.magister[j] - unit.out) * unit.input[j][i];

                }
        }
        //重みの変更がなかったか、100回繰り返しても解が得られないなら終了
        if( (err == 0) || (count >= 100) ) break;
}

/*-------------------------------------------------------------*/
/* 結果の表示 */
/*-------------------------------------------------------------*/
        printf("学習回数:%d\n", count);
        for (i=0; i<INPUT_SIG_NUM; i++)
                printf("weight[%d] = %d\n",i ,unit.weight[i]);

        return 0;
}


/*=============================================================*/
/* step関数:引数が0以上なら1を、0より小さいなら0を返す */
/*=============================================================*/
int step (int x){

        if (x >= 0)
                return 1;
        else
                return 0;
}

 このソースコードをコンパイルし、実行すると次のような結果が得られます。これは、4回の学習で重みが次のように変更されたことを意味しています。はじめ無秩序であった重みが、学習によって論理和を行える重みに変更されました。これで論理和を学習できました。

学習回数:4
weight[0] = -1
weight[1] = 1
weight[2] = 1

 次に、ソースコード中の教師信号を論理積の結果にした場合を考えて見ます。6回の学習で次のように重みが変更されました。これによって論理積も学習することができました。

学習回数:6
weight[0] = -3
weight[1] = 1
weight[2] = 2

● ひとつのユニットの限界

 ユニットがひとつの場合でも、先述のように論理和と論理積を学習することはできました。それでは排他的論理和を学習できるか試してみましょう。neuron.cの教師信号を排他的論理和の演算結果になるようにセットし、コンパイルして実行しますと、次のような結果になります。

学習回数:100
weight[0] = 0
weight[1] = 0
weight[2] = -1

 このプログラムでは学習回数が100回の時は学習できなかったということを意味していますので、排他的論理和を学習することはできませんでした。論理和、論理積と排他的論理和には後述する線形分離性のある、ないで違いがあります。この違いがひとつのユニットで学習できる、できないの違いとなります。

○ 線形分離性

 以前、「.: ニューロンモデル」で解説したニューロンの動作を表す式を次に記述します。ここでf()はstep関数を表しています。

 活性化関数によってユニットはnet値が0より大きいときには1を出力し、net値が0より小さいときには0を出力します。このことからユニットが0か1かのどちらの値を出力するかの境界線は次のように表現できます。

 この直線によって0を出力するべき点と1を出力するべき点を分離することができる問題を線形分離可能な問題と呼びます。次のグラフで確認できるように、ANDとORは線形分離可能な問題です。○は0を出力するべき点を表しており、●は1を出力するべき点を表しています。

» AND
(figure10)http://ruffnex.oc.to/defolos/text1/figure/linia_dev_and.jpg
» OR
(figure11)http://ruffnex.oc.to/defolos/text1/figure/linia_dev_or.jpg

 一方、XORのように一本の直線では分離できないような問題も存在しています。このような問題は線形分離不可能な問題と呼びます。XORが線形分離不可能であるのは次のグラフを参照することで確認してください。

» XOR
(figure12)http://ruffnex.oc.to/defolos/text1/figure/linia_dev_xor.jpg

 実は、XORなどの線形分離不可能問題はひとつだけのユニットあるいは中間層を持たないニューラルネットワークでは学習不可能であることが数学的に証明されています。1962年にFrank Rosenblattが提唱したニューラルネットワークのアルゴリズムの中で最初に提唱されたパーセプトロンは一世を風靡し、多くの分野で応用されました。パーセプトロンは1階層で表現されコスト関数によって学習しますが、線形分離可能問題しか学習することができないことがMinskyによって証明されました。これによってニューラルネットワークの研究は一時衰退傾向にあったようです。
 これを解決する方法としてニューロンに中間層を持たせて階層構造をとることが挙げられます。ニューラルネットワークの研究が盛り返したのは中間層をとるパーセプトロンとバックプロパゲーションという学習法の出現による部分が大きいとされています。


■参考文献


go back to the TOP page of Glazheim Lykeion.