学生による学生のためのプログラミング学習サイト

「ゼロから作るディープラーニング」 パーセプトロン

 BDARC勉強会では毎週月曜日、「ゼロから作るディープラーニング」の輪読を行っています。この記事は5月第2週分の「パーセプトロン」の回のまとめです

パーセプトロンとは?

 パーセプトロンは、1957年に心理学者・計算機科学者のローゼンブラッドによって発明されたアルゴリズムです。
 
 これは、ニューラルネットワーク、もといディープラーニングの起源となるもので、視覚と脳の関係をモデル化したものです。

ちなみに…

 この記事では実際にプログラミング言語のPythonを使ってパーセプトロンの実装を行います。しかし、これは実際には厳密な意味では、パーセプトロンではなく、人工ニューロンや単純パーセプトロンと呼ばれるものです。

パーセプトロンの仕組み

 パーセプトロンは簡単なアルゴリズムです。
 細かい説明に入る前に、まず概要を捉えましょう。

概要

 まず下の図のように入力を2つ受け取ります。これはどんな値でもいいですが、とりあえず数値データです。

図1

 そして、入力から伸びた矢印に「重み」と書いてあります。これも数値です。重みは矢印につき1つずつ用意されています。入力1側と2側の重みはそれぞれ異なる値(同値をとることもあります)です。この値を入力のそれぞれについてかけ合わせます。
 最後にかけ合わせた値を足して、その値がある基準値を超えたら、出力として1を返します。超えなければ、0を返します。

数式で考えてみる

 これまでのパーセプトロンの仕組みを数式で考えてみましょう。
 といっても、特に難しいものではありません。

 まず、入力を、$$x_1,x_2$$ とします。次に、重みを$$w_1,w_2$$ と表します。次に、さきほどは「ある基準値」と言いましたが、これを $$θ$$としましょう。このθは、閾値(しきい値)と言います。

これらの変数を使ってパーセプトロンを表現すると、

$$
\begin{align}
if \quad w_1x_1 + w_2x_2 \leqq θ\ \\
return\quad0 \ \\
if \quad w_1x_1 + w_2x_2 > θ \ \\
return\quad1 \
\end{align}
$$

となります。重みと入力を掛けた値がθを超えれば、パーセプトロンは「発火」します。

ANDゲートをパーセプトロンで実装する

 これでパーセプトロンを実装する準備が整いました。
 まずはANDゲートを実装してみましょう。

 ANDゲートとは、次のような関係を持つ論理関数です。

$$x_1$$$$x_2$$$$y$$
000
010
100
110

 2つの入力が1だった場合にのみ、出力yは1を返します。

 入力は1か0が来るとして、どのような値を重みと閾値をどのように設定すれば、ANDゲートを実装できるでしょうか。

 ANDゲートは、なかなか1になりにくい論理です。2つの入力が1になって初めてやっと1を出力できます。ですので、方針としては、片方が1になったくらいでは超えられないような閾値を設定すればいいですね。
 このような重みと閾値の組み合わせとして、例えば次のようなものが考えられます。

案1
$$\quad 100・x_1 + 100・x_2 $$
$$ θ =200 $$

案2
$$\quad 0.1・x_1 + 0.1・x_2 $$
$$ θ =0.2 $$

これを数式として埋め込めば、実装が出来ます。
Pythonのコードを見てみましょう。

def AND(x1,x2):
    #案1を使っています
    w1 = 100
    w2 = 100
    theta = 200
    tmp = x1 * w1 + x2 * w2
    if(tmp < theta):
        return

 tmpという変数に重みと入力の掛け算の和を入れ、θと比較するだけの単純なものです。

バイアスとは

 先程のパーセプトロンのモデルを少し改変してみましょう。

$$
\begin{align}
if \quad w_1x_1 + w_2x_2 \leqq θ\ \\
return\quad0 \ \\
if \quad w_1x_1 + w_2x_2 > θ \ \\
return\quad1 \
\end{align}
$$

この数式をθに着目して変形します。

$$
\begin{align}
\quad -θ+w_1x_1 + w_2x_2 \leqq 0\ \\
\end{align}
$$

ここで\(-θ\)を\(b\)とおくと、

$$
\begin{align}
\quad b+w_1x_1 + w_2x_2 \leqq 0\ \\
\end{align}
$$

となります。再びパーセプトロンのモデルにすると、

$$
\begin{align}
if \quad b+w_1x_1 + w_2x_2 \leqq 0\ \\
return\quad0 \ \\
if \quad b+w_1x_1 + w_2x_2 > 0 \ \\
return\quad1 \
\end{align}
$$

こうなります。この\(b\)をバイアスといいます。

バイアスのメリット

さて、このバイアスですが、こんな変形をして何が嬉しいのでしょうか。

 これまでは、重みと閾値という2つのパラメータについて、それぞれの関係を動的に考える必要がありました。しかし、閾値を左辺に移項することで、閾値を0に固定できます。こうすることで、2つの間の関係を同時に考える必要はなくなります。

 一方、バイアスは、パーセプトロンがどれくらい発火しやすいかを決定するパラメータとなります。

 このように、バイアスの導入によって、モデルよりシンプルにすることができます。

ORゲートの実装

 さて、バイアスを導入したところで、今度はORゲートを実装してみます。

 ORゲートは次のような真理値表を持ちます。

$$x_1$$$$x_2$$$$y$$
000
011
101
111

 ORゲートは、ANDゲートと異なり、1になりやすいのが特徴です。

 今度は先程とは考え方を変えて、バイアスと重みにどんな値を設定すればよいか考えましょう。

 バイアスは、次のように考えることも出来ます。例えば、入力がいずれも0だった場合、モデルはバイアスの値のみを0と比較することになります。つまり、バイアスとは、デフォルトで比較されるということになります。

 ORゲートの場合、このデフォルトの値は0を下回るように設定しなければいけません。なので、例えばバイアスを-99と決めてしまえば、後は重みを100以上の値にすることで、入力の片方が1だったとき、バイアスの値を足した値が0を超えることができます。もちろん、入力のどちらも1だったときも同様です。数値の設定例を示します。

案1
$$\quad 100・x_1 + 100・x_2 $$
$$ θ =100$$

案2
$$\quad 0.1・x_1 + 0.1・x_2 $$
$$ θ =0.1$$

例えば案1がうまくいくことは、次の数式で証明できます。

$$
\begin{align}
-99 + 0 + 0 = -99 \quad\leqq\quad0 \\\
-99 + 0 + 100 = 1 \quad>\quad 0 \\\
-99 + 100 + 0 = 1\quad>\quad 0 \\\
-99 + 100 + 100 = 101\quad>\quad 0 \
\end{align}
$$

 この数式に従って実装を行うと、

def bOR(x1,x2):
    #案1を使っています
    w1 = 100
    w2 = 100
    theta = 100
    tmp = x1 * w1 + x2 * w2
    if(tmp < theta):
        return

このようになります。

NANDの実装

 バイアスを使った論理ゲートの実装をもう一つ考えてみましょう。
 次に紹介するのはNAND(Not AND:否定論理積)です。名前はごついですが、考え方はシンプルです。ANDを否定する、つまり、ANDの出力を反転しただけということです。

$$x_1$$$$x_2$$$$y$$
00
011
101
11

 真理値表はこのようになります。入力が両方とも1のときにのみ、0を出力します。

 このような論理式は、バイアスを正の値-例えば199など-に設定して、重みをそれぞれー100に設定すれば、入力がどちらも1のときに-1となり、0を下回るため、NANDを実装できたことになります。

 Pythonのコードで書き下してみると、

def AND(x1,x2):
    w1 = 100
    w2 = 100
    theta = 200
    tmp = x1 * w1 + x2 * w2
    if(tmp < theta):
        return 0
    else:
        return 1

この関数がうまくいくことは、次の式から証明できます。

$$
\begin{align}
199 + 0 + 0 = 199 \quad>\quad0 \
199 + 0 – 100 = 99 \quad>\quad 0 \
199 – 100 + 0 = 99\quad>\quad 0 \
199 – 100 – 100 = -1\quad\leqq\quad 0 \
\end{align}
$$

パーセプトロンの限界

 XORとは

さて、ここまでパーセプトロンを使ってANDとORとNANDを表現出来ました。では、次にような論理関数はどうでしょうか。

$$x_1$$$$x_2$$$$y$$
000
011
101
110

 入力の値が異なるときだけに1を出力しています。これはXOR(eXclusive OR:排他的論理和)と呼ばれるものです。排他的とは、片方が成り立つとき、もう片方は必ず成り立たないような論理です。ベン図で見てみると分かりやすいです。

参考 https://www.monotalk.xyz/blog/Draw-a-Venn-diagram-with-Python-matplotlib-venn/

 このような論理を実現するバイアスと重みの関係を考えることはできるでしょうか?

線形分離

 このことを考える前に、ORとの違いを考えてみましょう。入力の2つの値と出力の関係を\(x_1\),\(x_2\)平面上にプロットした図を見て下さい。

 (勉強会で使ったスライドをそのまま流用しています。)
 赤い点が1を出力するときの入力の組み合わせで、星マークが0のときの入力です。このように、一本の直線によって1と0の場合が分けられていることが分かります。このような場合を線形分離が可能であるといいます。

 一方、XORはどうでしょうか。

 このように、赤点と星マークを分けるためにはどう考えても直線一本では無理そうです。つまり、XORはパーセプトロンで実現するような重みとバイアスの組み合わせは存在しないということが言えます。このような場合を線形分離が不可能であるといいます。

 このようにパーセプトロンだけでは、線形分離が不可能な論理を実装することは出来ません。では、どうすればいいでしょうか?

多層パーセプトロン

 そこで、問題を分割することを考えてみましょう。
 これまで、両方の値が1に揃ったときにだけ0を出す論理(NAND)、入力の片方でも1なら1を出力する論理(OR)を学びました。これらを組み合わせれば、XORのような複雑な論理も作れるような気ががします。

 実際、NANDとORとANDを巧妙に組み合わせることでXORを作ることができるということが、証明されています。例えば次のような回路を組むと、XORを表現できます。

 これは、入力をそれぞれNANDとORに入れその値をANDで統合しているということを意味します。ということは、これまでに実装したNANDとORのパーセプトロンの出力結果をAND関数に渡すことで、パーセプトロンによってXORを表現できるのでは無いでしょうか。

 このことを図にすると次のようになります。

 これが多層パーセプトロンと呼ばれるものです。既存の部品の組み合わせですので、もちろん実装することができます。Pythonのコードに落とし込むと、次のような実装が出来ます。

def XOR(x1,x2):
  #入力をNANDとORのそれぞれにかけ、s1、s2という変数に入れます
    s1 = NAND(x1,x2)
    s2 = OR(x1,x2)
    #NANDとORのそれぞれの結果をANDにかけます
    y = AND(s1,s2)
    return y

 このように、パーセプトロンを多層にすることで、非線形な問題を解くことが出来ます。パーセプトロンの限界を突破することに成功したということです。

 このように、あらゆる論理はパーセプトロンを使って実装することが出来ます。これは、もっと言えば論理回路の塊であるコンピュータもパーセプトロン飲みを用いて実装できるということです。

パーセプトロンの応用 シミュレータ作成

 さて、ここまで本にある通り論理回路の実装をしてみましたが、最後にパーセプトロンを使って簡単なシミュレータを作ってみようと思います。

 話は変わりますが、私は新潟大学の創生学部に所属しています。文理融合型の教育で、課題解決にフォーカスした教育をやろうぜってところです。

 ある人が、この学部に向いているか、向いていないのか、それをシミュレートするプログラムを書いてみます。

 さて、この創生学部ですが、この学部に向いている人が持っていそうな素質を私の独断と偏見で「課題解決志向」「コミュ力志向」「広い視点志向」としましょう。この3つの能力を評価する質問をこれまた独断と偏見で各項目3つずつ、合計9つ用意しました。それを下記に示します。

  • Q1 純粋な学問よりも、その応用に興味がある(100)
  • Q2 物事の原理を知るよりも、その使い方に目を向けたい(50)
  • Q3 プログラミングが得意だ(20)
  • Q4 人と合わないほうが正直楽だ(-50)
  • Q5 一人で考えるよりも、誰かと一緒に議論したほうが、よりよい結論を得られると思う(50)
  • Q6 人と議論することが好きだ(50)
  • Q7 人から個性的だと言われる(0)
  • Q8 1つのものを極めるよりも、色々なものに触れてみたいと思う(50)
  • Q9 分野によらず、様々なことを学ぶのが好きだ(50)

 質問の末尾の数値は重みです。この場合、重みは「どの要素(質問)をどれだけ重視するかを決めるパラメータ」という役割を持っています。(もちろん私の独断と偏見です。)

 さて、これらの質問によって3つの能力の有無を判定できます。次は、その結果をもとに、創生学部に向いているか、向いていないかを判定していきます。
 簡単化のため、今回は3つの要素のうち、1つ以上があれば向いていると判定することにしましょう。これは裏を返せば、素質を3つとも持っていないという場合にのみ、「向いていない」と判定されるということになります。ですので、判定には3値のORを設定します。

 ここまでの設定を図式化すると、このようになります。

 この図に従って、判定プログラムを書いたものを下記に示します。

#課題解決志向
def q1_3(x,y,z):
    x = np.array([x,y,z])
    w = np.array([100,50,20])
    b = -70
    tmp = np.sum(w*x) + b
    if(tmp <= 0):
        return 0
    else:
        return 1

#コミュ力
def q4_6(x,y,z):
    x = np.array([x,y,z])
    w = np.array([-50,50,50])
    b = 50
    tmp = np.sum(w*x) + b
    if(tmp <= 0):
        return 0
    else:
        return 1

#広い視点
def q7_9(x,y,z):
    x = np.array([x,y,z])
    w = np.array([0,50,50])
    b = 50
    tmp = np.sum(w*x) + b
    if(tmp <= 0):
        return 0
    else:
        return 1

#3値のORパーセプトロン
def three_OR(x1,x2,x3):
    x = np.array([x1,x2,x3])
    w = np.array([100,100,100])
    b = -99
    tmp = np.sum(w*x) + b
    if(tmp <= 0):
        return 0
    else:
        return 1

def to_input(a):
    if(a == "y"):
        return 1
    elif(a == "n"):
        return 0

#入力
print("以下の質問にyes(y)かNo(n)で答えて下さい。\n")
print("Q1 純粋な学問よりも、その応用に興味がある y/n") #100
q1_ = input()
q1 = to_input(q1_)
print("Q2 物事の原理を知るよりも、その使い方に目を向けたい y/n") #50
q2_ = input()
q2 = to_input(q2_)
print("Q3 プログラミングが得意だ y/n") #20
q3_ = input()
q3 = to_input(q3_)

print("Q4 人と合わないほうが正直楽だ y/n") #-50
q4_ = input()
q4 = to_input(q4_)
print("Q5 一人で考えるよりも、誰かと一緒に議論したほうが、よりよい結論を得られると思う y/n")#50
q5_ = input()
q5 = to_input(q5_)
print("Q6 人と議論することが好きだ y/n") #50
q6_ = input()
q6 = to_input(q6_)

print("Q7 人から個性的だと言われる y/n") #0
q7_ = input()
q7 = to_input(q7_)
print("Q8 1つのものを極めるよりも、色々なものに触れてみたいと思う y/n")#50
q8_ = input()
q8 = to_input(q8_)
print("Q9 分野によらず、様々なことを学ぶのが好きだ y/n") #50
q9_ = input()
q9 = to_input(q9_)

print("-----processing----\n")

#統合
sol = q1_3(q1,q2,q3)
com = q4_6(q4,q5,q6)
wid = q7_9(q7,q8,q9)
result = three_OR(sol,com,wid)

if(result == 1):
    print("おめでとう!創生学部に向いています!")
else:
    print("向いてないかも知れないけど大丈夫!!!")

 このプログラムはここで実際に動かしてみることが出来ます。

今後の展望

 パーセプトロンの広い可能性をご理解いただけたでしょうか。

 繰り返しますが、最後に示したシミュレータの重みやバイアスは、(それこそ私のバイアス入りまくりの)テキトーな値です。ですので、アレで実際に向いているか、向いていないのか判定することは難しいでしょう。

 では、どうすればきちんと判定できるシミュレータを作ることができるでしょうか。1つの方法は、モデルにから出力された結果と、真の結果ー例えば実際に入学してみての肌感覚などーを比較し、その差を重みやバイアスに反映させることです。

 そうすれば、モデルの出力結果は自然と真の結果を反映するものになるでしょう。このようなバイアスと重みの調整過程は誤差逆伝播法、もといバックプロパゲーションと呼ばれています。モデルが「賢くなる」ということは、モデルがデータから、適切な重みやバイアス調整していくということ意味していたわけです。

 今後の輪読会ではこの手法について学んでいきます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です