R) DL - 선형모델-02

R) DL - 선형모델-02

CRAN RStudio mirror downloads
ggplot2 패키지의 diamonds 데이터를 활용하여 다이아몬드 가격을 예측하는 다층 신경망 모델을 생성해본다.

개요

많은 딥러닝 예제가 임의의 데이터를 생성하고 모델링을 하는데 그치는 경우가 많다. 여기서는 테이블 형식의 ggplot2 패키지의 diamonds 데이터를 사용하고자 한다. 데이터 전처리 부터 모델 구동 및 손실 평가까지 진행하고자 한다.

환경설정

데이터 준비를 위해서 ggplot2, factDummies, caTools 를 불러오고 신경망 학습을 위해서 torch 를 불러온다. 그리고 오차를 시각적으로 확인하기 위해 ggplot2 를 활용한다. 추가로 사용자 정의 함수 minmax()는 정규화를 위해 지정하였다.

1
2
3
4
5
6
7
8
library("ggplot2")
library("fastDummies")
library("caTools")
library("torch")

minmax = function(x){
return((x - min(x)) /(max(x) - min(x)))
}

데이터 준비

ggplot2 패키지의 diamonds 데이터는 다음과 같다.

1
2
3
4
5
6
7
8
9
df = as.data.frame(diamonds)
head(df)
## carat cut color clarity depth table price x y z
## 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43
## 2 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31
## 3 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31
## 4 0.29 Premium I VS2 62.4 58 334 4.20 4.23 2.63
## 5 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75
## 6 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48

각 변수의 속성을 확인한다 다음 코드 관련한 상세 동작 설명 포스팅은 아래 링크 참고
※ 변수 속성 확인 포스팅 바로가기 -> [클릭]

1
2
3
4
5
6
df_classes = unlist(lapply(sapply(df, FUN = "class"), FUN = "[", 1))
df_classes
## carat cut color clarity depth
## "numeric" "ordered" "ordered" "ordered" "numeric"
## table price x y z
## "numeric" "integer" "numeric" "numeric" "numeric"

변수 속성이 순서형(ordered factor)이면 가변수 변환 함수가 제대로 동작하지 않기 때문에 다음과 같이 순서형 변수를 모두 문자형으로 변환해주었다.

1
2
3
4
5
6
7
pos_ordered = which(df_classes == "ordered")
df[, pos_ordered] = sapply(df[, pos_ordered], FUN = "as.character")
unlist(lapply(sapply(df, FUN = "class"), FUN = "[", 1))
## carat cut color clarity depth
## "numeric" "character" "character" "character" "numeric"
## table price x y z
## "numeric" "integer" "numeric" "numeric" "numeric"

물론 순서형 변수는 가변수 변환이 아니라 수치형으로 변환하여 활용해도 되는데 그 때는 as.numeric() 함수를 적용하면 되나 일단 factDummies 패키지의 dummy_cols() 함수로 가변수 변환을 해주었다. 여기서 가변수 변환 대상이 되는 변수의 제거를 쉽게 하기 위해 remove_select_columns = TRUE 로 지정해주었다. 다음으로는 신경망 학습을 보다 원활하게 하기 위해 사전에 정의한 minmax() 함수로 정규화를 해줬는데 이 과정을 생략하면 신경망 학습시 에러가 발생하거나 멈출 수 있으니(torch 패키지 버전이 높아지면 해결 될 수 있음) 주의하도록 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
df_dum = dummy_cols(df, 
remove_first_dummy = TRUE,
remove_selected_columns = TRUE)
df_dum = apply(X = df_dum, MARGIN = 2, FUN = "minmax")
head(df_dum, 2)
## carat depth table price x y z cut_Good cut_Ideal
## [1,] 0.006237006 0.5138889 0.2307692 0 0.3677840 0.06757216 0.07641509 0 1
## [2,] 0.002079002 0.4666667 0.3461538 0 0.3621974 0.06519525 0.07264151 0 0
## cut_Premium cut_Very Good color_E color_F color_G color_H color_I color_J clarity_IF
## [1,] 0 0 1 0 0 0 0 0 0
## [2,] 1 0 1 0 0 0 0 0 0
## clarity_SI1 clarity_SI2 clarity_VS1 clarity_VS2 clarity_VVS1 clarity_VVS2
## [1,] 0 1 0 0 0 0
## [2,] 1 0 0 0 0 0

데이터 분할 절차는 다음과 같이 한다. caTools 패키지를 활용하였으나 보다 간단하게 하려면 단순 sample() 함수를 사용해도 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
set.seed(123)
split_logical = sample.split(df_dum[, 1], SplitRatio = 0.8)
head(split_logical)
## [1] TRUE FALSE FALSE TRUE TRUE TRUE

df_dum_train_x = df_dum[split_logical == TRUE, -4] # without price
df_dum_train_y = df_dum[split_logical == TRUE, 4] # price
df_dum_test = df_dum[split_logical == FALSE, ]
head(df_dum_train_x, 2)
## carat depth table x y z cut_Good cut_Ideal
## [1,] 0.006237006 0.5138889 0.2307692 0.3677840 0.06757216 0.07641509 0 1
## [2,] 0.018711019 0.5388889 0.2884615 0.3910615 0.07181664 0.08270440 0 0
## cut_Premium cut_Very Good color_E color_F color_G color_H color_I color_J clarity_IF
## [1,] 0 0 1 0 0 0 0 0 0
## [2,] 1 0 0 0 0 0 1 0 0
## clarity_SI1 clarity_SI2 clarity_VS1 clarity_VS2 clarity_VVS1 clarity_VVS2
## [1,] 0 1 0 0 0 0
## [2,] 0 0 0 1 0 0

하이퍼파라미터 설정

D_in 객체는 입력되는 변수의 개수로 지정하였다.
D_out 객체는 출력되는 값이 price 하나여서 1로 지정하였다.

1
2
3
4
5
6
7
8
9
device = torch_device("cpu") # cuda

D_in = 23
H = 10
D_out = 1
learning_rate = 1e-3 # 1/1000

loss_fn = nnf_mse_loss # MSE
optimizer = optim_sgd # SGD Optimizer

하이퍼파라미터 설정에 맞춰 데이터 세트를 tensor로 변환한 결과는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
tensor_train_x = torch_tensor(data = as.matrix(df_dum_train_x),
device = device,
requires_grad = TRUE)
tensor_train_y = torch_tensor(data = as.matrix(df_dum_train_y),
device = device,
requires_grad = FALSE)
dim(tensor_train_x)
## [1] 43152 23
dim(tensor_train_y)
## [1] 43152 1

신경망 구조 정의

두 개의 레이어를 사용하며 각 레이어의 입출력은 udf_init() 함수에서 정의하고 신경망 구조는 udf_str() 함수에서 정의한다. 두 레이어 사이에 위치하는 활성함수(activation function)은 ReLU로 지정하였다.

1
2
3
4
5
6
7
8
9
10
11
udf_init = function(D_in, H, D_out) {
self$layer_01 = nn_linear(in_features = D_in, out_features = H)
self$layer_02 = nn_linear(in_features = H, out_features = D_out)
}

udf_str = function(input) {
input %>%
self$layer_01() %>%
nnf_relu() %>%
self$layer_02()
}

동일한 초기값을 위해 torch_manual_seed() 함수를 활용했고 최종 신경망 객체 model 를 생성하였다.

1
2
3
4
5
6
torch_manual_seed(123)
net_layer_02 = nn_module(classname = "two_layer_net",
initialize = udf_init,
forward = udf_str)

model = net_layer_02(D_in, H, D_out)

학습 및 평가

최적화 설정을 하고 200 스텝 마다 중간 결과가 출력되도록 하였다. 그리고 손실은 loss 객체에 모두 저장하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
optimizer = optimizer(model$parameters, lr = learning_rate)

loss_vec = c()
step = 2000
for(n_step in seq_len(step)){
# n_step = 1
y_pred = model(tensor_train_x)

loss = loss_fn(y_pred, tensor_train_y)
loss_vec = c(loss_vec, as.numeric(loss))
if(n_step %% 200 == 0){
cat("Step:", n_step, ":", as.numeric(loss), "\n")
}

optimizer$zero_grad()
loss$backward()
optimizer$step()
}
## Step: 200 : 0.05661283
## Step: 400 : 0.05305122
## Step: 600 : 0.05175806
## Step: 800 : 0.05105871
## Step: 1000 : 0.05053329
## Step: 1200 : 0.05007544
## Step: 1400 : 0.04965617
## Step: 1600 : 0.04926623
## Step: 1800 : 0.04890149
## Step: 2000 : 0.04855897

학습에 따른 손실 그래프를 그려보면 다음과 같다.

1
2
3
4
5
6
7
df_loss = data.frame(step = 1:step,
loss = loss_vec)
ggplot(data = df_loss,
aes(x = step,
y = loss)) +
geom_line() +
theme_bw()

학습에 따른 손실 그래프

정규화 이전의 자료 비교, 평가 데이터 세트 확인, 학습률, 최적화 함수 등 다양한 옵션은 별도의 포스팅에서 다룰 예정이다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×