은행 대출 채무 이행/불이행 예측
C5.0 결정 트리
17개의 변수와 1,000개의 관측치로 이루어진 데이터
default 변수 - yes (채무 불이행), no (채무 이행)
1. 데이터 준비
> credit = read.csv("C:/R/credit.csv", stringsAsFactors = F)
> str(credit)
'data.frame': 1000 obs. of 17 variables:
$ checking_balance : chr "< 0 DM" "1 - 200 DM" "unknown" "< 0 DM" ...
$ months_loan_duration: int 6 48 12 42 24 36 24 36 12 30 ...
$ credit_history : chr "critical" "good" "critical" "good" ...
$ purpose : chr "furniture/appliances" "furniture/appliances" "education" "furniture/appliances" ...
$ amount : int 1169 5951 2096 7882 4870 9055 2835 6948 3059 5234 ...
$ savings_balance : chr "unknown" "< 100 DM" "< 100 DM" "< 100 DM" ...
$ employment_duration : chr "> 7 years" "1 - 4 years" "4 - 7 years" "4 - 7 years" ...
$ 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 : chr "none" "none" "none" "none" ...
$ housing : chr "own" "own" "own" "other" ...
$ existing_loans_count: int 2 1 1 1 2 1 1 1 1 2 ...
$ job : chr "skilled" "skilled" "unskilled" "skilled" ...
$ dependents : int 1 1 2 2 2 2 1 1 1 1 ...
$ phone : chr "yes" "no" "no" "no" ...
$ default : chr "no" "yes" "no" "no" ...
> table(credit$default)
no yes
700 300
- 30%가 채무 불이행인 것이 확인된다.
이 데이터를 훈련 데이터와 테스트 데이터 (9:1)로 나눠보자
신용 데이터를 무작위로 섞은 뒤 나눠주었다. (잘 섞이게 하기 위해서)
> # 훈련 데이터와 테스트 데이터 생성
> set.seed(12345)
> credit_rand = credit[order(runif(1000)), ] #1000개 무작위로 섞기
>
> summary(credit$amount)
Min. 1st Qu. Median Mean 3rd Qu. Max.
250 1366 2320 3271 3972 18424
> summary(credit_rand$amount)
Min. 1st Qu. Median Mean 3rd Qu. Max.
250 1366 2320 3271 3972 18424
>
> credit_train = credit_rand[1:900, ]
> credit_test = credit_rand[901:1000, ]
>
> prop.table(table(credit_train$default))
no yes
0.7022222 0.2977778
> prop.table(table(credit_test$default))
no yes
0.68 0.32
- credit과 credit_amount의 summary가 같은 것을 확인하였다.
- credit_train에는 yes가 29.8%, credit_test에는 32%로 잘 섞인 것을 확인할 수 있다.
2. 모델 훈련
default를 먼저 factor()로 바꿔주자
> # install.packages("C50")
> library(C50)
>
> credit_train$default = factor(credit_train$default)
> str(credit_train)
'data.frame': 900 obs. of 17 variables:
$ checking_balance : chr "< 0 DM" "1 - 200 DM" "1 - 200 DM" "< 0 DM" ...
$ months_loan_duration: int 24 7 12 24 9 18 33 9 20 15 ...
$ credit_history : chr "critical" "good" "good" "good" ...
$ purpose : chr "car" "furniture/appliances" "furniture/appliances" "furniture/appliances" ...
$ amount : int 1199 2576 1103 4020 1501 1568 4281 918 2629 1845 ...
$ savings_balance : chr "< 100 DM" "< 100 DM" "< 100 DM" "< 100 DM" ...
$ employment_duration : chr "> 7 years" "1 - 4 years" "4 - 7 years" "1 - 4 years" ...
$ percent_of_income : int 4 2 4 2 2 3 1 4 2 4 ...
$ years_at_residence : int 4 2 3 2 3 4 4 1 3 1 ...
$ age : int 60 35 29 27 34 24 23 30 29 46 ...
$ other_credit : chr "none" "none" "none" "store" ...
$ housing : chr "own" "own" "own" "own" ...
$ existing_loans_count: int 2 1 2 1 2 1 2 1 2 1 ...
$ job : chr "unskilled" "skilled" "skilled" "skilled" ...
$ dependents : int 1 1 1 1 1 1 1 1 1 1 ...
$ phone : chr "no" "no" "no" "no" ...
$ default : Factor w/ 2 levels "no","yes": 2 1 1 1 2 1 2 2 1 1 ...
이제 모델을 만든뒤 모델에 대해 출력해보면 (길어서 생략)
> # 모델 훈련
> credit_model = C5.0(credit_train[-17], credit_train$default)
> summary(credit_model)
Call:
C5.0.default(x = credit_train[-17], y = credit_train$default)
C5.0 [Release 2.07 GPL Edition] Fri Apr 15 23:18:48 2022
-------------------------------
Class specified by attribute `outcome'
Read 900 cases (17 attributes) from undefined.data
Decision tree:
checking_balance = unknown: no (358/44)
checking_balance in {< 0 DM,1 - 200 DM,> 200 DM}:
:...credit_history in {perfect,very good}:
:...dependents > 1: yes (10/1)
: dependents <= 1:
: :...savings_balance = < 100 DM: yes (39/11)
: savings_balance in {500 - 1000 DM,unknown,> 1000 DM}: no (8/1)
: savings_balance = 100 - 500 DM:
. . .
# 시각화
# plot(credit_model, compress=T, margin=0.5)
# text(credit_model, cex=1.5)
- checking_balance가 unknown이면 no로 분류, 위에를 만족하면서 savings_balance가 100 미만이면 yes로 분류 등 각각의 가지에 대해 자세하게 볼 수 있다.
- 시각화도 해보려고 했는데 너무 복잡해서 나중에 가지치기나 깊이를 지정한 후 다시 해보는 것이 좋을 것 같다.
이 모델의 성능을 평가해보자
credit_test의 default도 마찬가지로 factor()로 지정해주어야 한다.
> credit_test$default = factor(credit_test$default)
> credit_pred = predict(credit_model, credit_test)
>
> library(gmodels)
>
> CrossTable(credit_test$default, credit_pred,
+ prop.chisq = F, prop.c = F,
+ dnn = c('actual default', 'predicted default'))
Cell Contents
|-------------------------|
| N |
| N / Row Total |
| N / Table Total |
|-------------------------|
Total Observations in Table: 100
| predicted default
actual default | no | yes | Row Total |
---------------|-----------|-----------|-----------|
no | 57 | 11 | 68 |
| 0.838 | 0.162 | 0.680 |
| 0.570 | 0.110 | |
---------------|-----------|-----------|-----------|
yes | 16 | 16 | 32 |
| 0.500 | 0.500 | 0.320 |
| 0.160 | 0.160 | |
---------------|-----------|-----------|-----------|
Column Total | 73 | 27 | 100 |
---------------|-----------|-----------|-----------|
- CrossTable() 함수를 사용해 예측값과 실제값을 비교하였다.
- 이 모델의 정확도는 (57+16)/100*100 = 73%이다.
- 실제 채무 불이행의 분류 오차율이 매우 높기 때문에 모델 성능 향상이 필요해보인다.
3. 모델 성능 높이기
① Boosting
Boosting 알고리즘은 성능이 낮은 학습기 여러 개를 합하는 기법(Ensemble) 중 하나이다.
trials 매개변수를 추가하여 개별 결증 트리의 수를 정해주면 된다.
> # Boosting
> credit_boost10 = C5.0(credit_train[-17], credit_train$default, trials=20)
> #summary(credit_boost10)
>
> credit_boost10_pred = predict(credit_boost10, credit_test)
> CrossTable(credit_test$default, credit_boost10_pred,
+ prop.chisq = F, prop.c = F, prop.r = F,
+ 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 | 9 | 68 |
| 0.590 | 0.090 | |
---------------|-----------|-----------|-----------|
yes | 13 | 19 | 32 |
| 0.130 | 0.190 | |
---------------|-----------|-----------|-----------|
Column Total | 72 | 28 | 100 |
---------------|-----------|-----------|-----------|
- 20회 반복을 통해 트리의 크기를 줄였다.
- 이 모델의 정확도는 78%로 오르긴 했지만, 실제 채무 불이행의 분류 오차율은 여전히 40%로 높은 것을 확인할 수 있다.
② Cost Matrix 사용
채무 불이행을 할 확률이 높은 지원자에게 대출을 해준다면 고비용의 실수가 발생할 위험이 크다
따라서 우리는 false negative의 수를 줄여야 한다.
그러기 위해서 false negative은 4, false positive는 1의 비용을 지불하도록 Cost Matrix를 구성해주자
> error_cost = matrix(c(0, 1, 4, 0), nrow=2)
> error_cost
[,1] [,2]
[1,] 0 4
[2,] 1 0
이제 이를 C5.0 모델의 costs에 적용해주면 된다
> # Cost Matrix
> error_cost = matrix(c(0, 1, 4, 0), nrow=2)
> credit_cost = C5.0(credit_train[-17], credit_train$default, costs=error_cost)
> credit_cost_pred = predict(credit_cost, credit_test)
> CrossTable(credit_test$default, credit_cost_pred,
+ prop.chisq = F, prop.c = F, prop.r = F,
+ 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 | 42 | 26 | 68 |
| 0.420 | 0.260 | |
---------------|-----------|-----------|-----------|
yes | 6 | 26 | 32 |
| 0.060 | 0.260 | |
---------------|-----------|-----------|-----------|
Column Total | 48 | 52 | 100 |
---------------|-----------|-----------|-----------|
- 이 모델의 정확도는 68%로 전체적으로는 정확도가 떨어졌다.
- 하지만, 실제 채무 불이행에 대해서는 32개 중 26개를 분류하여 오차율은 약 19%로 낮아진 것을 확인할 수 있다.
- Cost Matrix를 이용하여 실제 채무 불이행의 분류 오차율을 낮춰보았다.
'R > ML & DL 공부' 카테고리의 다른 글
[R] 와인 등급 예측 - 회귀트리, 모델트리 (0) | 2022.04.22 |
---|---|
[R] 의료비 예측 - 선형 회귀 (0) | 2022.04.22 |
[R] 휴대폰 스팸 분류 - Naive bayes (0) | 2022.04.20 |
[R] 독버섯 분류 - 분류 규칙 (OneR, Ripper) (0) | 2022.04.19 |
[R] Breast Cancer Wisconsin Diagnostic - K-NN (0) | 2022.04.15 |