[R] 기계학습 과제...

반응형

 

학교 다닐 때.. 기계학습 수업 때 했던 과제 포스팅해봅니다. 지금와서 보면 많이 부족했네요..

필요패키지

set.seed(2017)  # fix seed number
library(knitr)
library(cvTools)
library(MASS)
library(class)
library(mgcv)
library(rpart)
library(partykit)
library(randomForest)
library(e1071)
library(ggplot2)

Abstract

ISLR 패키지에 내장되어 있는 OJ 데이터를 가지고 최적의 분류모형을 찾아보았다. Logistic regression, Linear discriminant analysis 등 모수적 가정을 하는 분류모형과 비모수적 방법인 k-Nearest Neighbor, Generalized additive model 등을 적용해보았으며, 더 나아가 이 밖에 실생활에서 널리 쓰일 수 있는 Tree 기반 모형인 Random forest도 적용하였고 1990년 중반에 분류모형으로써 센세이션을 일으킨 Support vector machine도 적용해보았다.
데이터의 분포 및 성질에 따라 각각의 방법을 적용함으로써 존재하는 장단점이 달라질 수 있지만, OJ 데이터에서는 본 내용에서 소개하는 분류모형 중 비교적 가장 최신의 기법인 Support vector machine이 가장 예측력이 좋게 나왔다.


 

1. Introduction

  • 분류 문제는 관심이 대상이 되는 반응변수와 관련된 설명변수를 기반으로 사전 정의 된 그룹 또는 클래스에 객체를 할당해야할 때 발생한다.
  • Statistical classification 이란 기계학습에서 지도학습(Supervised learning)의 일종으로 분류하고자 하는 반응변수와 관련된 설명변수를 가지고 있는 데이터로부터 분류 모형을 설정 및 식별하고 새로운 데이터가 주어졌을 때 반응변수의 클래스를 예측하는 통계적 방법이다.
  • 일반적으로 전체 데이터의 일부분을 가지고 Training data로 정의하여 모형을 만들고 나머지를 Test data로 정의하여 모형의 예측력을 평가한다.
  • 여기서는 OJ 데이터를 가지고 다양한 분류 모델링을 적용하여 최적의 분류 예측모형을 찾는데 중점을 두겠다.

 

1. 1. Orange Juice(OJ) data

  • OJ 데이터는 18개의 변수와 1070개의 관측치로 이루어진 데이터이다.
  • 이 데이터에는 특정 고객이 Citrus Hill 또는 Minute Maid Orange Juice를 구매한 1070건의 구매기록이 포함되어 있는 자료이며 ISLR 패키지안에 내장되어 있다.
    ##   Purchase WeekofPurchase StoreID PriceCH PriceMM DiscCH DiscMM SpecialCH
    ## 1       CH            237       1    1.75    1.99   0.00    0.0         0
    ## 2       CH            239       1    1.75    1.99   0.00    0.3         0
    ## 3       CH            245       1    1.86    2.09   0.17    0.0         0
    ## 4       MM            227       1    1.69    1.69   0.00    0.0         0
    ## 5       CH            228       7    1.69    1.69   0.00    0.0         0
    ## 6       CH            230       7    1.69    1.99   0.00    0.0         0
    ##   SpecialMM  LoyalCH SalePriceMM SalePriceCH PriceDiff Store7 PctDiscMM
    ## 1         0 0.500000        1.99        1.75      0.24     No  0.000000
    ## 2         1 0.600000        1.69        1.75     -0.06     No  0.150754
    ## 3         0 0.680000        2.09        1.69      0.40     No  0.000000
    ## 4         0 0.400000        1.69        1.69      0.00     No  0.000000
    ## 5         0 0.956535        1.69        1.69      0.00    Yes  0.000000
    ## 6         1 0.965228        1.99        1.69      0.30    Yes  0.000000
    ##   PctDiscCH ListPriceDiff STORE
    ## 1  0.000000          0.24     1
    ## 2  0.000000          0.24     1
    ## 3  0.091398          0.23     1
    ## 4  0.000000          0.00     1
    ## 5  0.000000          0.00     0
    ## 6  0.000000          0.30     0
    • 출처 : Stine, Robert A., Foster, Dean P., Waterman, Richard P. Business Analysis Using Regression (1998). Published by Springer.
  • library(ISLR)
    data(OJ)
    head(OJ)

 

  • 데이터의 구조 및 변수에 대한 설명은 다음과 같다.
    ## 'data.frame':    1070 obs. of  18 variables:
    ##  $ Purchase      : Factor w/ 2 levels "CH","MM": 1 1 1 2 1 1 1 1 1 1 ...
    ##  $ WeekofPurchase: num  237 239 245 227 228 230 232 234 235 238 ...
    ##  $ StoreID       : num  1 1 1 1 7 7 7 7 7 7 ...
    ##  $ PriceCH       : num  1.75 1.75 1.86 1.69 1.69 1.69 1.69 1.75 1.75 1.75 ...
    ##  $ PriceMM       : num  1.99 1.99 2.09 1.69 1.69 1.99 1.99 1.99 1.99 1.99 ...
    ##  $ DiscCH        : num  0 0 0.17 0 0 0 0 0 0 0 ...
    ##  $ DiscMM        : num  0 0.3 0 0 0 0 0.4 0.4 0.4 0.4 ...
    ##  $ SpecialCH     : num  0 0 0 0 0 0 1 1 0 0 ...
    ##  $ SpecialMM     : num  0 1 0 0 0 1 1 0 0 0 ...
    ##  $ LoyalCH       : num  0.5 0.6 0.68 0.4 0.957 ...
    ##  $ SalePriceMM   : num  1.99 1.69 2.09 1.69 1.69 1.99 1.59 1.59 1.59 1.59 ...
    ##  $ SalePriceCH   : num  1.75 1.75 1.69 1.69 1.69 1.69 1.69 1.75 1.75 1.75 ...
    ##  $ PriceDiff     : num  0.24 -0.06 0.4 0 0 0.3 -0.1 -0.16 -0.16 -0.16 ...
    ##  $ Store7        : Factor w/ 2 levels "No","Yes": 1 1 1 1 2 2 2 2 2 2 ...
    ##  $ PctDiscMM     : num  0 0.151 0 0 0 ...
    ##  $ PctDiscCH     : num  0 0 0.0914 0 0 ...
    ##  $ ListPriceDiff : num  0.24 0.24 0.23 0 0 0.3 0.3 0.24 0.24 0.24 ...
    ##  $ STORE         : num  1 1 1 1 0 0 0 0 0 0 ...
    Variables Description
    Purchase A factor with levels CH and MM indicating whether the customer purchased Citrus Hill or Minute Maid Orange Juice
    WeekofPurchase Week of purchase
    StoreID Store ID
    PriceHH Price charged for CH
    PriceMM Price charged for MM
    DiscCH Discount offered for CH
    DiscMM Discount offered for MM
    SpecialCH Indicator of special on CH
    SpecialMM Indicator of special on MM
    LoyalCH Customer brand loyalty for CH
    SalePriceMM Sale price for MM : PriceMM - DiscMM
    SalePriceCH Sale price for CH : PriceCH - DiscHH
    PriceDiff Sale price of MM less sale price of CH : SalePriceMM - SalePriceCH
    Store7 A factor with levels No and Yes indication whether the sale is at Store 7
    PctDiscMM Percentage discount for MM : DiscMM / PriceMM
    PctDiscCH Percentage discount for CH : DiscCH / PriceCH
    ListPriceDiff List price of MM less list price of CH : PriceMM - PriceCH
    STORE Which of 5 possible stores the sale occured at
  • str(OJ)
  • 변수의 정의에 맞게끔 STORE 변수의 속성을 factor 형식으로 변경할 필요가 보이며, StoreID 변수와 STORE 변수는 똑같이 5개의 수준을 갖는 변수이므로 차후 모델링하는데 있어서 둘 중 STORE 변수 하나만을 이용할 것 이다.
  • SpeicalCH, SpecialMM 또한 0 아니면 1의 값을 갖는 indicator로서 변수의 속성을 factor 형식으로 변경할 필요가 있다.
  • OJ$StoreID <- as.factor(OJ$StoreID)
    OJ$STORE <- as.factor(OJ$STORE)
    OJ$SpecialCH <- as.factor(OJ$SpecialCH)
    OJ$SpecialMM <- as.factor(OJ$SpecialMM)
  • summary() 함수를 이용하여 데이터의 요약치를 살펴본 결과는 다음과 같다.
    ##  Purchase WeekofPurchase  StoreID    PriceCH         PriceMM     
    ##  CH:653   Min.   :227.0   1:157   Min.   :1.690   Min.   :1.690  
    ##  MM:417   1st Qu.:240.0   2:222   1st Qu.:1.790   1st Qu.:1.990  
    ##           Median :257.0   3:196   Median :1.860   Median :2.090  
    ##           Mean   :254.4   4:139   Mean   :1.867   Mean   :2.085  
    ##           3rd Qu.:268.0   7:356   3rd Qu.:1.990   3rd Qu.:2.180  
    ##           Max.   :278.0           Max.   :2.090   Max.   :2.290  
    ##      DiscCH            DiscMM       SpecialCH SpecialMM    LoyalCH        
    ##  Min.   :0.00000   Min.   :0.0000   0:912     0:897     Min.   :0.000011  
    ##  1st Qu.:0.00000   1st Qu.:0.0000   1:158     1:173     1st Qu.:0.325257  
    ##  Median :0.00000   Median :0.0000                       Median :0.600000  
    ##  Mean   :0.05186   Mean   :0.1234                       Mean   :0.565782  
    ##  3rd Qu.:0.00000   3rd Qu.:0.2300                       3rd Qu.:0.850873  
    ##  Max.   :0.50000   Max.   :0.8000                       Max.   :0.999947  
    ##   SalePriceMM     SalePriceCH      PriceDiff       Store7   
    ##  Min.   :1.190   Min.   :1.390   Min.   :-0.6700   No :714  
    ##  1st Qu.:1.690   1st Qu.:1.750   1st Qu.: 0.0000   Yes:356  
    ##  Median :2.090   Median :1.860   Median : 0.2300            
    ##  Mean   :1.962   Mean   :1.816   Mean   : 0.1465            
    ##  3rd Qu.:2.130   3rd Qu.:1.890   3rd Qu.: 0.3200            
    ##  Max.   :2.290   Max.   :2.090   Max.   : 0.6400            
    ##    PctDiscMM        PctDiscCH       ListPriceDiff   STORE  
    ##  Min.   :0.0000   Min.   :0.00000   Min.   :0.000   0:356  
    ##  1st Qu.:0.0000   1st Qu.:0.00000   1st Qu.:0.140   1:157  
    ##  Median :0.0000   Median :0.00000   Median :0.240   2:222  
    ##  Mean   :0.0593   Mean   :0.02731   Mean   :0.218   3:196  
    ##  3rd Qu.:0.1127   3rd Qu.:0.00000   3rd Qu.:0.300   4:139  
    ##  Max.   :0.4020   Max.   :0.25269   Max.   :0.440
  • summary(OJ)
  • 변수를 자세히 살펴보면 각 설명변수가 서로 연관되어 있음을 알 수 있다.
  • 따라서 앞으로 분석하는데 있어서 다음의 변수들을 사용할 것 이다.
    • Purchase : Class of response
    • WeekofPurchase : Week of purchase
    • SpecialCH : Indicator of special on CH
    • SpecialMM : Indicator of special on MM
    • LoyalCH : Customer brand loyalty for CH, 구매자들의 CH에 대한 브랜드 충성도를 반영
    • SalePriceMM : PriceMM - DiscMM, 실제 구매자들이 구매한 금액을 반영함으로써 해석상 용이 도모
    • SalePriceCH : PriceCH - DiscHH, 실제 구매자들이 구매한 금액을 반영함으로써 해석상 용이 도모
    • STORE : Which of 5 possible stores the sale occured at
    • ListPriceDiff : PriceMM - PriceCH, 할인되지 않은 실제 가격을 반영하지 못했지만 그 가격 차이를 반영함으로써 실제 가격에 의한 원인요소를 어느정도 반영할 있을 것이라고 예상

 

1. 2. Methods and functions

Logistic regression

  • Logistic regression은 다음과 같은 Logistic function을 이용하여 특정 클래스로 분류될 확률을 추정하는 회귀 모형이다.\[p(X) = \frac{e^{\beta_0 + \beta_1 X + \beta_2 X + ... + \beta_p X}}{1 + e^{\beta_0 + \beta_1 X + \beta_2 X + ... + \beta_p X}}\]
  • R에서 기본으로 제공하는 stats 패키지 안에 내장되어 있는 glm() 함수를 이용하여 모형을 적합시킬 수 있다.
  • glm(formula, data, family = binomial(), ...)

 

Linear discriminant analysis (LDA)

  • Logistic regression과 달리 분류하고자 하는 클래스가 3개 이상일 때도 적용시킬 수 있는 방법이다.
  • 오히려 클래스의 구분이 명확한 경우 Logistic regression의 회귀계수 추정치의 불안정성을 보완해주는 대안으로서 널리 쓰이고 있는 방법이다.
  • Linear discriminant function의 기본 형태는 다음과 같은 선형결합의 형태로 주어진다.
  • \[\mathbf{d}=\mathbf{x}^{\text{T}}\mathbf{b} = b_1 x_1 + b_2 x_2 + ... + b_p x_p\]
  • 각 클래스가 공통분산을 가지는 정규분포로부터 나온 것으로 가정하기 때문에 표본의 크기가 비교적 작으면서 반응변수의 분포가 근사적으로 정규분포를 따르게 되면 Logistic regression 보다 더 안정적인 방법이다.
  • MASS 패키지 안에 내장되어 있는 lda() 함수를 이용하여 모형을 적합시킬 수 있다.
  • lda(formulam, data, ...)
  • DiscriMiner 패키지 안에 내장되어 있는 linDA() 함수 역시 LDA 모형을 적합시킬 수 있는 함수이다.

 

Quadratic discriminant analysis (QDA)

  • LDA와 달리 각 클래스에 대해 서로 같거나 다른 분산을 가정하고 추정하는 방법이다.
  • 클래스를 결정하게 되는 경계는 1차식이 아닌 2차식의 형태를 띄게 된다.
  • 클래스별 분산에 큰 차이가 있으면서 관측치가 충분할 때 LDA보다 예측력이 좋을 것이라고 예상할 수 있다.
  • 이 또한 MASS 패키지 안에 내장되어 있는 qda() 함수를 이용하여 모형을 적합시킬 수 있다.
  • qda(formulam, data, ...)
  • DiscriMiner 패키지 안에 내장되어 있는 quaDA() 함수 역시 QDA 모형을 적합시킬 수 있는 함수이다.

 

K-nearest neighbors (kNN)

  • 모수를 가정하지 않은 비모수적인 방법으로 기준으로부터 가까운 k개의 관측치들을 가지고 클래스를 분류를 하는 방법이다.
  • Decision boundary가 비선형인 경우 LDA 및 Logistic regression 보다 예측력이 좋을 것이라고 예상할 수 있다.
  • 하지만 어떤 설명변수가 중요한 것인지 판단할 수 있는 유의성 판단의 부재가 단점이다.
  • 여기서 k는 Cross-Validation 을 통하여 오분류율을 가장 최소로 하는 k로 결정하게 된다.
  • class 패키지 안에 내장되어 있는 knn() 함수를 이용하여 모형을 적합시킬 수 있다.
  • knn(train, test, cl, k = 1, ...)

 

Generalized additive models (GAM)

  • 각 설명변수들을 알려지지 않는(unknown) 비선형 함수 \(f_j\)를 가지고 변수들간 가법성(additivity)을 유지한 상태로 다중 선형회귀 분석을 하는 것과 비슷하다.
  • GAM 역시도 Logistic regression(GLM)과 유사하게 link function을 이용할 수 있다.
  • \[\log \bigg(\frac{p(X)}{1-p(X)}\bigg) = \beta_0 + f_1(x_{i1}) + f_2(x_{i2}) + ... + f_p(x_{ip})\]
  • 비선형 함수를 고려하기 때문에 때때로 조금 더 정확한 예측력을 보일 수 있다.
  • mgcv 패키지 안에 내장되어 있는 gam() 함수를 이용하여 모형을 적합시킬 수 있다.
  • gam(formula, data, family = binomial(), ...)

 

Classification trees

  • 결과에 대해 비교적 쉬운 해석을 제공하고 의사결정이 편리하다는 장점이 있다.
  • Dummay variables이 존재해도 다루기가 쉽다.
  • 너무 많은 의사결정나무 가지가 나오면 pruning을 거쳐 조금 단순화할 수 있다.
  • 모형의 예측력을 평가하는 척도로 Gini Index, Cross-entropy 등을 이용할 수 있다.
    • Gini index가 0에 가까울수록 순도가 높다고 판단
  • rpart 패키지 안에 내장되어 있는 rpart() 함수를 이용하여 모형을 적합시킬 수 있다.
  • rpart(formula, data, ...)

 

Bagging & Random forest

  • Tree 모형의 high variance 및 예측력이 떨어지는 단점을 보완한 방법으로써 대표적으로 앙상블 기법인 Bagging과 Random forest를 이용할 수 있다.
  • Bagging은 데이터로부터 복원추출을 통하여 \(B\)개의 Bootstrap sample을 가지고 \(B\)개의 Tree 모형을 만들어 모형의 예측력을 평가하는 방법이다.
    • \(B\)개의 모형으로부터 나온 값의 평균 또는 최빈값을 가지고 예측력을 평가한다. 이 경우 일반 Tree 모형보다 분산을 작게 해준다.
    • 또한, re-sampling 하는 과정에서 추출된 데이터를 가지고 modeling을 하고 추출되지 않은 데이터는 자동적으로 모형을 평가하는데 쓰이게 된다.(Out of Bag, 자동적으로 cross-validation)
  • Random forest는 Bagging과 유사하나, Bootstrap sampling 할 때 과정이 조금 상이하다.
    • 관측치 뿐만 아니라 변수 또한 임의로 뽑아서 modeling을 한다. (\(p\)개의 변수가 있을 때 일반적으로 \(\sqrt{p}\))
    • 변수를 임의로 뽑아서 하는 이유는 서로 상관성이 높은 변수들이 있으면 모형의 분산이 커지기 때문이다.(Decorrelation)
    • 따라서 Bagging에 비해서 예측력이 더 좋다.
  • Random forest는 변수의 중요도(Variable Importance)를 확인할 수 있다.
  • randomForest 패키지 안에 내장되어 있는 randomForest() 함수를 이용하여 모형을 적합시킬 수 있다.
  • randomForest(formula, data, subset, mtry, importance = TRUE, ...)

 

Support vector machine

  • 커널(Kenrel) 함수에 기반하고 초평면(Hyperplane) \(f(x)\)을 고려하여 초평면으로부터 support vector와의 간격 \(m\)값이 가장 큰 초평면을 decision boundary로 판단하는 방법이다. (Maximum margin hyperplane)
  • 초평면의 선형결합 형태는 다음과 같다.
  • \[f(x) = \beta_0 + \beta_1 x_1 + ... + \beta_p x_p = 0\]
  • \(m\) 값은 제약조건이 있는 최적화 문제(Optimization problem)로 Linear한 형태의 경우 다음과 같이 정의할 수 있다.
    • Maximize \(m\) such that \(\sum^{p}_{j=1} \beta_j^2 = 1\)
      \[y_i(\beta_0 + \beta_1 x_{i1} + ... + \beta_p x_{ip}) \ge m(1-\epsilon_i)\]
      slack variable \(\epsilon_i \ge 0\), and \(\sum \epsilon_i \le C\)
  • 상당히 robust한 장점도 있으며 Linear한 형태 이 외에도 커널 함수를 접목하여 Nonlinear decision boundary 에도 적용시킬 수 있다.
  • e1071 패키지 안에 내장되어 있는 svm() 함수를 이용하여 모형을 적합시킬 수 있다.
  • svm(formula, data, kernel, cost, scale, ...)

 

1. 3. Evaluate performance

  • 위에서 언급했듯 전체 데이터를 가지고 k-Folds Cross-Validation을 통해 Training data와 Test data로 분할하여 모델링을 진행할 것 이다. \((k=5)\)
    • k-Folds Cross-Validation을 통해 과적합(overfitting)을 피하고 결과에 대한 일반화를 도모할 수 있다.
  • Training data와 Test data를 분류하는 데이터의 index는 cvTools 패키지 안에 내장되어 있는 cvFolds() 함수를 이용하여 할당 할 수 있다.
  • 각 Fold에 해당하는 값을 다음과 같이 index 객체에 저장한다.
  • index <- cvFolds( nrow(OJ), K = 5 )
  • 따라서 Training data와 Test data를 다음과 같이 할당한다. (\(k = 1, 2, 3, 4, 5\))
  • # example
    OJ_train <- OJ[index$which == 1, ]
    OJ_test <- OJ[index$which != 1, ]
  • Training data를 가지고 분류 모델링을 한 후 Test data를 적용시켜 각 모형마다 오분류율을 구하고 이를 가지고 예측력을 평가한다.

 

2. Modeling

  • 데이터에서 주어진 변수들 중 Purchase, WeekofPurchase, SpecialCH, SpecialMM, LoyalCH, SalePriceMM, SalepriceCH, STORE, ListPriceDiff 변수들을 활용하여 모형에 적용시킬 것 이다.
  • 여러 가지 모형을 적용시킨 후 예측력이 가장 좋은 분석모형(오분류율이 가장 낮은 모형)을 최적의 모형으로 판단하겠다.
    • Test MSE를 구하는 과정에서 5-folds Cross-Validation을 근거로 하였다.
  • 분석 방법 안에서 최적의 모형을 찾는 것이 아닌 각 모형들간의 비교를 주된 목적으로 하므로 분석하는데 있어 사용된 함수들의 구체적인 옵션 argument들은 기본값으로 하였다.
  • k-folds Cross-Validation을 활용하였기에 빠른 계산과 코드의 간결성을 고려하여 for 문을 이용하여 각 함수를 적용하였다.

 

2. 1. Logistic regression

  • k-Fold Cross-Validation에 의해 오분류율이 할당되는 NULL 벡터 logistic_error를 생성한다.
  • # generate null vector assigned error rate
    logistic_error <- NULL
  • 모형식은 다음과 같이 정의할 수 있다.

\[\Pr(\mathbf X) = \frac{e^{\beta_0 + \beta_1 \text{WeekofPurchase} + \beta_2 \text{SpecialCH} + ... + \beta_8 \text{ListPriceDiff}}}{1 + e^{\beta_0 + \beta_1 \text{WeekofPurchase} + \beta_2 \text{SpecialCH} + ... + \beta_8 \text{ListPriceDiff}}}\]

  • for() 문을 통해 Cross-Validation을 적용하면서 glm() 함수를 이용하여 로지스틱 회귀분석에 적합시키고 오분류율을 계산한다.
    • 반응변수가 어느 class에 할당될 지를 결정하는 확률을 구하기 위해 predict() 함수를 이용하였고 ifelse() 함수를 통하여 예측된 class를 정의하였다.
    for (k in 1:5) {
    
      # k-Fold STEP
      OJ_train <- OJ[index$which == k, ]
      OJ_test <- OJ[index$which != k, ]
    
      # Fit logistic regression
      logistic_fit <- glm(Purchase ~ WeekofPurchase + SpecialCH + SpecialMM + 
                                     LoyalCH + SalePriceMM + SalePriceCH + 
                                     STORE + ListPriceDiff, 
                          data = OJ_train, 
                          family = binomial())
    
      # Classify response class
      logistic_pred <- ifelse( predict(logistic_fit, 
                                       newdata = OJ_test, 
                                       type = "response") >= 0.5, 
                               "MM", "CH")
    
      # Calculate mis-classification rate
      logistic_error[k] = mean( OJ_test$Purchase != logistic_pred )
    
    }
  • 마지막 fold를 이용하여 적합시킨 모형을 가지고 summary() 함수를 통해 변수의 유의성을 살펴본 결과 브랜드의 충성도(LoyalCH)가 가장 중요한 요인으로 예상할 수 있다.
    ## 
    ## Call:
    ## glm(formula = Purchase ~ WeekofPurchase + SpecialCH + SpecialMM + 
    ##     LoyalCH + SalePriceMM + SalePriceCH + STORE + ListPriceDiff, 
    ##     family = binomial(), data = OJ_train)
    ## 
    ## Deviance Residuals: 
    ##     Min       1Q   Median       3Q      Max  
    ## -2.4806  -0.5556  -0.1855   0.4805   3.0382  
    ## 
    ## Coefficients:
    ##                Estimate Std. Error z value Pr(>|z|)    
    ## (Intercept)     5.47181    4.14369   1.321   0.1867    
    ## WeekofPurchase -0.01622    0.01517  -1.069   0.2850    
    ## SpecialCH1      0.54619    0.77959   0.701   0.4835    
    ## SpecialMM1      0.07300    0.62429   0.117   0.9069    
    ## LoyalCH        -6.52606    0.97330  -6.705 2.01e-11 ***
    ## SalePriceMM    -2.50900    1.11507  -2.250   0.0244 *  
    ## SalePriceCH     3.93520    2.22765   1.767   0.0773 .  
    ## STORE1          0.25298    0.70270   0.360   0.7188    
    ## STORE2          0.58416    0.61092   0.956   0.3390    
    ## STORE3          0.40467    0.69164   0.585   0.5585    
    ## STORE4         -0.45714    0.82703  -0.553   0.5804    
    ## ListPriceDiff  -4.06142    2.59399  -1.566   0.1174    
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ## 
    ## (Dispersion parameter for binomial family taken to be 1)
    ## 
    ##     Null deviance: 287.56  on 213  degrees of freedom
    ## Residual deviance: 153.77  on 202  degrees of freedom
    ## AIC: 177.77
    ## 
    ## Number of Fisher Scoring iterations: 6
  • summary(logistic_fit)
  • 로지스틱 회귀분석을 적용했을 경우 Test MSE는 다음과 같다.
    ## [1] 0.1817757
  • ( logistic_MSE <- mean(logistic_error) )

 

2. 2. Linear discriminant analysis

  • k-Fold Cross-Validation에 의해 오분류율이 할당되는 NULL 벡터 lda_error를 생성한다.
  • # generate null vector assigned error rate
    lda_error <- NULL
  • for() 문을 통해 Cross-Validation을 적용하면서 lda() 함수를 이용하여 LDA 모형에 적합시키고 오분류율을 계산한다.
    • 반응변수의 예측 class를 구하기 위해 predict() 함수를 이용하였다.
    for (k in 1:5) {
    
      # k-Fold STEP
      OJ_train <- OJ[index$which == k, ]
      OJ_test <- OJ[index$which != k, ]
    
      # Fit LDA model
      lda_fit <- lda(Purchase ~ WeekofPurchase + SpecialCH + SpecialMM + 
                                LoyalCH + SalePriceMM + SalePriceCH + 
                                STORE + ListPriceDiff, 
                     data = OJ_train)
    
      # Predicted response class
      lda_pred <- predict(lda_fit, 
                          newdata = OJ_test, 
                          type = "response")$class
    
      # Calculate mis-classification rate
      lda_error[k] = mean( OJ_test$Purchase != lda_pred )
    
    }
  • 마지막 fold를 이용하여 적합시킨 모형을 가지고 class별 각 변수의 평균과 계수추정치를 살펴보면 다음과 같다.
    ## Call:
    ## lda(Purchase ~ WeekofPurchase + SpecialCH + SpecialMM + LoyalCH + 
    ##     SalePriceMM + SalePriceCH + STORE + ListPriceDiff, data = OJ_train)
    ## 
    ## Prior probabilities of groups:
    ##        CH        MM 
    ## 0.6028037 0.3971963 
    ## 
    ## Group means:
    ##    WeekofPurchase SpecialCH1 SpecialMM1   LoyalCH SalePriceMM SalePriceCH
    ## CH       257.4961 0.20155039  0.1395349 0.7342251    2.028372    1.799225
    ## MM       252.2118 0.09411765  0.2235294 0.3234680    1.898000    1.845765
    ##       STORE1    STORE2    STORE3     STORE4 ListPriceDiff
    ## CH 0.1085271 0.1782946 0.1085271 0.17054264     0.2376744
    ## MM 0.1411765 0.2823529 0.2941176 0.05882353     0.1943529
    ## 
    ## Coefficients of linear discriminants:
    ##                        LD1
    ## WeekofPurchase -0.00658402
    ## SpecialCH1      0.17684793
    ## SpecialMM1      0.15029155
    ## LoyalCH        -3.79436566
    ## SalePriceMM    -1.30844756
    ## SalePriceCH     1.28178918
    ## STORE1          0.10136868
    ## STORE2          0.25239586
    ## STORE3          0.30360433
    ## STORE4         -0.18613585
    ## ListPriceDiff  -2.42348020
  • lda_fit
  • LDA를 적용했을 경우 Test MSE는 다음과 같다.
    ## [1] 0.1810748
  • ( lda_MSE <- mean(lda_error) )

 

2. 3. Quadratic discriminant analysis

  • k-Fold Cross-Validation에 의해 오분류율이 할당되는 NULL 벡터 qda_error를 생성한다.
  • # generate null vector assigned error rate
    qda_error <- NULL
  • for() 문을 통해 Cross-Validation을 적용하면서 lda() 함수를 이용하여 LDA 모형에 적합시키고 오분류율을 계산한다.
    • 반응변수의 예측 class를 구하기 위해 predict() 함수를 이용하였다.
    for (k in 1:5) {
    
      # k-Fold STEP
      OJ_train <- OJ[index$which == k, ]
      OJ_test <- OJ[index$which != k, ]
    
      # Fit QDA model
      qda_fit <- qda(Purchase ~ WeekofPurchase + SpecialCH + SpecialMM +
                                LoyalCH + SalePriceMM + SalePriceCH + 
                                STORE + ListPriceDiff, 
                     data = OJ_train)
    
      # Predicted response class
      qda_pred <- predict(qda_fit, 
                          newdata = OJ_test, 
                          type = "response")$class
    
      # Calculate mis-classification rate
      qda_error[k] = mean( OJ_test$Purchase != lda_pred )
    
    }
  • QDA를 적용했을 경우 Test MSE는 다음과 같다.
    ## [1] 0.2030374
  • ( qda_MSE <- mean(qda_error) )

 

2. 4. k-Nearest neighbor

  • k-Fold Cross-Validation에 의해 오분류율이 할당되는 행렬 knn_error를 생성한다.
  • # generate null matrix assigned error rate
    knn_error <- matrix(NA, nrow = 150, ncol = 5)
  • for() 문을 통해 Cross-Validation을 적용하면서 knn.cv() 함수를 이용해 kNN 방법을 적용시킨다.
  • 여기서 k 값을 1~150까지 적용하여 Test MSE를 최소로 하는 최적의 k 값을 구한다.
  • for (i in 1:150) {
    
      for (k in 1:5) {
    
        # k-Fold STEP
        OJ_train <- OJ[index$which == k, ]
        OJ_test <- OJ[index$which != k, ]
    
        # Fit kNN & Predicted response class
        knn_fit <- knn.cv(train = OJ_train[, c(2, 8:12, 17:18)],
                          cl = OJ_train$Purchase,
                          k = i)
    
        # Calculate mis-classification rate
        knn_error[i, k] = mean( OJ_test$Purchase != knn_fit )
    
      }
    
    }
  • 그 결과 최적의 k값은 101이다.
  • 최적의 k값을 가지고 kNN을 적용했을 경우 Test MSE는 다음과 같다.
    ( knncv_MSE = mean( OJ_test$Purchase != knn_fit.opt ) )
    ## [1] 0.3878505
  • knn_fit.opt <- knn.cv(train = OJ_train[, c(2, 8:12, 17:18)],
                          cl = OJ_train$Purchase,
                          k = which.min( rowMeans(knn_error) ))

 

2. 5. Generalized additive models

  • k-Fold Cross-Validation에 의해 오분류율이 할당되는 NULL 벡터 gam_error를 생성한다.
  • # generate null vector assigned error rate
    gam_error <- NULL
  • for() 문을 통해 Cross-Validation을 적용하면서 gam() 함수를 이용하여 GAM에 적합시키고 오분류율을 계산한다.
  • \[\log \bigg(\frac{p(\textbf{X})}{1-p(\textbf{X})}\bigg) = \beta_0 + f_1(\text{WeekofPurchase}_{i}) + f_2(\text{LoyalCH}_{i}) + ... + f_5(\text{ListPriceDiff}_{i}) + \text{SpecialCH}_i + \text{SpeicalMM}_i + \text{STORE}_i\]
  • 여기서 factor형인 변수들을 제외한 나머지 변수들은 s() 함수를 이용하여 해당 함수를 smooth function \(f_p(x)\)화 시키겠다. s() 함수의 나머지 arguments는 기본값으로 하였다.
    • Logistic regression과 마찬가지로 반응변수가 어느 class에 할당될 지를 결정하는 확률을 구하기 위해 predict() 함수를 이용하였고 ifelse() 함수를 통하여 예측된 class를 정의하였다.
    for (k in 1:5) {
    
      # k-Fold STEP
      OJ_train <- OJ[index$which == k, ]
      OJ_test <- OJ[index$which != k, ]
    
      # Fit generalized additive model
      gam_fit <- gam(Purchase ~ s(WeekofPurchase) + SpecialCH + SpecialMM + 
                                s(LoyalCH) + s(SalePriceMM) + 
                                s(SalePriceCH) + STORE + s(ListPriceDiff),
                     data = OJ_train,
                     family = binomial())
    
    
      # Classify response class
      gam_pred <- ifelse( predict(gam_fit, 
                                  newdata = OJ_test, 
                                  type = "response") >= 0.5, 
                          "MM", "CH")
    
      # Calculate mis-classification rate
      gam_error[k] = mean( OJ_test$Purchase != gam_pred )
    
    }
  • 마지막 fold를 이용하여 적합시킨 모형을 가지고 summary() 함수를 통해 parametric term과 smooth function term의 유의성을 확인할 수 있다.
    ## 
    ## Family: binomial 
    ## Link function: logit 
    ## 
    ## Formula:
    ## Purchase ~ s(WeekofPurchase) + SpecialCH + SpecialMM + s(LoyalCH) + 
    ##     s(SalePriceMM) + s(SalePriceCH) + STORE + s(ListPriceDiff)
    ## 
    ## Parametric coefficients:
    ##             Estimate Std. Error z value Pr(>|z|)   
    ## (Intercept)  -1.2700     0.4845  -2.621  0.00876 **
    ## SpecialCH1    0.9424     0.8669   1.087  0.27695   
    ## SpecialMM1    0.1002     0.6896   0.145  0.88449   
    ## STORE1        0.5238     0.7688   0.681  0.49562   
    ## STORE2        0.8531     0.6610   1.291  0.19681   
    ## STORE3        0.4079     0.7626   0.535  0.59278   
    ## STORE4       -0.9537     0.9123  -1.045  0.29583   
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ## 
    ## Approximate significance of smooth terms:
    ##                     edf Ref.df Chi.sq  p-value    
    ## s(WeekofPurchase) 1.000  1.000  3.842   0.0500 *  
    ## s(LoyalCH)        4.707  5.721 48.770 9.18e-09 ***
    ## s(SalePriceMM)    2.761  3.471  7.664   0.0796 .  
    ## s(SalePriceCH)    2.974  3.694  7.470   0.0917 .  
    ## s(ListPriceDiff)  1.002  1.004  2.184   0.1396    
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ## 
    ## R-sq.(adj) =   0.56   Deviance explained = 52.9%
    ## UBRE = -0.18561  Scale est. = 1         n = 214
  • summary(gam_fit)
  • GAM을 적용했을 경우 Test MSE는 다음과 같다.
    ## [1] 0.1880841
  • ( gam_MSE <- mean(gam_error) )

 

2. 6. Classification trees

  • k-Fold Cross-Validation에 의해 오분류율이 할당되는 NULL 벡터 tree_error를 생성한다.
  • # generate null vector assigned error rate
    tree_error <- NULL
  • for() 문을 통해 Cross-Validation을 적용하면서 rpart() 함수를 이용하여 Tree 모형에 적합시키고 오분류율을 계산한다.
    • 반응변수의 예측 class를 구하기 위해 predict() 함수를 이용하였다.
     for (k in 1:5) {
    
      # k-Fold STEP
      OJ_train <- OJ[index$which == k, ]
      OJ_test <- OJ[index$which != k, ]
    
      # Fit tree model
      tree_fit <- rpart(Purchase ~ WeekofPurchase + SpecialCH + SpecialMM +
                                   LoyalCH + SalePriceMM + SalePriceCH + 
                                   STORE + ListPriceDiff, 
                        data = OJ_train)
    
      # Classify response class
      tree_pred <- predict(tree_fit, 
                           newdata = OJ_test, 
                           type = "class")
    
      # Calculate mis-classification rate
      tree_error[k] = mean( OJ_test$Purchase != tree_pred )
    
    }
  • 더 나은 시각화를 위해 partykit 패키지 안에 내장되어 있는 as.party() 함수를 통하여 적합된 모형 객체의 class를 적절히 변형시킨 후 plot() 함수를 이용하여 Tree를 출력해 본 결과는 다음과 같다.
  • plot( as.party(tree_fit) )
  • Tree 모형을 적용했을 경우 Test MSE는 다음과 같다.
    ## [1] 0.203271
  • ( tree_MSE <- mean(tree_error) )

 

2. 7. Random forest

  • k-Fold Cross-Validation에 의해 오분류율이 할당되는 NULL 벡터 rf_error를 생성한다.
  • # generate null vector assigned error rate
    rf_error <- NULL
  • for() 문을 통해 Cross-Validation을 적용하면서 randomForest() 함수를 이용하여 모형에 적합시키고 오분류율을 계산한다.
  • 여기서 뽑히는 변수의 갯수를 설정하는 mtry argument는 \(\sqrt8 \approx 3\), random forest를 구성하는 나무의 갯수를 설정하는 ntree argument는 기본값으로 설정하였다.
    • 반응변수의 예측 class를 구하기 위해 predict() 함수를 이용하였다.
     for (k in 1:5) {
    
      # k-Fold STEP
      OJ_train <- OJ[index$which == k, ]
      OJ_test <- OJ[index$which != k, ]
    
      # Fit random forest model
      rf_fit <- randomForest(Purchase ~ WeekofPurchase + SpecialCH + SpecialMM +
                                        LoyalCH + SalePriceMM + SalePriceCH + 
                                        STORE + ListPriceDiff, 
                             data = OJ_train,
                             mtry = sqrt(8),
                             importance = TRUE)
    
      # Classify response class
      rf_pred <- predict(rf_fit, newdata = OJ_test)
    
      # Calculate mis-classification rate
      rf_error[k] = mean( OJ_test$Purchase != rf_pred )
    
    }
  • varImpPlot 함수를 이용하여 변수의 중요도를 살펴보면 다음과 같다.
  • varImpPlot(rf_fit, main = "Variable importance as measured by Random forest")
  • Random forest 모형을 적용했을 경우 Test MSE는 다음과 같다.
    ## [1] 0.1850467
  • ( rf_MSE <- mean(rf_error) )

 

2. 8. Support vector machine

  • k-Fold Cross-Validation에 의해 오분류율이 할당되는 NULL 벡터 svm_error를 생성한다.
  • # generate null vector assigned error rate
    svm_error <- NULL
  • for() 문을 통해 Cross-Validation을 적용하면서 svm() 함수를 이용하여 모형에 적합시키고 오분류율을 계산한다.
  • 여기서 커널 함수는 radial basis kernel을 이용하였으며 (kernel = "radial"), 그에 따른 costgamma argument는 기본값으로 설정하였다.
     for (k in 1:5) {
    
      # k-Fold STEP
      OJ_train <- OJ[index$which == k, ]
      OJ_test <- OJ[index$which != k, ]
    
      # Fit SVM model
      svm_fit <- svm(Purchase ~ WeekofPurchase + SpecialCH + SpecialMM + 
                                LoyalCH + SalePriceMM + SalePriceCH + 
                                STORE + ListPriceDiff, 
                     data = OJ_train,
                     kernel = "radial")
    
      # Classify response class
      svm_pred <- predict(svm_fit, newdata = OJ_test)
    
      # Calculate mis-classification rate
      svm_error[k] = mean( OJ_test$Purchase != svm_pred )
    
    }
  • \[K(\mathbf u, \mathbf v) = \exp \bigg(-\frac{||\mathbf u - \mathbf v||^2}{2\sigma^2} \bigg)\]
  • Support vector machine 모형을 적용했을 경우 Test MSE는 다음과 같다.
    ## [1] 0.1778037
  • ( svm_MSE <- mean(svm_error) )

 

3. Conclusion

  • Logistic regression 이나 Random forest 모형에서 보였듯이 LoyalCH 가 구매하고자 하는 브랜드에 가장 영향을 많이 미쳤으며, 판매가격 (SalePriceMM) 또한 적지 않은 영향을 미쳤다.
  • k-folds Cross-Validation를 기반으로 위에서 언급한 각 분류모형들을 적용하여 Test MSE를 비교하여 보았다.

3. 1. Comparison Test MSE

  • 각 분류모형에서 구한 MSE는 다음과 같다.
    ##          Method    MSE
    ## 1      Logistic 0.1818
    ## 2           LDA 0.1811
    ## 3           QDA 0.2030
    ## 4         kNNCV 0.3879
    ## 5           GAM 0.1881
    ## 6          Tree 0.2033
    ## 7 Random Forest 0.1850
    ## 8           SVM 0.1778
  • mse.value <- round( c(logistic_MSE, lda_MSE, qda_MSE, knncv_MSE, 
                          gam_MSE, tree_MSE, rf_MSE, svm_MSE), 4)
    MSE <- data.frame( Method = c("Logistic", "LDA", "QDA", "kNNCV", 
                                  "GAM", "Tree", "Random Forest", "SVM"),
                       MSE = mse.value ) 
    MSE
  • MSE를 비교한 그림은 다음과 같다. 값을 시각화하는데 쓰인 함수는 ggplot2 패키지 안에 내장되어 있는 ggplot() 함수를 이용하였다.
    p + geom_text(aes( x = 4, y = mse.value[1]-0.1, label = mse.value[1], size = 5, vjust = 0.5)) + 
        geom_text(aes( x = 3, y = mse.value[2]-0.1, label = mse.value[2], size = 5, vjust = 0.5)) + 
        geom_text(aes( x = 5, y = mse.value[3]-0.1, label = mse.value[3], size = 5, vjust = 0.5)) + 
        geom_text(aes( x = 2, y = mse.value[4]-0.1, label = mse.value[4], size = 5, vjust = 0.5)) + 
        geom_text(aes( x = 1, y = mse.value[5]-0.1, label = mse.value[5], size = 5, vjust = 0.5)) + 
        geom_text(aes( x = 8, y = mse.value[6]-0.1, label = mse.value[6], size = 5, vjust = 0.5)) + 
        geom_text(aes( x = 6, y = mse.value[7]-0.1, label = mse.value[7], size = 5, vjust = 0.5)) + 
        geom_text(aes( x = 7, y = mse.value[8]-0.1, label = mse.value[8], size = 5, vjust = 0.5))
  • theme_set(theme_bw())
    p <- ggplot(MSE, aes(Method, MSE, fill = Method)) + 
         geom_bar(stat = "identity") + 
         labs(title = "Comparison") 
    p <- p + theme(legend.position = "none", 
                   plot.title = element_text( hjust = 0.5 ), 
                   axis.text.x = element_text(size = 13, angle = 30),
                   axis.text.y = element_text(size = 10))
  • k-Nearest Neighbor 방법이 Test MSE가 가장 높게 나왔으며 Support vector machine을 이용한 방법이 Test MSE가 가장 낮게 출력되었다.

 

3. 2. Summary

  • 가격에 따른 구매차이보다는 브랜드 충성도에 따른 구매 차이가 더 큰 것으로 보인다.
  • 몇 가지 가정을 전제로하는 로지스틱 회귀모형이나 LDA 모형의 경우에는 비슷한 performance를 보이며, 가장 높은 오분류율을 나타낸 kNN의 경우 이 데이터의 반응변수를 예측하는 데 있어서 다소 부적절하다고 생각된다.
  • 또한 예상되어진대로 Tree 모형보다 보완점을 개선한 Random forest 모형이 더 좋은 예측력을 나타내고 있다.
  • Smooth function을 기반으로 하는 비모수적인 관점인 GAM 모형 역시 Random forest와 비슷한 performance를 나타내고 있다.
  • 하지만 결과적으로 Test MSE 관점에서 보면 Support vector machine을 이용한 분류모형이 가장 최적의 모형으로 판단되어진다.

반응형

'ETC' 카테고리의 다른 글

[R] R을 활용한 테일러 전개  (0) 2017.07.04
[R] R을 활용한 미분과 적분 계산  (0) 2017.07.04
TAGS.

Comments