이 시리즈는 R로 딥러닝을 구현하고 설명하는 것에 목표를 둔 글입니다. 신경망 챕터에 이어서 3번째 순서입니다. 이전 글과 내용이 이어집니다. 이전 글들은 이곳에서 찾아 보실 수 있습니다.

미니배치 학습

머신러닝에서 모델의 성능을 높이기 위해서는 훈련데이터를 사용합니다. 신경망 모델에서도 마찬가지로 훈련데이터를 이용해 손실 함수 값이 최소화가 되는 가중치와 편향 값을 찾게됩니다. 이 과정에서 모든 훈련데이터에 대한 손실 함수 값의 계산이 필요합니다. 이런 필요에 따라 데이터가 n개일 때도 계산이 가능하게끔 손실 함수를 변경해주어야 합니다. 그 방법은 평균 손실 함수 값을 구하는 것입니다. 방법으로는 계산된 손실 함수 값을 전부 더하고 데이터의 개수로 나누어주는 과정이 추가됩니다.

그러나 MNIST 데이터 셋의 훈련데이터만 하더라도 6만개의 데이터가 있으며, 빅데이터 수준이 되면 이보다 더 큰 경우도 종종 있습니다. 이렇게 훈련 데이터가 클 경우 전부 사용하여 손실 함수를 계산하면 시간이 오래 걸립니다. 따라서 데이터 중 일부를 추출하여 학습을 진행하는데 이를 미니배치 학습이라 합니다. 이제 직접 MNIST 훈련 데이터에서 100개씩 데이터를 추출하여 미니배치 학습을 위한 교차 엔트로피 오차 함수를 구현해보겠습니다.

먼저 링크 에서 다음과 같이 MNIST 데이터 셋을 불러와 데이터 정규화와 라벨의 원-핫 인코딩 과정을 진행해 줍니다. 데이터 셋을 불러오는 과정을 get_data()라는 함수로 처리해 데잍터를 불러와 줍니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
source("./utils.R")
library(dslabs)

mnist_data <- get_data()

making_one_hot_label <- function(t_label,nrow,ncol){
    data <- matrix(FALSE,nrow = nrow,ncol = ncol)
    t_index <- t_label+1
    for(i in 1:NROW(data)){
        data[i, t_index[i]] <- TRUE
    }
    return(data)
}

x_train_normalize <- x_train/255
x_test_normalize <- x_test/255

t_train_onehotlabel <- making_one_hot_label(t_train,60000,10)
t_test_onehotlabel <- making_one_hot_label(t_test,10000,10)

str(x_train_normalize)
str(t_train_onehotlabel)

데이터를 10개를 임의로 뽑을 경우 다음과 같이 할 수 있습니다. R에서는 sample() 함수를 사용하여 범위 내의 임의의 숫자를 n개 뽑을 수 있습니다. MNIST 훈련데이터에서 10개를 뽑아봅시다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
trainsize <- dim(x_train_normalize/255)[1]
> trainsize
[1] 60000
batch_size <- 10
> batch_size
[1] 10
batch_mask <- sample(trainsize,batch_size)
> batch_mask
 [1]  8299 18763 28706 35366 29752 59755 42776 25722 45897 55425
x_batch <- x_train_normalize[batch_mask,]
> str(x_batch)
 num [1:10, 1:784] 0 0 0 0 0 0 0 0 0 0 ...
t_batch <- t_train_onehotlabel[batch_mask,]
> str(t_batch)
 logi [1:10, 1:10] FALSE FALSE FALSE FALSE FALSE FALSE ...

이제 교차 엔트로피 오차 함수에 평균 값 계산을 추가해봅시다. 기존의 계산과정에서 달라지는 것은 데이터의 개수로 나누는 과정 말고는 없습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
y1  <- matrix(c(0.1,0.05,0.6,0,0.05,0.1,0,0.1,0,0), nrow = 1)
y2  <- matrix(y <- c(0.1,0.05,0.1,0,0.05,0.1,0,0.6,0,0), nrow = 1)
t <- matrix(c(0,0,1,0,0,0,0,0,0,0), nrow = 1)
new_y <- rbind(y1,y2)
new_t <- rbind(t,t)
> new_y
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]  0.1 0.05  0.6    0 0.05  0.1    0  0.1    0     0
[2,]  0.1 0.05  0.1    0 0.05  0.1    0  0.6    0     0
> new_t
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    0    0    1    0    0    0    0    0    0     0
[2,]    0    0    1    0    0    0    0    0    0     0

cross_entropy_error <- function(y, t){
    batchsize <- dim(y)[1]
    return(-sum(t * log(y + 1e-7))/batchsize)
}
> cross_entropy_error(y1,t)
[1] 0.5108255
> cross_entropy_error(new_y,new_t)
[1] 1.406705

평균 교차 엔트로피 오차 함수에 2개의 이미지 데이터를 넣어도 손실 함수의 값이 계산되는 것을 확인할 수 있습니다. 단 이 함수는 정답 라벨이 원-핫 인코딩으로 만들어진 경우만 교차 엔트로피 오차 값을 계산합니다.

왜 손실 함수를 사용할까요?

신경망 모델에서 왜 손실함수를 설정하는지 머신러닝 모델의 일반적인 성능 평가 지표인 정확도를 쓰면 안되는 것인지 등의 의문이 있을 것입니다. 이 의문을 해결하기에 앞서 신경망 모델의 성능 평가의 목적을 다시 한 번 떠올려보아야 합니다. 그 목적은 모델의 분류 정확도를 높이기 위한 가중치와 편향 값을 찾는 것입니다.

분류를 더 잘 해내기 위한 가중치와 편향 값은 손실 함수를 작게 하는 방향으로 새롭게 갱신되며, 손실 함수의 값이 최저가 될 때 값의 갱신도 종료됩니다. 이 과정은 가중치와 편향 값을 아주 작게 변화시켰을 때 손실 함수의 값이 어떻게 변화하는지를 살펴보는 것입니다. 만약 계산한 손실 함수의 값이 이전 값보다 커진다면 손실 함수의 값을 작은 값으로 만드는 방향으로 가중치와 편향 값을 갱신하는 것입니다. 예를 들어 가중치와 편향의 변화량(미분 값)이 음수면 가중치와 편향의 값을 양의 방향으로 변화시켜 손실 함수의 값을 줄일 수 있습니다. 반대로 가중치와 편향의 변화량(미분 값)이 양수면 가중치와 편향의 값을 음의 방향으로 변화시켜 손실 함수의 값을 줄일 수 있습니다. 만약 손실 함수 값이 0이 되면 변화가 없으므로 가중치와 편향의 갱신도 자동으로 멈추게 됩니다.

그렇다면 다시 왜 정확도를 성능 평가 모델로 사용하면 안되는지 알아봅시다. 정확도를 지표로 사용할 경우 가중치와 편향의 갱신에 문제가 생기게 됩니다. 만약 정확도를 지표로 사용할 경우 왜 가중치와 편향의 갱신이 어려운지를 알아봅시다. 정확도가 지표로 사용되면 가중치와 편향의 미분 값이 대부분에서 0이 되는 문제가 생깁니다. 이 문제로 인해 가중치와 편향의 갱신이 멈추기 때문에 정확도를 지표로 사용하지 못하게 됩니다.

그렇다면, 정확도를 지표로 사용했을 때 대부분의 가중치와 편향의 미분 값이 0이 되는 이유는 무엇 때문일까요? 만약 모델이 32장의 이미지를 정확하게 추정한 경우 정확도가 32%일 것입니다. 이때 가중치와 편향의 값을 조금 조정한다고해도 정확도는 여전히 32%이거나 변화하더라도 32.01% 같은 연속적인 값의 변화가 일어나는 것이 아니기 때문에 미분 값으로 인한 변화가 의미 없게 되어버립니다.

이런 불연속적이고 큰 변화는 신경망 모델의 학습을 잘 반영하지 못합니다. 활성화 함수로 계단 함수가 아닌 시그모이드 함수를 사용하는 것도 연속적인 값의 변화를 위해서 사용하듯 지표로서 정확도가 아닌 손실 함수를 사용합니다. 즉 가중치와 편향의 작은 변화가 연속적인 값으로 모델에 반영되고 그것을 이용해 최적의 값을 찾기위해서 정확도가 아닌 손실 함수를 지표로 사용합니다.