그리드서치로 랜덤포레스트 튜닝하기

Soonmo Seong
Cloud Villains
Published in
15 min readApr 9, 2024

이 블로그는 영문으로 작성된 Random Forest with Grid SearchClaude를 통해 국문으로 번역하여 작성되었습니다. 이해를 돕기 위해 약간 수정했습니다.

Generated by Gemini

나무를 보고 숲을 보지 못하다.

이 격언은 세부적인 것에 너무 집중하다 보면 전체적인 큰 그림을 놓치게 된다는 의미입니다. 즉, 큰 그림을 보면서 부분적인 세부사항을 종합하는 것이 중요하다는 교훈을 줍니다. 이는 기계 학습뿐만 아니라 다양한 분야에서 적용될 수 있는 의미 있는 통찰입니다. 기계 학습 분야에서도 적용되는데, 의사 결정 트리 모델은 개별 트리에 집중하지만, 랜덤 포레스트 모델은 다수의 의사 결정 트리를 결합하여 더 나은 성능을 발휘합니다. 랜덤 포레스트가 기계 학습 알고리즘 중 인기 있는 이유입니다.

Generated by Gemini

우리는 Google Colab에서 랜덤 포레스트를 구현하는 과정을 단계별로 살펴보겠습니다.

  • 데이터셋 얻기
  • 데이터셋 읽고 수치 변수에 대한 히스토그램 그리기
  • 범주형 변수에 대한 원-핫 인코딩
  • 학습 데이터와 테스트 데이터 분리
  • 마이너 클래스 인스턴스 오버샘플링
  • 랜덤 포레스트 모델 학습
  • 모델 성능 평가
  • 하이퍼파라미터 튜닝(그리드 서치)
  • 특성 중요도 분석

이런 단계를 통해 Google Colab에서 랜덤 포레스트 모델을 구현하고 이해할 수 있을 것 같습니다. 각 단계에서 필요한 코드와 설명을 잘 살펴보시면 좋을 것 같습니다.

데이터셋 얻기

Kaggle에서 제공하는 항공권 예약 데이터 세트를 사용하겠습니다. 이 데이터 세트는 50,000개의 기록과 14개의 특성으로 구성되어 있네요.

다음은 데이터 세트의 열 설명입니다. booking_complete가 타깃 변수로, 고객이 예약을 완료했는지 여부를 나타냅니다.

  • num_passengers: 여행하는 승객 수
  • sales_channel: 예약이 이뤄진 판매 채널
  • trip_type: 여행 유형(왕복, 편도, 원 스톱)
  • purchase_lead: 여행일과 예약일 사이의 일수
  • length_of_stay: 목적지에서의 체류 일수
  • flight_hour: 비행 출발 시간
  • flight_day: 비행 출발 요일
  • route: 출발지 -> 도착지 항공편 경로
  • booking_origin: 예약이 이뤄진 국가
  • wants_extra_baggage: 고객이 추가 수화물을 원했는지 여부
  • wants_preferred_seat: 고객이 우선석을 원했는지 여부
  • wants_in_flight_meals: 고객이 기내식을 원했는지 여부
  • flight_duration: 총 비행 시간(단위: 시간)
  • booking_complete: 고객이 예약을 완료했는지를 나타내는 플래그

데이터 세트 읽고 수치 변수에 대한 히스토그램 그리기

이 단계에서는 우선 데이터 세트를 읽어들이고, 수치형 변수들에 대한 히스토그램을 그려보겠습니다. 이를 통해 각 변수의 분포와 특성을 살펴볼 수 있습니다.

  • 데이터 세트 읽기
    - 제공된 항공권 예약 데이터 세트를 읽어들입니다.
    - 데이터 프레임 형태로 데이터를 저장합니다.
  • 수치 변수 히스토그램 그리기
    - 수치형 변수들(예: num_passengers, purchase_lead, length_of_stay, flight_duration 등)에 대한 히스토그램을 그립니다.
    - 이를 통해 각 변수의 분포와 특성을 시각적으로 확인할 수 있습니다.
    - 히스토그램 분석을 통해 이상치 탐지, 변수 변환 등의 전처리 작업을 수행할 수 있습니다.
import pandas as pd

df = pd.read_csv('customer_booking.csv', encoding='ISO-8859–1')
df.hist(figsize = (10, 10))

위의 히스토그램 분석 결과를 보면 다음과 같습니다:

  • 종속 변수인 booking_complete가 불균형되어 있습니다.
  • 5개의 변수가 범주형 변수로 확인되었습니다.
  • 이에 따라 범주형 변수에 대한 원-핫 인코딩이 필요할 것으로 보입니다.

범주형 변수를 확인하는 방법으로 for 루프를 활용할 수 있습니다. 첫 번째 행의 데이터 유형이 문자열인지 확인하는 조건을 사용하면 됩니다.

cat_cols = []
num_cols = []

for col in df.columns:
if type(df[col][0]) == str:
cat_cols.append(col)
else:
num_cols.append(col)

범주형 변수에 대한 원-핫 인코딩

범주형 변수에 대한 원-핫 인코딩 방법은 주로 두 가지가 있습니다:

  1. Sklearn 사용
  2. Pandas의 pd.get_dummies() 함수 사용

Pandas의 pd.get_dummies() 함수를 사용하는 것이 코딩 측면에서 더 간단합니다. 이 때 axis=1을 명시해야 합니다. 그렇지 않으면 행 방향으로 연결되기 때문입니다.

원-핫 인코딩이 완료되면 원래의 범주형 변수 열은 삭제해야 합니다.

이와 같은 방식으로 데이터 세트의 범주형 변수를 원-핫 인코딩할 수 있습니다.

from pandas import get_dummies

for col in cat_cols:
X = pd.concat([X, pd.get_dummies(X[col])], axis =1 )
X = X.drop(col, axis = 1)

학습 데이터와 테스트 데이터 분리

기계 학습 모델링에서 데이터 분할은 매우 중요한 단계입니다. 만약 완전히 분리되지 않으면 데이터 누출로 인해 치명적인 예측 문제가 발생할 수 있습니다. 예를 들어, 학습 정확도와 테스트 정확도가 모두 좋게 나올 수 있지만, 이 모델은 실 운영에서 실패할 수 있습니다. 데이터 누출로 인해 테스트 정확도가 높게 나오지만, 실제 일반화 성능은 좋지 않기 때문입니다. 이 데이터 누출 문제는 오버샘플링 단계에서도 다시 언급될 것입니다. 따라서 학습 데이터와 테스트 데이터를 완전히 분리하는 것이 매우 중요합니다. 그렇지 않으면 모델의 성능과 일반화 능력을 정확하게 평가할 수 없게 됩니다

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 1)
y_train.hist()

마이너 클래스 인스턴스 오버샘플링

위의 히스토그램에서 볼 수 있듯이, 타깃 변수의 분포가 매우 불균형합니다. 데이터셋의 이러한 불균형은 ML 모델의 성능을 저하시킵니다. 예를 들어, 데이터셋이 사기 관련이고 사기 비율이 0.01%라고 가정해 보겠습니다. 만약 모델이 모든 고객을 사기가 아닌 것으로 예측한다면, 정확도는 99.99%가 될 것입니다. 하지만 이 모델은 사기를 방지할 수 없습니다. 즉, 정확도가 99.99%임에도 불구하고 모델이 실패하게 되는 것입니다. 따라서 이러한 불균형은 마이너 클래스를 오버샘플링하거나 메이저 클래스를 언더샘플링하여 해결해야 합니다. ML 분야에서는 오버샘플링이 선호됩니다.

SMOTE(Synthetic Minority Over-sampling Technique)는 K-최근접 이웃(KNN) 알고리즘을 활용하여 마이너 클래스의 새로운 데이터포인트를 생성합니다. 구체적으로는 마이너 클래스의 한 데이터포인트를 선택하고, 이 데이터포인트와 가까운 데이터포인트들 중 하나를 선택하여 새로운 데이터를 생성합니다. 중요한 것은 오버샘플링은 오직 학습 데이터에만 적용해야 한다는 점입니다. 테스트 데이터를 분리하기 전에 오버샘플링을 수행하면 데이터 누출이 발생할 수 있습니다.

from imblearn.over_sampling import SMOTE

smote = SMOTE()
X_train_resampled, y_train_resampled = smote.fit_resample(X_train, y_train)
y_train_resampled.hist()

랜덤 포레스트 모델 학습

데이터 전처리 단계를 완료했으니, 이제 랜덤 포레스트 모델을 학습시켜 보도록 하겠습니다. 이처럼 데이터 세트를 준비하고 랜덤 포레스트 모델을 학습시킨 결과, 84%의 정확도를 달성할 수 있었습니다. 이 단계에서는 이렇게 간단하게 랜덤 포레스트 모델을 학습시키고 초기 성능을 확인할 수 있습니다. 다음 단계에서는 모델 성능을 더 자세히 검증하고, 하이퍼파라미터 튜닝을 통해 성능을 향상시키는 방법을 살펴보겠습니다. 이처럼 데이터 세트를 준비하고 랜덤 포레스트 모델을 학습시킨 결과, 84%의 정확도를 달성할 수 있었습니다. 이 단계에서는 이렇게 간단하게 랜덤 포레스트 모델을 학습시키고 초기 성능을 확인할 수 있습니다. 다음 단계에서는 모델 성능을 더 자세히 검증하고, 하이퍼파라미터 튜닝을 통해 성능을 향상시키는 방법을 살펴보겠습니다.

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

rf = RandomForestClassifier()
rf.fit(X_train_resampled, y_train_resampled)
y_pred = rf.predict(X_test)
accuracy_score(y_test, y_pred)

모델 성능 검증

모델 성능을 검증할 때 가장 먼저 고려해야 하는 것은 혼동 행렬(Confusion Matrix)입니다. 혼동 행렬이라는 이름에서 알 수 있듯이, 이 지표를 정확하게 이해하고 해석하는 것은 쉽지 않습니다. 많은 똑똑한 사람들도 혼동을 겪곤 합니다. 혼동 행렬은 실제 라벨과 예측 라벨 사이의 관계를 보여주는 매트릭스입니다. 이를 통해 모델의 정확도, 정밀도, 재현율 등 다양한 성능 지표를 계산할 수 있습니다. 따라서 혼동 행렬은 모델 평가에 있어 매우 중요한 도구이지만, 이를 올바르게 해석하고 활용하는 것이 관건입니다. 모델 성능 검증 시 혼동 행렬을 면밀히 분석하여 모델의 장단점을 파악해야 합니다.

from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(confusion_matrix=cm,
display_labels=rf.classes_)
disp.plot()
plt.show()

재현율(recall)이 23%라는 것은 실제로 완료된 예약 중 23%만 모델에 의해 완료된 것으로 예측되었다는 의미입니다. 즉, 모델이 실제 완료된 예약의 77%를 누락하여 예측하지 못했다는 뜻입니다. 이는 모델의 성능이 좋지 않다는 것을 나타내는 지표입니다. 모델이 실제 완료된 예약을 잘 찾아내지 못하고 있다는 문제점을 보여줍니다. 따라서 이 모델의 성능 개선을 위한 추가적인 최적화가 필요할 것으로 보입니다.

from sklearn.metrics import recall_score

recall_score(y_test, y_pred)

정밀도(Precision)가 42%라는 것은 모델이 완료된 것으로 예측한 예약 중 실제 완료된 예약은 42%에 불과하다는 것을 의미합니다. 즉, 모델이 완료된 것으로 예측한 예약 중 절반 이상이 실제로는 완료되지 않았다는 것을 의미합니다. 이는 모델의 정밀도가 좋지 않다는 것을 보여줍니다. 모델이 완료된 것으로 잘못 예측하는 경우가 많다는 문제점이 있다고 볼 수 있습니다. 재현율과 마찬가지로 이 모델의 성능 향상을 위한 추가적인 개선이 필요할 것으로 보입니다.

from sklearn.metrics import precision_score

precision_score(y_test, y_pred)

하이퍼파라미터 튜닝(그리드 서치)

현재의 모델로는 충분하지 않습니다. 더 나은 성능을 위해 하이퍼파라미터 튜닝을 시도해 보겠습니다. 교차 검증(CV)을 활용한 그리드 서치가 도움이 될 것 같습니다. 다음과 같은 하이퍼파라미터들을 조정해 보겠습니다.

  • max_depth: 각 트리의 최대 깊이. 깊이가 너무 깊으면 과적합 될 수 있습니다
  • n_estimators: 숲 속 트리의 개수
  • max_features: 고려할 특성의 개수. 과적합을 방지하는 주요 하이퍼파라미터입니다. 총 특성 개수의 제곱근을 추천합니다
  • min_samples_leaf: 각 리프 노드에 필요한 최소 샘플 수.

그리고 verbose=3으로 설정하면 그리드 서치 과정을 자세히 볼 수 있습니다. 이와 같은 하이퍼파라미터 튜닝을 통해 모델의 성능을 더 높일 수 있을 것으로 기대됩니다.

from sklearn.model_selection import GridSearchCV

rf_grid = RandomForestClassifier()
gr_space = {
'max_depth': [3,5,7,10],
'n_estimators': [100, 200, 300, 400, 500],
'max_features': [10, 20, 30 , 40],
'min_samples_leaf': [1, 2, 4]
}
grid = GridSearchCV(rf_grid, gr_space, cv = 3, scoring='accuracy', verbose = 3)
model_grid = grid.fit(X_train_resampled, y_train_resampled)
print('Best hyperparameters are '+str(model_grid.best_params_))
print('Best score is: ' + str(model_grid.best_score_))

Google Colab의 CPU를 사용하여 4시간 이상 실행했지만, 정확도는 이전보다 오히려 낮아졌습니다. 그러나 재현율(Recall)은 47%, 정밀도(Precision)는 30%로 향상되었습니다. F1 스코어 측면에서 보면 이전 모델의 F1=0.297에서 이번 모델의 F1=0.366으로 개선되었습니다. 이를 통해 지금까지 수행한 하이퍼파라미터 튜닝 작업이 효과를 발휘한 것으로 볼 수 있습니다. 비록 정확도가 개선되지 않았지만, 재현율과 정밀도, 그리고 F1 스코어 측면에서는 성능 향상이 있었다고 할 수 있습니다. 따라서 지금까지 진행한 모델 개선 작업은 긍정적인 방향으로 작용한 것으로 판단됩니다.

rf_optimized = model_grid.best_estimator_
y_pred = rf_optimized.predict(X_test)
accuracy_score(y_test, y_pred)
recall_score(y_test, y_pred)
precision_score(y_test, y_pred)

특성 중요도

랜덤 포레스트 모델은 특성 중요도를 제공하여 모델을 해석할 수 있게 해줍니다. 가장 상위 10개의 중요한 특성은 다음과 같습니다.

  • 출발 요일(월요일, 화요일, 수요일, 금요일, 일요일)
  • 출발지(호주, 남한)
  • 비행 시간
  • 체류 기간
  • 판매 채널(모바일)

흥미롭게도 목요일과 토요일은 모델에 상대적으로 적은 영향을 미치는 것으로 나타났습니다. 이러한 특성 중요도 정보를 통해 모델이 어떤 요인들을 더 중요하게 고려하는지 파악할 수 있습니다. 이는 모델의 성능 향상과 해석에 도움이 될 것입니다.

결론

이상의 과정을 요약하면 다음과 같습니다

  1. 항공권 예약 데이터를 활용하여 랜덤 포레스트 모델을 구축하였습니다.
  2. 데이터 전처리 단계에서 범주형 변수에 대한 원-핫 인코딩과 불균형 데이터에 대한 오버샘플링을 수행하였습니다.
  3. 학습 데이터와 테스트 데이터를 분리하여 모델 성능 평가의 신뢰성을 확보하였습니다.
  4. 초기 랜덤 포레스트 모델의 성능은 84%의 정확도로 나타났지만, 재현율과 정밀도가 낮았습니다.
  5. 이에 따라 하이퍼파라미터 튜닝을 수행하였고, 그 결과 F1 스코어가 0.366으로 향상되었습니다.
  6. 특성 중요도 분석을 통해 출발 요일, 출발지, 비행 시간, 체류 기간, 판매 채널 등이 주요 예측 요인임을 확인하였습니다.

위의 과정을 통해 불균형 데이터에 대한 효과적인 전처리와 하이퍼파라미터 튜닝, 그리고 특성 중요도 분석을 수행하여 랜덤 포레스트 모델의 성능을 개선할 수 있었습니다. 이는 실제 항공권 예약 예측 문제에서 유용한 인사이트를 제공할 것으로 기대됩니다.

--

--