줄리아 플럭스에서 MLP 구현하고 경사하강법으로 최적화하는 방법

줄리아 플럭스에서 MLP 구현하고 경사하강법으로 최적화하는 방법

How to Implement MLP and Optimize it using Gradient Descent in Flux

MLP 구현

우선 줄리아의 머신러닝 패키지인 Flux.jl와 옵티마이저 업데이트 메소드 update!를 불러오자.

using Flux
using Flux: update!

Dense() 함수로 선형층을 쓸 수 있다. Chain() 함수로 선형층을 쌓을 수 있다. 케라스와 파이토치에서 Sequential()과 같은 기능을 한다.

julia> model = Chain(
           Dense(10, 5, relu),
           Dense(5, 5, relu),
           Dense(5, 2)
           )
Chain(
  Dense(10, 5, relu),                   # 55 parameters
  Dense(5, 5, relu),                    # 30 parameters
  Dense(5, 2),                          # 12 parameters
)                   # Total: 6 arrays, 97 parameters, 772 bytes.

이제 임의의 두 벡터를 만들자.

x, y = randn(Float32,10), randn(Float32,2) # Dummy data

그러면 우리가 원하는 것은 모델이 입력 $\mathbf{x}$를 받아서 출력으로 $\mathbf{y}$를 내놓는 것이다. 즉 다음의 식이 성립하는 것이다.

$$ \begin{equation} \mathbf{y} = \text{model}(\mathbf{x}) \end{equation} $$

지금 모델의 가중치는 이와는 무관하게 초기화되어있으므로 당연히 $\mathbf{x}$를 입력으로 받아서 $\mathbf{y}$를 출력으로 내놓지 못한다. 손실함수를 정의해서 확인해보자.

julia> loss(x,y) = sum((model(x) .- y).^2)
loss (generic function with 1 method)

julia> loss(x,y)
2.1870441f0

경사하강법을 통한 최적화1

이제 $(1)$이 성립하도록 경사하강법을 통해 모델의 파라매터를 수정할 것이다. 우선 모델의 파라매터를 $\theta$, 학습률을 $\eta$라고 두자.

julia> θ = Flux.params(model) #Parameters of model
Params([Float32[-0.29360774 0.19699441 … 0.14506716 0.0025551221; -0.49602875 -0.16636823 … 0.5430107 -0.6045276; … ; -0.29023308 -0.092979304 … -0.32037085 0.5427146; -0.2713689 0.17020355 … 0.31819585 -0.15343323], Float32[0.0, 0.0, 0.0, 0.0, 0.0], Float32[0.7170611 -0.029388033 … -0.74007404 -0.6452301; 0.4532911 -0.020822287 … 0.13228391 -0.2621395; … ; -0.16696058 -0.3569812 … 0.50665516 0.68468684; 0.19336864 -0.7220591 … 0.66947246 0.5213879], Float32[0.0, 0.0, 0.0, 0.0, 0.0], Float32[-0.57012194 -0.12291523 … -0.22337069 -0.54895186; 0.45517293 0.5325932 … -0.16550031 -0.15918007], Float32[0.0, 0.0]])

julia> η = 0.1 # Learning Rate
0.1

gradient()로 그래디언트를 계산하고, update! 메서드로 파라매터를 수정할 수 있다. 로스가 $10^{-7}$보다 작아질 때까지 최적화해보면 다음과 같다.

julia> i=1
1

julia> @time while loss(x,y) > 0.0000001
           grads = gradient(() -> loss(x, y), θ)

           for param in θ
               update!(param, η * grads[param])
           end

           print("epoch_",i, " loss=",loss(x,y), "\n")
           i+=1
       end
epoch_1 loss=0.2048448
epoch_2 loss=0.5493529
.
.
.
epoch_90 loss=1.0927481e-7
epoch_91 loss=9.224256e-8
  0.136134 seconds (150.85 k allocations: 8.681 MiB, 66.42% compilation time)

다음의 코드는 위의 경우와 같은 기능을 한다.

opt = Descent(0.1) # Gradient descent with learning rate 0.1

i=1
@time while loss(x,y) > 0.0000001
    grads = gradient(() -> loss(x, y), θ)
    for param in θ
        update!(opt, param, grads[param])
    end
    print("epoch_",i, " loss=",loss(x,y), "\n")
    i+=1
end

실제로 $\mathbf{y}$와 $\text{model}(\mathbf{x})$의 값을 비교해보면 거의 차이가 없음을 알 수 있다.

julia> y
2-element Vector{Float32}:
  0.8913109
 -1.4473413

julia> model(x)
2-element Vector{Float32}:
  0.891474
 -1.4475975

코드 전문

## Load package
using Flux
using Flux: update!

## Define model as MLP
model = Chain(
    Dense(10, 5, relu),
    Dense(5, 5, relu),
    Dense(5, 2)
    )

# Create dummy data and label
x, y = randn(Float32,10), randn(Float32,2)

# Define loss function as MSE
loss(x,y) = sum((model(x) .- y).^2)

# Get parameters of model and set learning rate
θ = Flux.params(model) #Parameters of model
η = 0.1

# Learning using gradient descent
i=1
@time while loss(x,y) > 0.0000001
    grads = gradient(() -> loss(x, y), θ)
    for param in θ
        update!(param, η * grads[param])
    end
    print("epoch_",i, " loss=",loss(x,y), "\n")
    i+=1
end

환경

  • OS: Windows10
  • Version: Julia 1.6.2, Flux 0.12.8

  1. https://fluxml.ai/Flux.jl/stable/training/optimisers/#Optimisers ↩︎

댓글