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]]
)
概要
- ゼロから作る Deep Learning ―Python で学ぶディープラーニングの理論と実装の内容を Elixir で学ぶ
資料
- https://hexdocs.pm/nx
- Nxで始めるゼロ作DL 3章 ニューラルネットワーク by the_haigo
- ゼロ作DLをElixirで学ぶ 3章 ニューラルネットワーク by Yoosuke
ニューラルネットワーク
- 入力層、中間層、出力層から構成される
- 層と層の間の重みは、ニューロン同士のつながりの強さを示す
- 分類問題と回帰問題の両方に用いることができる
分類問題
- 入力データから何かを分類する問題
- 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, &Nx.greater(&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)
ソフトマックス関数
- ニューラルネットワークの最後の活性化関数としてよく用いられる
-
各成分は区間
(0, 1)
に収まり、全ての成分の和が1
になるため、確率として解釈できるようになる - https://ja.wikipedia.org/wiki/ソフトマックス関数
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