줄리아 플럭스에서 MLP 구현하고 경사하강법으로 최적화하는 방법
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