8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ゼロから作る Deep LearningをElixirで学ぶシリーズ ~ Perceptron(パーセプトロンの実装) ~

Last updated at Posted at 2023-01-04

東京にいるけどfukuokaexのYOSUKEです。
最近、エリクサーちゃんで学ぶ Elixirの動画を作成し始めてるので良かったらチャンネル登録お願いします。本格的に来週後半からの予定です。

ゼロから作る Deep Learningという本を見ながら Elixirの計算ライブラリ系を学ぶシリーズです。
正直このアウトプットは試行錯誤を書き連ねる予定なので、手っ取り早く知りたいって方は、学習し終えたらものからまとめ書くつもりなのでそれまでお待ちください。m(_ _)m

学習開始

この書籍を元にElixirで学びます。

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

と言う事で、今回は 第2章のパーセプトロンを学びます。
内容は、書籍に任せることにして、Elixirでの実装部分を学習しながら進めたいと思います。

前回の学習内容はこちらです。

2.3 パーセプトロンの実装

  • パーセプトロンとは

複数の信号を入力として受け取り、一つの信号を出力します。

と言う事で、論理演算のイメージみたいです。ただし、0と1の重みづけの際に、0と判断するのか、1と判断するのかの閾値を決める必要があり、論理演算の場合は人間があらかじめ決めた閾値の通りに動くのを、機械学習の問題ではこの閾値を決める作業をコンピュータに自動で行わせる作業で、人がやることはパーセプトロンの構造を考えることにあると思います。

2.3.1 簡単な実装

準び

$ mix new perceptron

ANDゲートの実装

lib/perceptron.ex
defmodule Perceptron do
  def _and(x1, x2) do
    [w1, w2, theta] = [0.5, 0.5, 0.7]
    tmp = x1 * w1 + x2 * w2
    case tmp do
      tmp when tmp > theta -> 1
      tmp when tmp <= theta -> 0
    end
  end
end

ANDゲートの実行結果

iex()> Perceptron._and(1,0)
0
iex()> Perceptron._and(0,0)
0
iex()> Perceptron._and(0,1)
0
iex()> Perceptron._and(1,1)
1

NANDゲートの実装

NANDゲートはANDゲートの実装のパラメータを反転する事で実現できるので、反転してみました。

  def _nand(x1, x2) do
    [w1, w2, theta] = [-0.5, -0.5, -0.7]
    tmp = x1 * w1 + x2 * w2
    case tmp do
      tmp when tmp > theta -> 1
      tmp when tmp <= theta  -> 0
    end
  end

NANDゲートの実行結果

iex()> Perceptron._nand(1,1)
0
iex()> Perceptron._nand(0,1)
1
iex()> Perceptron._nand(0,0)
1
iex()> Perceptron._nand(1,0)
1

ORゲートの実装

ORゲートは、どちらか1つが1であれば1になり0になるのはどちらも0の場合のみなのでパラメータのthetaを0にすればOKです。

  def _or(x1, x2) do
    [w1, w2, theta] = [0.5, 0.5, 0.0]
    tmp = x1 * w1 + x2 * w2
    case tmp do
      tmp when tmp > theta -> 1
      tmp when tmp <= theta  -> 0
    end
  end

実は、ここまで書くまでに最初、少しだけ、上記のプログラムを変更して書いてました。

例えば、NANDゲートに関しては、こんな感じで、ここでこのプログラムは同じ構造であることが重要なんだと気がつきました。要するにパラメータを変えるだけで挙動が変わる、構造は同じモデルである事が重要なのだと。そこで、Enum.map(& &1 * -1)の余計な処理を削除して、パラメータを変更する形に変えました。

  def _nand(x1, x2) do
    [w1, w2, theta] = [0.5, 0.5, 0.7] |> Enum.map(& &1 * -1)
    tmp = x1 * w1 + x2 * w2
    case tmp do
      tmp when tmp > theta -> 1
      tmp when tmp <= theta  -> 0
    end
  end

2.3.2 重みとバイアスの導入

Nxを使うので、Nxモジュールをインストールします。

mix.exs
  defp deps do
    [
      {:nx, "~> 0.4.1"}
    ]
  end
$ mix deps.get

wを重み、bをバイアスとしてθを-bにして式変換してANDを実装する、まずはインタラクティブモードで実行しながら確認します。

iex()> x = Nx.tensor([0,1])
#Nx.Tensor<
  s64[2]
  [0, 1]
>
iex()> w = Nx.tensor([0.5,0.5])
#Nx.Tensor<
  f32[2]
  [0.5, 0.5]
>
iex()> b = -0.7
-0.7
iex()> Nx.multiply(x, w)
#Nx.Tensor<
  f32[2]
  [0.0, 0.5]
>
iex()> Nx.multiply(x, w)|> Nx.sum 
#Nx.Tensor<
  f32
  0.5
>
iex()>  Nx.multiply(x, w)|> Nx.sum |> Nx.add(b)
#Nx.Tensor<
  f32
  -0.19999998807907104
>

2.3.3 重みとバイアスによる実装

ANDゲートの実装

  def nx_and(x1, x2) do
    x = Nx.tensor([x1, x2])
    w = Nx.tensor([0.5, 0.5])
    b = -0.7
    [tmp] =  Nx.multiply(x, w)
    |> Nx.sum
    |> Nx.add(b)
    |> Nx.to_flat_list()
    case tmp do
      tmp when tmp <= 0  -> 0
      tmp when tmp > 0 -> 1
    end
  end

wの重みづけと違い、bのバイアスは1の出力する度合いの調整をする役割を持っている。

NANDゲートの実装

ANDのバイアスと重みのパラメータを反転するだけで実装できます。

  def nx_nand(x1, x2) do
    x = Nx.tensor([x1, x2])
    w = Nx.tensor([-0.5, -0.5])
    b = 0.7
    [tmp] =  Nx.multiply(x, w)
    |> Nx.sum
    |> Nx.add(b)
    |> Nx.to_flat_list()
    case tmp do
      tmp when tmp <= 0  -> 0
      tmp when tmp > 0 -> 1
    end
  end

ORゲートの実装

バイアスの重みづけを0にするだけで変更できます。

  def nx_or(x1, x2) do
    x = Nx.tensor([x1, x2])
    w = Nx.tensor([0.5, 0.5])
    b = 0
    [tmp] =  Nx.multiply(x, w)
    |> Nx.sum
    |> Nx.add(b)
    |> Nx.to_flat_list()
    case tmp do
      tmp when tmp <= 0  -> 0
      tmp when tmp > 0 -> 1
    end
  end

2.4 パーセプトロンの限界

パーセプトロンを用いる事で、AND, OR, NANDを同じ構造で実装できました。そこで、次はXORについてみて行きます。

2.4.1 XORゲート

x1 x2 y
0 0 0
1 0 1
0 1 1
1 1 0

ここでの学習メモ:
AND, OR, NANDは線形な領域で直線で0と1の領域を分けられたが、XORの場合は線形では分けられず、
非線形な領域での分別が必要な為、パーセプトロンでは分けられない。
しかし、層を重ねるというアイデアを駆使すればXORも分別可能になる。

x1 x2 s1(NAND) s2(OR) y(AND)
0 0 1 0 0
1 0 1 1 1
0 1 1 1 1
1 1 0 1 0

という事で、ここまで書いたら実装は簡単ですね。

  def nx_xor(x1, x2) do
    s1 = nx_nand(x1, x2)
    s2 = nx_or(x1, x2)
    nx_and(s1,s2)
  end

XORは2つのパーセプトロンを介して解を出しているので、2層のパーセプトロンというらしい。
そして、この多重の層を重ねたパーセプトロンならもっと複雑な事も可能になるのと、次章のニューラルネットワークの基礎になるので重要との事でした。

よし、いよいよ次章、ニューラルネットワークに入っていける。楽しみだ!

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?