flux_mnist¶

FluxML/model-zoo を参考に手書き文字データセット MNIST を学習する.

Load packages¶

In [1]:
using Random

using Flux
using Flux.Data:DataLoader
using MLDatasets
using Images
using ProgressMeter

Setup constants¶

In [2]:
Base.@kwdef mutable struct Args
    η = 3e-4
    λ = 0
    batchsize=128
    epochs=5
    seed=12345
    use_cuda=false
end

args = Args()
Random.seed!(args.seed)
ENV["DATADEPS_ALWAYS_ACCEPT"] = true
Out[2]:
true
In [3]:
xtrain, ytrain = MLDatasets.MNIST.traindata(Float32)
xtest, ytest = MLDatasets.MNIST.testdata(Float32)

xtrain = Flux.unsqueeze(xtrain, 3) # (28, 28, 60000) -> (28, 28, 1 , 60000)
xtest = Flux.unsqueeze(xtest, 3)   # (28, 28, 10000) -> (28, 28, 1, 10000)
ytrain = Flux.onehotbatch(ytrain, 0:9) # (60000,) -> (10, 60000)
ytest = Flux.onehotbatch(ytest, 0:9);   # (10000,) -> (10, 10000)
┌ Warning: MNIST.traindata() is deprecated, use `MNIST(split=:train)[:]` instead.
└ @ MLDatasets ~/.julia/packages/MLDatasets/EXhDV/src/datasets/vision/mnist.jl:187
┌ Warning: MNIST.testdata() is deprecated, use `MNIST(split=:test)[:]` instead.
└ @ MLDatasets ~/.julia/packages/MLDatasets/EXhDV/src/datasets/vision/mnist.jl:195
In [4]:
train_loader = DataLoader((xtrain, ytrain), batchsize=args.batchsize, shuffle=true)
test_loader = DataLoader((xtest, ytest),  batchsize=args.batchsize)
imgs, labels = first(train_loader); # 最初のバッチを取得

Display example¶

In [5]:
example_idx = 2
Gray.(dropdims(imgs[:, :, :, example_idx], dims=3)') # 転置が必要
Out[5]:
In [6]:
struct LeNet
    cnn_layer
    mlp_layer
    nclasses
end

Flux.@functor LeNet (cnn_layer, mlp_layer) # cnn_layer と mlp_layer が学習パラメータであることを指定する

function create_model(imsize::Tuple{Int,Int,Int}, nclasses::Int)
    W, H, inC = imsize
    out_conv_size = (W ÷ 4 - 3, H ÷ 4 - 3, 16)
    cnn_layer = Chain(
        Conv((5, 5), inC => 6, relu),
        MaxPool((2, 2)),
        Conv((5, 5), 6 => 16, relu),
        MaxPool((2, 2))
    )
    mlp_layer = Chain(
        Dense(prod(out_conv_size), 120, relu),
        Dense(120, 84, relu),
        Dense(84, nclasses),
    )
    LeNet(cnn_layer, mlp_layer, nclasses)
end

(net::LeNet)(x) = x |> net.cnn_layer |> Flux.flatten |> net.mlp_layer
In [7]:
model = create_model((28, 28, 1), 10) |> f32
ps = Flux.params(model);
opt = ADAM(args.η)
loss(ŷ, y) = Flux.Losses.logitcrossentropy(ŷ, y)

for e in 1:args.epochs
    @showprogress for (x, y) in train_loader
        gs = Flux.gradient(ps) do
            ŷ = model(x)
            loss(ŷ, y)
        end
        Flux.Optimise.update!(opt, ps, gs)
    end
    acc = sum(Flux.onecold(model(xtest)) .== Flux.onecold(ytest))
    acc /= size(ytest, 2)
    println("acc", 100acc, "%")
end
Progress: 100%|█████████████████████████████████████████| Time: 0:00:37
acc94.81%
Progress: 100%|█████████████████████████████████████████| Time: 0:00:12
acc96.67999999999999%
Progress: 100%|█████████████████████████████████████████| Time: 0:00:12
acc97.69%
Progress: 100%|█████████████████████████████████████████| Time: 0:00:12
acc97.86%
Progress: 100%|█████████████████████████████████████████| Time: 0:00:12
acc98.11%
In [8]:
x_test, y_test = first(test_loader);
img = x_test[:, :, :, 1]
label = Flux.onecold(y_test[:, 1]) - 1
println("pred:", Flux.onecold(model(x_test))[1]-1)
println("actual:", label)
Gray.(dropdims(img, dims=3) |> transpose)
pred:7
actual:7
Out[8]: