R) DL - 선형모델-01

R) DL - 선형모델-01

CRAN RStudio mirror downloads
가장 간단한 단층 신경망을 생성하여 선형 모델을 돌려보자. 이 코드를 더 발전시키면 선형회귀분석을 할 수 있으니 기초 쌓기에 좋은 내용이 되겠다.

개요

여기서 구현할 내용은 정확하게는 단층 퍼셉트론 구조를 가지는 신경망이다. 이는 다원 일차 방정식과 같으며 단순 선형 모델이다. 여기서 입력층(input layer)은 학습 벡터 또는 입력 벡터가 입력되는 계층이고, 출력층(output layer)은 1개 이상의 뉴런으로 구성할 수 있으며 뉴런의 개수에 따라 출력값의 개수가 정해진다.
단층 퍼셉트론 구조

데이터 준비

torch 패키지를 불러오고 10x4 행렬을 생성한다. 숫자를 고정하기 위해 torch_manual_seed() 함수를 사용하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
library("torch")

torch_manual_seed(123)
x = torch_randn(10, 4)
x
## torch_tensor
## 0.3374 -0.1778 -0.3035 -0.5880
## 0.3486 0.6603 -0.2196 -0.3792
## 0.7671 -1.1925 0.6984 -1.4097
## 0.1794 1.8951 0.4954 0.2692
## -0.0770 -1.0205 -0.1690 0.9178
## 1.5810 1.3010 1.2753 -0.2010
## 0.9624 0.2492 -0.4845 -2.0929
## -0.8199 -0.4210 -0.9620 1.2825
## -0.3430 -0.6821 -0.9887 -1.7018
## -0.7498 -1.1285 0.4135 0.2892
## [ CPUFloatType{10,4} ]

신경망 구조 정의

먼저 is_torch_tensor() 함수와 nn_parameter() 함수를 정의해주는데 원 코드 출처를 각각 표기해놓았다. 이는 torch 0.1.0 이상을 사용한다면 굳이 정의하지 않아도 되기 때문에 이 코드 블럭은 무시해도 관계 없다. 참고로 패키지 버전 확인은 다음과 같이 packageVersion() 함수를 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
packageVersion("torch")
## [1] ‘0.0.3’

# https://github.com/mlverse/torch/blob/master/R/tensor.R
is_torch_tensor <- function(x){
inherits(x, "torch_tensor")
}

# https://github.com/mlverse/torch/blob/master/R/nn.R
# x `requires_grad = TRUE` tells that we are going to take derivatives over it.
nn_parameter = function(x, requires_grad = TRUE){
if (!is_torch_tensor(x))
stop("`x` must be a tensor.")
x$requires_grad_(requires_grad)
class(x) = c(class(x), "nn_parameter")
x
}

신경망의 입출력을 지정하기 위해서 udf_init() 함수를 생성한다. 초기값은 임의값을 생성하는 torch_randn() 함수와 0을 생성하는 torch_zeros() 함수를 사용하였다. 그리고 udf_mm() 함수는 신경망의 연산을 담당하는데 torch_mm() 함수가 matrix multiplication 으로 행렬곱을 수행한다. 즉, 입력된 데이터를 한 줄씩 읽어 self$w (가중치)와 곱연산을 하고 self$b 와 합연산을 하는 것이다. 그리고 이 모든 사항을 dense 객체에 저장한다.

1
2
3
4
5
6
7
8
9
10
11
12
udf_init = function(in_features, out_features) {
self$w = nn_parameter(torch_randn(in_features, out_features))
self$b = nn_parameter(torch_zeros(out_features))
}

udf_mm = function(x){
torch_mm(x, self$w) + self$b # matrix multiplication
}

dense = nn_module(clasname = "dense",
initialize = udf_init,
forward = udf_mm)

하이퍼파라미터 설정

모델에 입력되는 숫자는 4개, 출력은 1개로 각각 in_featuresout_features 인자에 지정하였다. 신경망을 생성하면서 첫 줄에 torch_manual_seed() 함수를 사용하였는데 이는 udf_init() 함수에서 torch_randn()을 사용하였기 때문에 고정해주지 않으면 다른 값이 생성되기 때문이다. 그리고 생성된 값은 model$parameters 로 볼 수 있으며 가중치와 절편을 개별로 보고자 할 경우 model$wmodel$b로 각각 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
torch_manual_seed(123)
model = dense(in_features = 4,
out_features = 1)

model$parameters
## $w
## torch_tensor
## -0.1115
## 0.1204
## -0.3696
## -0.2404
## [ CPUFloatType{4,1} ]
##
## $b
## torch_tensor
## 0
## [ CPUFloatType{1} ]

# model$w
# model$b

학습

이제 데이터와 모델이 다 준비되었으니 데이터를 통과시킬 일만 남은 셈이다. 이 부분은 학습이라고 쓰긴 했지만 학습이라기 보다는 지정된 4원 1차 방정식에 숫자를 흘려보내는 그 이상도 이하도 아니다. y_pred_tensor 객체에 입력 데이터 10개 row에 대한 출력값 10개를 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
y_pred_tensor = model(x)
y_pred_tensor
## torch_tensor
## 0.4048
## -0.3029
## -0.1744
## -2.0058
## 1.1118
## -2.6254
## 0.0603
## 1.7009
## 1.4871
## 0.4269
## [ CPUFloatType{10,1} ]

평가

앞에서 생성한 출력물을 기본 R 객체로 변환하여 행렬 연산을 해보고 신경망으로 연산한 것과 비교해보면 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
data_input = as.matrix(as_array(x))
model_weights = as.matrix(as_array(model$w))
model_bias = as.numeric(as_array(model$b))

y_pred_mat = data_input %*% model_weights + model_bias
y_pred_mat
## [,1]
## [1,] 0.40476032
## [2,] -0.30291313
## [3,] -0.17439960
## [4,] -2.00582914
## [5,] 1.11181484
## [6,] -2.62543551
## [7,] 0.06034881
## [8,] 1.70089479
## [9,] 1.48705762
## [10,] 0.42688437

library("Metrics")
rmse(actual = as_array(y_pred_tensor),
predicted = y_pred_mat)
## [1] 4.319239e-08

RMSE로 비교한 결과 4.319239e-08 로 거의 0에 가까워 두 절차의 차이는 거의 없다고 볼 수 있다. 하지만 신경망이 더 복잡해진다면 차이가 얼마나 벌어질지는 확실하지 않다.

Your browser is out-of-date!

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

×