줄리아 플럭스에서 MLP 구현해서 비선형함수 근사하는 방법

줄리아 플럭스에서 MLP 구현해서 비선형함수 근사하는 방법

How to Implement MLP and Approximate Nonlinear Function in Julia Flux

시작

using Flux
using Plots
using Distributions
using Flux: @epochs

function f(x)
    if -5 ≤ x < -2
        y = -3x + 2
    elseif -2 ≤ x < 1
        y = x + 10
    elseif 1 ≤ x < 2
        y = -2x + 13
    elseif 2 ≤ x < 3
        y = 4x + 1
    elseif 3 ≤ x ≤ 5
        y = -4x + 25
    end
    return y
end

x_ = -5:0.01:5
y_ = f.(x_)
plot(x_, y_)

필요한 패키지를 불러오고, 우리가 근사하고 싶은 비선형함수를 정의하자.

nonlinear_function.png

학습집합 생성

julia> Data = convert.(Float32, rand(Uniform(-5,5), 1024))
1024-element Vector{Float32}:

julia> Data = convert(Matrix, reshape(Data, (1,1024)))
1×1024 Matrix{Float32}:

함수의 정의역인 $[-5, 5]$에서 임의의 점 1024개를 뽑았다. 이렇게 뽑으면 자료형이 Float64인데, 딥러닝에서는 기본적으로 Float32 자료형을 다루므로 바꿔주었다. 물론 자료형이 Float64Int64인 데이터를 모델에 인풋으로 써도 알아서 자료형을 바꾸고 실행하기는 한다.

또한 각각의 열이 하나의 데이터를 의미하기 때문에(이해가 안된다면 행렬의 곱을 생각해보라) 1024-벡터를 1x1024 매트릭스로 바꿔주었다.

Label = f.(Data) 

Train_Set = Flux.DataLoader((Data, Label), batchsize=128)

위에서 정의한 함수 $f$로 레이블을 만들고, Flux.DataLoader로 훈련에 사용할 수 있게 만들었다.

모델 정의

model = Chain(
            Dense(1,5, relu),
            Dense(5,5, relu),
            Dense(5,1)
)

x_input = reshape(x_, (1,1001))
x_input = convert(Matrix{Float32}, x_input)

ŷ = model(x_input)

plot(vec(x_input), vec(ŷ))

Chain()으로 MLP를 만들고, 함수의 그래프를 그려보면 $f$와 전혀 다름을 확인할 수 있다.

initial_MLP.png

손실함수와 옵티마이저 정의

LOSS(x,y) = Flux.mse(model(x), y)
opt = ADAM()

손실함수는 MSE, 옵티마이저는 ADAM으로 정의했다.

훈련

julia> @epochs 5000 @time Flux.train!(LOSS, Flux.params(model), Train_Set, opt, 
cb = Flux.throttle(() -> @show(LOSS(Data, Label)), 1))
[ Info: Epoch 5000
LOSS(Data, Label) = 1.3867694f-6
  0.001494 seconds (3.04 k allocations: 549.953 KiB)

이제 @epochs 매크로로 훈련시킬 수 있다. 5000 에포크를 반복한 후, 로스가 아주 작아진 것을 확인할 수 있다.

MLP의 그래프를 그려보면 다음과 같다.

ŷ = model(x_input)
plot(vec(x_input), vec(ŷ))

trained_MLP.png

코드 전문

using Flux
using Plots
using Distributions
using Flux: @epochs

function f(x)
    if -5 ≤ x < -2
        y = -3x + 2
    elseif -2 ≤ x < 1
        y = x + 10
    elseif 1 ≤ x < 2
        y = -2x + 13
    elseif 2 ≤ x < 3
        y = 4x + 1
    elseif 3 ≤ x ≤ 5
        y = -4x + 25
    end
    return y
end

x_ = -5:0.01:5
y_ = f.(x_)
plot(x_, y_)
savefig("nonlinear_function.png")

Data = convert.(Float32, rand(Uniform(-5,5), 1024))
Data = convert(Matrix, reshape(Data, (1,1024)))
Label = f.(Data) 
Train_Set = Flux.DataLoader((Data, Label), batchsize=128)

model = Chain(
            Dense(1,5, relu),
            Dense(5,5, relu),
            Dense(5,1)
)

x_input = reshape(x_, (1,1001))
x_input = convert(Matrix{Float32}, x_input)
ŷ = model(x_input)
plot(vec(x_input), vec(ŷ))
savefig("initial_MLP.png")

LOSS(x,y) = Flux.mse(model(x), y)
opt = ADAM()

@epochs 5000 @time Flux.train!(LOSS, Flux.params(model), Train_Set, opt, cb = Flux.throttle(() -> @show(LOSS(Data, Label)), 1))

ŷ = model(x_input)
plot(vec(x_input), vec(ŷ))
savefig("trained_MLP.png")

환경

  • OS: Windows10
  • Version: Julia 1.7.1, Flux 0.12.8
댓글