Must Learning With Statistics

14. 기계학습 본문

MustLearning with R 1편

14. 기계학습

Doublek Park 2020. 1. 29. 21:12

Ch14. 기계학습

1. 기계학습에 대한 정의

데이터로부터 패턴을 뽑아내는 자동화 프로세스를 기계학습이라고 정의합니다. 기계학습의 적용 분야는 매우 다양한데, 예시를 들자면 다음과 같습니다.

  • 스팸메일 분류 알고리즘
  • 고객들의 구매 패턴 분석 알고리즘
  • 환자 진단 알고리즘
  • 비젼, 음성, 문자 등 많은 분야에 적용이 가능

기계학습 모형의 가장 큰 목적은 예측 모형을 만드는 것에 있습니다. 좋은 예측 모형이란, 현재에 존재하지 않는 데이터에 대해서도 예측값을 잘 맞추는 모형을 좋은 모형이라고 하며, 이런 모형을 일반화(generalize)가 잘 되었다고 합니다.

2. 기계학습에서 주의할 점

기계학습에서 예측 모형의 정확성을 떨어트리는 문제점은 2가지가 있습니다.

Underfitting

Underfitting은 Feature 변수가 Response 변수를 너무 간단하게 설명하려고 할 때 발생합니다.

Overfitting

Overfitting은 Underfitting과 대조적으로 알고리즘이 너무 복잡하고 데이터에 너무 밀접하게 짜여졌을 때, 잡음(noise)에 예민해지는 문제가 발생합니다.

Over(Under)fitting은 예시를 들자면, 이런 경우가 있습니다. 대부분의 대학교 학과에서 최소 강의 1개 이상은 시험 족보가 돌아다니길 마련입니다. 하지만 현명하신 교수님들께서는 족보그대로 시험문제를 출제하는게 아니라, 족보에서 60%, 안나왔던 문제 40%를 배치시켜 균형있게 문제를 내십니다.

이런 경우,

  • Underfitting은 그냥 공부를 안해서 시험을 못보는 거라고 할 수 있습니다.
  • Overfitting은 공부는 열심히 했는데 족보만 공부해가지고 새로운 문제를 못풀어 시험을 못보는 거라고 할 수 있습니다.

Machine Learning은 ’학습’입니다. 기계한테 데이터를 학습시킴으로써 새로운 데이터가 들어왔을 때, 문제 없이 예측값을 뽑아낼 수 있느냐가 중요합니다. 그런 이유로 항상 예측모형을 학습시킬 때, Training Set과 Test Set을 분리시켜 모형의 신뢰도를 높이고자 하는 앙상블 기법을 주로 사용합니다.

3. 앙상블(Ensomble) 기법

기계학습의 목적은 주어진 과제(데이터)에 대해서 가장 정확한 하나의 모형을 만드는 것입니다. 하지만 앙상블 기법은 목적과는 조금 다른 접근 방식입니다.

  1. 하나의 모형만 만들지 않고, 여러 개의 모형을 만들고 이를 한 세트로 구성합니다.
  2. 여러 개의 모형들의 예측값을 집계내서 결과값을 산출합니다.

이러한 방법을 Model ensemble이라고 합니다.

앙상블 기법은 전문가들이 모여 아이디어를 함께 내는 것이 한 명의 전문가가 해결하는 것보다 결과가 더 좋다는 경험과 비슷하게 시작되었습니다. 하지만 전문가들이 함께 일할 때여도, 전문가는 그룹에서의 생각과 다르게 독자적으로 결정을 내릴 수 있어야 합니다. 앙상블 기법도 마찬가지로 여러가지의 모형이 있어도 각 모형이 독립적으로 결과를 뽑아냅니다. 그 후, 각 모형들의 결과값을 집계내어 예측값을 정하도록 합니다.

  • Response 변수가 Categorical 변수 일 때 : 투표 형식으로 각 모형에서 가장 많이 뽑힌 값이 예측 값이 됩니다.
  • Response 변수가 Numerical 변수 일 때 : 평균 혹은 중위수를 계산함으로써 중심 경향을 나타내는 값이 예측 값이 됩니다.

다음으로는 앙상블 기법에서 대표적으로 쓰이는 2개의 접근법(Bagging, Boosting)에 대해 다루도록 하겠습니다.

Boosting

Boosting 기법은 가중치를 이용하여 약한 학습기(Weak learners)를 강한 학습기(Strong learners)로 만드는 과정을 말합니다. Boosting 기법은 독립적인 모델이 만들어질 때마다 이전 모델의 오류를 반영한 후, 다음 모형에는 오분류 된 데이터에 대해 가중치를 주고 모형을 만듭니다. 예를 들자면, 틀린 연습 문제에 대하여 다음 시험때까지 틀린 유형의 연습문제를 더 많이 풀게함으로써(가중치를 올려줌으로써) 오분류되었던 데이터에 대해 정분류를 할 수 있도록 수정을 하는 것입니다.

먼저 Bootstrap이란 용어에 대해 알아보도록 하겠습니다.

Bootstrap이란 예를 들어, ‘주어진 데이터 중에서 중복을 허용하여 m개를 뽑고 그 m개의 통계값을 구하는 작업’ 이 작업을 여러번 반복한다는 것을 의미합니다. 그럼 각 시행 별로 계산된 통계값이 쌓여 분포가 형성이 될 것입니다. Bootsraping은 보통 샘플 수가 적을 때 사용하기도 합니다.

그럼 이를 기계학습에서 어떤 논리 순서로 진행되는지 알아보도록 하겠습니다.

  • N개의 데이터가 있다고 할 때, 각 개별 데이터가 뽑힐 확률은 1/N입니다.

  • 1/N의 확률로 뽑힌 데이터 셋(Training Set)을 가지고 예측 모형을 만듭니다.

  • 만들어진 예측 모형을 평가하고 Error 발생 정도를 판단합니다.

  • Error, 즉 오분류 판정을 받은 데이터 셋들에 대하여 뽑힐 확률을 올려 준 다음, Random Sampling을 다시 진행합니다.

  • 오분류 판정을 받은 데이터들이 뽑힐 확률의 가중치 올릴 경우 계산식은 다음과 같습니다.

\[ w[i] = w[i] * \frac{1}{2*\varepsilon } \]

  • 반대로 뽑힐 확률의 가중치를 내릴 경우 계산식은 다음과 같습니다.

\[ w[i] = w[i]*\frac{1}{2*(1-\varepsilon)} \]

  • 오분류 판정을 받은 데이터들은 가중치를 더 받음으로써 다음 Random Sampling 때 뽑힐 확률이 더 높아지게 됩니다.

  • 새로 뽑힌 Training Set을 가지고 예측 모형을 만듭니다.

  • 오분류를 계산하고 오분류 데이터들의 가중치를 조정하여 다시 모형을 진행합니다.

  • 의도한 모형 갯수에 도달하게 되면 Bootstrap이 끝나게 되고 최종 결과값이 나오게 됩니다.

Boosting은 진행 할 때마다 데이터의 가중치를 달리함으로써 예측 모형에 만들어지는 Training Set의 분포를 변화시키는 것이 그 목적입니다. 주로 오분류가 발생하는 경우에서 흔한 문제는 데이터가 균형이 안맞춰져 있을 때 발생합니다. 예를 들어 병원에서 환자들의 질병 유무를 판단하고자 예측 모형을 만들려고 합니다. 하지만 해당 질병에 걸린 사람들은 질병에 걸리지 않은 사람들보다 절대적으로 적습니다. 이런 경우 데이터의 분포가 균형있지 못하게 됩니다. 결국 기계학습 과정에서 상대적으로 Minor한 질병 환자 데이터가 관심을 가지지 못하게 되고, 그 결과는 예측모형의 오분류로 이어지게 됩니다. 이러한 문제점을 해결하고자 하는 것이 앙상블 기법에서의 Boosting 접근방식입니다.

Bagging

Bagging 기법은 Boosting에 비해서는 조금은 간단하게 진행됩니다.

  • 각 모형별 복원 추출을 통해 Training Set을 만들고, 예측 모형을 만듭니다.
  • 원하는 갯수만큼의 모형을 만들었으면, 각 모형의 결과값들을 집계 하여 최종 결과물을 냅니다.
  • 각 모형 별로 Training Set이 다르기 때문에 예측 모형도 당연히 모형별로 차이가 존재할 것 입니다.

정말 이게 끝입니다. 간단하죠? 정말입니다.

4. Gradient Descent(경사하강법)

Error Surface

회귀분석에서의 Error는 이제까지 언급함 MSE라고 생각하시면 됩니다. Error surface는 모든 가능한 가중치(일반적으로 통계학에서는 회귀계수 \(\beta_0\), \(\beta_1\) 으로 표현하고, 학습쪽에서는 가중지 \(w[0], w[1]\) 로 표현하고는 합니다.)들에 대한 조합에 대해 Error값을 계산하여 시각화를 하고자 한 것입니다. 통계학에서는 최소제곱법을 이용하여 회귀계수를 추정하지만, 기계학습에서의 회귀계수 추정은 살짝 다릅니다. 이 Error Surface에서 MSE가 최소점이 되는 가중치의 조합을 찾는 방식으로 회귀계수를 추정합니다.

Error Surface는 일반적으로 볼록한 그릇 모양입니다. 그 의미는 MSE가 최소가 되는 바닥이 존재할 것이며, 그 바닥을 Global minimum이라고 합니다. 이러한 접근법을 저희는 least squares optimization이라고 합니다.

Gradient Descent

앞서 말했듯이, 선형 회귀분석에서 global minimum을 찾기 위하여 가능한 모든 가중치의 조합(회귀계수의 조합)을 계산하는 방식은 데이터의 크기가 방대할 경우, 연산 속도가 너무 길어지기 때문에 현실적으로 구현하기에는 어려운 점이 있습니다. 하지만, Error surface에서 global minimum이 존재한다는 사실을 기반으로, 효율성을 높이기 위해 gradient descent(경사 하강법)방법을 사용합니다.

Gradient Descent(경사 하강법)의 원리는 다음과 같습니다.

  • 앞을 보기 힘들 정도로, 안개가 낀 협곡에 있다고 가정합시다.
  • 앞이 보이지 않아, 협곡을 내려가는 길을 찾을 수가 없지만, 바로 앞에서의 협곡의 경사는 확인할 수는 있습니다.
  • 경사가 내려가면 해당 방향으로 가고, 경사가 올라가면 내려가는 경사를 찾아 방향을 정합니다.
  • 이렇게 꾸준히 조심히 내려가다 보면, 언젠가는 바닥에 도착할 것입니다.

이를 그대로 알고리즘 원리에 비유를 하면,

  • Gradient Descent는 임의의 위치에서 시작합니다.(위치 : 가중치, 회귀계수)
    • 임의로 주어지는 가중치는 범위로 주어집니다.
      • 전체 조합이 아닌, 해당 위치를 기준으로한 가중치를 범위로 잡습니다.
  • \(SSE\)(Sum of Squared Error)을 계산합니다.
    • \(SSE\)를 통해 해당 지역(가중치, 회귀계수의 범위 내)에서의 Error Surface를 찾을 수 있습니다.
  • Error surface에서 경사를 계산합니다.
    • 경사가 내려가는 곳을 방향으로 설정한 후, 새로운 Error Surface로 이동을 합니다.
  • 계산이 무수히 반복되다보면, global minumum을 찾을 수 있습니다.
    • 가중치들은 계산이 진행됨에 따라서 한 곳으로 수렴하게 되는 데, 해당 부분이 Globar minimum이 될 것입니다.

일반적으로 Gradient Descent는 다음의 그래프를 통해 이해한다면 더 수월해질 것입니다.

여기서, learning rate \(\alpha\)라는 것이 등장하는데, 이는 쉽게 말하면 계곡에서 한걸음씩 내려갈 때 보폭을 어느 정도의 크리고 설정하는 정도라고 생각하시면 됩니다. 보폭이 크면 씩씩하게 걸을 수는 있지만, 방향을 제대로 잡지 못해 알고리즘이 잘못 작동할 가능성이 있습니다. 반대로 보폭을 너무 짧게 하면 방향은 정확하게 정해서 내려갈 수 있겠지만, 속도가 너무 느리게 됩니다. 이는 저희의 퇴근시간에 매우 방해되는 문제입니다. 이 역시도 learning rate를 잘 설정하는 센스가 중요한 영역입니다.

아래의 그래프는 가중치가 변할 때마다 Error값이 어떻게 낮아지는지 나타냈습니다. 흔히, Cost function이라고 합니다.

왼쪽은 learning rate가 너무 크게 잡혔을 경우, 방향을 잘못 잡아 오히려 올라가는 현상이 발생할 수도 있습니다. 오른쪽은 반대로 learning rate가 너무 낮게 잡혀, 내려가는 과정이 매우 뎌지게 진행됩니다.

learning rate가 내려가는 보폭을 정한다면, 방향을 정하는 것은 Delta Function이 담당합니다. 계산 방식은 편미분을 통해 진행이 되는데, 수식이 조금 많이 복잡할 수도 있지만, 간단하게 원리를 설명해드리겠습니다. 여러분들도 아시다시피 미분을 활용한 최소 혹은 최대점을 찾기 위한 방법입니다. Delta Function은 error surface가 낮아지는 방향을 제시해주는 역할을 합니다.

Error Loss function 정의

\[ L2:Error\;loss\;function\\ L2(Mw,X)=\frac{1}{2}\Sigma^{n}{i=1}(yi-(w\cdot xi))^2 \]

먼저 Error function을 정의하는데, 여기서 \(L_2\)는 SSE라고 생각하시면 됩니다. \((w \cdot x_i)\)가 벡터 형태로 표시되어 이질감이 드는 것일 뿐, 풀어쓰면 \(w[0] + w[1] *x_i[1]\)이기 때문입니다.

Error Surface에서 편미분을 통해 global minimum 찾기

\[ \frac{\partial}{\partial w[0]}=\frac{1}{2}\Sigma(yi-(w[0]+w[1]xi[1]))^2 =0\\ \frac{\partial}{\partial w[1]}=\frac{1}{2}\Sigma(yi-(w[0]+w[1]xi[1]))^2 =0 \]

Loss Function을 각 가중치(기울기, 회귀계수)로 편미분을 하여 0 이되는 지점을 찾습니다. 이는 회귀분석에서 최소제곱법과 비슷한 방식이라고 생각하면 됩니다. 만약 회귀식이 Multiple인 경우에는 식의 형태는 조금 변하겠지만 원리는 바뀌지 않습니다.

\[ \frac{\partial}{\partial w[j]}L2(Mw,X)=\Sigma{i=1}^{n}((yi-Mw(xi))-xi[j])\\ errorDelta(X,w[j])=-\frac{\partial}{\partial w[j]}L2(Mw,X)\\=\Sigma{i=1}^{n}((yi-Mw(xi))xi[j]) \] 복잡한 편미분을 계산하면 Delta Function이 계산이 됩니다.

방향 설정 방법

\[ w[j] = w[j] + \alpha\;\Sigma{i=1}^{n}((yi-Mw(xi))xi[j]) \]

이렇게 learning rate \(\alpha\)와 \(ErrorDelta\)를 곱해준 값을 더해줌으로써 방향과 보폭을 결정할 수 있습니다.

Learning rate 및 초기 가중치 설정 방법

마지막으로 최적의 learning rate 및 초기 가중치 설정 방법을 찾아주는 그렇다할 이론은 없다는 것이 학계의 정설입니다. 초기 가중치 설정은 변수를 Normalizaton을 한 후 추정하는 것이 유리합니다. 여기서 Normalization이란, \[ Normalization(x) =\frac{x-min(x)}{max(x)-min(x)} \] 변환을 의미합니다. 변환 후에 x 값은 [0,1]사이의 상대적인 비율값을 가지게 됩니다. Learning rate 및 초기 가중치 설정은 경험적인 판단에서 결정을 내리는 것이 좋습니다.

요약하면 열심히 반복문 코드를 구성해서 실험하라는 뜻입니다.

5. 경사하강법(R Code)

Gradient Descent를 R code로 구현해보도록 하겠습니다.

샘플 데이터 생성(난수)

x = runif(300,-10,10) # 균일분포 난수 생성
Noise = rnorm(n = 300, mean = 0 , sd = 3)
y = x + Noise
DF = data.frame(x  = x, 
                y = y)

library(ggplot2)

ggplot(DF,aes(x= x, y= y)) +
  geom_point(col = 'royalblue') +
  theme_bw() 

Learning Rate 설정

alpha = 0.01

초기 가중치 행렬 생성

Weights = matrix(c(0,0),nrow = 2)
Weights
     [,1]
[1,]    0
[2,]    0

회귀식 계산을 위한 행렬 생성

\[ \begin{bmatrix} \beta_0 \\ \beta_1 \end{bmatrix} * \begin{bmatrix} 1 & x_1\\ 1 & x_2\\ 1 & x_3\\ 1 & x_4\\ \cdot & \cdot \\ 1 & x_{n-1}\\ 1 & x_n\\ \end{bmatrix}= \begin{bmatrix} \beta_0+\beta_1x_1\\ \beta_0+\beta_1x_2\\ \beta_0+\beta_1x_3\\ \beta_0+\beta_1x_4\\ \cdot \\ \beta_0+\beta_1x_{n-1}\\ \beta_0+\beta_1x_{n}\\ \end{bmatrix} \]

이러한 식을 R에서 만들어주어야 하는데, 코드는 다음과 같습니다.

# 행렬형태로 만들어 주기
X = matrix(x)
X = cbind(1, X)
colnames(X) = c("V1", "V2")
V1 V2
1 -6.925399
1 -1.020608
1 4.546692
1 6.626809
1 -9.660918

Error Loss function 만들기

\[ L_2(M_w,X)=\frac{1}{2}\Sigma^{n}_{i=1}(y_i-(w\cdot x_i))^2 \]

# Error 계산 %*%는 행렬의 곱셈을 할 때 사용합니다.

Error = function(x, y, Weight) {
    sum((y - x %*% Weight)^2)/(2 * length(y))
}

알고리즘 학습

\[ w[j] = w[j] + \alpha\;\Sigma{i=1}^{n}((yi-Mw(xi))xi[j]) \]

학습을 돌리기 전에, Error(Cost)값과 가중치(회귀계수)가 저장될 빈공간을 만들어야 합니다.

Error_Surface = c()
Weight_Value = list()
for (i in 1:300) {
    # X는 (300,2) 행렬 Weights는 (2,1)행렬 X * Weights => (300,1) 행렬[각
    # 데이터에서의 Error 연산]
    error = (X %*% Weights - y)
    # Delta Funtion 계산
    Delta_function = t(X) %*% error/length(y)
    # 가중치 수정
    Weights = Weights - alpha * Delta_function
    Error_Surface[i] = Error(X, y, Weights)
    Weight_Value[[i]] = Weights
}

시각화

p = ggplot(DF, aes(x = x, y = y)) + geom_point(col = "royalblue", alpha = 0.4) + 
    theme_bw()

for (i in 1:300) {
    p = p + geom_abline(slope = Weight_Value[[i]][2], intercept = Weight_Value[[i]][1], 
        col = "red", alpha = 0.4)
}

p

다음과 같이 가중치(회귀계수)가 조금씩 움직이면서 최적의 회귀선을 찾아가는 것을 알 수 있습니다.

DF$num = 1:300
DF$Error_value = Error_Surface

ggplot(DF) + geom_line(aes(x = num, y = Error_value), group = 1) + geom_point(aes(x = num, 
    y = Error_value)) + theme_bw() + ggtitle("Error Function") + xlab("Num of iterations")

Error 값 또한 감소하면서 최적의 가중치(기울기)에 수렴하는 것을 확인할 수 있습니다.

최소제곱법을 이용한 선형회귀식과의 비교

  • 일반 선형회귀(최소제곱법)
REG = lm(y ~ x)
A = summary(REG)
print(paste("R Square :", round(A$r.squared, 4)))
[1] "R Square : 0.7691"

Gradient

경사하강법으로 회귀식을 추정했을 경우, \(R^2\)를 구해보면 다음과 같습니다. 우리가 계산한 가중치들은 앞서 저장공간으로 만들어둔 weight_value에 저장이 되어 있습니다.

GR_MODEL = Weight_Value[[300]][1] + Weight_Value[[300]][2] * x

actual = y
rss = sum((GR_MODEL - actual)^2)
tss = sum((actual - mean(actual))^2)
rsq = 1 - rss/tss

print(paste("R square :", round(rsq, 4)))
[1] "R square : 0.7691"

최소제곱법과 비교하였을 때, 비슷한 값이 계산되는 것을 확인할 수가 있습니다.

6. 의사결정나무

심리 테스트 책을 보면 복잡한 순서도를 따라 ‘당신은 어떤 유형의 사람입니다.’ 라고 알려주는 페이지를 본 적이 있을 것입니다. 의사결정나무는 비슷한 형태의 분류 알고리즘입니다.

예를 들어, 남자 100명, 여자 100명씩 총 200명으로 구성되어 있는 집단에서 남녀를 분리시키는 분류규칙 알고리즘을 만들고자 합니다. 단, 생물학적 특성은 분류 규칙에 사용할 수가 없습니다.

분류 규칙 알고리즘을 만들기에 앞서, 의사결정 나무에서 필수적으로 알아야 되는 개념은 불순도(Impurity)입니다. 불순도는 데이터 내에서 여러 카테고리가 섞여 있어, 원하는 표본을 뽑을 확률이 적을 수록 커지는 개념입니다.

만약 통계학과 학부생을 뽑고 싶을 때, 다른 학과의 비율이 높아질 수록, 자연스럽게 통계학과를 뽑을 확률은 감소하게 됩니다. 이는 반대로 불순도가 증가한다는 것을 의미합니다. 이 불순도를 지표로 만든 것을 엔트로피(Entropy)라고 합니다.

\[ H(t)=-\Sigma^{k}_{i=1}(P[t=i]*log_s(P[t=i]))\\ P[t=i]:Probability\;of\;element\;t\;is\;the\;type\; i \]

  • 불순도가 높을 수록 데이터에서 특정 레벨의 데이터가 뽑힐 확률은 낮아지게 됩니다.
  • 엔트로피는 매우 작은 확률 값을 계산해야 하기에, log 변환을 해줌으로써 값이 큰 음수를 가지도록 합니다. 여기서 음수를 취해주면 불순도가 높은 값은 높은 엔트로피를 가지게 됩니다.

의사결정나무는 엔트로피를 최대한 낮추는 방향으로 분류 규칙을 만듭니다. 여기서 감소한 엔트로피를 정보 획득(Information Gain)이라고 합니다.

다시 처음의 예시로 돌아가서, 남녀 200명을 분류하는 규칙을 만들어보겠습니다.

남/녀를 구별하기 위한 규칙을 만들기 위하여, 저희는 다음과 같은 특성을 구했습니다.

  • 머리 길이 x(cm)이상 여부
  • 반지 착용 여부
  • 귀걸이 착용 여부
  • 손톱 케어 여부
  • 안경 착용 여부

의사결정나무에서는 각각의 특성에 따라 얻을 수 있는 정보획득을 측정합니다. 그리고 정보획득이 가장 큰 특성이 첫번째 가지에 자리를 잡게 됩니다.

손톱케어 여부가 정보획득이 가장 높은 것을 알 수 있습니다. 그 결과 분류규칙은 다음과 같습니다.

7. 랜덤 포레스트

랜덤 포레스트는 의사결정나무를 많이 심어, 숲을 만든다는 의미로 Forest라 불립니다. 여기서 Random이 붙은 이유는 숲에 심는 의사결정나무들에 쓰이는 특성들을 랜덤하게 뽑아 만들기 때문에 랜덤 포레스트라고 부릅니다.

  • 랜덤 포레스트에서 특성들을 임의로 선택하여 의사결정 나무를 만드는 이유는 모든 특성들의 조합을 고려하기 위해서입니다.
    • 같은 조합의 특성으로 의사결정나무를 만들 경우, 대부분의 의사결정나무는 늘 사용하는 특성만 사용할 확률이 높습니다.
  • 특성을 임의적으로 뽑아 사용하게 함으로써, 모든 요소를 고려한 숲을 만드는 것이 목표입니다.
  • 데이터에 대한 분류 결과 및 예측은 숲에 있는 각 의사결정나무들이 출력한 결과들을 투표하여 종합합니다.
  • 숲에 나무가 많아질수록, 랜덤 포레스트의 결과는 더 정교하게 되나, 어느 시점에서부터는 정확도가 수렴하게 됩니다.

8. 의사결정나무, 랜덤포레스트(R Code)

패키지 불러오기

library(C50)
library(caret)
library(xtable)

Train / Test Set 나누기

HR = read.csv("D:\\Dropbox\\DATA SET\\HR_comma_sep.csv")

SL = sample(1:nrow(HR), nrow(HR) * 0.7, replace = FALSE)

HR$left = as.factor(HR$left)

TRAIN = HR[SL, ]
TEST = HR[-SL, ]

FEATURE <- TRAIN[, c(1:6, 8:10)]
RESPONSE <- TRAIN[, 7]

의사결정나무 분류규칙 생성

tree <- C5.0(FEATURE, RESPONSE, control = C5.0Control(noGlobalPruning = FALSE, 
    minCases = 100), trials = 10)
plot(tree)

  • C5.0은 Tree 알고리즘 명령어 중 하나입니다.
  • trials는 boosting을 실행하는 옵션입니다.
  • minCasese 옵션은 최종 결과 노드에 최소 몇개의 관측치를 포함해야되는지 설정해줍니다.
  • 회색은 0(이직을 하지 않은 직원), 검은색은 1(이직을 한 직원)을 의미합니다.
  • 각 분류규칙에 따라 분류 오차율을 확인할 수 있습니다.

만약 mincases옵션 값을 높게 조정해주면, 의사결정나무가 단순해집니다.

tree <- C5.0(FEATURE, RESPONSE, control = C5.0Control(noGlobalPruning = FALSE, 
    minCases = 300), trials = 10)
plot(tree)

TEST SET 검증

y_pred = predict(tree, newdata = TEST)
confusionMatrix(y_pred, TEST$left)
Confusion Matrix and Statistics

          Reference
Prediction    0    1
         0 3435   87
         1   18  960

               Accuracy : 0.9767          
                 95% CI : (0.9718, 0.9809)
    No Information Rate : 0.7673          
    P-Value [Acc > NIR] : < 2.2e-16       

                  Kappa : 0.9331          

 Mcnemar's Test P-Value : 3.22e-11        

            Sensitivity : 0.9948          
            Specificity : 0.9169          
         Pos Pred Value : 0.9753          
         Neg Pred Value : 0.9816          
             Prevalence : 0.7673          
         Detection Rate : 0.7633          
   Detection Prevalence : 0.7827          
      Balanced Accuracy : 0.9558          

       'Positive' Class : 0               

앞선, 로지스틱 회귀분석보다 분류 결과가 더 좋은 것을 확인할 수가 있습니다.

랜덤 포레스트

### Random Forest

library(randomForest)

rf.fit = randomForest(left ~ ., data = TRAIN, mtry = 3, ntree = 200, importance = T)

y_pred = predict(rf.fit, TEST)

confusionMatrix(y_pred, TEST$left)
Confusion Matrix and Statistics

          Reference
Prediction    0    1
         0 3448   33
         1    5 1014

               Accuracy : 0.9916         
                 95% CI : (0.9884, 0.994)
    No Information Rate : 0.7673         
    P-Value [Acc > NIR] : < 2.2e-16      

                  Kappa : 0.9761         

 Mcnemar's Test P-Value : 1.187e-05      

            Sensitivity : 0.9986         
            Specificity : 0.9685         
         Pos Pred Value : 0.9905         
         Neg Pred Value : 0.9951         
             Prevalence : 0.7673         
         Detection Rate : 0.7662         
   Detection Prevalence : 0.7736         
      Balanced Accuracy : 0.9835         

       'Positive' Class : 0              
  • mtry는 랜덤으로 투입할 변수의 갯수를 의미합니다.
  • ntree는 몇 개의 의사결정 나무를 만들지 정해주는 옵션입니다.
  • 의사결정나무보다 더 높은 결과를 얻은 것을 확인할 수 있습니다.
varImpPlot(rf.fit, type = 2, pch = 19, col = 1, cex = 1, main = "")

  • importance = T 옵션을 줌으로써, 특성의 중요도 plot을 그릴 수 있습니다.
plot(rf.fit$err.rate[, 1], col = "red")

  • Random Forest는 알고리즘 내부에서 Train / Test Set을 나눠서 검증을 합니다. 여기서 Train Set에 포함되지 않은 데이터들을 Out of Bag이라하여, 오류율을 계산합니다.
  • 해당 plot은 랜덤포레스트 내부에서 의사결정나무가 많아질수록 오류율이 어떻게 변하는지 보여주고 있습니다.
  • 오류율이 수렴하는 거를 확인할 수 있습니다.

ROC 커브

기계학습 방법론을 활용한 결과값에 대해서도 모형의 성능평가를 위한 ROC커브를 만들 수가 있습니다. 대신에 predict 단계에서 확률값을 추정하도록 옵션을 추가해야 합니다. 비교를 위하여 전에 진행했던 로지스틱 모형도 다시 만들어보도록 하겠습니다.

GLM = step(glm(left ~ ., data = TRAIN, family = binomial(link = "logit")))
Start:  AIC=9038.94
left ~ satisfaction_level + last_evaluation + number_project + 
    average_montly_hours + time_spend_company + Work_accident + 
    promotion_last_5years + sales + salary

                        Df Deviance     AIC
<none>                       9000.9  9038.9
- sales                  9   9027.9  9047.9
- last_evaluation        1   9012.4  9048.4
- promotion_last_5years  1   9036.2  9072.2
- average_montly_hours   1   9068.3  9104.3
- number_project         1   9174.9  9210.9
- time_spend_company     1   9208.0  9244.0
- Work_accident          1   9250.9  9286.9
- salary                 2   9303.6  9337.6
- satisfaction_level     1  10576.9 10612.9
GLM_pred = predict(GLM, newdata = TEST, type = "response")
Tree_pred = predict(tree, newdata = TEST, type = "prob")
RF_pred = predict(rf.fit, newdata = TEST, type = "prob")

library(pROC)

GLM_ROC = roc(TEST$left, GLM_pred)
Tree_ROC = roc(TEST$left, Tree_pred[, 2])
RF_ROC = roc(TEST$left, RF_pred[, 2])
ROC_DF = data.frame(
  SEN = c(GLM_ROC$sensitivities,Tree_ROC$sensitivities,RF_ROC$sensitivities),
  SPE = c(GLM_ROC$specificities,Tree_ROC$specificities,RF_ROC$specificities),
  Model = c(rep("GLM",length(GLM_ROC$sensitivities)),
            rep("Tree",length(Tree_ROC$sensitivities)),
            rep("RF",length(RF_ROC$sensitivities)))

)


ggplot(ROC_DF) +
  geom_line(aes(x = 1-SPE, y = SEN, col = Model),size = 1.2) +
  xlab("1-specificity") + ylab("sensitivity") +
  theme_bw() +
  theme(legend.position = "bottom",
        legend.box.background = element_rect(),
        legend.box.margin = ggplot2::margin(2,2,2,2),
        text = element_text(size = 15))

위 ROC 커브를 보시면 Random Forest가 가장 성능이 좋은 것을 확인할 수가 있습니다. 다만, 기계학습의 알고리즘은 종류가 매우 많으며, 딥러닝과 함께 성능이 훌륭하다고 알려져있습ㄴ니다. 하지만, 그렇다고 만능 알고리즘인 것은 아닙니다. 데이터에 따라 적합한 기계학습 방법론이 있는 것이고, 단순한 통계분석이 더 효과가 좋을 때가 있기도 합니다. 즉, 데이터 분석은 좋은 알고리즘을 쓰면 해결이 되는 것이 아니라, 데이터를 잘 정릴한 후, 적절한 분석방법을 적용하는 것이 가장 좋은 방법인 것을 유의하시길 바랍니다.

Comments