logo

Julia FluxでMLPを実装し、MNISTで学習する方法 📂機械学習

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つの方法がある。

  1. repeated((X, Y), n)
julia> using Base.Iterators: repeated

julia> Train_Data = repeated((X,Y), 1)

(X, Y)をn回繰り返して付け加える。したがって、n=10に設定してデータセットを作成し、トレーニングすれば、10エポック分のトレーニングと同じになる。

  1. 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()をデータに適用したい場合は、その他の部分を同じに保ちつつ、XChain()を次のように修正すればよい。ただし、速度に大きな違いはない。

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