Powered by AppSignal & Oban Pro

Nx で ニューラルネットワークやってみた

パーセプトロンの作成/Elixirでニューラルネットワークを学ぶ.livemd

Nx で ニューラルネットワークやってみた

Mix.install([
  {:nx, "~> 0.9"},
  {:axon, "~> 0.6"},
  {:kino, "~> 0.16.0"},
  {:kino_vega_lite, "~> 0.1.9"}
])

単層パーセプトロン

単層パーセプトロンで実装できる AND,OR,NAND を以下は以下のように実装できる。

defmodule Gate do

  @doc """
    ANDゲート
    
    重み: [0.5, 0.5]
    バイアス: -0.7
  """
  def and_gate(x1, x2) do
    x = Nx.tensor([x1, x2])
    w = Nx.tensor([0.5, 0.5])
    b = Nx.tensor(-0.7)
    temp =  Nx.add(Nx.dot(x, w), b)

    if Nx.to_number(temp) > 0 do
      1
    else
      0
    end
  end

  @doc """
    ORゲート
    
    重み: [0.5, 0.5]
    バイアス: -0.3
  """
  def or_gate(x1,x2) do
    x = Nx.tensor([x1, x2])
    w = Nx.tensor([0.5, 0.5])
    b = Nx.tensor(-0.3)
    temp = Nx.add(Nx.dot(x, w), b)

    if Nx.to_number(temp) > 0 do
      1
    else
      0
    end
  end

  @doc """
    ANDゲート
    
    重み: [-0.5, -0.5]
    バイアス: 0.7
  """
  def nand_gate(x1,x2) do
    x = Nx.tensor([x1, x2])
    w = Nx.tensor([-0.5, -0.5])
    b = Nx.tensor(0.7)
    temp =  Nx.add(Nx.dot(x, w), b)

    if Nx.to_number(temp) > 0 do
      1
    else
      0
    end
  end
end
IO.puts "入力: [1, 0], 出力 AND : #{Gate.and_gate(1,0)}"
IO.puts "入力: [0, 1], 出力 OR  : #{Gate.or_gate(0, 1)}"
IO.puts "入力: [1, 1], 出力 NAND: #{Gate.nand_gate(1,1)}"

AND、OR、NANDは 線形分離可能 であるため、単一のパーセプトロンで実装することができる

# AND

points =
  VegaLite.new()
  |> VegaLite.data_from_values([
    %{x1: 0, x2: 0, label: "0"},
    %{x1: 0, x2: 1, label: "0"},
    %{x1: 1, x2: 0, label: "0"},
    %{x1: 1, x2: 1, label: "1"}
  ])
  |> VegaLite.mark(:point)
  |> VegaLite.encode_field(:x, "x1", type: :quantitative)
  |> VegaLite.encode_field(:y, "x2", type: :quantitative)
  |> VegaLite.encode_field(:color, "label", type: :nominal)

line_data = for x <- 0..100, do: %{x1: x / 100, x2: 1.0 - x / 100}

line =
  VegaLite.new()
  |> VegaLite.data_from_values(line_data)
  |> VegaLite.mark(:line)
  |> VegaLite.encode_field(:x, "x1", type: :quantitative)
  |> VegaLite.encode_field(:y, "x2", type: :quantitative)

VegaLite.new()
|> VegaLite.layers([points, line])


# OR

points =
  VegaLite.new()
  |> VegaLite.data_from_values([
    %{x1: 0, x2: 0, label: "0"},
    %{x1: 0, x2: 1, label: "1"},
    %{x1: 1, x2: 0, label: "1"},
    %{x1: 1, x2: 1, label: "1"}
  ])
  |> VegaLite.mark(:point)
  |> VegaLite.encode_field(:x, "x1", type: :quantitative)
  |> VegaLite.encode_field(:y, "x2", type: :quantitative)
  |> VegaLite.encode_field(:color, "label", type: :nominal)

VegaLite.new()
|> VegaLite.layers([points, line])
# NAND

points =
  VegaLite.new()
  |> VegaLite.data_from_values([
    %{x1: 0, x2: 0, label: "1"},
    %{x1: 0, x2: 1, label: "0"},
    %{x1: 1, x2: 0, label: "0"},
    %{x1: 1, x2: 1, label: "0"}
  ])
  |> VegaLite.mark(:point)
  |> VegaLite.encode_field(:x, "x1", type: :quantitative)
  |> VegaLite.encode_field(:y, "x2", type: :quantitative)
  |> VegaLite.encode_field(:color, "label", type: :nominal)

VegaLite.new()
|> VegaLite.layers([points, line])

たとえばANDを図で表すなら、次のように表すことができ、単層のパーセプトロンで実装することができる。

graph LR
  subgraph 入力層 
    x1[x1]
    x2[x2]
  end

  subgraph 出力層
    y[y: AND]
  end

  x1 --> y
  x2 --> y

多層パーセプトロン

XORは、単層のパーセプトロンでは実装できない。 それは線形で表現できない、非線形だからである。

# XOR

points =
  VegaLite.new()
  |> VegaLite.data_from_values([
    %{x1: 0, x2: 0, label: "0"},
    %{x1: 0, x2: 1, label: "1"},
    %{x1: 1, x2: 0, label: "1"},
    %{x1: 1, x2: 1, label: "0"}
  ])
  |> VegaLite.mark(:point)
  |> VegaLite.encode_field(:x, "x1", type: :quantitative)
  |> VegaLite.encode_field(:y, "x2", type: :quantitative)
  |> VegaLite.encode_field(:color, "label", type: :nominal)

VegaLite.new()
|> VegaLite.layers([points, line])

XORの実装は、NAND、OR、ANDを組み合わせることで実装ができる

defmodule GateExtra do
  def xor_gate(x1, x2) do
    s1 = Gate.nand_gate(x1, x2)
    s2 = Gate.or_gate(x1, x2)
    Gate.and_gate(s1, s2)
  end
end

y = GateExtra.xor_gate(1,1)
IO.puts y

XORの実装を図で表すと以下のようになる。

graph LR
  subgraph 入力層 
    x1[x1]
    x2[x2]
  end

  subgraph 中間層
    s1[s1: NAND]
    s2[s2: OR]
  end

  subgraph 出力層
    y[y: OR]
  end

  x1 --> s1
  x2 --> s1

  x1 --> s2
  x2 --> s2

  s1 --> y
  s2 --> y

XORは、中間層でNANDとORを計算し、出力層でANDすることで実装できる 2層の 多層パーセプトロンということになる。