Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Deep Learning from zero - Neural network

dl_from_zero_03_neural_network.livemd

Deep Learning from zero - Neural network

import IEx.Helpers

Mix.install(
  [
    {:nx, "~> 0.4.0"},
    {:exla, "~> 0.4.0"},
    {:kino_vega_lite, "~> 0.1.7"}
  ],
  config: [nx: [default_backend: EXLA.Backend]]
)

概要

資料

ニューラルネットワーク

  • 入力層、中間層、出力層から構成される
  • 層と層の間の重みは、ニューロン同士のつながりの強さを示す
  • 分類問題と回帰問題の両方に用いることができる

分類問題

  • 入力データから何かを分類する問題
  • NNの出力層の活性化関数をソフトマックス関数にする

回帰問題

  • 入力データから連続的数値を予測する問題
  • NNの出力層の活性化関数を恒等関数にする

出力層のニューロンの数

  • 出力層のニューロンの数は解くべき問題に応じて決める
  • 分類を行う問題では、出力層のニューロンの数は分類したいクラスの数に設定する

活性化関数

> 古典的にはステップ関数が提案されたのだが、他にもいろいろと考えることはできるので、1986年のバックプロパゲーションの発表以降はシグモイド関数が最も一般的だったが、現在はReLU(ランプ関数)の方が良いと言われる。

https://ja.wikipedia.org/wiki/活性化関数

Nx.tensorの比較

# WRONG!!!
false = Nx.tensor(-1) < 0

# https://hexdocs.pm/nx/Nx.html#greater/2
# https://hexdocs.pm/nx/Nx.Defn.Kernel.html#%3E/2
Nx.less(Nx.tensor(-1), 0)
# WRONG!!!
true = Nx.tensor(-1) > 0

# https://hexdocs.pm/nx/Nx.html#greater/2
# https://hexdocs.pm/nx/Nx.Defn.Kernel.html#%3E/2
Nx.greater(Nx.tensor(-1), 0)

等差数列

arange = fn start, stop, step ->
  how_many = trunc((stop - start) / step)

  Nx.iota({how_many})
  |> Nx.multiply(step)
  |> Nx.add(start)
end

arange.(-3, 3, 0.5)

ステップ関数(階段関数)

  • 0より上なら1それ以外は0となる
  • Nxで各要素に処理を行う場合はNx.mapを使用
step_fun = fn tensor ->
  Nx.map(tensor, &amp;Nx.greater(&amp;1, 0))
end

x = arange.(-5.0, 5.0, 0.1)
y = step_fun.(x)

data_to_plot = %{
  x: Nx.to_flat_list(x),
  y: Nx.to_flat_list(y)
}
VegaLite.new(width: 600, height: 400, title: "Step function")
|> VegaLite.data_from_values(data_to_plot, only: ["x", "y"])
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "x", type: :quantitative)
|> VegaLite.encode_field(:y, "y", type: :quantitative)

シグモイド関数

x = arange.(-5.0, 5.0, 0.1)
y = Nx.sigmoid(x)

data_to_plot = %{
  x: Nx.to_flat_list(x),
  y: Nx.to_flat_list(y)
}
VegaLite.new(width: 600, height: 400, title: "Sigmoid function")
|> VegaLite.data_from_values(data_to_plot, only: ["x", "y"])
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "x", type: :quantitative)
|> VegaLite.encode_field(:y, "y", type: :quantitative)

ReLU関数

relu_fun = fn tensor -> Nx.max(tensor, 0) end

x = arange.(-5.0, 5.0, 0.1)
y = relu_fun.(x)

data_to_plot = %{
  x: Nx.to_flat_list(x),
  y: Nx.to_flat_list(y)
}
VegaLite.new(width: 600, height: 400, title: "ReLU activation function")
|> VegaLite.data_from_values(data_to_plot, only: ["x", "y"])
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "x", type: :quantitative)
|> VegaLite.encode_field(:y, "y", type: :quantitative)

ソフトマックス関数

softmax_fun = fn x ->
  a = Nx.tensor(x)
  c = a |> Nx.flatten() |> Nx.to_flat_list() |> Enum.max()
  e = Nx.subtract(a, c) |> Nx.exp()
  Nx.divide(e, Nx.sum(e))
end

x = arange.(-5, 5, 0.1)
y = softmax_fun.(x)

data_to_plot = %{
  x: Nx.to_flat_list(x),
  y: Nx.to_flat_list(y)
}

[0.3, 2.9, 4.0] |> softmax_fun.() |> Nx.sum() |> dbg()

:ok
VegaLite.new(width: 600, height: 400)
|> VegaLite.data_from_values(data_to_plot, only: ["x", "y"])
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "x", type: :quantitative)
|> VegaLite.encode_field(:y, "y", type: :quantitative)

恒等関数

  • 入力した値に等しい値を返す関数
identity_fun = fn x -> x end

x = arange.(-5, 5, 0.1)
y = identity_fun.(x)

data_to_plot = %{
  x: Nx.to_flat_list(x),
  y: Nx.to_flat_list(y)
}
VegaLite.new(width: 600, height: 400)
|> VegaLite.data_from_values(data_to_plot, only: ["x", "y"])
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "x", type: :quantitative)
|> VegaLite.encode_field(:y, "y", type: :quantitative)

3層ニューラルネットワークの各層における信号伝達

  • xは入力、wは重み、bはバイアス
defmodule ThreeLayerNetwork do
  def init do
    %{
      w1:
        Nx.tensor([
          [0.1, 0.3, 0.5],
          [0.2, 0.4, 0.6]
        ]),
      b1:
        Nx.tensor([
          [0.1, 0.2, 0.3]
        ]),
      w2:
        Nx.tensor([
          [0.1, 0.4],
          [0.2, 0.5],
          [0.3, 0.6]
        ]),
      b2:
        Nx.tensor([
          [0.1, 0.2]
        ]),
      w3:
        Nx.tensor([
          [0.1, 0.3],
          [0.2, 0.4]
        ]),
      b3:
        Nx.tensor([
          [0.1, 0.2]
        ])
    }
  end

  def forward(network, x) do
    {w1, w2, w3} = {network.w1, network.w2, network.w3}
    {b1, b2, b3} = {network.b1, network.b2, network.b3}

    x
    |> Nx.dot(w1)
    |> Nx.add(b1)
    |> Nx.sigmoid()
    |> Nx.dot(w2)
    |> Nx.add(b2)
    |> Nx.sigmoid()
    |> Nx.dot(w3)
    |> Nx.add(b3)
    |> dbg()
  end
end

x = Nx.tensor([1.0, 0.5])
y = ThreeLayerNetwork.init() |> ThreeLayerNetwork.forward(x)

:ok