Julia言語でニューラルネットワークを理解する
はじめに
Julia言語は自然科学とも機械学習とも相性が良く, それらの組み合わせにおいて絶大な威力を発揮します. ここではJulia言語の機械学習パッケージと数式処理システムを組み合わせることで, ブラックボックスとして扱われがちなニューラルネットワーク(neural network)の関数形を明示的に書き下して理解していきましょう.
パッケージ
下記のパッケージをインストールしておきます.
import Pkg
Pkg.add("Symbolics")
Pkg.add("CairoMakie")
Pkg.add("Flux")
Pkg.add("Lux")
以降はJulia言語の機械学習パッケージであるFlux.jlとLux.jlそれぞれを用いて機械学習モデル(ここではニューラルネットワーク)を構築し, Symbolics.jlで作成したオブジェクトを代入することで, その関数形を確認します. Flux.jlとLux.jlについてはこちらの記事を, Symbolics.jlについてはこちらの記事を参照してください.
Symbolic.jlの使い方
Symbolics.jlは @variables x
のように作成したオブジェクト x
の計算結果をLaTeXで綺麗に表示してくれます. この x
をニューラルネットワークに代入すれば, その関数形をLaTeXで表示できます.
using Symbolics
@variables x
simplify(exp(x) * exp(2x))
Flux.jlによるニューラルネットワークの構築とその関数形の確認
永井 佑紀 著『Juliaではじめる数値計算入門』(技術評論社, 2024) 5-5. ニューラルネットワークによる関数近似 のコードを参考に, 次のような隠れ層1層の全結合型(fully-connected)ニューラルネットワークを構築します.
ここでは関数形をシンプルにするために活性化関数(activation function)に正弦関数sin
を採用しました. 普通はsin
ではなくFlux.relu
やFlux.sigmoid
などが使われます.
# Flux.jlの読み込み
using Flux
# Flux.jlによるモデルの構築
model = Flux.Chain(
x -> [x], # 引数は1行n列の行列が前提なので実数から変換しておく
Flux.Dense(1, 2, sin), # 入力層 -> 隠れ層 (2ユニット)
Flux.Dense(2, 1), # 隠れ層 -> 出力層
x -> sum(x) # 1行n列の行列が出力されるので実数に直す
)
# 関数形の確認
model(x)
ニューラルネットワークの関数形が確認できましたが, ウェイトの初期値は乱数によって決定されており, バイアスの初期値は0になっていることに注意してください.
model[2].bias
# 2-element Vector{Float32}:
# 0.0
# 0.0
ウェイトとバイアスを次のように指定すると関数形を確認しやすくなります. 例えば, ウェイトは行列なので層と行と列の3桁の数字で 323
で 212
と表します. バイアスはベクトルなので2桁の数字で 31
と表します.
model[2].weight[1,1] = 211
model[2].weight[2,1] = 221
model[2].bias[1] = 21
model[2].bias[2] = 22
model[3].weight[1,1] = 311
model[3].weight[1,2] = 312
model[3].bias[1] = 31
model(x)
富谷 昭夫, 橋本 幸士, 金子 隆威, 瀧 雅人, 広野 雄士, 唐木 田亮, 三内 顕義 著, 橋本 幸士 編『学習物理学入門』(朝倉書店, 2024) A2. ニューラルネットワーク(NN)の記号に合わせると, 次のような関数形であることが確認できています.
ただし
@show model(0.0)
@show model(0.5)
@show model(1.0)
# model(0.0) = 288.4383f0
# model(0.5) = 425.71756f0
# model(1.0) = -389.70456f0
電卓で検算してみましょう.
確かに合っています. Juliaだと次のように検算できます.
x = 0
311 * sin(211*x+21) + 312 * sin(221*x+22) + 31
# 288.4382950861074
当然ながら, 次のようにグラフとして書くこともできます. ただし, 上記とはパラメータが違うので注意してください. ここでは
# ニューラルネットワークの構築
using Flux
model = Flux.Chain(
x -> [x], # 引数は1行n列の行列が前提なので実数から変換しておく
Flux.Dense(1, 2, sin), # 入力層 -> 隠れ層 (2ユニット)
Flux.Dense(2, 1), # 隠れ層 -> 出力層
x -> sum(x) # 1行n列の行列が出力されるので実数に直す
)
# ウェイトとバイアスの指定
model[2].weight[1,1] = 1.0
model[2].weight[2,1] = 0.0
model[2].bias[1] = 0.0
model[2].bias[2] = 0.0
model[3].weight[1,1] = 1.0
model[3].weight[1,2] = 0.0
model[3].bias[1] = 0.0
# プロット
using CairoMakie
fig = Figure(size=(420,300), fontsize=11.5, backgroundcolor=:transparent)
axis = Axis(fig[1,1], xlabel=L"$x$", ylabel=L"$y$", xlabelsize=16.5, ylabelsize=16.5)
lines!(axis, 0..5, x -> model(x), label="model")
fig
入力や出力をベクトルにしたり, 各層のユニットを増やすこともできます. もはやブラックボックスとして扱った方が楽ですが, 具体的な数式として書き下すこともできるという実感が重要です.
# Symbolics.jlによるオブジェクトの作成
using Symbolics
@variables x₁, x₂
# Flux.jlによるニューラルネットワークの構築
using Flux
model = Flux.Chain(
Flux.Dense(2, 4, sin), # 入力層 -> 隠れ層 (4ユニット)
Flux.Dense(4, 4, sin), # 隠れ層 -> 隠れ層 (4ユニット)
Flux.Dense(4, 2), # 隠れ層 -> 出力層
)
# ウェイトとバイアスの指定
for i in 1:length(model)
for k in keys(model[i].weight)
model[i].weight[k] = 100i + 10k[1]+ k[2]
end
for j in keys(model[i].bias)
model[i].bias[j] = 10i + j
end
end
# 関数形の確認
model([x₁, x₂])
Lux.jlによるニューラルネットワークの構築とその関数形の確認
Flux.jlからLux.jlへの移植方法についてはこちらの記事を参照してください. 同じ結果が得られます.
# Lux.jlの読み込み
using Lux
using Random
# Flux.jlによるニューラルネットワークの構築
model = Lux.Chain(
x -> [x],
Lux.Dense(1 => 2, sin),
Lux.Dense(2 => 1),
x -> sum(x),
)
# ウェイトとバイアスの指定
rng = Random.MersenneTwister(123)
ps, st = Lux.setup(rng, model)
ps = (
layer_1 = NamedTuple(),
layer_2 = (weight = [211; 221;;], bias = [221, 222]),
layer_3 = (weight = [311 312], bias = [321]),
layer_4 = NamedTuple()
)
# 関数形の確認
using Symbolics
@variables x
first(model(x, ps, st))
まとめ
Flux.jlとLux.jlそれぞれを用いてニューラルネットワークを構築し, Symbolics.jlで作成したオブジェクトを代入することで, その関数形を確認しました. 関数形を明示的に書き下すことによってニューラルネットワークについての理解が深まったのではないでしょうか.
バージョン情報
Julia v1.11.2
Symbolics v6.22.1
CairoMakie v0.12.18
Flux v0.16.0
Lux v1.4.4
参考文献
- 富谷 昭夫, 橋本 幸士, 金子 隆威, 瀧 雅人, 広野 雄士, 唐木 田亮, 三内 顕義 著, 橋本 幸士 編『学習物理学入門』(朝倉書店, 2024) A2. ニューラルネットワーク(NN)
- 永井 佑紀 著『Juliaではじめる数値計算入門』(技術評論社, 2024) 5-5. ニューラルネットワークによる関数近似
Discussion