パーセプトロン

パーセプトロン（）は、人工ニューロンやニューラルネットワークの一種である。心理学者・計算機科学者のフランク・ローゼンブラットが1957年に考案し、1958年に論文を発表した。モデルは同じく1958年に発表されたロジスティック回帰と等価である。
概要.
視覚と脳の機能をモデル化したものであり、パターン認識を行う。ただし学習については自明ではなく、特に多層パーセプトロンの機械学習に関する歴史は、それがパーセプトロンの歴史だと言っても過言ではない。1960年代に爆発的なニューラルネットブームを巻き起こしたが、60年代末のミンスキーらによる、単層パーセプトロンは線形分離可能なものしか学習できないという指摘は、多層パーセプトロンの学習が当時まだよくわからなかったことから、一時研究を停滞させた。影響を受けた変種といえるニューラルネットワークも多数提案されているが、それらについてはここでは略す（ニューラルネットワークの記事を参照）。パーセプトロン自体はその後、ボルツマンマシンや、多層パーセプトロンの機械学習をある程度実用化した誤差逆伝播法(バックプロパゲーション)などによって1980年代に再興隆した後、1990年代にはまた停滞した。21世紀に入った後の研究の進展などにより、より層を増やしたパーセプトロンでの機械学習を指すいわゆる「深層学習(ディープラーニング)」の実用化と、それによる画像認識などの成果により、2020年現在はブームにある。
形式ニューロン.
パーセプトロンは1943年に発表された形式ニューロンに基づく。
パーセプトロン.
ローゼンブラットはこの形式ニューロンの考え方を基にしてパーセプトロンを開発した。S層（感覚層、入力層）、A層（連合層、中間層）、R層（反応層、出力層）の3つの部分からなる。S層とA層の間はランダムに接続されている。S層には外部から信号が与えられる。A層はS層からの情報を元に反応する。R層はA層の答えに重みづけをして、多数決を行い、答えを出す。パーセプトロンにおいてこの重みと呼んでいる値が人間でいうところの記憶となる。ただし、記憶の学習というと見聞き感じた物を覚える様子を想像しがちだが、パーセプトロンにおける学習は、入力を期待する出力値に変換できる最適な値(重み)を探す作業となる。
1970年頃、デビッド・マーとジェームズ・アルブスによって小脳はパーセプトロンであるという仮説が相次いで提唱された。後に神経生理学者伊藤正男らの前庭動眼反射に関する研究によって、平行繊維-プルキンエ細胞間のシナプスの長期抑圧（LTD; long-term depression）が見つかったことで、小脳パーセプトロン説は支持されている。
単純パーセプトロン.
入力層と出力層のみの2層からなり単一のニューロンを持つ。単純パーセプトロン (Simple perceptron) は線形分離可能な問題を有限回の反復で解くことができる一方で、線形非分離な問題を解けないことがマービン・ミンスキーとシーモア・パパートによって指摘された。
多層パーセプトロン.
多層にすれば線型分離不可能な問題が解けることはマカロックとピッツの頃から解っていたわけだが、機械学習が問題であった。誤差逆伝播法は多層パーセプトロンの学習への道を開いた。深層学習は、より層を増やした場合の学習を実用化しブームとなった。
実装例.
単純パーセプトロン.
下記にSmalltalk(Pharo環境)による単純パーセプトロンの例を示す。この例では論理演算用途を想定している。
■class定義及び初期化部
"重みの集合とニューロンは一対であるため
ニューロンを重みの配列として定義している。"
Array variableSubclass: #Neuron
 instanceVariableNames: "
 classVariableNames: "
 package: 'Machine-Learning'.
Neuron class methodsFor: 'instance creation'
new: aSizeInterger
 | weights |
 weights :=
 super
 new: aSizeInterger.
 "重みは初期値は確率的勾配降下法に基づくため
 全要素を乱数で初期化する必要がある。"
 Random new
 next: weights size
 into: weights.
 ^ weights.
■思考部
Neuron methodsFor: 'accessing evaluation'
activationFunction
 "出力値を整形する活性化関数を提供する。
 今回は論理値を返せれば良いので出力が0以下なら0、1以上なら1を返す。"
 ^ [ :value | ( 0 < value ) asInteger ].
Neuron methodsFor: 'evaluation'
thought: aInputValuesCollection
 "学習結果に基づき、入力値から出力値を出す思考関数。
 パーセプトロンの本体といえる。
 入力値としては下記の形式の値を期待する。(末尾以外は任意の値で末尾は必ず1)
 #( 0 0 1 )
 #( 0 1 1 )
 末尾の値である1は閾値を表す。
 
 "入力値の制限。入力値の数は重みの数と一致している必要がある。"
 self size = aInputValuesCollection size
 ifFalse:
 self
 error: 'Should aInputValuesCollection size to equal self size'
 "パーセプトロンの最重要部。入力値と重みをそれぞれ掛け合わせ、
 その合計から出力値を出す。( selfは重みの配列 )"
 ^ self activationFunction
 value: ( aInputValuesCollection * self ) sumNumbers.
■学習部
Neuron methodsFor: 'accessing learning'
errorFunction
 "誤った値を正解の値に近づけるための誤差関数を提供する。
 正確には誤差関数そのものではないが、誤差関数は偏微分により
 誤差関数を構成する式の半分が消失するためここでは下記を誤差関数として扱う"
 ^
 [ :outputValueCollection :expectedNumber |
 expectedNumber - outputValueCollection.
!!
Neuron methodsFor: 'learning'
learnFrom: aInputValueCollection
to: aExpectedNumber "期待値・正解値"
 "1通り分の学習関数。
 #( 1 1 1 )であれば1であるべきという値を受け取り、
 そうなっていなければ重みを正解に近づくよう調整する。"
 | result |
 result :=
 self
 thought: aInputValueCollection.
 {
 "上記の実行結果を正解の値に近づける。( selfは重みの配列 )
 調整後の重みを戻り値として返す。"
 self +
 (
 self errorFunction
 value: result
 value: aExpectedNumber
 "0.001は精度と学習速度を調整する値で、
 それらを鑑みてちょうど良い値を指定する。"
 ) * aInputValueCollection * 0.001
 "後で使えるように、実行結果も返す。"
 result
!
learnAll: aExpectedWithInputValuesCollection
 "全通り分の学習関数。
 例えば論理積であれば下記をすべて満たす必要があるが、
 1通り分の学習関数では、下記のうちどれか1つを満たす
 パーセプトロンしか作ることができない。
 この関数では下記すべてを満たすパーセプトロンを作る。
 #( 0 0 1 )であれば0。
 #( 0 1 1 )であれば0。
 #( 1 0 1 )であれば0。
 #( 1 1 1 )であれば1。
 入力値としては下記の形式の値を期待する。(末尾以外は任意の値で末尾は必ず1)
 一番左の値は正解値となる。
 ( 0 ( 0 0 1 ) )
 ( 1 ( 1 1 1 ) )
 | weight |
 weights := self.
 "全ての条件が正解になるまで学習を繰り返す。"
 (
 aExpectedWithInputValuesCollection
 collect: 
 [ :each |
 | tuple |
 tuple :=
 weights
 learnFrom: each last
 to: each first.
 weights := tuple first.
 "実行結果が正解になっているか確認する。"
 each first = tuple last.
 )
 "不正解が残っているか確認し、残っていればもう一度学習させる。"
 includes: false.
 ] whileTrue.
 ^ weights.
■使用部
and :=
 Neuron
 new: 3.
"論理積を学習させる。"
and :=
 and
 learnAll:
 ( 0 ( 0 0 1 ) )
 ( 0 ( 0 1 1 ) )
 ( 0 ( 1 0 1 ) )
 ( 1 ( 1 1 1 ) )
"論理積回路として動作する。"
and
 thought: #( 0 1 1 ). "→ 0"
and
 thought: #( 1 1 1 ). "→ 1"