본문 바로가기

R-PAGO 노트

부스팅(Boosting) 이론과 예제

부스팅(Boosting) 알고리즘

부스팅(Boosting) 알고리즘부터 살펴보자.(상기 그림) 알다시피 배깅은 여러 Boostrap 샘플을 독립적으로 복원추출한 다수의 Boostrap 트리의 평균 또는 다수결을 통해 단일 예측모델을 만든다. 그러나 부스팅은 병렬의 독립된 트리가 아닌 하나의 트리를 순차적으로 업그레이드하여 완성한다. 상세 순서는 아래와 같다.

  1. 최초 추정값이며 훈련데이터 각각은 터미널노드 값으로 인식한다. 
  2. 첫번째 시도(b=1)
  3. d개의 분할을 가진 트리를 만들어 상기 훈련데이터에 적합한다. 당연히 터미널도드수는 분할 수보다 1개 많은 d+1가 된다.
  4. 추정값 업그레이드: 이전 추정값에 첫번째 추정값에 수축파라미터()를 곱합 값을 더해 추정값을
    업그레이드한다. 수축파라미터가 작을 수록 추정값은 더 천천히 업그레이드된다. 수축파라미터는 양수이다.
  5. 잔차 업그레이드: 훈련데이터에 추정값에 수축파라미터를 곱한 값을 뺀다. 3번과 같이 추정값을 직접 빼서 잔차를 만들지 않는 이유는 잔차도 수축파라미터 만큼 천천히 업그레이드하기 위함이다.
  6. 2,3,4 번을 b가 B가 될 때까지 반복한다.
  7. 최종 추정값은 업그레이드된 3번 추정값의 마지막 값이 된다.

위 순서와 같이 부스팅은 앞서 만든 트리들에 강하게 영향을 받으며 결과값이 아닌 현재의 잔차들을 반응변수로 사용해 트리를 적합한다. > 부스팅에서의 트리는 최초 분할(d)이후에는 따로 분할하지 않는다. 대신 훈련데이터 각각의 추정값과 잔차값을 업데이트해 각각을 예측하는 방식이다.


적용 Point

수축파라미터와 B의 관계: 

수축파라미터가 작을 수록 B값은 어느 정도 커야 추정값이 수렴할 수 있다. 따라서 수축파라미터와 B값의 관계를 잘 이용해야한다. 랜덤포리스트의 B는 400~500이면 충분했지만 부스팅은 수축파라미터 0.01기준으로 최소 2,000은 돼야 한다. 문제는 수축파라미터가 얼마가 돼야 적당한가는 문제에 따라 다르다는 것이다.

분할수 d: 

분할수 d는 변수 갯수와 관련될 수 있기 때문에 상호작용 깊이(interaction depth)라 하며 상호작용 순서를 제어한다. 분할수가 크면(예:d=2) 분할수(d=1)가 작을때보다 B가 작아도 검정오차는 작다. 그러나 B가 적당히 커지면 분할수가 작을 때 오히려 검정오차는 더 작아진다. 상기 2가지 점을 고려한 적용 Point는 다음과 같다. 

1. 수축파라미터는 0.01 또는 0.001을 사용하자. B값은 2,000~5,000까지 확인하자. 

2. 보통 트리분할은 d=1이어도 충분하다. 이미 성장된 이전 트리들을 통해 트리가 성장했기 때문이다.


실전 예제

  • 이전글 랜덤포리스트 총정리와 같이 보스턴 집값을 예로 들자.
  • 부스팅은 gbm 패기지의 gbm합수를 사용한다.
  • 회귀트리는 distribution을 “gaussian”으로 분류트리는 “bernoulli”이다.
  • n.trees는 B이며 interaction.depth는 d이다. 수축파라미터인 shrinkage의 default는 0.001이다.
library(MASS)
library(gbm)
set.seed(1)
train<-sample(1:nrow(Boston),nrow(Boston)/2)
boost.boston=gbm(medv~.,Boston[train,],distribution="gaussian",n.trees=5000,interaction.depth=4)

B가 5,000이라 시간이 조금 걸린다. summary함수는 고맙게도 각 변수간 영향력을 출력해준다.

summary(boost.boston)

##             var     rel.inf
## lstat     lstat 45.96792013
## rm           rm 31.22018272
## dis         dis  6.80567724
## crim       crim  4.07534048
## nox         nox  2.56586166
## ptratio ptratio  2.26983216
## black     black  1.78740116
## age         age  1.64495723
## tax         tax  1.36917603
## indus     indus  1.27052715
## chas       chas  0.80066528
## rad         rad  0.20727091
## zn           zn  0.01518785

가장 높은 영향력을 가진 변수는 rm과 lstat이다. 주택가격 medv는 rm에 따라 증가하고 lstat에 따라 감소함을 알 수 있다.

par(mfrow=c(1,2))
plot(boost.boston,i="rm")
plot(boost.boston,i="lstat")

검정MSE를 확인해보자. 11.85이다. 이전 글 랜덤포리스트 검정MSE 값 11.5와 유사하다. 랜덤포리스트의 predict와 달리 tree 갯수를 입력해야 한다.

boston.test<-Boston[-train,"medv"]
y.hat.boost<-predict(boost.boston,newdata=Boston[-train,],n.trees=5000)
mean((y.hat.boost-boston.test)^2)
## [1] 11.84694

최소 검정 MSE를 가지는 수축파라미터(람다)와 트리 분할수(d)를 찾아보자. 용량 관계로 반복문 코드는 markdown밖에서 작성하였다. 아래와 같다.

boostMSE.df<-data.frame(depth=rep(NA,250),shrinkage=rep(NA,250),MSE=rep(NA,250))
lamda<-seq(0.001,0.2,length=50)
for (j in 1:5){ #트리분할 수 for (i in 1:50){ #수축파라미터
l<-lamda[i]
boost.boston<-gbm(medv~.,Boston[train,],distribution=“gaussian”,n.trees=5000,interaction.depth=j,shrinkage=l)
y.hat.boost<-predict(boost.boston,newdata=Boston[-train,],n.trees=5000)
boostMSE.df[i+(j-1)*50,1]<-j #트리분할수
boostMSE.df[i+(j-1)*50,2]<-l #수축파라미터
boostMSE.df[i+(j-1)*50,3]<-mean((y.hat.boost-boston.test)^2)} #MSE
}

아래 결과를 보면 하위 11개의 검정 MSE를 가지는 depth는 4 또는 5이다. d=1일 때 검정 MSE는 d=2 ~4일 때와 차이가 크다. 아래 그래프는 분할수 d와 수축파라미터에 따라 검정 MSE가 상당히 개선됨을 보여준다. 상기 훈련데이터와 검정데이터의 경우 검정 MSE최소는 d=5, 수축파라미터 약 0.1일때 9.69이다.

##       X depth  shrinkage       MSE
## 163 163     4 0.04973469 10.089042
## 167 167     4 0.06597959  9.986678
## 169 169     4 0.07410204  9.931877
## 172 172     4 0.08628571 10.082444
## 184 184     4 0.13502041  9.866725
## 204 204     5 0.01318367 10.096750
## 210 210     5 0.03755102 10.009984
## 220 220     5 0.07816327 10.014482
## 226 226     5 0.10253061  9.690825
## 230 230     5 0.11877551  9.764487


의문점

1. 수축파라미터가 0.01 또는 0.001일때 과적합이 생기지 않을 b의 갯수(B)는 얼마여야 할까? 

2. 상기 수축파라미터 0.1, d=5에서 B=5000일때 과적합이 아니라고 어떻게 알 수 있을 까?

3. 분류트리의 부스팅은 어떻게 하나? 트리외에 다른 부스팅은 어떻게 하나? 

4. 훈련셋에 따라 부스팅 결과 값의 차이는 달라지지 않는가?