• Home

정확한 처리 효과 분석을 위한 성향점수분석(PSA)

비교는 비교할 대상과 비교해야 된다.

apple and orange

흔히들 적절한 비교 대상을 정해주지 않고 자신이 보고 싶은 효과만 비교하는 경우를 많이 본다. 이미 다른 성격을 가진 과일 두가지를 비교해 뭔가 유의미한 특징을 발견했다고 해서 대단한 발견이라고 하기 어려운 것과 마찬가지이다. 사과, 오렌지라는 사실 자체에 효과가 포함되어 있을 가능성이 굉장히 높으며 이는 특정 실험효과로 인한 결과라 보기 어렵다.

예를 들어 “통신사 데이터 요금제의 선택이 매출 증대를 가져왔는가?"를 보고자 한다면, 데이터 요금제 선택이 매출 증대를 가져왔는지 매출에 영향을 미칠만한 사람들이 데이터 요금제를 선택한 것이지 구별하기 매우 어려울 것이다. 따라서 특정 변수(나이, 성별, 소득수준, 가입기간 등)과 같은 다양한 변수들로 실험군을 설명하는 정도에 대해서 각각 실험군, 대조군의 레코드를 매핑하면 변수들이 서로 유사한 쌍끼리 밸런스를 맞출 수 있을 것이다. 이들 쌍으로 매핑한 것들이 모두 사과-사과, 오렌지-오렌지가 되는 것이다.

변수들이 조치여부에 대해 설명하는 효과들이 모두 상쇄되면 비로소 조치여부(데이터 요금제 선택)가 매출증대에 미치는 영향도를 정확히 측정할 수 있을것이며, 몇가지 조건만 추가적으로 만족하면 인과관계로서도 설명이 가능하게 된다.

사실 간단한 예로서 PSA의 개념적인 설명은 다 된 것이나, 좀더 상세하게 방법론에 대해서 알아보자!

공변량 그리고 인과관계

공변량(covariate)라는 말은 많은 서적에서 출몰하지만 이 뜻에 대해서 명확히 알 수 있는 기회는 그다지 없는것 것다.
몇몇 책에서는 "독립변수”, “설명변수"와 같은 의미로 사용된다는 정도로만 언급하고 더 이상 언급은 없었고, 혹은 회귀모형 모델링시 반드시 (무작위 할당으로) 통제 되어야 되는 것으로만 언급하고 마는 경우가 허다하다. 아마도 이런 설명이 어려운 이유는 실험설계와 회귀모형이 깊은 연관이 있다는 것을 대부분의 기초 데이터 분석 교육에서 언급하기 어려워서 그럴 것이란 생각을 해본다. 개인적으로는 이러한 이유 때문에 데이터 분석을 위해 몇몇 통계학 개론서만을 공부하기 보다는 하나의 학문에 대해서 같은 뿌리를 두고 있는 커리큘럼 자체를 공부하는게 필요하다는 생각을 해본다.

공변량이 회귀모형에서 주로 나오지만 이 회귀모형이 실험에 기인한 방법이라는 것을 아는건 통계학과나 실험을 주로 하는 연구자가 아니라면 알기 어렵다. 특히나 예측만을 위한 기법으로 이 방법을 인식하고 있는 사람들에게는 더더욱이 공변량이 뭘 의미하는지 알기 어렵다. 그러나 회귀모형이 실험을 기반으로 한 방법론이라는 것을 알기 시작하면 바로 이해가 가능해진다.

예를 들어 커피가 콜레스테롤 수치 저하에 관련 있는지 실험을 하고자 한다면, 랜덤으로 선택한 실험군과 대조군을 기반으로 커피섭취를 시켜 콜레스테롤 수치의 변화를 측정해 이의 효과에 대해서 분석하면 될 것이다. 이럴 경우에는 문제가 깔끔해진다. 왜냐하면 랜덤으로 실험군과 대조군을 선택해 성별, 나이, 식생활 등과 같은 콜레스테롤 수치에 영향을 줄 수 있는 대부분의 변수 효과가 상쇄되기 때문이다. 여기서 우리가 관심 있어하는 변수 이외에 종속변수에 영향을 줄 수 있는 모든 변수가 바로 공변량이 된다. 만일 무작위 실험이 아닌 경우 공변량을 추가하지 않고 관심을 두는 독립변인만을 넣어서 모델링을 한다면 과대추정이 될 수 있어 정확한 효과측정이 불가능해져 의미없는 연구가 될 것이다.

많은 연구가 무작위 실험이 가능하지는 않다. 일정 기간 대조군, 실험군에 속한 사람들의 모든 생활을 통제하는건 비용적인 측면에서나 윤리적인 측면에서 적절하지 않은 경우가 많기 때문이며, 게다가 측정하고 싶어하는 차이의 정도인 검정력을 고려할 경우는 그 비용이라는 것은 어머어마하게 커질 가능성이 많기 때문이다. 그래서 이미 커피 섭취를 하고 있는 군과 하지 않고 있는 군에 대한 관찰연구를 수행할 수 있는데, 이 시점에서 목적변수에 영향을 줄 수 있는 공변량 통제 문제가 발생하기 시작한다.

필자가 공변량의 의미를 명확히 알게 된 때는 통계적 공변량 통제방법인 PSA(Propensity Score Analysis)를 학습하고 난 이후였다.

공변량은 여러 변인들이 공통적으로 공유하는 변량을 의미하며, 이들을 통제하지 않고서는 특정 독립변수가 종속변수에 미치는 영향의 정도를 정확하게 파악하는게 어려워 진다.

PSA

PSA의 목적은 사과는 사과끼리 오렌지는 오렌지끼리 비교할 수 있게 샘플링을 수행해 선택편향을 줄이는데 있다. 여기서 비슷한 공변량을 가지고 있다는 것을 의미할수 있게 스코어를 도출하는 과정을 거치게 되는데 이런 이유 때문에 번역어로 '성향점수'라고 한다. 따라서 스코어가 같다면 같은 공변량 성향을 가지고 있다고 이야기 할 수 있다.

PSA 분석 결과가 인과관계를 이야기할 수 있기 위해서는 가정이 필요하다.

  1. 처리(treatment)할당이 종속변수에 무관하게 되어야 된다.
  2. 하나의 관측값의 처리 할당이 다른 관측값의 처리할당에 영향을 주지 않아야 된다.

예를 들어서 고객의 이통사 요금제 변경이 가입기간을 늘려준다는 가설을 검증하고 싶다고 한다면, 자신의 데이터 셋에서 1,2번 가정을 모두 만족하는지 확인해야 된다. 하지만 특히 2번 가정은 현실적으로 매우 만족하기 어려운 가정인데, 예를 들어 실제 두 사람의 요금제 변경이 데이터로 드러나지 않는 오프라인 관계로 인해 상호 영향을 받았을 경우가 있기 때문이고 이에대한 확인도 어렵기 때문이다(혹은 같은 TV광고의 영향이 있을 수 있는데, 이걸 봤는지 아닌지조차 확인 불가능하다).

이러한 만족하기 어려운 가정을 가지고 있기 때문에 의학연구에서는 최근 다소 홀대를 받고 있는 상황이지만, 과거의 데이터를 기반으로 다양한 관찰연구를 할 수 있고 선택편향이 완벽하게 없어지지는 않았지만 편향이 다소 줄어든 데이터를 가지고 좀더 정답에 가까운 연구를 할 수 있다는 장점 때문에 사회과학 연구에서는 많이 사용되는 추세로 알고 있다.

PSA에서 가장 중요한 과정은 실험군 대조군 사이 공변량 특징의 밸런스를 맞추기 위한 관측치 스코어링(성향점수 추출)이다. 성향점수를 산정하는데 가장 많이 쓰이는 방법은 아래와 같은 Logistic Regression 방법이다.
간단하게 설명하면 실험군, 대조군을 설명하는 모형을 공변량을 기반으로 빌드하고, 이 빌드된 모형을 기반으로 실험군일 확률값을 도출하면 이게 바로 성향점수가 되는 것이다.

  • \(\ln( \frac{e(X_i)}{1-e(X_i)}) = \beta X_i\)

만일 세개 이상의 처리효과를 보고자 한다면 다항 로지스틱 회귀를 쓰면 된다. 또한 Boosting방법 등 다양한 스코어링 방법을 활용하기도 한다.

만들어진 스코어를 유사한 공변량 효과로 매칭하는 다양한 방식이 존재한다.

  • nearest neighber matching : 대조군과 실험군의 데이터 레코드간의 성향점수 거리가 가장 적은 쌍을 매칭하는 방법
  • cliper matching : 성향점수가 특정 경계 내에 존재하는 것들의 쌍을 매칭하는 방법
  • Mahalanobis distance : 마할라노비스 거리 기반 매칭 방법. subclassification : 실험군 대조군 스코어를 각각 층화로 나눠서 층화별로 매칭하는 방법으로 샘플 데이터 수가 부족할 경우 유용하다.

등등 많은 매칭 방법이 있으며, 일단 스코어가 나왔으니 어떠한 방법을 사용하든 용도에 맞는 방식을 사용하면 되는데, 이를 위해 매칭 방법을 평가하는 방법들이 존재한다.

  • 선택편향(B) 유의성 테스트 (selection bias with significant test)
  • 표준화 편향(standardized bias) : SB
  • 편향 감소율(percentage bias reduction) : PBR

B는 실험군과 대조군의 공변량의 평균 차이를 의미하는데, 이 값이 0이 아닌지를 검증하는 테스트를 통해 매칭 방법의 유의미성을 평가한다. 하지만 샘플 사이즈에 따라서 유의성 테스트 결과가 달라지기 때문에 추천하고 있는 테스트 방법은 아니다. SB는 B의 값을 각 실험군 대조군 공변량의 분산에 따라 표준화시킨 값이라 생각하면되는데, 표준화된 값을 가치고 평가하기 때문에 유의성 테스트보다는 샘플 사이즈에 robust한 특징을 보이며, 선택편향 감소치의 effect size라고 이해하면 될거라 생각한다(effect size는 보고자 하는 효과의 표준화된 값이므로…). SB의 5%정도 감소는 매칭 방법이 꽤 잘 동작했다고 봐도 된다고 알려져 있다.
PBR은 매칭 이전의 B값과 이후의 B값의 차이값을 매칭 이전의 B값으로 나눈 선택편향의 감소 비율을 의미한다. 80%정도 감소했다면 매칭 방법이 바람직했다고 이야기 할 수 있다.

이 밖에 Q-Q plot으로 실험군 대조군의 성향점수의 분포를 시각적으로 비교하고 이상치를 찾아서 제거하는 분석을 수행하기도 한다.

예제

가장 많이 사용하는 MatchIt 패키지를 기반으로 성향점수 매칭을 해보도록 하자!

예제 데이터는 성향점수 예시로 가장 많이 사용되는 "교육 프로그램이 소득에 영향을 미치는가?"에 대한 데이터로 조치여부 필드와 더불어 성별, 나이 등 다양한 변수들이 포함되어 있다.

데이터 역시 관찰 연구 데이터로 아래와 같이 무작위 배정 실험인 마냥 통계 분석 결과를 내면 교육의 효과가 636달러 감소를 시키는 결과가 도출되고 만다. 이런 결과가 사실이라면 벤자민 프랭클린의 "교육에 대한 투자가 최고의 수익을 가져다 준다"라는 명언은 허언이 되버렸을 것이다. ^^;

suppressPackageStartupMessages(
  library(MatchIt)
)

data(lalonde)

summary(lm(re78 ~ treat , data=lalonde))
## 
## Call:
## lm(formula = re78 ~ treat, data = lalonde)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
##  -6984  -6349  -2048   4100  53959 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   6984.2      360.7  19.362   <2e-16 ***
## treat         -635.0      657.1  -0.966    0.334    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 7471 on 612 degrees of freedom
## Multiple R-squared:  0.001524,   Adjusted R-squared:  -0.0001079 
## F-statistic: 0.9338 on 1 and 612 DF,  p-value: 0.3342
mat <- matchit(treat~ age + educ + black + hispan + married + nodegree, data=lalonde, method = 'nearest',caliper = .03, nearest=TRUE,  
        distance = 'logit')

summary(mat)
## 
## Call:
## matchit(formula = treat ~ age + educ + black + hispan + married + 
##     nodegree, data = lalonde, method = "nearest", distance = "logit", 
##     caliper = 0.03, nearest = TRUE)
## 
## Summary of balance for all data:
##          Means Treated Means Control SD Control Mean Diff eQQ Med eQQ Mean
## distance        0.5734        0.1839     0.2290    0.3895  0.5143   0.3896
## age            25.8162       28.0303    10.7867   -2.2141  1.0000   3.2649
## educ           10.3459       10.2354     2.8552    0.1105  1.0000   0.7027
## black           0.8432        0.2028     0.4026    0.6404  1.0000   0.6432
## hispan          0.0595        0.1422     0.3497   -0.0827  0.0000   0.0811
## married         0.1892        0.5128     0.5004   -0.3236  0.0000   0.3243
## nodegree        0.7081        0.5967     0.4911    0.1114  0.0000   0.1135
##          eQQ Max
## distance  0.6009
## age      10.0000
## educ      4.0000
## black     1.0000
## hispan    1.0000
## married   1.0000
## nodegree  1.0000
## 
## 
## Summary of balance for matched data:
##          Means Treated Means Control SD Control Mean Diff eQQ Med eQQ Mean
## distance        0.5006        0.4971     0.2537    0.0035  0.0044   0.0044
## age            24.9231       25.2981    10.2264   -0.3750  2.0000   2.8558
## educ           10.4327       10.3462     2.3140    0.0865  0.0000   0.2788
## black           0.7212        0.7212     0.4506    0.0000  0.0000   0.0000
## hispan          0.1058        0.1250     0.3323   -0.0192  0.0000   0.0192
## married         0.1923        0.2308     0.4234   -0.0385  0.0000   0.0385
## nodegree        0.6538        0.6635     0.4748   -0.0096  0.0000   0.0096
##          eQQ Max
## distance  0.0084
## age      11.0000
## educ      2.0000
## black     0.0000
## hispan    1.0000
## married   1.0000
## nodegree  1.0000
## 
## Percent Balance Improvement:
##          Mean Diff.   eQQ Med eQQ Mean eQQ Max
## distance    99.1088   99.1436  98.8800  98.599
## age         83.0630 -100.0000  12.5302 -10.000
## educ        21.6951  100.0000  60.3180  50.000
## black      100.0000  100.0000 100.0000 100.000
## hispan      76.7553    0.0000  76.2821   0.000
## married     88.1156    0.0000  88.1410   0.000
## nodegree    91.3664    0.0000  91.5293   0.000
## 
## Sample sizes:
##           Control Treated
## All           429     185
## Matched       104     104
## Unmatched     325      81
## Discarded       0       0

다소 엄격하게 하기 위해 옵션으로 caliper 밴드를 0.03로 줘서 각 조치그룹의 성향점수 차이가 0.03 이내인 것들만 매칭하게끔 했다.
caliper 밴드를 왜 0.03으로 줬느냐는 Percent Balance Improvement에서 다수의 변수가 매칭 후 밸런스가 되는지 여부로 정해준 숫자이다. 다소 작은 숫자인데, 데이터를 조금 더 버리더라도 정확한 측정을 하고 싶다는 의지의 표명이라고 봐도 될 것이다(^^;).

성향점수로 매칭된 데이터가 대조군 104개, 실험군 104개이며 대조군의 경우 325개의 데이터가 매칭이 되지 않아 버려진 것을 볼 수 있다. 사실 매칭이 안된 데이터들은 실험 기준에서는 쓸모 없는 데이터라 이야기 할 수 있을 것이다.

매칭 결과 요약으로 나오는 데이터에서 Percent Balance Improvement는 매칭 전과 후의 처리여부에 따른 변량의 평균 차이가 어느정도 나느냐로 어느정도 매칭이 잘 되었는지 확인 가능하다. 모든 변수가 매칭 후 밸런스 향상이 있었음을 볼 수 있다.

mat.dat<- match.data(mat,distance='psa')

head(mat.dat)
##      treat age educ black hispan married nodegree re74 re75       re78
## NSW1     1  37   11     1      0       1        1    0    0  9930.0460
## NSW2     1  22    9     0      1       0        1    0    0  3595.8940
## NSW3     1  30   12     1      0       0        0    0    0 24909.4500
## NSW5     1  33    8     1      0       0        1    0    0   289.7899
## NSW6     1  22    9     1      0       0        1    0    0  4056.4940
## NSW7     1  23   12     1      0       0        0    0    0     0.0000
##            psa weights
## NSW1 0.5876022       1
## NSW2 0.2139931       1
## NSW3 0.6401683       1
## NSW5 0.6873434       1
## NSW6 0.6955235       1
## NSW7 0.6234021       1
tail(mat.dat)
##         treat age educ black hispan married nodegree re74 re75     re78
## PSID408     0  46   11     1      0       1        1    0    0    0.000
## PSID411     0  18   10     1      0       0        1    0    0 5306.516
## PSID412     0  53   12     1      0       0        0    0    0    0.000
## PSID416     0  34   12     1      0       0        0    0    0    0.000
## PSID419     0  51    4     1      0       0        1    0    0    0.000
## PSID423     0  27   10     1      0       0        1    0    0 7543.794
##               psa weights
## PSID408 0.6098633       1
## PSID411 0.7183909       1
## PSID412 0.6927340       1
## PSID416 0.6496005       1
## PSID419 0.5906444       1
## PSID423 0.7367549       1
plot(mat, type='jitter', interactive=F)

plot of chunk unnamed-chunk-3

플로팅 결과, 매칭된 후 처리여부에 따른 스코어 분포가 다소 유사한 것을 확인해 볼 수 있다. 시각화 결과만 보면 다소 극단적인 스코어값을 제거하더라도 매칭 효과와 유사한 결과를 볼 수 있다는 것도 예상해 볼 수 있을 거라 생각한다.

mat.lm <- lm(re78~treat,data=mat.dat)
summary(mat.lm)
## 
## Call:
## lm(formula = re78 ~ treat, data = mat.dat)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
##  -7494  -5000  -2266   2803  52814 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   5000.5      713.7   7.006 3.42e-11 ***
## treat         2493.4     1009.4   2.470   0.0143 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 7279 on 206 degrees of freedom
## Multiple R-squared:  0.02877,    Adjusted R-squared:  0.02406 
## F-statistic: 6.102 on 1 and 206 DF,  p-value: 0.01431

매칭 후 교육효과에 대한 평가를 해본 결과 흥미롭게도 이전 밸런싱 전의 결과와 상반되는 결과가 도출 되었는데, 교육을 받은 그룹이 그렇지 않은 그룹에 비해 약 2494달러 소득이 향상되는 효과가 있다는 것을 보여준다.

만일 이런 매칭 작업을 수행하지 않고 분석 결과를 냈을 경우 정확한 효과는 고사하고 정반대의 분석 결과를 도출할 수 있는 위험을 가지고 있다. 따라서 무작위 배정을 하지 않은 데이터를 기반으로 특정 변수의 정확한 효과를 측정해야 된다면 반드시 이런 과정을 거쳐야 된다.


사족

최근 데이터 분석 결과들을 보면 상관관계를 인과관계로 해석하는 실수를 많이 본다. 사실 인과관계는 매우 엄격한 조건을 만족해야 되고, 실험연구에서는 무작위 실험이 아니면 그 인과를 대부분 인정하지 않는다. 이 PSA는 인과관계를 특정하기 위해서 만족해야될 조건이 상당히 엄격하기 때문에 개인적으로는 근 인과를 파악하거나 혹은 정확한 상관도를 측정하는 용도로 사용하는게 맞다고 생각한다. 무엇보다 수많은 데이터가 쏟아지는 상황에서 다수의 관찰 데이터를 매칭을 통해 잃는게 과거보다는 덜 아까운 시점이 된 시대적인 잇점도 존재하기 때문에 이런 분석을 하지 않을 이유는 더더욱이 없다고 생각한다.

참고문헌

모델링 그리고 Boosting

응용 예측 모델링과 통계 학습 모델링

위 두 단어에 대해서 차이를 안다는건 두 모델링의 목적이 다르다는 것을 안다는 것을 의미하며, 그러한 관점으로 목적에 맞게 두 도구를 사용할 수 있다는 것을 의미할 것이다.

최근 간만에 Machine Learning Modeling을 하면서 다시금 모델의 이상적인 모습과 그 유용성에 대해서 고민을 많이 했다. 일단 우리가 모델을 만든다는 건 아래와 같은 목적이 많은 부분을 차지한다.

  1. 모델링은 우리가 특정 시스템이 어떻게 동작할 것이라는 것에 대한 이해의 과정
  2. 데이터의 요약 방식 자체가 모델링
  3. 예측값과 실측값의 비교를 통한 시스템 이해
  4. 의사 결정을 위한 예측

대부분 4번에 집중을 하고, 이는 곧 우리가 우리 자신의 환경이나 자신을 이해하는 용도(1,2,3)보다는 한정적인 용도이며, 특히나 자동화에 많은 부분이 포커싱 되고 있다.

특히 우리 사회에 대한 이해를 하기 위한 학문인 사회학, 교육학, 철학, 경제, 경영 등과 같은 학문 영역은 1,2,3번 을 위해서 모델링을 많이 하는 편이며, 컴퓨터 공학과 같은 자동화나 인공지능에 집중된 학문 영역은 4번에 포커싱을 하는 경향을 보인다. 따라서 특정인이 어느 학문 출신이라는 것을 아는것만으로도 모델링에 대해서 어떠한 관점을 가지고 있는지 잘 알 수 있다.

성능상에만 목적을 두고 4번에 집중하고자 마음만 먹으면, 예측 성능을 높이는 수많은 기계학습 도구를 사용할 수 있는 여지가 생기나, 블랙박스 모델일 가능성이 많아져 모형 해석은 매우 어렵게 된다. 모형해석이 중요한 시점은 바로 모형 이면의 동작방식이 정확하게 어떻게 돌아가는지 알고 싶을때 중요하게 되는데, 수많은 데이터가 교호작용을 일으켜 모형이 만들어지고, 그러한 작은 모형이 수백개 수천개가 모여 weighted voting을 통해 의사결정을 이룬다 하면 과연 특정 예측이 어떻게 되는지 한마디로 설명이 어렵게 된다. 이 부분은 왜 알아야 되냐고 반문할 수 있으나, 문제는 그렇게 간단하지 않다. 예를 들어 이통사 해지를 할 고객을 아주 정확하게 예측했다 하자면, 과연 이 고객을 잡아두기 위해서 비용대비 가장 효과적인 마케팅 요소를 활용해 잡아두고 싶어할 것이다. 바로 이런 “왜?” 라는 질문을 던지기 시작하면 블랙박스 모형에서는 답변을 하기가 매우 어려워진다. 물론 이런 경우를 위해 예측을 위한 모형 별도… 설명을 위한 모형을 별도 만들기도 하지만, 정확한 설명은 사실상 매우 어렵다.

필자의 경우 Machine Learning 옹호론자로 이쪽에 발을 들여 놓았다가, 2년 전 쯤부터 최근까지 Linear Model이나 Bayesian과 같은 설명 가능한 통계 모형에 심취해 왔었고, 최근 다시금 Machine Learning기반 방법론을 활용해야 되는 프로젝트를 수행하면서 다시금 두 성격이 다른 모델링 작업에 대해서 고민하는 시간을 가지게 되었다. 개인적으로 Deep Learning 방법론은 인공지능 혹은 자동화 관점에서 문제를 접근할 때 이외에는 활용폭이 적다는 생각을 가지고 있어서 실무적으로 활용할 기회가 별로 없는 관계로 일단 논외로 두고, 실무적으로 가장 많은 활용을 하고 있는 모형인 Bagging과 Boosting방법론을 간단하게 설명하고자 한다. 필자가 참고로한 논문은 In The boosting: A new idea of building models이다.

Bagging은 학습셋에서 랜덤하게 서브셋을 추출해(boostrap resampling) 이를 기반으로 모형을 만드는데 이런 과정을 N번만큼 반복해 복수개의 모형을 만들어 이들의 voting으로 예측을 하는 모형을 의미한다. 대표적으로 최근 묻지마 모델로 불리는 randomForest가 이 계열인데, SVM과 더불어 기본 모형으로 가장 좋은 분류 성능을 보인다고 알려져 있기도 하다. 모형의 에러중에서 학습셋의 변동으로 생기는 모형의 에러를 모형의 variance 에러 라고 하는데 이를 줄여주는데 bagging이 효과적인 방법이라고 알려져 있다. 따라서 분류 클래스의 밸런스가 맞지 않거나 하는 문제일때 매우 안정적인 분류 결과를 보여주는 장점을 가지고 있다. 한마디로 막 돌려도 기본 이상을 하는 모델링 방식이라는 것이다.

Bagging이 variance를 줄이는 효과를 가지고 있는 반면 Boosting의 경우 모형 자체가 가지는 가정과 실제 데이터와의 모순으로 생기는 bias까지도 줄이는 효과를 보여준다. 참고로 bias와 varince의 차이를 알고 싶다면 필자의 포스팅(http://freesearch.pe.kr/archives/1412)을 참고하길 바란다. varince를 줄이는 방식은 bagging과 유사하게 다수의 모형의 voting(실제는 weighted된)을 통해서 줄이지만 bias는 분류가 어려운 문제를 풀기 위해 지속적으로 weak learner를 만들어가기 때문이다. 이 때문에 boosting이 outlier나 anomaly detection 문제를 잘 푸는 이유가 된다. 게다가 몇몇 연구는 SVM의 margin을 최대화 하는 기법이 boosting 과정에서도 수행된다고 하는 연구 결과도 있다.

개인적인 경험으로도 역시 Boosting이 Bagging보다 성능이 좋았다. 물론 이는 엄청난 학습 시간과 파라메터 서치에 소요되는 시간과 노력을 제외하고 이야기하는 것이라서 Boosting 알고리즘에 대해서 상세하게 튜닝하지 못한다면 이런 성능 향상을 경험하기 다소 어려운 점이 있으나 최근 gradient boosting계열의 모형 기반으로 심심치 않게 kaggle 대회에서 우승하는걸 보자면 역시나 도구도 알고 써야 되는 것이란 생각을 해본다.

Boosting

서론이 매우 길었는데, Boosting에 대해서 좀더 알아보도록 하겠다.

예제는 논문의 Fig 7에서 가져왔다.

suppressMessages({
library(randomForest)
library(data.table)
library(gbm)
library(ggplot2)
library(plyr)
library(dplyr)
library(rpart)
})

x <- seq(-2,2,by=0.01)
lenx<- length(x)
y <- 2 + 3*x^2 + rnorm(lenx, 0, 0.5)
y_r <- 2 + 3*x^2
x.y <- data.frame(x=x,y=y, y_r=y_r)

x.y.samp <- x.y %>% sample_frac(0.5)
x.y.samp.test <- x.y %>% sample_frac(0.1)

ggplot(x.y.samp, aes(x,y_r)) + geom_line(size=1.5, colour='red') + geom_point(aes(y=y), size=2)

위와 같은 피팅할 대상이 있다고 가정하자면 cart는 아래와 같이 피팅을 하게 된다.

mdl_cart <- rpart(y ~ x,data=x.y.samp)
x.y.samp.test$cart_fit <- predict(mdl_cart,newdata=x.y.samp.test)

ggplot(x.y.samp, aes(x,y_r)) + geom_line(size=1.5, colour='red') + geom_point(aes(y=y), size=1) + geom_line(data =  x.y.samp.test, aes(x=x, y=cart_fit))

randomForest는 좀더 모수에 가까운 피팅을 하는 것을 볼 수 있다.

mdl_rf <- randomForest(y ~ x,data=x.y.samp)
x.y.samp.test$rf_fit <- predict(mdl_rf,newdata=x.y.samp.test)

ggplot(x.y.samp, aes(x,y_r)) + geom_line(size=1.5, colour='red') + geom_point(aes(y=y), size=1) + geom_line(data =  x.y.samp.test, aes(y=rf_fit))

위 모형의 피팅을 Boosting을 기반으로 수행해 보자!

Boosting 기법은 아래와 같은 방식으로 수행된다.

  1. \(\hat{y_1} = f_1(x)\) 와 같이 모형을 피팅한다.
  2. \(y_{res} = y – v_{1}\hat{y_1}\) 와 같이 잔차를 계산한다.
    • 여기서 \(v\)는 0 < \(v\) < 1 사이의 값을 가지는 shrinkage 파라메터로 모형이 오버피팅을 하는걸 방지하는 효과를 보여주는데, 이는 특정 모형 하나가 너무 많은 부분을 설명하는걸 방지하면서 가능해진다. 따라서 일종의 weak learner가 shrinkage 파라메터로 만들어지며 randomForest처럼 모든 데이터를 사용해 모형을 만들지 않고 부트스트랩 샘플링으로 weak learner를 만들게 함으로써 randomForest 처럼 일반화를 잘 하게 하는 모형을 만들어준는 효과를 다시한번 발휘하게 된다.
  3. 위의 과정을 t=2…T번 반복하는데, 이를 일반화 하면 아래와 같이 표현할 수 있다.
    • \(\hat{y_t} = f_t(x)\)
    • \(y_{res,t} = y_{res,t-1} – v_{t}\hat{y_t}\)
    • \(v_{t}\hat{y_t}\) 부분이 최종 regression 모형으로 결과를 예측하는데 쓰인다.
  4. 결과 모형은 아래와 같다.
    • \(y_{pre}=v_1\hat{y_1} + v_2\hat{y_2} + v_3\hat{y_3}+ … + v_{t-1}\hat{y_{t-1}} + v_{T}\hat{y_{T}}=\sum_{t=1}^T v_tf_t(X)\)

위와 같은 동작 방식을 정확하게 이해하기 위해서는 구현을 간단하게나마 해보는게 도움이 된다. 물론 regression이나 regression tree가 아닌 neural network등 다양한 모형을 weak learner로 사용 가능하다.

shrink <- 0.1

#regression based boosting
y_n <- x.y.samp$y
x <- x.y.samp$x
v_y_l <- list()
for(i in 1:100){
  lm_fit <- lm(y_n ~ x*I(0 < x))
  v_y <- shrink * predict(lm_fit)
  v_y_l[[i]] <- shrink * predict(lm_fit, newdata=x.y.samp.test)
  resid_n <-  y_n - v_y
  y_n <- resid_n
}

x.y.samp.test$lm_fit <- apply(as.data.table(v_y_l),1,sum)

x.y.samp.test$lm_fit_3 <- apply(as.data.table(v_y_l)[,1:10,with=F],1,sum)

x.y.samp.test$lm_fit_2 <- apply(as.data.table(v_y_l)[,1:5,with=F],1,sum)

x.y.samp.test$lm_fit_1 <- apply(as.data.table(v_y_l)[,1:2,with=F],1,sum)

ggplot(x.y.samp, aes(x=x,y=y_r)) + geom_line(size=1.5, colour='red') + geom_point(aes(y=y), size=1) + geom_line(data=x.y.samp.test, aes(x=x,y=lm_fit), colour='purple', linetype=2, size=1) + geom_line(data=x.y.samp.test, aes(x=x,y=lm_fit_2),colour='purple',linetype=4) + geom_line(data=x.y.samp.test, aes(x=x,y=lm_fit_3),colour='purple',linetype=4) + geom_line(data=x.y.samp.test,aes(x=x,y=lm_fit_1),colour='purple',linetype=4) 

#cart based boosting
y_n <- x.y.samp$y
x <- x.y.samp$x
v_y_l <- list()
for(i in 1:100){
  rpart_fit <- rpart(y_n ~ x)
  v_y <- shrink * predict(rpart_fit)
  v_y_l[[i]] <- shrink * predict(rpart_fit, newdata=x.y.samp.test)
  resid_n <-  y_n - v_y
  y_n <- resid_n
}



x.y.samp.test$rpart_fit <- apply(as.data.table(v_y_l),1,sum)

x.y.samp.test$rpart_fit_3 <- apply(as.data.table(v_y_l)[,1:10,with=F],1,sum)

x.y.samp.test$rpart_fit_2 <- apply(as.data.table(v_y_l)[,1:5,with=F],1,sum)

x.y.samp.test$rpart_fit_1 <- apply(as.data.table(v_y_l)[,1:2,with=F],1,sum)


ggplot(x.y.samp, aes(x=x,y=y_r)) + geom_line(size=1.5, colour='red') + geom_point(aes(y=y), size=1) + geom_line(data=x.y.samp.test, aes(x=x,y=rpart_fit), colour='purple', linetype=2, size=1) + geom_line(data=x.y.samp.test, aes(x=x,y=rpart_fit_2),colour='purple',linetype=4) + geom_line(data=x.y.samp.test, aes(x=x,y=rpart_fit_3),colour='purple',linetype=4) + geom_line(data=x.y.samp.test,aes(x=x,y=rpart_fit_1),colour='purple',linetype=4) 

위 코드에서는 weak learner를 학습할때 전체 학습셋에서 부트스트랩 샘플링을 하지 않았다. 따라서 이 부분을 변경해서 결과를 확인해 보자.

shrink <- 0.1

#regression based boosting
x.y.samp$y_n <- x.y.samp$y
x <- x.y.samp$x
v_y_l <- list()
for(i in 1:100){
  x.y.samp.sub <- x.y.samp %>% sample_frac(0.2,replace=T)
  lm_fit <- lm(y_n ~ x*I(0 < x),data=x.y.samp.sub)
  v_y <- shrink * predict(lm_fit,newdata=x.y.samp)
  v_y_l[[i]] <- shrink * predict(lm_fit, newdata=x.y.samp.test)
  resid_n <-  x.y.samp$y_n - v_y
  x.y.samp$y_n <- resid_n
}


x.y.samp.test$lm_fit <- apply(as.data.table(v_y_l),1,sum)

x.y.samp.test$lm_fit_3 <- apply(as.data.table(v_y_l)[,1:10,with=F],1,sum)

x.y.samp.test$lm_fit_2 <- apply(as.data.table(v_y_l)[,1:5,with=F],1,sum)

x.y.samp.test$lm_fit_1 <- apply(as.data.table(v_y_l)[,1:2,with=F],1,sum)


ggplot(x.y.samp, aes(x=x,y=y_r)) + geom_line(size=1.5, colour='red') + geom_point(aes(y=y), size=1) + geom_line(data=x.y.samp.test, aes(x=x,y=lm_fit), colour='purple', linetype=2, size=1) + geom_line(data=x.y.samp.test, aes(x=x,y=lm_fit_2),colour='purple',linetype=4) + geom_line(data=x.y.samp.test, aes(x=x,y=lm_fit_3),colour='purple',linetype=4) + geom_line(data=x.y.samp.test,aes(x=x,y=lm_fit_1),colour='purple',linetype=4)