본 게시글은
서울대학교 데이터사이언스대학원 오민환 교수님의
데이터사이언스를 위한 머신러닝 및 딥러닝1 수업을
학습을 목적으로 재구성하였습니다

지난 시간에 이어서 오늘은 tree-based method
bagging, random forests, boosting에 대해서 배워본다

우선 bagging에 대해서 살펴보자
지난 시간에 tree-based method들의 장단점에 대해서 배웠었는데
단점부터 살펴보자면 우선 decision tree는
prediction accuracy가 그렇게 썩 좋지 않았다
bagging은 이러한 낮은 성능을 극복하기 위한 방법이다
bagging을 지금은 decision tree에 적용할 수 있는 방법으로 배우고있지만
bagging은 굉장히 general한 method이다
bagging에 대해서 쉽게 표현하자면
bootstrap으로 생성한 데이터로 학습시킨 각각의 모델들을
aggregate 시킨 것이다
우리가 이전에 한 번 배웠는데 어떤 데이터셋을 평균을 내면
variance도 데이터 개수만큼 나눠지기 때문에
variance가 낮아진다는 장점이 있다
따라서 observation들을 잘 averaging 한다면
variance를 낮춰주기 때문에 prediction이 더 좋아질수가 있다
따라서 평균을 내서 variance를 낮춰주기 위해서
bootstrap을 사용을 하는 것이다

bootstrap은 우리가 이전에도 잠깐 배웠는데
training data 자체는 1개지만 마치 여러 개인 것처럼 만드는 것이었다
따라서 bagging은 이렇게 bootstrap으로 만든 여러 개의 training dataset에
각각의 decision tree를 fit 시키고
그런 다음 각 decision tree들의 aggregate한 prediction 값을 가져오는 것이다
다시 한 번 정리하면 bagging을 한다는 것은
training dataset에서 sample들을 여러 개를 만들어내서
bootstrap dataset을 만든다
그렇게 총 B개의 training data copy를 생성하면
각각의 bootstrap data에서 학습된 모델을 f^b(x)라고 한다
그래서 각 f^b(x) 모델에서 나온 prediction의 평균값을
이 bagging의 output으로 보는 것이다
한 마디로 training data에서 B개를 bootstrap으로 만든 다음
각각의 bootstrap dataset에서 seperate한 function들을 만들고
그 function들의 전체의 평균값을 전체 bagging된 function의 output으로 보는 것이다

그렇다면 bagging에서 classification은?
regression은 우리가 위에서 말한 것처럼 그냥 평균값으로 하면 되는데
classification은 어떻게 할 수 있을까?
동일하게 각각의 bootstrap dataset에서 classification을 위한
decision tree들을 학습시킨 다음
전체에서 가장 표를 많이 받은
즉, majority vote를 선택하면 되는 것이다
한 마디로 bagging은 bootstrap을 활용해서
여러 개의 decision tree를 묶는 것이다

bagging을 활용해서 performance가 얼마나 좋아졌는지 한 번 보자
위 그래프에서 x는 number of trees이고
y는 error rate이다
heart data라서 classification task이다
까만색 실선이 bagging
노란색 실선은 우리가 뒤에 배울 random forests
초록색과 파란색도 각각 bagging과 random forests인데
test가 아니고 out of bagging으로 측정한 값이다
잠깐 out of bagging(oob)에 대해 설명을 하자면
우리가 bootstrap할 때 bootstrap copy로 가는 것이
실제 데이터의 3분의 2정도가 간다고 했다
그럼 bootstrap copy로 가지 않는 데이터는 3분의 1정도일텐데
얘네들을 prediction하는 것을 out of bagging이라고 한다
근데 그냥 cross-validation으로 해도 된다고한다..
아무튼 위 그래프를 보면 bagging에서 tree가 증가하면 증가할수록
에러가 조금씩 낮아지는 것을 확인할 수 있다
우리가 뒤에서 볼 random forests도 마찬가지
그럼 bagging에서 tree가 300개 있다는 것은
bootstrap을 총 몇 번 했다는 뜻인가?
당연히 300번 했다는 뜻이다
그럼 여기서 한 번 생각해볼만한게 있다
이게 tree의 개수가 증가하면 overfit이 발생할까?
위 그래프를 슥 봤을 때는 딱히 tree의 개수가 증가할 때마다
딱히 overfit이 되고있다는 생각은 들지 않는다
결론부터 얘기하자면 bagging도
뒤에서 배울 random forests도
tree의 개수가 증가한다고해서 overfit이 발생하지 않는다
이걸 이해하려면 overfit이 정확하게 뭔지를 이해해야한다
overfit이란 training data의 noise마저 fit을 시키는 것을 의미한다
하지만 bagging도 random forests도
tree를 여러 개 만든다고 해서 각각의 training data의 noise에
fit을 시키는 것이 아니기 때문이다

이제 random forests에 대해서 살펴보자
random forests는 bagging에서 아주 조금 modification을 한 것이다
bagging에서 여러 개의 tree들이 나오는데
random forests는 각 tree들의 correlation을 감소하고자 한 것이다
tree들의 correlation을 감소함으로써 variance를 감소시키는 것이다
random forests도 bagging과 동일하게
bootstrap training sample을 만들어서
각각의 deicision tree를 fit 시킨다
여기까지는 똑같은데 decision tree를 build하는 과정이 조금 다르다
우리가 decision tree를 build 할 때 계속해서 split을 하는데
이때 random forests는 전체 predictor의 개수 p개에서
전체 p개의 Predictor를 모두 활용하는 것이 아니고
p개 중에서 randomly m개의 subset을 나눈다음 거기서만 split을 한다
다시 한 번 정리하자면
예를 들어서 100개의 predictor가 있다고 하자
어떤 predictor를 어디서 자를지 결정하는게 decision tree인데
split을 할 때 전체 100개를 대상으로 하는 것이 아니고
m=10이라고 한다면 100개 중에서 10개만 randomly sample 한 다음
그 10개 중에서만 split을 수행하겠다는 뜻이다
그리고 또 다음 split에서는 새로 10개를 뽑고 그 안에서 split을 수행한다
그리고 그걸 계속해서 반복하는 것이다
우리가 bagging을 수행할 때는 각각의 트리들이 비슷할 수가 있는데
random forests에서는 이 트리들을 덜 비슷하게하고자 하는 것이다
전체 predictor p개 중에서 m개를 선택하는거니까
m은 p보다 작아야하고 m=p이면 결국 그냥 bagging이 되는 것이다
m<p인 경우가 random forests이다
그런데 보통 m은 루트p 정도로 설정한다고한다
즉, random forests는 bagging과 크게 다르지 않은데
random forests의 가장 큰 차이점은 split할 때
m개의 subset 안에서만 수행한다는 점이다
이로 인해 각 tree들이 조금씩 달라지게 되고 correlation이 작아지면서
variance가 작아진다

그렇다면 위 gene data로 예시를 한 번 살펴보자
349명의 환자로부터 얻은 4718개의 gene 데이터가 있고
이는 15개의 class로 classification 될 수가 있다
우선 y값은 보지 않고 training set에서 variance가 높은
500개의 gene data만 일단 가져왔다
training dataset과 test dataset은 randomly 나누었고
training set에서 m을 여러가지로 바꿔가며
random forests를 수행했다

test data에서 error를 측정한 것이다
class가 15개인 task인데 사실 이게 되게 어려운 task이다
이걸 random forests로 수행했다
그리고 위 데이터에 사실 predictor가 되게 많은데
그 중에서 500개만 뽑았다고 한다
그 다음에 계속해서 m값을 바꿔가면서 random forests를 수행했다
m=p인건 그냥 bagging이고 25% 정도의 error rate를
보이고 있는 것을 확인할 수 있다
그리고 p/2도 어느정도 비슷한 error rate을 보이는데
루트 p정도가 확실히 다른거에 비해서 error rate이 낮은 것을 알 수 있다
그럼 여기서의 메세지가 뭘까?
일단 루트 p가 가장 성능이 좋았다는 것인데
m이 무조건 작다고 좋다는게 아니라는 뜻이다
적당한 제약을 두니까 tree간의 correlation이 작아졌고
그래서 variance가 감소해서 prediction이 더 좋아진 것이다
이걸 single decision tree로 했을 때는 0.45정도가 나온다고한다
15개의 class를 classification 하는 것은
굉장히 어려운 task라서 이정도 error rate은 굉장히 잘하는 것이다
random forests도 역시 tree 개수 증가에 따라
overfitting은 발생하지 않는다
overfitting은 트리의 개수보다는 깊이와 더 관련이 있다
tree의 개수가 증가하면 계산량이 증가하긴 하겠지만
그렇게 sensitive하지 않다
그래서 그냥 tree의 개수는 적당히 잘..하면 된다고한다 ㅋㅋ

boosting에 대해서도 배워보자
boosting도 마찬가지로 bagging처럼 general한 메소드이다
random forests는 bagging을 적절히
tree라는 구조에 잘 맞게 한 테크닉인거고
bagging 자체는 general approach이다
꼭 decision tree에서만 사용되는 것은 아니다
우리 앞에서 배운 bagging을 잘 생각해보면
오리지널 데이터셋을 multiple copy로 만든 다음에
각각의 seperate한 decision tree들을 잘 합쳐서 평균내는 거였다
여기서 잘 생각해보면 각각의 bootstrap에서
학습된 tree들은 다 독립적으로 해당 데이터셋에 학습이 된 것이다
다른 tree들이 어떻게 학습했냐에 영향을 받지 않는다
각각의 주어진 bootstrap 데이터에 독립적으로 학습을 하는데
이건 bagging, random forests가 모두 동일하다
그런데 boosting은 여러 개의 트리를 만드는 것은 맞는데
얘네들이 sequentially tree가 grow한다는 점이 다르다
즉, 여러 개의 트리를 만드는 것은 맞지만
트리를 만드는 과정이 전의 트리가 어떻게 학습이 되었느냐를 바탕으로
다음 트리가 생성되게 된다
이게 fundamental한 차이점이다

앞의 bagging과 random forests가 너무 간단했어서
boosting은 상대적으로 복잡하게 느껴질 수 있다
차근차근 한 번 살펴보자
boosting은 training dataset 1개가 우선 있다고 한다
다른 bagging이나 random forest처럼 training dataset을 여러개 만들지 않는다
그리고 몇 개의 트리를 만들지 정하는 B라는 하이퍼파라미터가 있다
여기서 중요한 점은 bagging과 random forests와는 달리
한 개의 데이터셋만 존재한다는 점이다
처음에 우리는 initial model을 만들거다
그런데 이 initial model은 모든 input에 대해서
output을 0으로 내뱉은 그냥 constant function을 한 개 잡을거다
여기에다 residual인 r을 define하는데
맨 처음은 그냥 모든 output에 대해서 0으로 뱉는 function이기 때문에
각 데이터포인트 i에 대한 residual ri는 yi와 동일하다
다시 정리하면 initial model의 prediction은 모두 0이기 때문에
residual r도 그냥 y 라벨 값이 되고
모든 observation에 대해서 그렇게 세팅을 한다
그런 다음 우리가 B번 iteration을 반복하는데
B의 small b인 b번째 모델에서 tree를 fit 시키는데
tree를 fit 시킬 때 몇 번을 split할건지 정하는 하이퍼파라미터 d를 정의한다
일반적으로 d는 되게 작은 수가 된다
정말 extreme한 경우는 d=1일때도 있다
즉, 한번만 split한다는 뜻이다
split을 우리가 d번 한다는 것은
terminal node가 d+1개가 된다는 뜻이다
아무튼 우리가 이 boosting은 순차적으로 트리모델을 fit 시킨다고 했는데
데이터 {xi, yi}로 fit을 시키는게 아니고
우리가 아까 정의한 residual ri를 사용해서
{xi, ri}로 만들어진 surrogate dataset에다가 tree를 fit 시킨다
첫 번째 iteration에서는 residual이 그냥 y label이니까
첫번째 iteration에서만 original dataset에 fit을 시키는 것이다
그 이후부터는 residual ri는 계속해서 변하기 때문이다

그 다음 2.2를 보면 위 식처럼 f^을 update 한다고 나와있다
우리가 surrogate dataset으로 fit 시킨 f^b(x) 모델을
람다를 이용해서 살짝만 shrink 시킨 뒤에 다시 f^(x)에 update한다
따라서 initial에는 그냥 0이었기 때문에 shrink된 모델만 update되는 것이다
이런 방식으로 계속해서 fit시킨 모델들이 add가 된다

매번 모델 fit을 수행시킬때마다 residual도 update된다
방금 우리가 학습해서 update한 f^b(x)를 가지고
현재 residual에 빼주면서 update 시킨다
그렇다면 initial model을 예시로 순서를 한 번 정리해보자
1. 모든 output이 0인 constant function f^(x) 정의
2. 첫 번째 iteration에서 residual은 y label과 동일하므로 {xi, yi}로 모델 fitting
3. 새로 fitting한 모델*람다를 기존 f^(x)에 더해서 update
4. 기존 residual에서 새로 update된 모델의 prediction값을 빼주며 residual update
5. 이 과정을 B번 반복
boosting에서 모델 update를 시켜줄 때
이전까지의 모델에서 계속해서 새로 fitting시킨 모델 * 람다를 더해주는데
그럼 이게 최종적으로는
그냥 모든 B개의 모델에 람다만큼 shrink한 것을 더해준 형태가 된다

최종 bagging 모델의 output은 위와 같이 되는 것이다
그럼 이게 정확하게 어떤 의미를 가지냐?
의미론적으로 기존의 base tree들이 이전에서 학습된 것만큼은 제외하고
나머지 학습되지 못한 부분들을 계속 보완하면서
sequentially building을 수행하는 것이다
즉, boosting에서 residual이 갖는 의미는
이전 모델들이 제대로 학습한 것은 학습이 잘 되었으니까 덜어내고
이전 모델들이 틀리는 부분, 학습이 잘 되지 못한 부분을
그 다음 tree가 학습을 할 수 있도록 계속해서 보완을 하는 것이다
그리고 매번 모델을 update하는 과정에서
람다를 곱해서 모델을 shrink 해주는 이유는
학습을 천천히 시키기 위함이다
아무튼 개념적으로는 연속적으로 가면서
이전 모델이 못했던 것을 다음 모델을 이용해서 보완하겠다는 뜻이고
람다만큼 shrink 시키면서 그 학습을 천천히 수행하겠다는 뜻이다
그래서 일반적인 람다는 보통 0.001 이정도로 매우 작다
사실 람다가 1이면 그냥 shrink 없이
이전 모델이 학습못한 부분을 모두 다 보완하겠다는 것인데
이런 방식보다는 천천히 조금씩 보완해나가는 것이 좋다고 한다
그리고 람다가 너무 작으면 모델 성능이 안좋을 수 있기 때문에
거기에 맞춰서 충분히 학습할 수 있게끔 B도 커져야한다

결국 boosting이 하고자하는 것은
우리가 엄청 큰 트리를 가지고 데이터를 한 번에 학습시키자는게 아니고
작은 트리를 가지고 천천히 학습시키자는 것이다
즉, 큰 트리를 가지고 hard하게 한번에 확 학습시키는게 아니고
작은 트리를 가지고 slowly softly하게 학습시키는 것이다
여기서 또 중요한 점은 base model이 되는 트리에서
d번 split 할 때의 이 d가 매우 작다
심지어는 딱 1번만 split하는 경우도 있는데
이렇게 하는게 실제로 performance가 매우 좋다고 한다
작은 tree를 조금씩 조금씩 fit을 시키게 만드는 것이다

boosting에서의 classification도 알아보자
classification도 regression과 학습 과정을 동일하다
하지만 조금 더 복잡한 것은 residual의 개념이
regression처럼 단순히 prediction에서 빼버리는 것이 아니기 때문이다
일단 개념적으로는 true label에서 estimate된 probability를 빼는 방식인데
이건 그냥 알아만 두면 좋다고 한다

앞의 bagging과 random forests는
tree의 개수인 B가 커진다고해서 overfitting이 발생하지 않는다고 했다
그러나 boosting같은 경우는 순차적으로 모델 fitting을 하기 때문에
B가 너무 늘어나게 되면 overfit이 될 수도 있다
하지만 우리가 생각하는 것보다 엄청 커져야한다고한다
람다가 크면 B가 조금만 커도 overfit이 될 수 있는데
람다가 작으면 천천히 학습되기 때문에
B가 커도 overfit이 금방 생기지는 않는다고 한다
결국 람다가 작아질 때 이게 학습이 잘 되려면
베이스모델의 개수인 B가 커져야하는 것인데
이 두개의 균형이 잘 맞아야한다
boosting에서 우리가 정해야하는 하이퍼파라미터는 d, B, 람다인데
d는 1인 경우가 굉장히 많이 쓰인다고 한다
딱 한번만 split 하겠다는 것인데 이걸 stump라고 한다고 한다
왜 굳이 d=1로 해주냐?
어차피 다 slowly 학습할 것이기 때문이다
그럼 여기서 적당한 하이퍼파라미터들을 찾기 위해서는
우리가 이전에도 그랬던 것처럼 cross-validation을 수행해주면된다
그런데 이 3개의 하이퍼파라미터들을 다 찾아주는게 쉽지않을 것 같은데 괜찮을까?

다행인건 이 하이퍼파라미터들이
그정도로 sensitive하지는 않다
아까봤던 gene 데이터에서 class가 15개가 있는데
이 performance를 랜덤포레스트와 비교한것이다
random forest에 비해서 boosting으로 한게 성능이 현저히 좋은 것을 알 수 있다
위에서 boosting을 했을 때 d를 1과 2로 한 것을 알 수 있는데
d가 1인 경우가 성능이 더 좋다
트리 개수가 적당히 한 1000개 정도 일 때는
d=2인게 조금 더 좋아보였는데
충분히 tree개수를 키우니까 d가 1인 boosting의 error rate이
거의 뭐 0.1보다도 작아졌다
이 15개의 class에 대해서 classification 한다는 것이
굉장히 어려운 task인데도 불구하고 error rate이 10%보다 작다는 것이다
베이스 모델들은 굉장히 simple한데 퍼포먼스가 굉장히 좋다
그리고 B와 람다 하이퍼파라미터에 그렇게 sensitive하지않다
B가 너무 커지면 overfitting이 일어날 수 있는데
람다가 작으면 어느정도는 보완이 된다
위 예시에서도 트리가 5000개나 있었는데도 overfitting은 일어나지 않았다
이렇게 여러 개의 tree를 학습시키는게 오래 걸리지 않냐는 질문이 나왔는데
depth가 작기 때문에 트리 한 개를 학습시키는 속도가 매우 빠르다고한다

다른 데이터셋의 경우도 한 번 살펴보자
random forests와 boosting의 예시이다
트리가 적당한 사이즈였을 때는 performance reduction이
굉장히 빠른 것을 확인할 수 있다
그러다가 시간이 지나면 어느정도 평평해진다
gradient boosting method들은 depth가 4나 6정도 됐을 때
랜덤포레스트보다 잘되는 것을 볼 수 있다
depth가 크면 크면 클수록, B가 커질수록 overfitting이 생길 수 있다

다른 데이터에서도 이메일 스팸여부를 가지고
bagging, random forest, boosting을 수행했다
위 3개 중에서는 역시 boosting이 가장 error rate가 낮다
맨 처음에는 random forests가 가장 성능이 좋은가 싶더니
tree 개수가 증가하면서 천천히 boosting이 성능이 높아지는 것을 볼 수 있다
boosting 모델의 단점을 꼽자면
어떻게든 얘네들은 순차적으로 학습을 시켜야하기 때문에
parallel하게 수행하는 것이 불가능하다
하지만 bagging과 random forests는 parallel이 가능하다
보통 현업에서는 퍼포먼스를 끌어올리기 위해서
대부분 boosting을 많이 사용한다고 한다
XGBoost나 이런 모델들이 현업에서 보통 SOTA를 찍는다고 한다
그리고 boosting은 꼭 tree-based model이 아닌
다른 모델들을 사용해도 상관없다

bagging을 하건 random forests를 하건
우리가 이 variable들 중에서 어떤게 더 중요한지 알고싶을 수 있다
우리가 얘네들을 split 할 때마다 RSS의 reduction이 있을 것이고
그럼 해당 predictor을 잘랐을 때 RSS reduction이 커지면
그 predictor가 중요하다고 볼 수 있다
학습하면서 그런것들을 계속해서 체킹할 수 있고
어떤 variable을 잘랐을 때 퍼포먼스가 어떻게 가장 향상이 되었는지 볼 수 있다
그래서 그런 것들을 keep해서 variable의
importance measure을 수행할 수 있다
classification의 경우 variable의 gini index reduction이
가장 컸던 것을 중요하다고 볼 수 있다
아무튼 그래서 우리는 어떤 feature를 잘랐을 때
가장 효과가 좋았는지를 위처럼 나열할 수 있다

그렇다면 오늘 배운 것을 정리해보자
decision tree는 굉장히 simple한데 비해서
prediction performance는 상대적으로 좋지 않다
bagging이나 random forests만 봐도
simple decision tree보다는 성능이 훨씬 좋다
random forests나 boosting같은 경우는
supervised learning에서는 대부분 SOTA를 찍는 방법론들이라서 알아두면 좋다
하지만 이렇게 갈수록 decision tree가 갖고있던 장점인
해석력은 점점 떨어지게 된다
우리가 랜덤포레스트로 위에서 봤던 variable importance는 구할 수 있어도
모델의 해석 자체는 어려워지게 되는 것이다
'강의 > machine learning & deep learning' 카테고리의 다른 글
| [ML] Principle Components Analysis (PCA) (0) | 2025.10.17 |
|---|---|
| [ML] Support Vector Machine (SVM) (0) | 2025.10.15 |
| [ML] Tree-based Methods 1편 (Decision Trees) (1) | 2025.10.12 |
| [ML] Subset Selection 2편 (Shrinkage Methods: Ridge, Lasso) (1) | 2025.10.10 |
| [ML/과제 1] Linear Regression, Logistic Regression scikit-learn으로 구현하고 결과 분석하기 (0) | 2025.10.08 |