logo

줄리아 플럭스에서 MLP 구현하고 MNIST 학습하는 방법 📂머신러닝

줄리아 플럭스에서 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

이제 데이터와 레이블을 묶어서 훈련 집합을 만들자. 다음의 두 가지 방법이 있다.

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

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

(X, Y)를 n번 반복해서 붙인다. 따라서 n=10으로 두고 데이터 셋을 만들어 학습시키면 10epoch만큼 학습하는 것과 같다.

  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
  • Version: Julia 1.7.1, Flux 0.12.8, MLDatasets 0.5.14