1. Introduction

2007~2008년의 세계 금융 위기는 은행 업무의 투명성과 엄격함의 중요성을 부각시켰다.
신용의 유효성이 제한되면서 은행은 대출 시스템을 강화하고 위험 대출을 더 정확하게 찾아내기 위해 머신 러닝으로 전환했다.

의사 결정 트리는 쉽게 말하면 높은 정확성과 통계 모델을 표현하는 능력 때문에 은행 업계에서 널리 사용되고 있다.
많은 나라의 정부기관은 대출 업무를 면밀히 감시하고 있기 때문에 경영진은 다른 사람들은 대출이 승인되고 한 신청자만
대출이 거절됐다면 그 사유를 설명할 수 있어야만 한다. 이런 정보는 신용 평가가 만족스럽지 않은 이유를 확인하려는 고객들에게도 유용하다.

자동화된 신용 평가 모델을 이용해 전화나 웹에서 대출 신청을 즉시 승인할 수도 있다.
C5.0 의사결정 트리를 이용해 간단한 대출 승인 모델을 개발한다. 또한 기관의 재정적 손실을 야기하는 오류를 최소화하기 위해 모델의 성능을 개선할 것이다.

1.1. Aim

독일 신용 데이터를 기준으로 통계 예측 모형을 개발한다.

2. Collecting data

신용 모델 이면의 아이디어는 채무불이행될 위험이 높은지를 예측할 수 있는 요소를 식별하는 것이다. 따라서 과거 은행 대출에 대한 대량 데이터,
대출의 채무불이행 여부, 신청자에 대한 정보를 입수해야 한다.

이런 특성을 갖는 데이터는 [UCI 머신러닝 데이터 저장소](https://archive.ics.uci.edu/ml/datasets/statlog+(german+credit+data) 에서 얻을 수 있다. 이 데이터셋에는 독일의 한 신용기관에서 얻은 대출 정보가 들어있다.

2.1. Data Description

1,000개의 대출 데이터셋에는 대출과 대출 신청자의 특성을 나타내는 일련의 수치 특징과 명목 특징을 포함하고 있다. 클래스 변수는 대출이 채무불이행으로 갔는지 여부를 나타낸다. 즉, 20개의 설명 변수와 1개의 종속 변수(class)가 있다.

변수명 속성 변수 설명
check 범주형 자유예금형태 (Status of existing checking account)
cduration 수치형 기간 (Duration in month)
history 범주형 과거신용정보 (Credit history)
purpose 범주형 목적 (Purpose)
credit 수치형 신용대출금액 (Credit amount)
savings 범주형 저축예금/채권 (Savings account/bonds)
employment 범주형 현직장 재직기간 (Present employment since)
installment 수치형 가처분소득 대비 적금비율 (Installment rate in percentage of disposable income)
personal 범주형 결혼상황 및 성별 (Personal status and sex)
debtors 범주형 여타 채무/채권 (Other debtors / guarantors)
residence 수치형 현 거주기간 (Present residence since)
property 범주형 재산 (Property)
age 수치형 나이(Age in years)
others 범주형 여타적금 (Other installment plans)
housing 범주형 주거형태 (Housing)
numcredits 수치형 해당 은행 신용계좌 수 (Number of existing credits at this bank)
job 범주형 직업 (Job)
residpeople 수치형 부양가족수 (Number of people being liable to provide maintenance for)
telephone 범주형 전화소유 (Telephone)
foreign 범주형 외국인 노동자 여부 (foreign worker)
y 범주형 신용등급 양호 또는 불량 (credit:Good or Bad)

3. Exploring and preparing the data

3.1. Import Libary & Load Data

suppressMessages(library(dplyr)) #edit
suppressMessages(library(readxl)) #excel load
suppressMessages(library(doBy))
suppressMessages(library(fmsb)) # radar chart
suppressMessages(library(ggplot2)) #visualization
suppressMessages(library(corrplot)) #correlation
suppressMessages(library(VIM)) #missing data detection
suppressMessages(library(DMwR)) #outlier detection
suppressMessages(library(corrplot)) #correlation plot
suppressMessages(library(PerformanceAnalytics)) #correlation chart
suppressMessages(library(rpart)) #decision tree
suppressMessages(library(C50)) # decision tree
suppressMessages(library(rattle)) #decision tree fancy tree
suppressMessages(library(rpart.plot)) #decision tree fancy tree
suppressMessages(library(RColorBrewer)) #decision tree fancy tree
suppressMessages(library(ipred)) # bagging
suppressMessages(library(randomForest)) # random forest
suppressMessages(library(adabag)) # adaptive boosting
suppressMessages(library(lme4)) #dummy function
suppressMessages(library(caret)) #standard
suppressMessages(library(class)) #KNN 
suppressMessages(library(VennDiagram)) #VennDiagram
suppressMessages(library(neuralnet)) #ann
suppressMessages(library(e1071)) #SVM
suppressMessages(library(ROCR)) #ROC

suppressMessages(library(mctest))
suppressMessages(library(dummies))
suppressMessages(library(Information))
suppressMessages(library(pROC))

credit <- read.csv('input/credit.csv')

3.2. Exploring data

  • 1000개의 관측치와 17개의 변수
    • 채무 불이행을 예측할 것 같은 일부 대출 특징에 대해 table 출력
str(credit)
## 'data.frame':    1000 obs. of  17 variables:
##  $ checking_balance    : Factor w/ 4 levels "< 0 DM","> 200 DM",..: 1 3 4 1 1 4 4 3 4 3 ...
##  $ months_loan_duration: int  6 48 12 42 24 36 24 36 12 30 ...
##  $ credit_history      : Factor w/ 5 levels "critical","good",..: 1 2 1 2 4 2 2 2 2 1 ...
##  $ purpose             : Factor w/ 6 levels "business","car",..: 5 5 4 5 2 4 5 2 5 2 ...
##  $ amount              : int  1169 5951 2096 7882 4870 9055 2835 6948 3059 5234 ...
##  $ savings_balance     : Factor w/ 5 levels "< 100 DM","> 1000 DM",..: 5 1 1 1 1 5 4 1 2 1 ...
##  $ employment_duration : Factor w/ 5 levels "< 1 year","> 7 years",..: 2 3 4 4 3 3 2 3 4 5 ...
##  $ percent_of_income   : int  4 2 2 2 3 2 3 2 2 4 ...
##  $ years_at_residence  : int  4 2 3 4 4 4 4 2 4 2 ...
##  $ age                 : int  67 22 49 45 53 35 53 35 61 28 ...
##  $ other_credit        : Factor w/ 3 levels "bank","none",..: 2 2 2 2 2 2 2 2 2 2 ...
##  $ housing             : Factor w/ 3 levels "other","own",..: 2 2 2 1 1 1 2 3 2 2 ...
##  $ existing_loans_count: int  2 1 1 1 2 1 1 1 1 2 ...
##  $ job                 : Factor w/ 4 levels "management","skilled",..: 2 2 4 2 2 4 2 1 4 1 ...
##  $ dependents          : int  1 1 2 2 2 2 1 1 1 1 ...
##  $ phone               : Factor w/ 2 levels "no","yes": 2 1 1 1 1 2 1 2 1 1 ...
##  $ default             : Factor w/ 2 levels "no","yes": 1 2 1 1 2 1 1 1 1 2 ...
table(credit$checking_balance)
## 
##     < 0 DM   > 200 DM 1 - 200 DM    unknown 
##        274         63        269        394
table(credit$savings_balance)
## 
##      < 100 DM     > 1000 DM  100 - 500 DM 500 - 1000 DM       unknown 
##           603            48           103            63           183
summary(credit$months_loan_duration)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     4.0    12.0    18.0    20.9    24.0    72.0
summary(credit$amount)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     250    1366    2320    3271    3972   18424

Summary 1 : EDA를 통해 알아본 대출금과 기간의 통계량

- 대출금은 4개월에서 72개월의 기한에 걸쳐 250DM에서 18,424DM까지의 범위에 있고,
- 기간의 중앙값은 18개월, 대출금의 중앙값은 2,320DM이다(100마르크 = 6~7만원).

수표 계좌 잔고와 저축 계좌 잔고는 대출 채무 불이행 상태의 중요한 예측 변수가 될 수 있다.

- 신청자의 수표 계좌 잔고와 저축 계좌 잔고가 범주형 변수로 저장되어 있다.
- 대출 신청시 예금 계좌와 적금 계좌를 확인해서 예금액이 많을 수록 대출이 안전하다고 가정할 수 있다.
- 대출 데이터가 독일에서 입수됐기 때문에 통화의 단위가 독일 마르크(DM)으로 기록
# look at the class variable
table(credit$default)
## 
##  no yes 
## 700 300

Summary 2 : EDA를 통해 알아본 대출의 성공과 실패

- default 벡터는 대출 신청자가 합의된 지불 조건을 맞출 수 없어서 채무 불이행을 했는지를 나타낸다.
- 대출의 30%는 채무 불이행

부도율이 높으면 은행이 투자를 완전히 회수하지 못할 가능성이 있다는 의미기 때문에 은행에게 바람직하지 않다.
모델 개발에 성공한다면 이 모델이 채무불이행의 위험이 높은 신청자를 찾아내 은행이 대출 신청을 거절하게 만들어줄 것이다.

4. Modeling 1 : Decision Tree- C5.0 Algorithm

4.1. data preparation - creating random training and test datasets

  • 데이터의 수에 따라 Train / Test의 비율을 6:4~9:1로 정한다.
  • 훈련 데이터셋과 테스트 데이터셋을 9:1로 정했고, 각 데이터셋은 약 30%의 채무 불이행 대출을 가졌다.
# create a random sample for training and test data
# use set.seed to use the same random number sequence as the tutorial
set.seed(123)
train_sample <- sample(1000, 900)

str(train_sample)
##  int [1:900] 288 788 409 881 937 46 525 887 548 453 ...
# split the data frames
data_train <- credit[train_sample, ]
data_test  <- credit[-train_sample, ]

# check the proportion of class variable
prop.table(table(data_train$default))
## 
##        no       yes 
## 0.7033333 0.2966667
prop.table(table(data_test$default))
## 
##   no  yes 
## 0.67 0.33
dim(data_train)
## [1] 900  17
prop.table(table(data_train$default))
## 
##        no       yes 
## 0.7033333 0.2966667
dim(data_test)
## [1] 100  17
prop.table(table(data_test$default))
## 
##   no  yes 
## 0.67 0.33

4.2. training a model on the data - C5.0 Decision Trees

# build the simplest decision tree
data_model <- C5.0(data_train[-17], data_train$default)

# display simple facts about the tree
data_model
## 
## Call:
## C5.0.default(x = data_train[-17], y = data_train$default)
## 
## Classification Tree
## Number of samples: 900 
## Number of predictors: 16 
## 
## Tree size: 57 
## 
## Non-standard options: attempt to group attributes
# display detailed information about the tree
#summary(data_model)

Summary 3 : Decision Tree

checking_balance in {> 200 DM,unknown}: no (412/50)
checking_balance in {1 - 200 DM,< 0 DM}:
:...credit_history in {perfect,very good}: yes (59/18)
  • 수표 계좌를 모르거나 200DM 이상이면 ’채무불이행 가능성 없음(not likely default)’으로 분류한다.
  • 그렇지 않으면 수표 계좌 잔고가 0보다 작거나 1에서 200DM 이하인 경우다.
  • 대출 이력이 ’완벽(perfect)’이나 ’매우 좋음(very good)’이면 ’채무불이행 가능성이 있음(likely to default)’으로 분류한다.

가끔 트리는 논리적으로 이해가 안 되는 결정을 만든다. 예를 들어, “왜 대출 이력이 매우 좋은 신청자가 채무불이행의 가능성이 있고, 수표 계좌 잔고를 모르는 신청자가 왜 채무불이행의 가능성이 없는가?” 이처럼 모순된 규칙이 가끔씩 발견된다. 그런 규칙들은 데이터에 실제 패턴을 반영하기도 하고, 통계적 이상치일 수도 있다. 두 경우 모두, 트리의 논리가 비즈니스 용도로 합당한지 확인하기 위해 이런 이상한 결정을 조사해보는 것이 중요하다.

Evaluation on training data (900 cases):

        Decision Tree   
      ----------------  
      Size      Errors  

        56  133(14.8%)   <<


       (a)   (b)    <-classified as
      ----  ----
       598    35    (a): class no
        98   169    (b): class yes

Errors 출력은 모델이 14.8%의 오류율로 900개의 훈련 인스턴스 중 113개를 제외하고 전부 정확하게 분류했음을 말하고 있다.
총35개의 실제 no값은 yes로 부정확하게 분류된 반면(거짓 긍정), 98개의 yes 값은 no로 오분류 됐다(거짓 부정).

의사결정 트리는 모델을 훈련 데이터에 과적합 시키는 경향이 있는 것으로 알려져 있다. 이런 이유로 훈련 데이터에 대해 보고된 오류율은 매우 낙관적일 수 있으며, 테스트 데이터셋에 대해 의사결정 트리를 평가하는 것은 중요하다.

4.3. Evaluating model performance

## Evaluating model performance
# create a factor vector of predictions on test data
data_pred <- predict(data_model, data_test)

# cross tabulation of predicted versus actual classes
library(gmodels)
CrossTable(data_test$default, data_pred,
           prop.chisq = FALSE, prop.c = FALSE, prop.r = FALSE,
           dnn = c('actual default', 'predicted default'))
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  100 
## 
##  
##                | predicted default 
## actual default |        no |       yes | Row Total | 
## ---------------|-----------|-----------|-----------|
##             no |        59 |         8 |        67 | 
##                |     0.590 |     0.080 |           | 
## ---------------|-----------|-----------|-----------|
##            yes |        19 |        14 |        33 | 
##                |     0.190 |     0.140 |           | 
## ---------------|-----------|-----------|-----------|
##   Column Total |        78 |        22 |       100 | 
## ---------------|-----------|-----------|-----------|
## 
## 

Summary 4 : Test set 평가 지표

100개 테스트 대출 신청 레코드 중에서 59개는 채무불이행되지 않았고,
14개는 채무불이행됐음을 모델이 정확하게 예측해서 정확도는 73%이고, 오류율은 27%가 됐다.

처음 보는 데이터에 대해 모델 성능이 더 나빠진다는 점을 감안하면 이 결과가 훈련 데이터에 대한 성능보다 다소 나쁘기는 하지만 전혀 예상 밖인 것은 아니다. 또한 모델이 테스트 데이터에서 실제 33개의 대출 채무불이행 중 단지 14개(42%)만 정확하게 예측했다는 점을 주목하자.

유감스럽게도 이런 오류 유형은 각각의 채무 불이행에 대해 은행이 손해를 보기 때문에 잠재적으로 비용이 매우 많이 드는 실수다.
더 노력해서 결과를 개선할 수 있는지 살펴보겠다.

4.4. Improving model performance

모델 성능 개선의 필요성

실시간 신용 평가 애플리케이션에 모델을 배포하기에는 이 모델의 오류율이 높다. 실제 모델이 모든 테스트 케이스에 대해 ’채무불이행이 아님’으로 예측했다면, 67%는 정확했을 것이다(이 모델보다는 성능이 많이 나쁘진 않지만, 훨씬 적은 노력이 드는 결과다). 900개의 예시에서 대출의 채무불이행을 예측한다는 것은 어려운 문제 같아 보인다.

설상가상으로 자신의 대출을 채무불이행하는 신청자를 찾을 때 이 모델은 특히 저조한 성능을 보인다. 다행히 전반적인 실수 유형이나 더 비용이 많이 드는 실수 유형에 대해 모델의 성능을 향상시키는 몇 가지 간단한 C5.0 알고리즘 조정 방법이 있다.

4.4.1. Boosting the accuracy of decision trees (의사결정 트리의 정확도 향상)

  • C5.0 알고리즘이 C4.5 알고리즘을 개선한 방법 중 하나는 적응형 부스팅(AdaBoost)을 추가한 것이다. 이 방법은 여러 개의 의사결정 트리를 만들어서 각 예시에 대해 최고 클래스를 투표하게 만드는 과정이다.

  • 부스팅은 성능이 약한 여러 학습자를 결헙함으로써 어느 한 학습자 혼자보다 훨씬 강한 팀을 만들 수 있다는 생각에 뿌리를 두고 있다. 각 모델은 각자의 유일한 강점과 약점을 찾으며, 특정 문제를 풀 때 더 좋거나 더 나쁠 수 있다. 그러므로 상호 보완적인 장점과 단점을 갖는 여러 학습자를 조합해 분류기의 정확도를 극적으로 향상시킬 수 있다.

  • 부스팅 팀에 사용할 독립적인 의사결정 트리의 개수를 나타내는 trials 파라미터를 간단히 추가한다. trials 파라미터는 상한선을 설정한다. 추가 시행이 정확도를 향상시키지 못할 것으로 보이면 알고리즘은 더 이상 트리를 추가하지 않는다.

  • 10회 시행으로 시작할 것인데, 연구에 따르면 10회 시행 시 테스트 데이터에 대한 오류율이 약 25% 정도 줄기때문에 이 숫자는 사실상 표준이 된 상태다.

## Improving model performance
## Boosting the accuracy of decision trees
# boosted decision tree with 10 trials
data_boost10 <- C5.0(data_train[-17], data_train$default,
                       trials = 10)
data_boost10
## 
## Call:
## C5.0.default(x = data_train[-17], y = data_train$default, trials = 10)
## 
## Classification Tree
## Number of samples: 900 
## Number of predictors: 16 
## 
## Number of boosting iterations: 10 
## Average tree size: 47.5 
## 
## Non-standard options: attempt to group attributes
#summary(data_boost10) 

10회의 반복을 통해 트리의 크기가 줄어들었다.

Evaluation on training data (900 cases):

Trial       Decision Tree   
-----     ----------------  
      Size      Errors  

   0        56  133(14.8%)
   1        34  211(23.4%)
   2        39  201(22.3%)
   3        47  179(19.9%)
   4        46  174(19.3%)
   5        50  197(21.9%)
   6        55  187(20.8%)
   7        50  190(21.1%)
   8        51  192(21.3%)
   9        47  169(18.8%)
boost            34( 3.8%)   <<


       (a)   (b)    <-classified as
      ----  ----
       629     4    (a): class no
        30   237    (b): class yes

Summary 5 : Boosting model

이 분류기는 오류율이 3.8%로 900개의 훈련 예시에 대해 34개의 실수를 했다.
부스팅을 추가하기 전의 훈련 오류율 13.9%에 비하면 엄청나게 개선된 것이다.
하지만 테스트 데이터에 대해서도 비슷한 개선을 확인할 수 있는지는 더 지켜봐야 한다.
data_boost_pred10 <- predict(data_boost10, data_test)
CrossTable(data_test$default, data_boost_pred10,
           prop.chisq = FALSE, prop.c = FALSE, prop.r = FALSE,
           dnn = c('actual default', 'predicted default'))
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  100 
## 
##  
##                | predicted default 
## actual default |        no |       yes | Row Total | 
## ---------------|-----------|-----------|-----------|
##             no |        62 |         5 |        67 | 
##                |     0.620 |     0.050 |           | 
## ---------------|-----------|-----------|-----------|
##            yes |        13 |        20 |        33 | 
##                |     0.130 |     0.200 |           | 
## ---------------|-----------|-----------|-----------|
##   Column Total |        75 |        25 |       100 | 
## ---------------|-----------|-----------|-----------|
## 
## 

Summary 6 : Boosting model

- 이 결과를 보면 전체 오류율이 부스팅 이전 27%에서 부스팅 모델의 18%로 줄었다.
    - 이득이 큰 것처럼 보이지 않을 수도 있겠지만, 예상했던 25%의 감소보다 실제 더 크게 감소 됐다.
    - 한편으로 모델은 여전히 채무 불이행을 잘 예측하지 못하고, 20/33 = 61%만 정확히 예측하고 있다.
    - 더 크게 개선되지 않는 이유는 상대적으로 작은 훈련 데이터셋과 함수 관계에 있거나, 이 문제가 원래 해결하기 매우 어려운 문제이기 때문일 수도 있다.

- "부스팅이 이렇게 쉽게 추가될 수 있다면 왜 모든 의사결정 트리에 디폴트로 적용하지 않는가?"
    - 두 가지 이유가 있다.
    - 첫째, 의사결정 트리를 한 번 만드는데 계산 시간이 많이 걸린다면 여러 개의 트리를 만드는 것은 계산적으로 비현실적일 수 있다.
    - 둘째, 훈련 데이터에 잡음이 많으면 부스팅으로 전혀 개선되지 않을 수 있다.
    - 여전히 더 높은 정확도가 필요하다면 시도해볼 가치는 있다.
    

4.5. Making some mistakes more costly than others

Summary 7 : 비용 행렬 (Cost Matrix)

- 채무불이행 가능성이 있는 신청자에게 대출을 해주는 것은 비용이 많이 드는 실수가 될 수 있다.
- 거짓 부정 개수를 줄일 수 있는 방안 중 하나는,
    - 은행이 위험한 대출로부터 돈을 전혀 상환받지 못할 때 일어날 대규모의 손실이 벌게 될 이자를 능가한다는 가정 하에
    - 애매한 신청자를 좀 더 많이 거절하는 것이다.

- C5.0 알고리즘은 트리가 좀 더 비용이 많이 드는 실수를 하지 못하게 여러 오류 유형에 패널티를 줄 수 있다.
- 패널티는 비용 행렬(Cost Matrix)에 지정되며, 각 오류가 다른 예측에 비해 얼마나 비용이 많이 드는 지를 명시한다.

- 비용 행렬을 구성하기 위해 먼저 차원을 지정한다. 예측된 값과 실제 값 모두 yes 또는 no 두 값을 갖기 때문에
    이 두 값으로 이뤄진 두 벡터의 리스트를 이용해 2x2 행렬을 기술한다. 이와 함께 이후의 혼란을 피하기 위해 행렬의 차원에 이름을 지정할 것이다.

4.5.1. create dimensions for a cost matrix

## Making some mistakes more costly than others

# create dimensions for a cost matrix
matrix_dimensions <- list(c("no", "yes"), c("no", "yes"))
names(matrix_dimensions) <- c("predicted", "actual")
matrix_dimensions
## $predicted
## [1] "no"  "yes"
## 
## $actual
## [1] "no"  "yes"

4.5.2. build the matrix

다음은 다양한 오류 유형에 패널티를 주기 위해 네 개의 값을 제공해 행렬을 채운다.
R은 행렬을 채울 때 열 단위로 위에서 아래 방향으로 채우기 때문에 특정 순서대로 값을 제공해야 한다.

  • Predicted : no / Actual : no
  • Predicted : yes / Actual : no
  • Predicted : no / Actual : yes
  • Predicted : yes / Actual : yes

은행은 대출의 채무불이행으로 놓친 기회의 4배만큼 비용이 드는 것으로 생각한다고 가정해보자.
그러면 패널티 값은 아래와 같이 정의될 수 있다.

# build the matrix
error_cost <- matrix(c(0, 1, 4, 0), nrow = 2, dimnames = matrix_dimensions)
error_cost
##          actual
## predicted no yes
##       no   0   4
##       yes  1   0

이 행렬에 정의된 것처럼 알고리즘 분류기가 no 또는 yes를 정확히 분류할 때는 비용이 할당되지 않지만, 거짓 부정은 거짓 긍정의 비용 1에 대해 비용 4를 갖는다. 비용 행렬이 분류에 어떻게 영향을 주는지 확인하기 위해 C5.0()함수의 costs 파라미터를 이용해서 의사결정 트리에 적용해보자. 그렇지 않으면 이전에 했던 것과 같은 단계를 적용할 것이다.

4.5.3. apply the cost matrix to the tree

# apply the cost matrix to the tree
data_cost <- C5.0(data_train[-17], data_train$default,
                          costs = error_cost)
data_cost_pred <- predict(data_cost, data_test)

CrossTable(data_test$default, data_cost_pred,
           prop.chisq = FALSE, prop.c = FALSE, prop.r = FALSE,
           dnn = c('actual default', 'predicted default'))
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  100 
## 
##  
##                | predicted default 
## actual default |        no |       yes | Row Total | 
## ---------------|-----------|-----------|-----------|
##             no |        37 |        30 |        67 | 
##                |     0.370 |     0.300 |           | 
## ---------------|-----------|-----------|-----------|
##            yes |         7 |        26 |        33 | 
##                |     0.070 |     0.260 |           | 
## ---------------|-----------|-----------|-----------|
##   Column Total |        44 |        56 |       100 | 
## ---------------|-----------|-----------|-----------|
## 
## 

Summary 8 : 새로운 평가 지표

부스팅 모델과 비교하면 비용 행렬 37% vs. 부스팅 18% 오류로 이 버전이 전체적으로 좀 더 많은 실수를 한다. 하지만 실수의 유형은 매우 다르다. 이전 모델이 채무불이행을 단지 39% 부정확하게 분류했고, 61%를 정확하게 분류했다면, 이 모델의 경우 실제 채무불이행의 79%가 채무 불이행한 것으로 예측됐다. 거짓 긍정을 증가시킨 대가로 거짓 부정을 줄인 이 거래는 비용추정이 정확하다면 수용 가능하다.

5. Modeling 2 : Decision Tree - CART

5.1. Training a model on the data (모델 훈련)

  • cp(Complexity Parameter) : cp 값을 작게 줄수록 복잡도가 올라감
  • rpart 함수에서 조절 가능한 parameter
    • minsplit : min of observations (20)
    • xval : of closs validation (10)
    • maxdepth : max depth of any node (30)
## Training
dt <- rpart(as.factor(default)~., data = data_train, cp = 0.1^20) # 모든 변수 사용, Full tree 생성

xerror_min_which <- which.min(dt$cptable[, "xerror"])
xerror_min <- min(dt$cptable[, "xerror"])

printcp(dt) # cptable 출력
## 
## Classification tree:
## rpart(formula = as.factor(default) ~ ., data = data_train, cp = 0.1^20)
## 
## Variables actually used in tree construction:
##  [1] age                  amount               checking_balance    
##  [4] credit_history       employment_duration  housing             
##  [7] job                  months_loan_duration other_credit        
## [10] percent_of_income    purpose              savings_balance     
## [13] years_at_residence  
## 
## Root node error: 267/900 = 0.29667
## 
## n= 900 
## 
##           CP nsplit rel error  xerror     xstd
## 1 4.1199e-02      0   1.00000 1.00000 0.051325
## 2 1.8727e-02      4   0.80899 0.88390 0.049421
## 3 1.4981e-02      5   0.79026 0.92884 0.050202
## 4 1.3733e-02      6   0.77528 0.93633 0.050326
## 5 7.4906e-03     16   0.62172 0.91760 0.050012
## 6 6.2422e-03     17   0.61423 0.94007 0.050388
## 7 5.6180e-03     20   0.59551 0.95506 0.050631
## 8 3.7453e-03     22   0.58427 0.98127 0.051042
## 9 1.0000e-20     33   0.53184 1.03745 0.051862
plotcp(dt) # cpplot 출력

abline(v = xerror_min_which, lty = 2, col = "red")
text(xerror_min_which, xerror_min, labels = round(xerror_min_which, 2), pos = 3, col = "red")

5.2. Pruning (가지 치기)

  • Training Set을 이용하여 Cross Validation 수행
    • 이유 : training할 때, 포함되지 않았던 data로 error를 측정해서 성능을 검증하기 위해서

  • validation Error가 증가하는 시점에서 가지치기
# pruning
dt_prune <- prune(dt, cp = dt$cptable[which.min(dt$cptable[, "xerror"]), "CP"])

5.3. Evaluating model performance (모델 성능 평가)

5.3.1. create a factor vector of predictions on test data - Accuracy

# training accuracy
pred_tr_dt <- predict(dt_prune, type = "class") # class(범주형)으로 예측
t_tr_dt <- table(pred_tr_dt, data_train$default) # confusion matrix
t_tr_dt
##           
## pred_tr_dt  no yes
##        no  551 134
##        yes  82 133
print("Accuracy")
## [1] "Accuracy"
acc_tr_dt <- sum(diag(t_tr_dt)) / sum(t_tr_dt) # accuracy
acc_tr_dt
## [1] 0.76
CrossTable(x = data_train$default, y = pred_tr_dt, prop.chisq = FALSE)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  900 
## 
##  
##                    | pred_tr_dt 
## data_train$default |        no |       yes | Row Total | 
## -------------------|-----------|-----------|-----------|
##                 no |       551 |        82 |       633 | 
##                    |     0.870 |     0.130 |     0.703 | 
##                    |     0.804 |     0.381 |           | 
##                    |     0.612 |     0.091 |           | 
## -------------------|-----------|-----------|-----------|
##                yes |       134 |       133 |       267 | 
##                    |     0.502 |     0.498 |     0.297 | 
##                    |     0.196 |     0.619 |           | 
##                    |     0.149 |     0.148 |           | 
## -------------------|-----------|-----------|-----------|
##       Column Total |       685 |       215 |       900 | 
##                    |     0.761 |     0.239 |           | 
## -------------------|-----------|-----------|-----------|
## 
## 
# test accuracy
pred_te_dt <- predict(dt_prune, data_test, type = "class")
t_te_dt <- table(pred_te_dt, data_test$default)
t_te_dt
##           
## pred_te_dt no yes
##        no  62  20
##        yes  5  13
acc_te_dt <- sum(diag(t_te_dt)) / sum(t_te_dt)
acc_te_dt
## [1] 0.75
CrossTable(x = data_test$default, y = pred_te_dt, prop.chisq = FALSE)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  100 
## 
##  
##                   | pred_te_dt 
## data_test$default |        no |       yes | Row Total | 
## ------------------|-----------|-----------|-----------|
##                no |        62 |         5 |        67 | 
##                   |     0.925 |     0.075 |     0.670 | 
##                   |     0.756 |     0.278 |           | 
##                   |     0.620 |     0.050 |           | 
## ------------------|-----------|-----------|-----------|
##               yes |        20 |        13 |        33 | 
##                   |     0.606 |     0.394 |     0.330 | 
##                   |     0.244 |     0.722 |           | 
##                   |     0.200 |     0.130 |           | 
## ------------------|-----------|-----------|-----------|
##      Column Total |        82 |        18 |       100 | 
##                   |     0.820 |     0.180 |           | 
## ------------------|-----------|-----------|-----------|
## 
## 
confusionMatrix(data_test$default, pred_te_dt)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction no yes
##        no  62   5
##        yes 20  13
##                                           
##                Accuracy : 0.75            
##                  95% CI : (0.6534, 0.8312)
##     No Information Rate : 0.82            
##     P-Value [Acc > NIR] : 0.97051         
##                                           
##                   Kappa : 0.3609          
##  Mcnemar's Test P-Value : 0.00511         
##                                           
##             Sensitivity : 0.7561          
##             Specificity : 0.7222          
##          Pos Pred Value : 0.9254          
##          Neg Pred Value : 0.3939          
##              Prevalence : 0.8200          
##          Detection Rate : 0.6200          
##    Detection Prevalence : 0.6700          
##       Balanced Accuracy : 0.7392          
##                                           
##        'Positive' Class : no              
## 
confusionMatrix(data_test$default, pred_te_dt, positive = "yes")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction no yes
##        no  62   5
##        yes 20  13
##                                           
##                Accuracy : 0.75            
##                  95% CI : (0.6534, 0.8312)
##     No Information Rate : 0.82            
##     P-Value [Acc > NIR] : 0.97051         
##                                           
##                   Kappa : 0.3609          
##  Mcnemar's Test P-Value : 0.00511         
##                                           
##             Sensitivity : 0.7222          
##             Specificity : 0.7561          
##          Pos Pred Value : 0.3939          
##          Neg Pred Value : 0.9254          
##              Prevalence : 0.1800          
##          Detection Rate : 0.1300          
##    Detection Prevalence : 0.3300          
##       Balanced Accuracy : 0.7392          
##                                           
##        'Positive' Class : yes             
## 

5.4. Plotting Decision Tree (의사결정 나무 시각화)

# plotting
plot(dt_prune, margin = 0.1)
text(dt_prune, use.n = T)

fancyRpartPlot(dt_prune, cex = 1) #fancy tree

5.5. Feature Importance (변수 중요도 파악)

dt_prune$variable.importance
##     checking_balance       credit_history      savings_balance 
##           46.7036272           14.9289045           13.5494871 
## months_loan_duration               amount                  age 
##           10.7319256            4.3876137            1.7003748 
##              purpose              housing                phone 
##            1.0868401            0.9660801            0.9258267 
##  employment_duration 
##            0.9068665
barplot(dt_prune$variable.importance, ylim = c(0, 55))

6. Improving Model Performance (성능 개선을 위한 신용 모델 튜닝)

6.1.Finding Tuning parameter

  • 파라미터는 최대 p^3개의 후보 모델로 테스트를 진행한다.
    • Decision Tree의 튜닝은 model, trials, winoow 설정에 대해 12개의 조합의 그리드로 이뤄진 후보 모델을 비교한다.
      • 3^3 = 27개가 되어야 하지만, model과 winnow 파라미터가 두 개의 값(각각 tree, rules와 TRUE, FALSE)만 취할 수 있기 때문에
      • 그리드 크기는 3 * 2 * 2 = 12가 된다.
modelLookup("C5.0")
##   model parameter                 label forReg forClass probModel
## 1  C5.0    trials # Boosting Iterations  FALSE     TRUE      TRUE
## 2  C5.0     model            Model Type  FALSE     TRUE      TRUE
## 3  C5.0    winnow                Winnow  FALSE     TRUE      TRUE

6.2. Creating a simple tuned model (간단한 튜닝 모델 생성)

# automated parameter tuning of C5.0 decision tree 
set.seed(300) # 시뮬레이션할 때, 동일한 결과를 반복하기 위해 난수 고정

m <- train(default ~ ., data = credit, method = "C5.0")
# summary of tuning results
m
## C5.0 
## 
## 1000 samples
##   16 predictor
##    2 classes: 'no', 'yes' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 1000, 1000, 1000, 1000, 1000, 1000, ... 
## Resampling results across tuning parameters:
## 
##   model  winnow  trials  Accuracy   Kappa    
##   rules  FALSE    1      0.6960037  0.2750983
##   rules  FALSE   10      0.7147884  0.3181988
##   rules  FALSE   20      0.7233793  0.3342634
##   rules   TRUE    1      0.6849914  0.2513442
##   rules   TRUE   10      0.7126357  0.3156326
##   rules   TRUE   20      0.7225179  0.3342797
##   tree   FALSE    1      0.6888248  0.2487963
##   tree   FALSE   10      0.7310421  0.3148572
##   tree   FALSE   20      0.7362375  0.3271043
##   tree    TRUE    1      0.6814831  0.2317101
##   tree    TRUE   10      0.7285510  0.3093354
##   tree    TRUE   20      0.7324992  0.3200752
## 
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were trials = 20, model = tree
##  and winnow = FALSE.

Summary 9

  • 적용된 전처리 방법과 리샘플링 방법에 대한 기록
    • 모델을 훈련하기 위해 25개의 부트스트랩 샘플이 사용됐으며, 각각은 1,000개의 예시를 포함
  • 평가된 후보 모델 목록
    • 12개의 모델이 C5.0 튜닝 파라미터 model, trials, winnow의 조합을 토대로 테스트 되었고,각 후보 모델의 정확도와 카파 통계의 평균과 표준편차 표시
  • 최고 모델의 선택
    • 가장 큰 정확도를 갖는 모델이 선택 됐는데, 20회 시행과 winnow = FALSE 설정을 갖는 의사결정 트리를 사용한 모델이다.

최고의 모델을 식별한 후에 train()함수는 튜닝 파라미터를 사용해서 전체 입력 데이터셋에 대해 모델을 구축한다. 이 모델은 m 리스트 객체에 m$finalModel로 저장된다. 대부분의 경우 직접 작업할 필요가 없다. m객체와 함께 predict() 함수를 다음과 같이 사용한다.

# apply the best C5.0 candidate model to make predictions
p <- predict(m, credit)
table(p, credit$default)
##      
## p      no yes
##   no  700   2
##   yes   0 298

최종 모델을 훈련하기 위해 사용된 1,000개의 예시 중 2개만 잘못 분류됐다. 하지만 모델이 훈련 데이터와 테스트 데이터에 대해 만들어졌기 때문에 정확도가 낙관적이다. 따라서 처음 보는 데이터에 대한 성능 지표로 보지 않도록 주의해야 한다. (요약 출력에 나타난) 73%의 부트스트랩 추정이 좀 더 실질적인 미래의 성능 추정이다.

# obtain predicted classes (default: type = "raw", probabilities: type = "prob")
head(predict(m, credit))
## [1] no  yes no  no  yes no 
## Levels: no yes

6.3. Customizing the tuning process (튜닝 절차 커스터마이징)

디폴트 설정을 사용하면 최적화된 모델을 쉽게 만들 수 있다. 하지만 디폴트 설정을 학습 작업에 좀 더 특화된 설정으로 변경하게 되면 성능을 높은 수준으로 올리는 데 도움이 된다. 파라미터를 최적화하기 위해 10-fold 교차검증(10-Fold C.V)를 이용해서 카파 통계량을 추정하고 반영할 것이다.

trainControl() 함수는 제어 객체로 알려진 설정 옵션 집합을 생성하기 위해 사용된다. 제어 객체는 train()함수를 이끈다. 이 옵션들을 통해 모델의 평가 기준을 관리할 수 있으며, 리샘플링 전략과 최고 모델을 선택하는 데 사용되는 척도 같은 것들이 해당된다. 이 함수는 튜닝 실험의 거의 모든 측면을 수정하는 데 사용할 수 있지만, 지금은 주요 파라미터인 method와 selectionFunction에 집중할 것이다.

trainControl() 함수의 경우 method parameter를 홀드아웃 샘플링 또는 k-fold 교차 검증과 같은 리샘플링 방법을 설정하는 데 사용할 수 있다.

selectionFunction 파라미터는 다양한 후보 중에 최적의 모델을 선택하는 함수를 지정하는데 사용한다. 그런 함수는 세 개가 있다. best 함수는 단순히 명시된 성능 척도에 대해 최고 값을 갖는 후보를 선택한다. 이 함수가 디폴트로 사용된다. 다른 두 함수는 최고 모델의 특정한 성능 임계치 내에 있는 가장 인색하거나 가장 단순한 모델을 선택하기 위해 사용된다. oneSE 함수는 최고 성능의 1표준 오차 내에 가장 단순한 후보를 선택하며, tolerance는 사용자 지정 비율 내에 가장 단순한 후보를 사용한다.

# use trainControl() to alter resampling strategy
# 10-fold Cross Validation과 oneSE 선택 함수를 사용해 ctrl이란 이름의 제어 객체를 생성
ctrl <- trainControl(method = "cv", number = 10,
                     selectionFunction = "oneSE")

최적화할 파라미터의 그리드 생성.
그리드의 열은 희망하는 모델의 파라미터 이름으로 구성된다. 그리드의 행은 원하는 파라미터 값의 조합으로 구성된다.
지금은 C5.0 의사결정 트리를 사용하기 때문에 .model, .trials, .winnow라는 이름으로 열이 만들어진다.

# use expand.grid() to create grid of tuning parameters
# 상수 model = "tree"와 winnow = "FALSE"를 유지하면서 8 종류의 trials를 검색
grid <- expand.grid(.model = "tree",
                    .trials = c(1, 5, 10, 15, 20, 25, 30, 35),
                    .winnow = FALSE)

# look at the result of expand.grid()
grid
##   .model .trials .winnow
## 1   tree       1   FALSE
## 2   tree       5   FALSE
## 3   tree      10   FALSE
## 4   tree      15   FALSE
## 5   tree      20   FALSE
## 6   tree      25   FALSE
## 7   tree      30   FALSE
## 8   tree      35   FALSE

train() 함수는 각 행의 모델 파라미터 조합을 이용해서 평가를 위한 후보 모델을 구축할 것이다.

직전에 생성된 검색 그리드와 제어 리스트가 있으므로 train() 실험을 철저히 맞춤화해서 시행할 준비가 됐다.
이전에 했던 것처럼 반복 가능한 결과를 보장하기 위해 시드를 임의의 값 300으로 설정한다.
하지만 이번에는 파라미터 metric = “Kappa”를 추가하면서 제어 객체와 튜닝 그리드를 전달할 것이다.
이 경우 모델 평가 함수(이 경우 “oneSE”)가 카파 통계량을 사용한다는 것을 말한다.

# customize train() with the control list and grid of parameters 
set.seed(300)
m <- train(default ~ ., data = credit, method = "C5.0",
           metric = "Kappa",
           trControl = ctrl,
           tuneGrid = grid)
m
## C5.0 
## 
## 1000 samples
##   16 predictor
##    2 classes: 'no', 'yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 900, 900, 900, 900, 900, 900, ... 
## Resampling results across tuning parameters:
## 
##   trials  Accuracy  Kappa    
##    1      0.735     0.3243679
##    5      0.722     0.2941429
##   10      0.725     0.2954364
##   15      0.731     0.3141866
##   20      0.737     0.3245897
##   25      0.726     0.2972530
##   30      0.735     0.3233492
##   35      0.736     0.3193931
## 
## Tuning parameter 'model' was held constant at a value of tree
## 
## Tuning parameter 'winnow' was held constant at a value of FALSE
## Kappa was used to select the optimal model using  the one SE rule.
## The final values used for the model were trials = 1, model = tree
##  and winnow = FALSE.

Summary 10

  • 출력의 대부분이 자동 튜닝 모델과 비슷하지만 몇 가지 눈에 띄는 차이점이 있다.
    • 10-fold cross validation이 사용되기 떄문에 후보 모델을 만들기 위한 샘플 크기는 부트스트랩에 사용된 1,000이 아닌 900으로 줄었다.
    • 요청했던 것처럼 8개의 후보 모델이 테스트됐다.
    • 또한 model과 winnow는 상수로 유지되고 있기 때문에 값이 더 이상 결과에 나타나지 않고 대신 각주에 나열됐다.
  • 최고 모델 또한 이전의 시행과 차이를 보인다. 이전의 최고 모델이 trials = 20을 사용했는데 여기서는 trials = 15를 사용했다. best 규칙이 아닌 oneSE 규칙을 사용했기에 선택된 것이다. Kappa에 따르면 25-trials model이 최고의 원시 성능을 제공하지만, 15-trials model은 단순한 형태로 비슷한 성능을 제공한다. 단순한 모델이 계산적으로 좀 더 효율적일 뿐 아니라 훈련 데이터에 과적합될 가능성을 줄여준다.

7. 메타 학습으로 모델 성능 개선

단일 모델의 성능을 향상시키는 방법에 대한 대안으로 몇 개의 모델을 결합해서 강력한 팀을 만들 수 있다. 최고의 스포츠 팀이 중복되는 기술이 아닌 상호 보완적인 기술을 갖는 선수로 이뤄지는 것처럼, 일부 최고의 머신 러닝 학습 알고리즘은 상호 보완적인 모델로 이뤄진 팀을 활용한다. 모델은 학습 작업에 고유한 편향을 가져오기 때문에 예시의 어떤 부분은 순조롭게 학습하지만, 다른 부분에서는 어려움이 있을 수 있다. 따라서 여러 다양한 팀 구성원의 재능을 현명하게 사용해서 여러 약한 학습자(weak learner)로 이뤄진 강한 팀을 만들 수 있다.

여러 모델의 예측을 결합하고 관리하는 이 기법은 광범위한 메타 학습(meta-learning) 방법에 속하며 학습 방법을 학습하는 기법을 정의한다. 이 방법은 반복적으로 설계를 결정하면서 점진적으로 성능을 향상시키는 단순한 알고리즘(자동 파라미터 튜닝)부터 학습 작업에 맞춰 스스로 수정하거나 적응하기 위해 진화 생물학과 유전학에서 빌려온 개념을 이용하는 매우 복잡한 알고리즘까지 모든 것을 포함한다.

7.1. Understanding ensemble

텔레비전 퀴즈 쇼 참가자라고 생각해보자. 100만 달러 상금이 걸린 마지막 질문에 대한 답변을 도와줄 다섯 사람의 패널을 선택할 수 있다고 가정하자. 대부분의 사람들은 다양한 주제 관련 전문가로 패널을 채우려고 할 것이다. 대중문화 전문가와 함께 문학, 과학, 역사, 예술 교수를 포함하는 패널은 틀림없이 균형 잡힌 그룹이 될 것이다. 그들의 폭넓은 지식을 고려하면 그룹을 난처하게 할 질문은 없을 것이다.

다양한 전문가 팀을 만드는 것과 유사한 원리를 활용하는 메타 학습 방법을 앙상블(ensemble)이라고 한다. 모든 앙상블 방법은 약한 학습자 여러 개를 결합하면 강한 학습자가 만들어진다는 아이디어를 기반으로 한다. 다음 두 가지 질문에 대한 답변으로 다양한 앙상블 방법을 대부분 구분할 수 있다.

- 약한 학습 모델이 어떻게 선택되고 구성되는가?
- 하나의 최종 예측을 만들기 위해 약한 학습자의 예측이 어떻게 결합되는가?

이 질문에 대한 답을 할 때 앙상블을 다음과 같은 프로세스 다이어그램의 관점에서 상상하는 것이 도움이 될 것이다.

training data는 여러 모델을 구성하는데 사용된다. 할당 함수(allocation function)는 각 모델이 받을 훈련 데이터 양을 규정한다. 이상적인 앙상블은 다양한 모델을 포함하지만, 생성된 학습자가 같은 유형이더라도 할당 함수는 학습자를 편향시키기 위해 입력 데이터를 인위로적으로 변화시켜 다양성을 높인다. 예를 들어 각 모델에 고유한 훈련 데이터셋을 구축하거나 각 모델에 특징이나 예시의 다른 부분집합을 넘기기 위해 부트스트랩 샘플링을 사용한다. 한편 앙상블이 이미 (신경망, 의사결정 나무, k-NN 분류기와 같은) 다양한 알고리즘을 포함하고 있다면 할당 함수는 상대적으로 변경 없이 데이터를 각 알고리즘에 전달할 것이다.

모델이 구성되고 나면 예측을 생성하는데 사용되며, 예측은 어떤 방식으로든 관리돼야 한다. 결합 함수(combination function)는 예측 간에 불일치를 어떻게 조율할지를 관리한다. 예를 들어 앙상블은 최종 예측을 결정하기 위해 과반수 의결을 사용하거나 각 모델의 표(vote)에 과거 성능 기반으로 가중치를 부여하는 것과 같은 좀 더 복잡한 전략을 사용할 수 있다.

어떤 앙상블은 다양한 예측 조합에서 결합 함수를 학습하기 위해 다른 모델을 이용하기도 한다. 예를 들어 M1, M2가 모두 예(yes)로 투표할 때, 실제 클래스 값은 항상 아니오(no)라고 가정해보자. 이 경우, 앙상블은 M1, M2가 일치할 때 투표를 무시하게 학습할 수 있다. 최종 결정권자를 훈련하기 위해 여러 모델의 예측을 사용하는 과정을 __스태킹(stacking)__이라고 한다.

앙상블을 사용하는 이점 중 하나는 단일의 최고 모델을 추구하는 데 소요되는 시간을 단축할 수 있다는 점이다. 대신 적당히 강한 여러 후보를 훈련하고 결합할 수 있다. 하지만 편의성만이 앙상블 기반의 방법이 머신 러닝 대회에서 승리를 계속 거두는 유일한 이유는 아니다. 앙상블은 단일 모델에 비해 많은 성능적 혜택도 제공한다.

- 미래 문제에 대한 더 나은 일반화 가능성
    - 여러 학습자의 의견이 하나의 최종 예측으로 통합되므로 하나의 편향이 우세할 수 없다. 따라서 학습 작업에 과적합될 가능성이 줄어든다.
    
- 대용량 또는 극소량의 데이터 셋에 대해 향상된 성능
    - 극도로 많은 특징이나 예시가 사용될 때, 모델이 대부분 메모리나 복잡도의 한계에 도달하므로, 하나의 완전한 모델보다 여러 소형 모델을 훈련하는 것이 좀 더 효율적이다. 역으로 앙상블은 작은 데이터셋에 대해서도 잘 수행되는데, 부트스트래핑과 같은 리샘플링 방법이 기본적으로 대부분의 앙상블 설계의 일부이기 때문이다. 가장 중요한 것은 분산 컴퓨팅 방법을 이용해서 앙상블을 병렬로 훈련할 수 있다는 것이다.
    
- 여러 도메인 데이터를 합성하는 능력
    - 모든 분야에 적용되는 학습 알고리즘은 없기 때문에 복잡한 현상은 다양한 분야에서 추출된 데이터에 의존하므로 여러 유형의 학습자로부터 얻은 증거를 통합하는 앙상블의 능력은 점점 더 중요해지고 있다.
    
- 여러운 학습 작업에 대한 좀 더 섬세한 이해
    - 실제 현상은 보통 상호작용을 하는 여러 복잡한 요소들로 극도로 복잡하다. 작업을 작은 부분으로 분할하는 모델은 하나의 전역적인 모델이 놓치는 미세한 패턴을 좀 더 정확히 포착할 수 있다.
    

가장 대중적인 앙상블 방법 몇 가지를 이용해서 지금까지 작업했던 신용모델의 성능을 개선할 것이다.

7.2. Boosting

또 다른 일반적인 앙상블 기반의 방법은 부스팅(boosting)이라고 하는데, 약한 학습자의 성능을 올려서(boost) 강한 학습자의 성능을 얻기 때문이다. 배깅과 유사하게 부스팅은 리샘플링된 데이터에 대해 훈련된 모델의 앙상블과 최종 예측을 결정하기 위해 투표를 사용한다. 두 가지 주요 차이점이 있다. 첫 번째로 부스팅에서 __리샘플링된 데이터셋은 상호 보완적인 학습자를 생성하게 특별히 구성__돼야 한다. 두 번째로 부스팅은 각 학습자에게 __동일한 표를 주는 대신 표에 과거 성능을 기반으로 하는 가중치를 부여__한다. 따라서 성능이 좋은 모델은 앙상블의 최종 예측에 더 큰 영향을 미친다.

부스팅은 앙상블의 최고 모델보다 보통 매우 좋거나 분명히 더 나쁘지 않은 성능을 보인다. 앙상블의 모델은 상호 보완적으로 구축되기 때문에 각 분류기가 임의의 우연(random choice)보다 잘 수행한다는 가정하에 단순히 그룹에 분류기를 추가함으로써 앙상블의 성능을 임의의 임계치까지 올릴 수 있다.

부스팅은 임의의 낮은 오류율을 만족하는 모델을 생성할 수 있지만, 이렇게 하는 것이 늘 합리적이지 않다. 첫째, 학습자가 추가될 때마다 성능 획득이 점점 줄어드므로 어떤 임계치의 경우 실제 실현이 불가능하다. 둘째, 순수한 정확도를 추구하게 되면 훈련 데이터에 과적합되는 모델이 만들어질 수 있으며, 처음 보는 데이터 대해 일반화되기 어렵다.

에이다부스트(AdaBoost) 또는 __적응형 부스팅(adaptive boosting)__으로 불리는 부스팅 알고리즘은 자주 오분류 되는 예시에 좀 더 집중함으로써(즉, 가중치를 더 주어) 분류하기 힘든 예시의 대부분을 반복적으로 학습하는 약한 학습자를 만든다는 아이디어를 기반으로 한다.

가중치가 없는 데이셋에서 시작해서 최초의 분류기는 결과를 모델링하려고 시도한다. 분류기가 정확하게 예측한 예시는 다음 분류기를 위한 훈련 데이터셋에 나타날 가능성이 적으며, 역으로 분류가 힘든 예시는 좀 더 자주 나타날 것이다. 약한 학습자의 라운드가 추가되면 연속해서 더 어려운 예시를 갖는 데이터에 대해 훈련된다. 원하는 전체 오류율에 도달하거나 성능이 더 이상 향상되지 않을 때까지 과정은 계속된다. 그 시점에서 각 분류기를 구축한 훈련 데이터의 정확도에 따라 분류기의 표(vote)에 가중치가 부여된다.

부스팅의 원리는 어떤 종류의 모데렝도 거의 적용될 수 있지만, 가장 일반적으로 의사결정 나무와 같이 사용된다. 부스팅을 C5.0 의사결정 나무의 성능을 개선하는 방법은 위에서 이미 사용했다.

에이다부스트.M1(AdaBoost.M1) 알고리즘은 분류를 위한 에이다부스트 트리 기빈의 다른 구현을 제공한다. 에이다부스트.M1 알고리즘은 adabag 패키지에서 찾을 수 있다. 신용 데이터에 대해 AdaBoost.M1 분류기를 생성해보자. 알고리즘에 대한 일반적인 구문은 다른 모델링 기법과 비슷하다.

## Using C5.0 Decision Tree (not shown in book)
#library(C50)
m_c50_bst <- C5.0(default ~ ., data = credit, trials = 100)

평소와 같이 만들어진 객체에 predict() 함수를 적용해 예측을 한다.

## Using AdaBoost.M1
library(adabag)

# create a Adaboost.M1 model
set.seed(300)
m_adaboost <- boosting(default ~ ., data = credit)
p_adaboost <- predict(m_adaboost, credit)

이 명령은 관행을 벗어나서 예측 벡터를 반환하는 대신, 모델 정보가 포함된 객체를 반환한다.
예측은 class라고 불리는 서브객체에 저장된다. 혼동 행렬은 confusion 서브객체에서 발견된다.

head(p_adaboost$class)
## [1] "no"  "yes" "no"  "no"  "yes" "no"
p_adaboost$confusion
##                Observed Class
## Predicted Class  no yes
##             no  700   0
##             yes   0 300

에이다부스트 모델이 실수가 전혀 없다. 이 혼동 행렬은 훈련 데이터에 대한 모델 성능을 기반으로 한다는 것을 기억하자. 부스팅은 오류율이 임의의 수준으로 낮아지는 것을 허용하기 때문에 학습자는 단순히 더 이상 오류가 없을 때까지 계속 진행됐다. 이렇게 되면 훈련 데이터 셋에 대해 과적합될 가능성이 있다.

처음 보는 데이터에 대한 좀 더 정확한 성능 평가를 하려면 다른 평가 방법을 사용할 필요가 있다. adabag 패키지는 10-폴드 교차 검증을 사용하는 간단한 함수를 제공한다.

# create and evaluate an Adaboost.M1 model using 10-fold-CV
set.seed(300)
adaboost_cv <- boosting.cv(default ~ ., data = credit)
## i:  1 Wed Oct 10 22:20:13 2018 
## i:  2 Wed Oct 10 22:20:44 2018 
## i:  3 Wed Oct 10 22:22:40 2018 
## i:  4 Wed Oct 10 22:23:47 2018 
## i:  5 Wed Oct 10 22:24:22 2018 
## i:  6 Wed Oct 10 22:25:28 2018 
## i:  7 Wed Oct 10 22:26:00 2018 
## i:  8 Wed Oct 10 22:26:30 2018 
## i:  9 Wed Oct 10 22:27:01 2018 
## i:  10 Wed Oct 10 22:27:35 2018
adaboost_cv$confusion
##                Observed Class
## Predicted Class  no yes
##             no  594 151
##             yes 106 149

컴퓨터의 용량에 따라 실행하는 데 약간의 시간이 필요할 수 있으며, 반복할 때마다 화면에 로그를 남긴다. 완료된 후에 좀 더 합리적인 혼동 행렬을 볼 수 있다.
vcd 패키지를 사용해서 카파 통계량을 확인할 수 있다.

# calculate kappa
library(vcd)
Kappa(adaboost_cv$confusion)
##             value    ASE     z  Pr(>|z|)
## Unweighted 0.3607 0.0323 11.17 5.914e-29
## Weighted   0.3607 0.0323 11.17 5.914e-29

Summary 12

약 0.36의 카파를 가지므로 이 모델이 지금까지 만들 모델 중 최고의 성능을 갖는 신용 평가 모델이다. 앙상블 방법과 비교해보자.

7.3. Random Forest

랜덤 포레스트(random forests, 또는 의사결정 트리 포레스트 decision tree forests)라고 하는 다른 앙상블 기반의 방법은 의사결정 트리의 앙상블에만 집중한다. 이 방법은 의사결정 트리 모델에 다양성을 추가하기 위해 배깅의 기본 원리와 임의의 특징 선택을 결합한다. 트리 앙상블이 생선된 후 모델은 트리의 예측 을 결합하기 위해 투표를 사용한다.

랜덤 포레스트는 융통성과 힘을 하나의 머신 러닝 방법으로 결합한다. 앙상블은 전체 특징 집합의 작고 임의의 부분만을 사용하기 때문에 랜덤 포레스트는 소위 ’차원의 저주(curse of dimensionality)’로 다른 모델이 실패하곤 하는 극도로 큰 데이터 셋을 다룰 수 있다. 동시에 대부분의 학습 작업에 대한 오류율은 거의 다른 모든 방법과 동등하다.

다른 앙상블 기반의 방법들과 비교해서 랜덤 포레스트는 매우 경쟁력이 있고 상대적으로 대회에 주요 이점을 제공한다는 점은 주목할 가치가 있다. 예를 들어 랜덤 포레스트는 사용이 쉽고 쉽게 과적합되지 않는다. 다음 표는 랜덤 포레스트 모델의 일반적인 장단점을 나열한 것이다.

- 장점 단점
1 모든 문제에 대해 잘 수행되는 다목적 모델이다. 의사결정 트리 같지 않게 모델 해석이 쉽지 않다.
2 범주형 또는 연속 특징 뿐 아니라 잡음이 있는 데이터나 누락 데이터를 다룰 수 있다. 모델을 데이터에 맞춰 튜닝하려면 약간의 작업이 필요할 수 있다.
3 가장 중요한 특징만을 선택한다. -
4 극도로 큰 개수의 특징이나 예시가 있는 데이터에 사용될 수 있다. -

힘, 융통성, 쉬운 사용 덕분에 랜덤 포레스트는 빠른 속도로 가장 대중적인 머신 러닝 기법 중 하나가 되었다. 후반부에는 부스팅 C5.0트리와 랜덤 포레스트 모델을 비교할 것이다.

7.3.1. Training random forest

랜덤 포레스트 구문

  • 분류기 구축
    • m <- randomForest(train, class, ntree = 500, mtry = sqrt(p))
      • train은 훈련 데이터를 포함하는 데이터 프레임
      • class는 훈련 데이터의 각 행에 대한 클래스를 갖는 팩터 벡터
      • ntree는 성장시킬 트리 개수를 지정하는 정수
      • mtry는 각 분할 시점에 임의로 선택되는 특징 개수를 지정하는 정수 옵션
        (디폴트로 sqrt(p)를 사용하며, p는 데이터의 특징 개수)

    이 함수는 예측을 위해 사용되는 랜덤 포레스트 객체를 반환한다.

  • 예측
    • p <- predict(m, test, type = “response”)
      • m은 randomForest() 함수로 훈련된 모델
      • test는 분류기를 구축하는 데 사용된 훈련 데이터와 가틍ㄴ 특징을 갖는 테스트 데이터를 포함하는 데이터 프레임
      • type은 “response”, “prob”, “votes”, 예측 벡터가 각각 예측 클래스, 예측 확률, 투표수 행렬을 갖는지를 표시하는데 사용된다.

    이 함수는 type 파라미터의 값에 따라 예측을 반환한다.

  • 예제
    • credit_model <- randomForest(credit_train, loan_default)
    • credit_prediction <- predict(credit_model, credit_test)

디폴트로 randomForest() 함수는 각 분할 시점에 sqrt(p)개의 랜덤 특징을 고려하는 500개 트리의 앙상블을 생성한다. 이때 p는 훈련 데이터셋의 특징 개수고, sqrt()는 r의 제곱근 함수다. 이 디폴트 파라미터가 적절한지는 학습 작업과 훈련 데이터의 본질에 따라 다르다. 일반적으로 학습 문제가 복잡해지고 (특징이 많거나 예시가 많아서) 데이터 셋이 커질수록 트리에 더 잘 작동하지만, 많은 트리를 훈련하는 계산 비용과 균형을 맞춰야 할 필요가 있다.

대량의 트리를 이용하는 목표는 각각의 특징이 여러 모델에 나타날 기회를 갖게 충분히 훈련하는 것이다. 이 목표가 mtry 파라미터의 디폴트 값이 sqrt(p)인 근거가 된다. 이 값을 이용하면 특징이 충분히 제약되므로 트리 간에 상당히 불규칙한 변동이 발생한다. 예를 들어 신용 데이터는 16개의 특징을 갖기 때문에 각 트리는 어떤 시점이든 네 개의 특징으로 분할되도록 제약된다.

randomForest()의 디폴트 파라미터가 신용 데이터에 어떻게 작동죄는지 살펴보자. 다른 학습자가 했던 것처럼 이 모델을 훈련할 것이다. 다시 set.seed() 함수가 동일한 결과를 반복될 수 있게 보장한다.

## Random Forests ----
# random forest with default settings
library(randomForest)
set.seed(300)
rf <- randomForest(default ~ ., data = credit)

모델 성능의 요약을 보려면 간단히 만들어진 객체의 이름을 입력하면 된다.

rf
## 
## Call:
##  randomForest(formula = default ~ ., data = credit) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 4
## 
##         OOB estimate of  error rate: 23.3%
## Confusion matrix:
##      no yes class.error
## no  638  62  0.08857143
## yes 171 129  0.57000000

예상처럼 출력은 랜덤 포레스트가 500개의 트리를 포함하고 각 분할 시점에 네 개의 변수를 시도했다는 것을 보여준다. 언뜻 보기에 혼동 행렬에 따르면 외관상 나쁜 성능에 놀랐을지도 모른다(오류율(error rate) 23.3%는 지금까지 어떤 다른 앙상블 방법의 재치환 오류(resubstitution error)보다 훨씬 나쁘다). 하지만 이 혼동 행렬은 재치환 오류를 보여주지 않는다. 대신 (출력에 OOB estimate of error rate로 나열돼 있는) OOB 오류율(out-of-bag error rate)을 나타내는데, 이 값은 재치환 오류와는 달리 편향되지 않은 테스트 집합의 오류 추정치다. 이것은 미래 성능의 매우 합리적인 추정치라는 것을 의미한다.

OOB 추정치는 랜덤 포레스트를 구축하는 동안 계산된다. 기본적으로 단일 트리의 부트스트랩 샘플로 선택되지 않은 모든 예시는 처음 보는 데이터에 대해 모델의 성능을 테스트하는 데 사용될 수 있다. 포레스트 구축의 마지막에 각 예시에 대한 에측을 집계하고, 투표를 하여 최종 예측을 결정한다. 그런 예측의 전체 오류율은 OOB 오류율이 된다.

7.3.2. Evaluating random forest performance

이전에 언급했듯이 randomForest() 함수는 caret에서 지원되며, OOB 오류율을 초과하는 성능 척도를 계산하면서 동시에 모델을 최적화한다. 상황을 흥미롭게 만들기 위해 자동 튜닝 랜덤 포레스트와 앞에서 개발했던 자동 튜닝 부스팅 C5.0 최고 모델과 비교해보자. 이 실험을 마치 머신 러닝 대회에 제출하려는 후보 모델을 식별하려는 것으로 간주할 것이다. 먼저 caret을 로드하고 훈련 제어 옵션을 설정해야 한다. 가장 정확하게 모델 성능을 비교하기 위해 반복 10-폴드 교차 검증(또는 10회 반복하는 10-폴드 교차 검증)을 이용할 것이다. 이 방법은 모델을 구축하는데 시간이 많이 걸리고 평가를 하기 위해 계산적으로 강도가 좀 더 높지만, 최종 비교이기 때문에 옳은 선택이라고 확신해야 한다. 이 결전의 승자는 머신 러닝 대회로 가는 유일한 관문이 될 것이다.

# library(caret)
ctrl <- trainControl(method = "repeatedcv",
                     number = 10, repeats = 10)

다음은 랜덤 포레스트를 위한 튜닝 그리드를 설정할 것이다. 이 모델의 유일한 튜닝 파라미터는 mtry로, 각 분할 시점에 임의로 선택되는 특징의 개수를 정의한다. 디폴트로 랜덤 포레스트가 트리별로 sqrt(16)또는 4개의 특징을 사용할 것을 알고 있다. 철저히 실험하기 위해 전체 16개 특징뿐 아니라 그것의 반과 두 배 값을 테스트할 것이다. 따라서 다음과 같이 2, 4, 6, 8, 16으로 된 그리드를 생성해야 한다.

# auto-tune a random forest
grid_rf <- expand.grid(.mtry = c(2, 4, 8, 16))

각 분할 시점에 전체 특징 집합을 고려하는 랜덤 포레스트는 배깅 의사결정 트리모델과 근본적으로 동일하다.

만들어진 그리드를 ctrl 객체와 함께 train 함수에 다음과 같이 제공할 수 있다. 최고 모델을 선택하기 위해 카파 척도를 사용할 것이다.

set.seed(300)
m_rf <- train(default ~ ., data = credit, method = "rf",
              metric = "Kappa", trControl = ctrl,
              tuneGrid = grid_rf)

이 명령은 해야 할 일이 많기 때문에 완료되는 데 어느 정도 시간이 소요된다. 완료되면 10, 20, 30, 40 반복을 이용하는 부스팅 트리와 비교할 것이다.

# auto-tune a boosted C5.0 decision tree
grid_c50 <- expand.grid(.model = "tree",
                        .trials = c(10, 20, 30, 40),
                        .winnow = "FALSE")

set.seed(300)
m_c50 <- train(default ~ ., data = credit, method = "C5.0",
                metric = "Kappa", trControl = ctrl,
               tuneGrid = grid_c50)

C5.0 의사결정 트리가 최종적으로 완료되면 두 방법을 나란히 비교할 수 있다. 랜덤 포레스트 모델에 대한 결과는 다음과 같다.

m_rf
## Random Forest 
## 
## 1000 samples
##   16 predictor
##    2 classes: 'no', 'yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold, repeated 10 times) 
## Summary of sample sizes: 900, 900, 900, 900, 900, 900, ... 
## Resampling results across tuning parameters:
## 
##   mtry  Accuracy  Kappa    
##    2    0.7223    0.1157648
##    4    0.7469    0.2830979
##    8    0.7504    0.3287980
##   16    0.7568    0.3638677
## 
## Kappa was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 16.

부스팅 C5.0 모델에 대한 결과는 다음과 같다.

m_c50
## C5.0 
## 
## 1000 samples
##   16 predictor
##    2 classes: 'no', 'yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold, repeated 10 times) 
## Summary of sample sizes: 900, 900, 900, 900, 900, 900, ... 
## Resampling results across tuning parameters:
## 
##   trials  Accuracy  Kappa    
##   10      0.7293    0.3083621
##   20      0.7362    0.3285118
##   30      0.7349    0.3229257
##   40      0.7368    0.3251888
## 
## Tuning parameter 'model' was held constant at a value of tree
## 
## Tuning parameter 'winnow' was held constant at a value of FALSE
## Kappa was used to select the optimal model using the largest value.
## The final values used for the model were trials = 20, model = tree
##  and winnow = FALSE.

Summary

mtry = 16인 랜덤 포레스트 모델이 카파가 약 0.361이므로 8개 모델 중 승자다. 카파가 약 0.334인 최고 C5.0 의사결정 트리보다 높으며 카파가 약 0.360인 에이다 부스트.M1 모델보다 약간 높다. 이 결과를 기반으로 랜덤 포레스트를 최종 모델로 제출할 것이다. 실제 대회 데이터에 대해 모델을 평가하지 않고는 우승을 할지 확실히 알 방법은 없지만 성능 추정치가 있으므로 안전한 내기가 된다. 운이 조금만 따르면 상을 받게 될 것이다.

7.4. Random Forest - grid search & feature importance

# Random Forest
library(randomForest)
library(e1071)
set.seed(123)
numrow = nrow(credit)
trnind = sample(1:numrow,size = as.integer(0.7*numrow))
train_data = credit[trnind,]
test_data = credit[-trnind,]
rf_fit = randomForest(default~.,data = train_data ,mtry = 4, maxnodes = 2000, ntree = 1000, nodesize = 2)
rf_pred = predict(rf_fit,data = train_data,type = "response")
rf_predt = predict(rf_fit,newdata = test_data,type = "response")
tble = table(train_data$default,rf_pred)
tblet = table(test_data$default,rf_predt)

acc = (tble[1,1]+tble[2,2])/sum(tble)
acct = (tblet[1,1]+tblet[2,2])/sum(tblet)
print(paste("Train acc",round(acc,4),"Test acc",round(acct,4)))
## [1] "Train acc 0.7486 Test acc 0.7567"
# Grid Search
rf_grid = tune(randomForest,default~., data = train_data, ranges = list(
              mtry = c(4,5),
              maxnodes = c(700,1000),
              ntree = c(1000,2000,3000),
              nodesize = c(1,2)
          ),
          tunecontrol = tune.control(cross = 5)
)

summary(rf_grid)
## 
## Parameter tuning of 'randomForest':
## 
## - sampling method: 5-fold cross validation 
## 
## - best parameters:
##  mtry maxnodes ntree nodesize
##     5      700  1000        1
## 
## - best performance: 0.2485714 
## 
## - Detailed performance results:
##    mtry maxnodes ntree nodesize     error  dispersion
## 1     4      700  1000        1 0.2514286 0.010594569
## 2     5      700  1000        1 0.2485714 0.005976143
## 3     4     1000  1000        1 0.2557143 0.018488827
## 4     5     1000  1000        1 0.2571429 0.015971914
## 5     4      700  2000        1 0.2600000 0.008144110
## 6     5      700  2000        1 0.2600000 0.009583148
## 7     4     1000  2000        1 0.2571429 0.011293849
## 8     5     1000  2000        1 0.2614286 0.022360680
## 9     4      700  3000        1 0.2585714 0.010594569
## 10    5      700  3000        1 0.2542857 0.014811744
## 11    4     1000  3000        1 0.2571429 0.005050763
## 12    5     1000  3000        1 0.2485714 0.018488827
## 13    4      700  1000        2 0.2585714 0.013739560
## 14    5      700  1000        2 0.2528571 0.013923992
## 15    4     1000  1000        2 0.2571429 0.015152288
## 16    5     1000  1000        2 0.2571429 0.011293849
## 17    4      700  2000        2 0.2585714 0.012777531
## 18    5      700  2000        2 0.2528571 0.010832679
## 19    4     1000  2000        2 0.2557143 0.011736912
## 20    5     1000  2000        2 0.2571429 0.012371791
## 21    4      700  3000        2 0.2571429 0.011293849
## 22    5      700  3000        2 0.2528571 0.010832679
## 23    4     1000  3000        2 0.2585714 0.013739560
## 24    5     1000  3000        2 0.2585714 0.013739560
best_model = rf_grid$best.model
summary(best_model)
##                 Length Class  Mode     
## call               6   -none- call     
## type               1   -none- character
## predicted        700   factor numeric  
## err.rate        3000   -none- numeric  
## confusion          6   -none- numeric  
## votes           1400   matrix numeric  
## oob.times        700   -none- numeric  
## classes            2   -none- character
## importance        16   -none- numeric  
## importanceSD       0   -none- NULL     
## localImportance    0   -none- NULL     
## proximity          0   -none- NULL     
## ntree              1   -none- numeric  
## mtry               1   -none- numeric  
## forest            14   -none- list     
## y                700   factor numeric  
## test               0   -none- NULL     
## inbag              0   -none- NULL     
## terms              3   terms  call
y_pred_train = predict(best_model, data = train_data)
train_conf_mat = table(train_data$default, y_pred_train)
print(paste("Train Confusion Matrix - Grid Search:"))
## [1] "Train Confusion Matrix - Grid Search:"
print(train_conf_mat)
##      y_pred_train
##        no yes
##   no  445  46
##   yes 130  79
train_acc = (train_conf_mat[1,1] + train_conf_mat[2,2])/sum(train_conf_mat)
print(paste("Train_accuracy-Grid Search:", round(train_acc,4)))
## [1] "Train_accuracy-Grid Search: 0.7486"
y_pred_test = predict(best_model, newdata = test_data)
test_conf_mat = table(test_data$default, y_pred_test)
print(paste("Test Confusion Matrix - Grid Search:"))
## [1] "Test Confusion Matrix - Grid Search:"
print(test_conf_mat)
##      y_pred_test
##        no yes
##   no  195  14
##   yes  55  36
test_acc = (test_conf_mat[1,1] + test_conf_mat[2,2])/sum(test_conf_mat)
print(paste("Test_accuracy-Grid Search:", round(test_acc,4)))
## [1] "Test_accuracy-Grid Search: 0.77"
confusionMatrix(test_data$default, y_pred_test)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  no yes
##        no  195  14
##        yes  55  36
##                                           
##                Accuracy : 0.77            
##                  95% CI : (0.7182, 0.8164)
##     No Information Rate : 0.8333          
##     P-Value [Acc > NIR] : 0.9982          
##                                           
##                   Kappa : 0.3765          
##  Mcnemar's Test P-Value : 1.469e-06       
##                                           
##             Sensitivity : 0.7800          
##             Specificity : 0.7200          
##          Pos Pred Value : 0.9330          
##          Neg Pred Value : 0.3956          
##              Prevalence : 0.8333          
##          Detection Rate : 0.6500          
##    Detection Prevalence : 0.6967          
##       Balanced Accuracy : 0.7500          
##                                           
##        'Positive' Class : no              
## 
a <- confusionMatrix(test_data$default, y_pred_test)
a$overall
##       Accuracy          Kappa  AccuracyLower  AccuracyUpper   AccuracyNull 
##   7.700000e-01   3.765060e-01   7.181680e-01   8.164118e-01   8.333333e-01 
## AccuracyPValue  McnemarPValue 
##   9.981510e-01   1.468802e-06
a$byClass
##          Sensitivity          Specificity       Pos Pred Value 
##            0.7800000            0.7200000            0.9330144 
##       Neg Pred Value            Precision               Recall 
##            0.3956044            0.9330144            0.7800000 
##                   F1           Prevalence       Detection Rate 
##            0.8496732            0.8333333            0.6500000 
## Detection Prevalence    Balanced Accuracy 
##            0.6966667            0.7500000
a$byClass[1]
## Sensitivity 
##        0.78
# Variable Importance
vari = varImpPlot(best_model)

print(paste("Variable Importance - Table"))
## [1] "Variable Importance - Table"
print(vari)
##                      MeanDecreaseGini
## checking_balance            36.509882
## months_loan_duration        29.348506
## credit_history              19.861459
## purpose                     19.377138
## amount                      44.586679
## savings_balance             16.932539
## employment_duration         20.020070
## percent_of_income           14.344997
## years_at_residence          13.101301
## age                         33.781667
## other_credit                 8.077092
## housing                      9.296663
## existing_loans_count         6.441806
## job                         10.662125
## dependents                   3.896758
## phone                        5.290919