まったりAI勉強記

AI(人工知能)について、特にゆかりがあるわけでもない社会人が、まったりとAIについて勉強していく勉強日記です。

2章 パーセプトロン その1 - ゼロから作るDeep Learning

どうも、カタミチです。

さて、今日もゼロから作るDeep Learningを見てみますかねー。今日のテーマは「パーセプトロン(perceptron)」ですね。

パーセプトロンとは?

パーセプトロンは、1957年に、ローゼンブラットというアメリカの研究者によって考案されたアルゴリズムです。1957年というと、ジョン・マッカーシーがAIという言葉を使った、ダートマス会議が開催された1956年の翌年ですね。そんな頃から、第三次AIブームの萌芽が生まれていた…というのは興味深いですね。

ちなみに、ジョン・マッカーシーについては、以前に人生を紹介した記事を書きました。なかなかエキサイティングな人生でしたねー(遠い目)。

で、パーセプトロンがどんなやつかというと、こんなやつですね。

f:id:feh_asama:20220218124830j:image

はい、前にも出てきましたコレ。

x1、x2は入力信号で、yは出力信号。w1、w2は重みです。x1やx2、yを囲っている、ひとつひとつの「○」のことをニューロンと呼びます。yはwとx使って、こんな感じで表します。

$$ y= \begin{cases} 0\;(w_1x_1+w_2x_2\leqq\theta) \\ 1\;(w_1x_1+w_2x_2>\theta) \end{cases} $$

はい、数式出てきましたね。私も、AIの勉強を2ヶ月続けてきたので、さすがに数式への拒絶反応は薄まってきました。ところで今回、初めて数式を画像ではなく文字でWebページ内に埋め込むことに成功しました!…ちゃんと表示されてますかね?(ドキドキ)

さて、本題に戻りましょう。出力信号yの値は0か1のどちらかですね。0をOFF、1をONと捉えると、ONかOFFかってことになりますね。で、ONになるかOFFになるかを決めるのが、入力値であるxと重みであるwです。

入力値と重みを掛け算したものを合計して、それがある値(閾値:θ)を超えるかどうか?を見て、超える場合は1を、超えない場合は0を返すというものですね。

で、ここから論理回路の話が始まるんですが、以前、初めて私がパーセプトロンを勉強した時、その発展形として出てくるニューラルネットワークの理解につながったかというと…逆に混乱していまいました(汗)

とりあえず、パーセプトロン論理回路論理回路で、そんなものだと思いつつ先に進むというのも一案かもしれませんね。まぁ、きっちりとつながりを追いかけられるのであれば、それに越したことはないですが…。

論理回路としてAND回路、OR回路、NAND回路、XOR回路が出てきますので、とりあえず実装を見つつ理解を深めてみますかねー。

AND回路

まずはAND回路です。AND回路とは、入力信号x1,x2にそれぞれ0か1を入れた場合の出力信号yが、以下のようになる関数のことです。

・入力信号x1、x2がともに1の場合、出力信号yは1になる
・入力信号x1、x2のどちらかが1でどちらかが0の場合、出力信号yは0になる
・入力信号x1、x2がともに0の場合、出力信号yは0になる

とりあえずPythonで書いてみるとこんな感じになります…

どん!

f:id:feh_asama:20220219191405j:image

Pythonのコードをちゃんと語るのは今回が初めてなので、じっくりやっていきますかねー。

まず、薄いグレーの部分がコードなのですが、最初の行にある[1]っていうのはコードとは関係ありません。私がPythonの実行環境として使っているColaborateryが勝手に振ってる行番号みたいなものです。

行頭に「#」と書かれた行はコメント行と言うやつですね。Pythonのコードとは認識されず、コンピュータ的には読み飛ばしますので、好きなことが書けます。行全体が緑色になっていて、コメントであることがひと目で分かります。今回は「AND回路」なので、単純にそれが分かるように書きました。

次の行にある「def」は、関数を定義する宣言ですね。(「define(定義する)」の略ですかね、たぶん)

def AND(x1, x2):

今回は、直後に書いてある「AND」というのが関数の名前です。人呼んで、AND関数ということになりますかね。これ、自分で勝手に付けた名前なので、本来は何でもいいです。気合入れるために「AaaanD」とかにしても問題はありません。…まぁ、大人しく「AND」のままでいきましょう。

その後ろに、カッコに囲まれて(x1, x2)と書かれています。このx1やx2のことを、その関数の引数(ひきすう)と言います。カンマで区切って書くことで、たくさん定義することができますが、今回の引数は2つですね。で、コロン(:)で宣言を終わらせます。関数ってやつは「何かの値を渡すと、別の何かの値が返ってくる」ものなのですが、この時渡す「何かの値」っていうのを引数として定義します。

次の行からが、AND関数の中身です。半角スペース2文字分くらい行頭がズレて居ますが、これがPythonでは「関数の中にいるよー」というアピールなんだそうです。AND関数の中の1行目は…

w1, w2, theta = 0.5, 0.5, 0.7

とあります。これは、変数であるw1に0.5を、w2に0.5を、thetaに0.7を代入することを示しています。簡略化のために一行で書かれていますが、こう書いてあるのと同じですね。

w1 = 0.5
w2 = 0.5
theta = 0.7

ちなみに、プログラムの世界では「=」は左辺に右辺のもの代入する(ぶち込む)という意味になりますので注意が必要です。つまりこれで、w1という変数に0.5がセットされ、w2という変数に0.5がセットされ、thetaという変数に0.7がセットされたことになります。なお、thetaは上のパーセプトロンの式にあった「θ(閾値)」のことですね。プログラムの世界では、基本的に全角文字は使わないので、「theta」としたんでしょう。

さて、次の行です。

tmp = x1*w1 + x2*w2

右辺は、パーセプトロンの式の右辺そのままですね。一応言っておきますが、梅干しを食べた時の口の形みたいな「*」は掛け算のことです。数式だと掛け算の記号は省略されがちですが、プログラムで書く場合はしっかり書かないといけません。

左辺は最終的には「y」になるはずですが、閾値の判定をする必要があるので、tmpという変数を作ってますね。おそらく「temporary(一時的な)」の略でしょう。

ちなみに、この行を含めたこれ以降の行は、OR回路でもNAND回路でも変わりません。変わるのは、関数の名前と、重みの数値、閾値の数値だけです。

で、次の行は…

if tmp <= theta :

ですね。ifというのは、条件によって処理を分岐させたいときに使います。ifの後ろに書かれた条件が正しい時のみ、中の処理が実行される仕掛けです。

今回の場合、「tmp <= theta」というのが条件になります。日本語で読み換えると「tmpがtheta以下」ということになります。つまり、tmpがtheta以下の時のみ、中の処理が実行されることになります。

で、その中の処理というのが…

return 0

ですね。中の処理かどうか?というのは、defのときと同じく、スペース2文字分くらい行頭がズレ(インデントと言います)ているかどうかで判断します。今回はこの「return 0」の文がインデントされており、その次の行はインデントがもとに戻っているので、この行のみがifの中身だということになります。

で、この「return」というのは、関数の説明のところで「何かの値を渡すと、別の何かの値が返ってくる」と書きましたが、その「別の値を返す」という命令です。今回は、returnの後ろに書かれた「0」を返すことになります。で、returnが出てきたら関数は終りを迎えますので、処理はここで終わりになります。

で、処理が終わったはずなのにまだ関数の中身が続いています。なぜか?

それは、ifの条件が正しくない場合があるからです。今回の例で行くと、「tmpがtheta以下」ではない、という場合には「return 0」は通りません。華麗にスルーします。で、次の文。

elif tmp > theta :

「elif」というのが来ました。「elif」はたぶん「else if」の略ですね。日本語で言うと「ほかに、もし…」という感じ。「if」が上にあるのが前提で使われるものになります。今回、中の処理に入るかどうかの判定は「tmp > theta」になります。

今回の場合、tmpとthetaがともに数値だとすると、ifの条件が正しくないならelifの条件が必ず正しくなりますね。で、この場合の中の処理は…

return 1

となります。文法の説明は不要ですね。

しかし、ifとelifだけで表現するこの書き方って、どうなんでしょうねー。錆び付きプログラマーなのでよく分かりませんが、どちらの条件も満たさない場合を想定しておく必要は無いんだろうか…などと思ってしまいます。

ともあれ、これでAND関数…つまりAND回路が完成しました。あとはこのAND回路に、入力信号であるx1とx2の値を与えてみましょう。

x1 = 0, x2 = 0を入れた出力信号yを求めたい場合は、

AND(0, 0)

と書きます。さて、行ってみましょう…

どん!

 

f:id:feh_asama:20220219191407j:image

グレーになってる部分が実行部分で、白になってる部分が実行結果です。

うまくいきましたね。「y」って記述がどこにも無いのが気持ち悪い方がいるかもしれませんが、yは、この「AND関数そのもの」だと思ってもらえれば良いかと思います。x1=1,x2=1の場合のみ「1」が返ってきており、それ以外は「0」が返ってきました。定義と合っていますね。

OR回路

次にOR回路です。簡単です。上のAND関数の関数名をORに変え(「OoooR」とかでもいいです(しつこい))て、重みと閾値を変えるだけです。とりあえず…

w1, w2, theta = 0.5, 0.5, 0.2

って感じにするとOR回路の出来上がりです。実行すると…

f:id:feh_asama:20220219215041j:image

はい、x1=x2=0のときにyが0になって、それ以外のときはyが1になるのがOR回路です。ちなみに、AND回路にも同じことが言えるのですが、重みと閾値の値は、今回選んだもの一択…というわけではなく、いくつもの組み合わせがある中で、今回たまたま選んだのがこの組み合わせだった、くらいに思っておいた方が良いと思います。

NAND回路

で、NAND回路。同じく関数名をANDのからNANDに変えて、重みと閾値を…

w1, w2, theta = -0.5, -0.5, -0.7

とするとNAND回路の出来上がりです。

f:id:feh_asama:20220219215048j:image

x1=x2=1のときのみyが0で、それ意外のときはyが1の回路になっています。

ということで

初めてのPythonコーディングだったのでだいぶ紙幅を使ってしまいましたが、とりあえずここまでですかねー。

最初にも言いましたが、この論理回路の理解からディープラーニングの入り口の理解に到達するまでには、かなりの道のりがあります。とりあえず論理回路からは、「入力値と重みを掛け算したものを足し合わせる」って計算の考え方が生き残ってくるので、そこだけエッセンスとして抽出できればいいんじゃないかなー、と現時点では思っています。

これでパーセプトロンの話は終わり…と、思いきや、実はまだ終わってないんです。そう、途中でちらっと出てきたXOR回路です。この話がまだできていないんですよねー。実は、XOR回路だけちょっと別扱いになるので、次回に回したいと思います。

ではまた。