Julia FluxでMLPを実装し、MNISTで学習する方法
MNIST データセットをロードする
古い例ではFlux.Data
を使うコードが見られるが、もうフラックスではサポートされていない。
julia> Flux.Data.MNIST.images()
┌ Warning: Flux's datasets are deprecated, please use the package MLDatasets.jl
公式ドキュメント1では`MLDatasets.jl’パッケージを使うように案内している。
julia> using Flux
julia> using MLDatasets
julia> imgs = MLDatasets.MNIST.traintensor()
28×28×60000 reinterpret(FixedPointNumbers.N0f8, ::Array{UInt8, 3})
julia> labs = MLDatasets.MNIST.trainlabels()
60000-element Vector{Int64}
画像やラベルをそのまま使用することはできない。画像はデータ型を32ビット浮動小数点に変更し、ラベルはワンホットエンコーディングを行う必要がある。
julia> X = float.(imgs)
28×28×60000 Array{Float32, 3}
julia> Y = Flux.onehotbatch(labs, 0:9)
10×60000 OneHotMatrix(::Vector{UInt32}) with eltype Bool
今、データとラベルを組み合わせて訓練セットを作成しよう。以下の2つの方法がある。
repeated((X, Y), n)
julia> using Base.Iterators: repeated
julia> Train_Data = repeated((X,Y), 1)
(X, Y)
をn回繰り返して付け加える。したがって、n=10
に設定してデータセットを作成し、トレーニングすれば、10エポック分のトレーニングと同じになる。
Flux.DataLoader((X, Y); batchsize=1, shuffle=false, partial=true, rng=GLOBAL_RNG)
julia> Train_Data = Flux.DataLoader((X,Y), batchsize=60000)
60,000個のデータで一度にトレーニングするため、バッチサイズを60,000に設定した。
MLPの実装
MLPはChain()
関数で実装できる。データ$X$を平らにしていないので、最初の層にFlux.flatten
を追加する必要がある。
julia> MLP = Chain(Flux.flatten,
Dense(28*28, 28, relu),
Dense(28, 10),
softmax)
Chain(
Flux.flatten,
Dense(784, 28, relu), # 21_980 parameters
Dense(28, 10), # 290 parameters
NNlib.softmax,
) # Total: 4 arrays, 22_270 parameters, 87.242 KiB.
なお、flatten()
をデータに適用したい場合は、その他の部分を同じに保ちつつ、X
とChain()
を次のように修正すればよい。ただし、速度に大きな違いはない。
X = flatten(float.(imgs))
julia> MLP = Chain(Dense(28*28, 28, relu),
Dense(28, 10),
softmax)
損失関数とオプティマイザーの定義
損失関数はクロスエントロピーで、オプティマイザーはADAMと定義しよう。
julia> LOSS(x,y) = Flux.crossentropy(MLP(x), y)
LOSS (generic function with 1 method)
julia> LOSS(X,Y)
2.3614984f0
julia> opt = ADAM()
ADAM(0.001, (0.9, 0.999), IdDict{Any, Any}())
トレーニング
train!()
メソッドでモデルをトレーニングする。
julia> Flux.train!(LOSS, params(MLP), Train_Data, opt)
トレーニングプロセスの出力2
しかし、この方法ではトレーニングプロセスを知ることができない。以下のようにコールバックを設定すると、1秒に一回ロスを出力する。
julia> Flux.train!(LOSS, params(MLP), Train_Data, opt, cb = Flux.throttle(() -> @show(LOSS(X, Y)), 1))
LOSS(X, Y) = 2.2364092f0
エポックの設定
@epochs
マクロでエポックを設定することができる。
julia> using Flux: @epochs
julia> @epochs 5 Flux.train!(LOSS, params(MLP), Train_Data, opt, cb = Flux.throttle(() -> @show(LOSS(X, Y)), 1))
[ Info: Epoch 1
LOSS(X, Y) = 2.1822426f0
[ Info: Epoch 2
LOSS(X, Y) = 2.13155f0
[ Info: Epoch 3
LOSS(X, Y) = 2.0831218f0
[ Info: Epoch 4
LOSS(X, Y) = 2.0359747f0
[ Info: Epoch 5
LOSS(X, Y) = 1.9894044f0
トレーニング時間の測定
@time
マクロでトレーニング時間を測定できる。
julia> @epochs 5 @time Flux.train!(LOSS, params(MLP), Train_Data, opt, cb = Flux.throttle(() -> @show(LOSS(X, Y)), 1))
[ Info: Epoch 1
LOSS(X, Y) = 1.9431362f0
0.366819 seconds (238.95 k allocations: 269.987 MiB, 33.97% compilation time)
[ Info: Epoch 2
LOSS(X, Y) = 1.8971274f0
0.300713 seconds (418 allocations: 257.373 MiB, 18.77% gc time)
[ Info: Epoch 3
LOSS(X, Y) = 1.8515143f0
0.247089 seconds (418 allocations: 257.373 MiB)
[ Info: Epoch 4
LOSS(X, Y) = 1.8064859f0
0.284937 seconds (418 allocations: 257.373 MiB, 13.61% gc time)
[ Info: Epoch 5
LOSS(X, Y) = 1.7620988f0
0.258424 seconds (418 allocations: 257.373 MiB)
コード全文
using Flux
using MLDatasets
using Base.Iterators: repeated
using Flux: @epochs
# Loading MNIST data set
imgs = MLDatasets.MNIST.traintensor()
labs = MLDatasets.MNIST.trainlabels()
# Convert to usable
X = float.(imgs)
Y = Flux.onehotbatch(labs, 0:9)
# Making training set
Train_Data = Flux.DataLoader((X,Y), batchsize=60000)
# or Train_Data = repeated((X,Y), 1)
# Define MLP, loss function, optimizer
MLP = Chain(Flux.flatten,
Dense(28*28, 28, relu),
Dense(28, 10),
softmax)
LOSS(x,y) = Flux.crossentropy(MLP(x), y)
opt = ADAM()
# Training
@epochs 5 @time Flux.train!(LOSS, params(MLP), Train_Data, opt, cb = Flux.throttle(() -> @show(LOSS(X, Y)), 5))
環境
- OS: Windows10
- バージョン: Julia 1.7.1, Flux 0.12.8, MLDatasets 0.5.14