본문 바로가기
의학 연구

[비전공자의 머신러닝 의학연구 #5] Random Forest, Random Survival Forest

by Dr CK 2026. 5. 3.

단일 트리 모형은 불안정하고 과적합되기 쉽다. 이를 극복하는 방법은 여러 트리의 결과를 결합하는 것이고, 그것이 랜덤포레스트(Random Forest) 이다. 의학연구의 생존분석에 적용하는 확장판이 랜덤생존포레스트(Random Survival Forest, RSF) 이다. 두 가지를 함께 정리한다.

 

1. 랜덤포레스트 Random Forest

랜덤포레스트란 어느 데이터에서 여러개의 단일 트리모형을 만들어 이들을 결합하여 최종 모형을 만드는 것으로 여러개의 트리모형을 결합한다는 점에서 Forest, 이 과정에서 무작위성을 부여함에서 Random 이라는 말이 붙었다.

랜덤포레스트에는 중요한 개념이 있는데, 부트스트랩 Bootstrap, 부작위 변수 선택 Random Feature Selection, 집계 Aggregate 이다. 이중 BootstrapAggregate을 합해서 배깅 Bagging 이라고 하고, 이는 랜덤포레스트의 핵심 개념이다.

여러 개의 모델을 결합해 단일 모델보다 더 나은 예측을 만드는 방법을 앙상블 Ensemble 이라고 하며, Random Forest 는 트리모형들을 결합한 대표적인 앙상블 모형이다.

 

부트스트랩 Bootstrap

  • 원본 데이터에서 복원 추출로 매번 다른 샘플을 만들어 트리마다 다른 데이터로 학습하는 방법이다.
  • 핵심은 복원추출이란 점으로 샘플데이터를 추출할때 동일한 사람이 여러번 추출되어 포함될 수 있다는 점이다.
  • 복원 추출의 의미 — 부트스트랩 샘플은 원본과 다르지만, 통계적으로는 같은 분포를 가진다고 본다. 매번 다른 부트스트랩 샘플을 만들면, 각각 조금씩 다른 데이터가 된다. 이걸로 학습한 트리들도 서로 조금씩 다른 모양이 된다. 이것이 다양성의 핵심이다.
  • 복원 추출이라서 어떤 환자는 여러 번, 어떤 환자는 한 번도 안 뽑힐 수 있다. 수학적으로 원본 데이터의 평균 63%만 부트스트랩 샘플에 포함되고, 37%는 Out-of-Bag (OOB)가 된다. 이 OOOB 샘플들은 그 트리의 학습에 안 쓰인 데이터로, 그 트리에 대한 검증 데이터의 역할도 할 수 있다. 따라서 랜덤포레스트는 별도의 Validation 세트가 없어도 일반화 성능 추정이 가능하다.
  • 이름자체가 부츠끈이라는 뜻으로 이는 "자기 부츠 끈을 잡고 자신을 들어올린다"는 의미로 이는 데이터 자신만으로 분포를 추정한다는 의미에서 이름이 붙었다.

변수 무작위 선택 Random Feature Selection

  • Bootstrap을 통해서 선택된 샘플에서 하나의 트리를 학습할 때 모든 피쳐(변수)에 대해서 학습에 이용하지 않는다. 학습에서 매 노드 분할마다 전체 변수 중 일부만 후보로 사용하며 이를 Random Feature Selection이라고 한다.
  • 데이터가 다르더라도 가장 강력한 변수가 있으면 모든 트리가 그걸 첫 분할로 선택하게 된다. 결과적으로 트리들의 상위 구조가 비슷해지고, 트리 간 상관성이 높아져서 평균을 내도 분산 감소 효과가 작아진다.
  • 노드마다 후보군을 무작위로 제한함으로써, 평소에는 주목받지 못했던 특성들에게도 기회를 주고 결과적으로 서로 다른 개성을 가진 트리들을 만들어 모델의 전체적인 예측력을 높이는 것이다.
  • 따라서, 변수 무작위 선택을 추가하면 강력한 변수가 항상 후보에 들어있지는 않다. 어쩔 수 없이 다른 변수로 분할하게 되고, 트리들이 더 다양해진다.

집계 Aggregate

  • Bootstrap으로 추출한 샘플들에 대해서 Random Feature Selection을 통해서 무작위성을 부여하여 학습한 수백개의 단일 트리들을 합하여 하나의 모델로 만드는것을 의미한다.
  • 새 환자가 들어오면 모든 트리가 각각 예측하고, 그 결과를 결합한다. 분류는 다수결, 회귀는 평균.
  • 이 결합 과정에서 개별 트리의 변동이 상쇄되어 분산이 줄어든다. 통계학의 기본, 독립인 추정량의 평균은 분산이 1/n로 줄어든다는 원리.

 

2. 핵심 하이퍼파라미터

파라미터 의미 권장값
n_estimators 트리 개수 100~500
max_features 분할에서 고려할 변수 수 분류 'sqrt', 회귀 1/3
max_depth 각 트리 최대 깊이 None (앙상블이 과적합 완화)
oob_score OOB 점수 계산 True 권장
n_jobs 병렬 코어 수 -1
class_weight 클래스 가중치 불균형 데이터에 'balanced'

단일 결정트리는 max_depth로 깊이를 제한해야 했지만, 랜덤포레스트는 보통 트리를 끝까지 자라게 둔다. 앙상블의 평균 효과가 과적합을 자연스럽게 줄여주기 때문이다.

  • n_estimators : 생성할 트리의 수. default=100, 일반적으로 많은 트리를 생성할수록 성능이 향상됨. 하지만 일정수준을 넘어가면 성능향상보다 계산비용이 증가함.
  • max_features : 각 노드를 분할할 때 고려할 특성의 무작위로 선택되는 특성의 개수. 값을 작게 설정하면 분산 감소, 편향 증가, 복잡도 감소, 모델의 성능 감소. 값을 크게 설정하면 분산 증가, 편향 감소, 복잡도 증가, 모델의 성능 증가, 과적합 위험

 

3. 파이썬 코드

 

RandomForestClassifier

Gallery examples: Probability Calibration for 3-class classification Comparison of Calibration of Classifiers Classifier comparison Inductive Clustering OOB Errors for Random Forests Feature transf...

scikit-learn.org

미리 설치가 필요한 패키지는 "scikit-learn" 이다.

pip install scikit-learn
from sklearn.ensemble import RandomForestClassifier

model = RandomForestClassifier(
    n_estimators=300,
    max_features='sqrt',
    class_weight='balanced',
    oob_score=True,
    n_jobs=-1,
    random_state=42
)
model.fit(X_train, y_train)

# OOB 점수 확인
print(f'OOB Score: {model.oob_score_:.3f}')

# 예측
y_pred = model.predict(X_test)
y_proba = model.predict_proba(X_test)[:, 1]

필요한 데이터의 형태는 트리모형의 그것과 동일하다. 

X (array-like, shape = (n_samples, n_features)) – Data matrix
y (structured array, shape = (n_samples,)) – A structured array containing the binary event indicator as first field, and time of event or time of censoring as second field.

 

4. 랜덤생존포레스트 (RSF)

의학연구에서 많이 이용되는 통계모형은 Cox 회귀분석이고 이는 생존분석으로 우측절단(Right truncation)이 존재하는 불완전한 데이터를 이용하여 분석하는 방법이다. 이런 데이터에는 검열(censoring) 이라는 특수성이 있다 — 추적이 끝났는데도 사건이 일어나지 않은 환자. 일반 분류·회귀 모델은 검열을 제대로 못 다룬다.

생존분석의 도구로 통계학의 Cox 회귀가 있다면 머신러닝에는 Random Survival Forest (RSF) 이다 (Ishwaran et al. 2008).

RSF는 랜덤포레스트의 모든 핵심 — 부트스트랩, 변수 무작위, 결과 결합 — 을 그대로 가져온다. 다만 두 가지가 다르다.

  • 분할 기준 — Log-rank 통계량 : 분류 트리는 Gini 감소가 큰 분할을 선택했다. RSF는 두 자식의 생존 곡선이 가장 다른 분할을 선택한다. 이 차이를 측정하는 표준이 의학통계의 log-rank 검정이다.
  • 잎 노드 출력 — 생존 곡선 : 분류 트리의 잎은 클래스 라벨을, 회귀 트리는 평균값을 출력한다. RSF의 잎은 — 그 잎에 도달한 환자들의 생존 곡선 (Kaplan-Meier 또는 Nelson-Aalen)을 출력한다.

5. RSF 파이썬 코드 — scikit-survival

설치가 필요한 패키지는 "scikit-survival" 이다.

pip install scikit-survival

scikit-survival은 y를 구조화된 NumPy 배열로 받는다 — (event: bool, time: float) 형식으로 입력받는다.

from sksurv.ensemble import RandomSurvivalForest
from sksurv.metrics import concordance_index_censored
from sksurv.util import Surv

# y의 구조화 배열 형식 만들기
y = Surv.from_arrays(event=df['event'], time=df['time'])

# 모델 학습
model = RandomSurvivalForest(
    n_estimators=500,
    max_features='sqrt',
    min_samples_leaf=15,
    n_jobs=-1,
    random_state=42
)
model.fit(X_train, y_train)

 

반응형