한치록(2022). 계량경제학강의, 제4판, 박영사, 19장 부록 “머신러닝 예제”, 버전 0.2.2.
이 문서에 제시된 R 명령들은 주로 James, Witten, Hastie and Tibshirani (2013, An Introduction to Statistical Learning: with Applications in R, Springer)을 참고하였으며, 그 책으로 충분하지 않은 부분은 패키지 문서에 기초하여 직접 코딩하였거나 인터넷 검색으로부터 얻은 정보를 확인하여 작성하였다.
회귀(regression) | 분류(classifiction) |
---|---|
데이터 준비 Subset Selection Splines Ridge와 Lasso PCR과 PLS Decision Tree Tree Ensemble Support Vector Regression Neural Networks Super Learner |
데이터 준비 Logit, LDA, QDA ROC와 Cutoff 값 Class Imbalance Ridge와 Lasso Decision Tree Tree Ensemble Support Vector Machines Neural Networks Super Leaner |
이항반응모형에서 로지스틱 회귀가 대표적으로 사용된다.
TrainSet
데이터셋에서 목표변수(deny
)를
제외한 나머지 전체 변수들을 예측변수로 사용하여 로지스틱 회귀를 한
결과는 다음과 같다.
<- glm(deny~., data=TrainSet, family=binomial)
full summary(full)
# Call:
# glm(formula = deny ~ ., family = binomial, data = TrainSet)
#
# Deviance Residuals:
# Min 1Q Median 3Q Max
# -2.6580 -0.4182 -0.3067 -0.2214 3.0105
#
# Coefficients:
# Estimate Std. Error z value Pr(>|z|)
# (Intercept) -7.16606 0.57680 -12.424 < 2e-16 ***
# dir 4.56694 1.07889 4.233 2.31e-05 ***
# hir -0.07120 1.29376 -0.055 0.956109
# lvr 1.81944 0.51468 3.535 0.000408 ***
# ccs 0.30188 0.04108 7.348 2.02e-13 ***
# mcs 0.23081 0.14776 1.562 0.118268
# pbcryes 1.24497 0.21237 5.862 4.57e-09 ***
# dmiyes 4.45624 0.55904 7.971 1.57e-15 ***
# selfyes 0.58139 0.22057 2.636 0.008394 **
# singleyes 0.42718 0.16191 2.638 0.008330 **
# uria 0.07359 0.03463 2.125 0.033571 *
# condominium -0.10736 0.17705 -0.606 0.544277
# blackyes 0.71092 0.18717 3.798 0.000146 ***
# ---
# Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#
# (Dispersion parameter for binomial family taken to be 1)
#
# Null deviance: 1621.8 on 2212 degrees of freedom
# Residual deviance: 1178.3 on 2200 degrees of freedom
# AIC: 1204.3
#
# Number of Fisher Scoring iterations: 6
위 결과에서 “Residual deviance” 1178.3
은 -2 곱하기
최대화된 우도함수값(-2logL)이다. 이 값이 작을수록 train set이 잘
맞춰지는 것이라 생각하면 된다. AIC 1204.3
은 -2logL + (2*모수
개수)이다. 다음과 같이 계산해도 똑같은 값을 얻는다.
$deviance + 2*13
full# [1] 1204.26
Train set에서 학습이 얼마나 잘 이루어졌는지 살펴보자. 목표변수인
deny
변수는 "no"
또는 "yes"
의
factor
변수이다.
str(Hmda$deny)
# Factor w/ 2 levels "no","yes": 1 1 1 1 1 1 1 1 2 1 ...
첫 번째 나오는 "no"
가 기본값이고 두 번째 나오는
"yes"
가 ’설정되었음’을 의미한다. 이에 기초하여 확률
예측값이 0.5보다 크면(’작으면’이 아니라) "yes"
로 예측하도록
목표변수의 이진적 예측값을 구하자. 다음의 첫 번째 줄은 확률을 예측하고
두 번째 줄은 확률예측값이 0.5보다 크면
yes
로 예측한다.
<- predict(full, TrainSet, type='r')
train.phat.full <- ifelse(train.phat.full > 0.5, "yes", "no") train.denyhat.full
deny
의 실제값과 예측값 조합의 빈도를 나타내는 2×2 행렬
confusion matrix를 구하면 다음과 같다.
SummPred(TrainSet$deny, train.denyhat.full) # defined in "데이터 준비"
# $ConfusionMatrix
# pred
# actual no yes
# no 1921 27
# yes 182 83
#
# $Summary
# Sensitivity Specificity Precision Accuracy
# 0.3132075 0.9861396 0.7545455 0.9055581
이상의 절차는 ‘데이터 준비’ 단원에 만들어
놓은 Performance()
함수를 사용해서 간편하게 처리할 수
있다.
Performance(full, TrainSet) # defined in "데이터 준비"
# $ConfusionMatrix
# pred
# actual no yes
# no 1921 27
# yes 182 83
#
# $Summary
# Sensitivity Specificity Precision Accuracy
# 0.3132075 0.9861396 0.7545455 0.9055581
결과를 설명하면, 학습 데이터셋에서 실제로 거절되지 않은(truth가
no
, negative) 1,948건(=1,921+27) 중 1,921건은 거절되지 않은
것으로 제대로 예측되었고 27건은 거절된 것으로 잘못 예측되었다. 그러므로
false positive rate (FPR, 1 - specificity, 실제 거절되지 않은 것 중
거절되었다고 잘못 예측된 비율)은 27/1948 = 0.0139이고 특정도(specificity, 실제 거절되지 않은 것 중 올바르게
예측된 비율, 1-FPR)는 1-0.0139=0.9861이다. 거절된(yes
,
positive) 265건 중 올바르게 예측한 것은 83건에 불과하고 나머지 182건은
잘못 맞추었다. True positive rate (TPR, 민감도 sensitivity, 거절된 것 중 올바르게 예측한 비율)은
83/265 = 0.3132에 지나지 않는다. 또, 총 2,213건 중 제대로 맞춘 것(대각
원소)은 1921 + 83 = 2,004건이고, 제대로 맞추지 못한 것은 209건이다.
오차율(1-accuracy, 전체 중 잘못 예측한 비율)은 209/2213 = 0.0944,
적중률(accuracy, 전체 중 올바르게 예측한 비율)은
0.9056이다. 정밀도(precision, 거절이라고 예측된 것 중 실제로 거절된
건의 비율)는 83/(27+83) = 0.7545이다.
학습 데이터셋 예측 성과지표 Sensitivity = 83/(182+83) = 0.3132 = True Positive Rate = Recall Specificity = 1921/(1921+27) = 0.9861 = 1 - False Positive Rate Precision = 83/(27+83) Accuracy = (1921+83)/(1921+27+182+83) = 0.9056 = 1 - Error rate Error rate = (27+182)/(1921+27+182+83) = 0.0944 = 1 - Accuracy
오차율(1-Accuracy) 자체만 보면 괜찮아 보이나 sensitivity가 너무 낮다. 이는 전반적으로
yes
예측을 하지 못함을 의미한다. 그 이유는 전체 데이터셋에
yes
의 비율이 낮아(약 11.2% = (182+83)/(1921+27+182+83)),
로짓 알고리즘에서 yes
를 맞추는 것이 등한시되기 때문이다. 이
문제는 나중에 ROC 곡선 부분과 Class Imbalance 부분에서 자세히 다루기로
한다.
이처럼 이 예측모델은 train set에 대해서도 positive를 positive로
제대로 예측해 주지 못하니(sensitivity가 너무 낮음) test set에 대해서도
yes
를 yes
로 예측할 빈도가 낮을 것으로 예상할
수 있다. 정말 그런지 한번 해 보자. 테스트셋에 대해 ’설정’될(yes
가 될)
확률을 우선 예측한 후, 이 확률이 0.5보다 크면 yes
로,
0.5보다 작거나 같으면 no
로 예측한다. 그리고 나서 confusion matrix를 구하면 다음과 같다.
Performance(full, TestSet) # defined in "데이터 준비"
# $ConfusionMatrix
# pred
# actual no yes
# no 146 1
# yes 15 5
#
# $Summary
# Sensitivity Specificity Precision Accuracy
# 0.2500000 0.9931973 0.8333333 0.9041916
예상대로 “no”를 “no”로 제대로 예측한 비율인 specificity는 146 / (146 + 1) = 0.9932로 매우
높지만, “yes”를 “yes”로 제대로 예측한 비율인 sensitivity (true positive rate)는 5/(15+5) = 0.25로 매우 낮다.
학습한 모델이 대체로 거절이 이루어지지 않는다(no
)고
예측하기 때문이다. 표본에 negative의 비율이 높고(약 88%가
no
) negative를 negative라고 잘 예측하기 때문에 전반적인
예측 정확도(accuracy)는 (146+5)/(146+1+15+5) = 0.9042로 상당히
높다. 하지만 이는 당초부터 테스트셋에서 no
인 경우가
대부분이기 때문이지 모델의 변별력이 특별히 좋기 때문은 아니다. 그냥 항상
no
라고 예측을 하여도 88%를 맞춘다.
Confusion matrix와 관련된 통계들은
caret
패키지의 confusionMatrix()
함수로도 간편하게
구할 수 있다. 단, 앞에서는 ’실제’가 세로, ’예측’이 가로임에 반하여, 이
caret
패키지 함수에서는 ’실제’가 가로, ’예측’이 세로로
열거되어 있음에 유의하라. ROC 곡선(yes와 no 예측 시 확률 경계값을 0에서 1까지
바꾸면서 sensitivity와 1-specificity를 표시한 그림) 등에 대해서는 다음 소절을 참조하라.
AIC 기준 최적 모형의 backward stepwise selection을 해 보자. A. Kassambara의 포스트와 토론토 대학 Jerry Brunner 교수가 작성하였다고 생각되는 pdf 문서에서 도움을 얻었다.
<- step(full, direction = 'backward', trace = 0)
backward
backward# Call: glm(formula = deny ~ dir + lvr + ccs + mcs + pbcr + dmi + self +
# single + uria + black, family = binomial, data = TrainSet)
#
# Coefficients:
# (Intercept) dir lvr ccs mcs pbcryes
# -7.19496 4.54731 1.81017 0.30115 0.22937 1.24642
# dmiyes selfyes singleyes uria blackyes
# 4.46472 0.58426 0.40419 0.07597 0.69080
#
# Degrees of Freedom: 2212 Total (i.e. Null); 2202 Residual
# Null Deviance: 1622
# Residual Deviance: 1179 AIC: 1201
이 기준에 의하여 최종 선택된 모형은 10개 변수로 이루어진 모형이다.
Full model로부터 제외된 2개의 변수는 hir
(housing expenses
to income ratio)과 condominium
(is unit a
condominium?)이다. 선택된 모형의 residual deviance는
1179
로서 full model의 1178.3
보다 약간 크나(즉,
train set 설명력은 약간 떨어지나), AIC는 1201
로서 full
model의 1204.3
보다 약간 더 작다(AIC는 작을수록 예측 성능이
좋은 것이라는 믿음이 있다).
테스트셋에 적용하여 confusion matrix를 만들자.
Performance(backward, TestSet) # defined in "데이터 준비"
# $ConfusionMatrix
# pred
# actual no yes
# no 146 1
# yes 15 5
#
# $Summary
# Sensitivity Specificity Precision Accuracy
# 0.2500000 0.9931973 0.8333333 0.9041916
결과는 full logit의 경우와 동일하다(다른 데이터에서는 그렇지 않을 수도 있다).
AIC에 기반한 forward stepwise selection을 하자(토론토 대학 Jerry Brunner 교수가 작성하였다고 생각되는
pdf 문서에서 도움을 얻었음). 이를 위해서는 가장 작은 모형과 가장 큰
모형을 지정해 주어야 한다. 가장 작은 모형은 null model (우변에 상수항만
있는 모형)이다. Forward stepwise selection을 위해 우선 null model을
추정하자(step
명령이 이걸 요구하니까).
<- glm(deny~1, data=TrainSet, family=binomial) null
이제 forward stepwise selection을 하자.
step(null, scope = list(formula(null), upper = formula(full)), direction = 'forward', trace = 0)
# Call: glm(formula = deny ~ dmi + ccs + dir + pbcr + black + lvr + self +
# single + uria + mcs, family = binomial, data = TrainSet)
#
# Coefficients:
# (Intercept) dmiyes ccs dir pbcryes blackyes
# -7.19496 4.46472 0.30115 4.54731 1.24642 0.69080
# lvr selfyes singleyes uria mcs
# 1.81017 0.58426 0.40419 0.07597 0.22937
#
# Degrees of Freedom: 2212 Total (i.e. Null); 2202 Residual
# Null Deviance: 1622
# Residual Deviance: 1179 AIC: 1201
Backward stepwise selection과 비교할 때 변수들의 순서만 다르고 선택된 변수들의 집합은 서로 동일하다.
Backward stepwise selection과 forward stepwise selection을 결합할 수도 있다.
step(null, scope = list(lower = formula(null), upper = formula(full)), direction = 'both', trace = 0)
# Call: glm(formula = deny ~ dmi + ccs + dir + pbcr + black + lvr + self +
# single + uria + mcs, family = binomial, data = TrainSet)
#
# Coefficients:
# (Intercept) dmiyes ccs dir pbcryes blackyes
# -7.19496 4.46472 0.30115 4.54731 1.24642 0.69080
# lvr selfyes singleyes uria mcs
# 1.81017 0.58426 0.40419 0.07597 0.22937
#
# Degrees of Freedom: 2212 Total (i.e. Null); 2202 Residual
# Null Deviance: 1622
# Residual Deviance: 1179 AIC: 1201
결과는 forward stepwise selection 및 backward stepwise selection과 동일하다.
판별분석(Discriminant analysis) 중 Linear discriminant analysis (LDA)와 Quadratic discriminant analysis (QDA)를 살펴보자. James et al. (2013)의 4장을 참고하였다. 참고로, LDA와 QDA는 예측변수(X)들이 정규분포를 갖는다는 가정하에서 도출되는데, 이 예제에서는 더미변수들이 X변수로 포함되어 있고 더미변수가 정규분포를 가질 리는 만무하므로 정규분포 가정은 만족되지 않는다. 하지만 그렇다고 하여 LDA와 QDA를 사용해서는 안 된다는 법도 없으므로 실습해 보겠다. 참고로 Stack Exchange에 이런 글타래도 있다. 이하에서 LDA와 QDA의 이론에 대하여는 길게 설명하지 않겠다.
LDA는 피예측변수(deny
)가 yes
인 경우와
no
인 경우에 X변수들의 평균이 서로 다르고 분산공분산행렬은
동일한 정규분포를 갖는다고 가정한다. 이 가정에서 도출되는 바에 의하면
yes
와 no
의 경계면이 예측변수(X변수)들에 대하여
선형이다. MASS
라이브러리의 lda()
함수를
이용한다.
library(MASS)
<- lda(deny~., data=TrainSet)
lda.fit
lda.fit# Call:
# lda(deny ~ ., data = TrainSet)
#
# Prior probabilities of groups:
# no yes
# 0.8802531 0.1197469
#
# Group means:
# dir hir lvr ccs mcs pbcryes dmiyes
# no 0.3231376 0.2507864 0.7272820 1.963039 1.700719 0.04671458 0.002053388
# yes 0.3909430 0.2928115 0.8176361 3.324528 1.883019 0.27169811 0.154716981
# selfyes singleyes uria condominium blackyes
# no 0.1119097 0.3814168 3.736910 0.2864476 0.1160164
# yes 0.1622642 0.4943396 4.046793 0.3245283 0.3320755
#
# Coefficients of linear discriminants:
# LD1
# dir 3.13808213
# hir -0.32409794
# lvr 0.67157884
# ccs 0.21987557
# mcs 0.11490580
# pbcryes 1.43952374
# dmiyes 4.91088057
# selfyes 0.36114631
# singleyes 0.25441415
# uria 0.04145506
# condominium -0.06456575
# blackyes 0.59438727
table(TrainSet$deny)
를 보면 알 수 있듯이
TrainSet
의 deny
변수는 1,948개 관측치에서
no
, 265개 관측치에서 yes
이다. 즉, 전체의
88.0%에서 no
, 12.0%에서 yes
이다. 이것이 위
결과의 “Prior probabilities of groups”으로 제시되어 있다. 그 아래에는
추정 결과들이 적당한 선에서 열거되어 있다.
TestSet
에 대하여 예측하고 confusion matrix를 만들자.
Performance(lda.fit, TestSet) # defined in "데이터 준비"
# $ConfusionMatrix
# pred
# actual no yes
# no 145 2
# yes 14 6
#
# $Summary
# Sensitivity Specificity Precision Accuracy
# 0.3000000 0.9863946 0.7500000 0.9041916
위 ‘Logit: 전체 예측변수 이용’ 소절로부터의 결과와 비교하자면, full logistic regression에서 sensitivity가 0.25 (=5/20)였다. 여기 LDA에서는 근소하게 상승하나(6/(14+6)=0.3) 낮은 것은 여전히 마찬가지이다.
Quadratic discriminant analysis (QDA)에서는 X의 평균뿐 아니라
분산공분산행렬도 피예측변수가 no
인 집단과
yes
인 집단 간에 서로 다를 수 있다고 가정한다. 이 가정하에서
경계면이 feature들의 2차함수가 된다(그래서 “QDA”). MASS
라이브러리의 qda()
함수를 이용한다.
library(MASS)
<- qda(deny~., data=TrainSet)
qda.fit
qda.fit# Call:
# qda(deny ~ ., data = TrainSet)
#
# Prior probabilities of groups:
# no yes
# 0.8802531 0.1197469
#
# Group means:
# dir hir lvr ccs mcs pbcryes dmiyes
# no 0.3231376 0.2507864 0.7272820 1.963039 1.700719 0.04671458 0.002053388
# yes 0.3909430 0.2928115 0.8176361 3.324528 1.883019 0.27169811 0.154716981
# selfyes singleyes uria condominium blackyes
# no 0.1119097 0.3814168 3.736910 0.2864476 0.1160164
# yes 0.1622642 0.4943396 4.046793 0.3245283 0.3320755
결과를 테스트셋에 적용하면 다음과 같다.
Performance(qda.fit, TestSet) # defined in "데이터 준비"
# $ConfusionMatrix
# pred
# actual no yes
# no 138 9
# yes 13 7
#
# $Summary
# Sensitivity Specificity Precision Accuracy
# 0.3500000 0.9387755 0.4375000 0.8682635
Sensitivity는 full logit = 5/20 < LDA = 6/20 < QDA = 7/20이다. 하지만 0.35는 여전히 낮은 값이다.
## -------------------------------------------------------------------- full <- glm(deny~., data=TrainSet, family=binomial) summary(full) full$deviance + 2*13 str(Hmda$deny) train.phat.full <- predict(full, TrainSet, type='r') train.denyhat.full <- ifelse(train.phat.full > 0.5, "yes", "no") SummPred(TrainSet$deny, train.denyhat.full) Performance(full, TrainSet) Performance(full, TestSet) backward <- step(full, direction = 'backward', trace = 0) backward Performance(backward, TestSet) null <- glm(deny~1, data=TrainSet, family=binomial) step(null, scope = list(formula(null), upper = formula(full)), direction = 'forward', trace = 0) step(null, scope = list(lower = formula(null), upper = formula(full)), direction = 'both', trace = 0) library(MASS) lda.fit <- lda(deny~., data=TrainSet) lda.fit Performance(lda.fit, TestSet) library(MASS) qda.fit <- qda(deny~., data=TrainSet) qda.fit Performance(qda.fit, TestSet) ## --------------------------------------------------------------------