Machine Learning/XGBoost

XGBoost 간단 사용 가이드

Jay김 2020. 9. 16. 20:10

XGBoost는 초보자들도 대략 100,000개 이하의 관측 개체들만 주어진 상황에서 쓸 수 있는 강력하고 연산과정이 최적화된 경사 부스팅 트리에 해당되는 기계학습 알고리즘 중 하나다.

해당 글은 Telco Churn (고객 이탈) 데이터를 이용한 간단한 XGBoost를 사용한 이탈 고객 예측 튜토리얼이다. 데이터셋 자체는 바로 IBM 사이트 자체에서 구하기는 힘드므로 구할 수 있는 Github 주소를 올리도록 하겠다. 물론 그저 해당 글을 본인의 데이터를 위한 참고용으로만 봐도 된다.

목차(Ctrl + F로 임시 이동)

  1.  필요 모듈
  2.  데이터 가져오기 및 간단한 데이터 가공
  3.  누락값 (missing data) 찾기 및 교체 혹은 제거
  4.  독립 변수와 의존 변수로 나누기
  5.  원핫 인코딩
  6.  XGBoost 모델 기본 틀 만들기
  7.  초매개변수 튜닝 (교차검증과 그리드 서치)
  8.  트리 그려보기

XGBoost 예측 성능 최적화를 위해 데이터를 가공하는 기법들과 XGBoost 모델을 구축하고 매개변수를 최적화하는 방법과 만들어진 XGBoost 모델을 시각화하는 방법을 다루도록 하겠다.

1. 필요 모듈

쉬운 데이터 가공을 위해 필요한 모듈들이다. 2020년 기준으로 최소 다음 버젼 이상을 추천한다.

  • pandas 0.25.1
  • numpy 1.17.2
  • sklearn 0.22.1
  • xgboost 0.90

만약 Anaconda를 통해 파이썬 3을 설치한 상태라면 아나콘다 명령 프롬트에 conda list 로 버젼 정보를 확인할 수 있다. 만약 전부 업데이트하고 싶다면 conda update --all 을 통해 아나콘다 패키지를 전부 업데이트하면 된다. 만약 한 가지 패키지만 업데이트 하고 싶다. 그럼, 예를 들어 scikit learn 이 버젼 0.22.1 보다 오래된 버젼이라면 conda install scikit-learn=0.22.1 명령어를 치면 된다.

xgboost 모듈을 설치하고자 한다면 conda install -c conda-forge xgboost

결정 트리(tree)를 그리고 싶다면 graphviz 패키지가 필요하므로 conda install graphviz python-graphviz

그럼 이제 가져와야할 모듈들은 다음과 같다.

import pandas as pd
import numpy as np
import xgboost as xgb
from sklearn.model_selection import train_test_split #데이터를 훈련집합이랑 시험집합으로 나눔
from sklearn.metrics import balanced_accuracy_score, roc_auc_score, make_scorer
from sklearn.model_selection import GridSearchCV #교차검증
from sklearn.metrics import confusion_matrix #혼동행렬
from sklearn.metrics import plot_confusion_matrix #혼동행렬 그리기용

만약 위의 개념들이 생각나지 않는다면 통계이론 훑어보기 목차로 가서 훑어보고 오자.

 

2. 데이터 가져오기 및 간단한 데이터 가공

df = pd.read_csv('Telco_customer_churn.csv')
df.head()

그럼 다음과 같이 33열의 데이터셋을 볼 수 있을 것이다.

마지막 4열인 Churn Value, Churn Score, CLTV, Churn Reason은 전부 고객이 정말 이탈을 했을 때, 그러니까 Telcom 서비스 사용을 중단했을 때 수집되는 정보이다. 그러니까 고객이 떠날 때 설문 조사에 응하면서 수집되는 정보라는 것이다. 당연히 해당 정보를 고객이 떠날 확률을 계산하는 모델에 집어넣어버리면 그냥 답을 떠먹여 주는 것이니 우리 모델은 거의 100% 완벽한 예측을 할 것이다. 게다가 고객이 떠나기도 전에 해당 데이터를 얻을 수도 없을 것이다. 따라서 정확한 모델 구축을 위해 해당 열들을 제거한다.

df.drop(['Churn Label', 'Churn Score', 'CLTV', 'Churn Reason'], axis=1, inplace=True)
#axis는 행(0)인지 열(1)인지 표기
df.head()
#데이터 확인

몇 가지 열들은 그저 한 가지 값만 가지고 있을 수도 있다. 이들은 모델에 딱히 도움이 될만한 정보를 주지 않는다. 다음 코드들을 차례차례 돌려보자.

df['Count'].unique()
df['Country'].unique()
df['State'].unique()

Count 열은 1 밖에 없다. Country 열은 전부 미국(United States)이다. State 열은 전부 캘리포니아(California)다. 이 열들 전부 제거한다.

CustomerID 열은 당연히 고객 아이디이기 때문에 전부 다르다. 이 열도 예측에 도움이 안된다.

Lat Long 열은 이미 따로 포함되어 있는 위도, 경도 열들에 있는 값들이 다시 나온다.

앞서 언급한 열들을 전부 제거하겠다.

df.drop(['CustomerID', 'Count', 'Country', 'State', 'Lat Long'], axis=1, inplace=True)
df.head()

이제 City 열을 확인해보자. df['City'].unique()로 고유값들을 보면 Huntington Park 같이 띄어쓰기가 되어있는 도시 이름들이 보일 것이다. XGBoost에게 이 값들이 띄어쓰기 되어있는 밑줄로 연결 되어있든 전혀 차이가 나지 않기 때문에 문제가 되지 않는다. 그러나 의사 결정 트리를 그리는 graphviz 패키지가 제대로 작동하기 위해선 필요하므로 띄어쓰기를 제거하고 전부 '_'로 교체하겠다.

#빈 띄어쓰기 칸을 탐색한 후 _로 교체
df['City'].replace(' ', '_', regex=True, inplace=True)
df.head()
#제대로 _이 들어갔는지 고유값(도시 이름들) 열개 확인
df['City'].unique()[0:10]

열 이름들 자체도 빈칸을 _로 교체해준다.

df.columns = df.columns.str.replace(' ', '_')
df.head()

3. 누락값 (missing data) 찾기 및 교체 혹은 제거

일단 모종의 이유로 누락된 데이터값들을 찾아낸다. 이 데이터값들은 그저 빈칸일수도 있고 NA로 표기가 되어있을수도 있다. XGBoost는 누락된 데이터를 기본적으로 예상하고 상대할 수 있도록 설정되어 있다. 우리가 해야되는 건 이들을 찾아서 전부 0으로 설정하는 것이다. 여기서 만약 0이 누락값을 나타내는 것이 아니라 실제 관측값일 경우를 걱정하는 사람도 있을테지만 XGBoost를 쓴 사람에 따르면 둘 다 동시에 써도 XGBoost의 성능은 여전히 좋다고 한다. 

dtypes 메소드로 확인을 해보면 열 데이터 자료형에 object 자료형이 많은 것을 확인할 수 있다. 괜찮다. 이는 설문조사에 예/아니오와 같은 글로 된 답변들이 많았기 때문이다.

df.dtypes

df['Phone_Service'].unique()

그렇다면 한 개의 열에 우리가 원하는 답변들이 있는지 직접 확인해보면 된다. 예를 들어 폰 서비스 열에 예/아니오가 제대로 입력되었는지 해당 열의 고유값들을 확인해보는 방법이 있다. 이는 모든 열에 걸쳐서 해봐야 하는 확인 작업이지만 일단 하나만 보도록 하자.

Total_Charges (총 청구금액) 열을 앞서 확인해봤을 때 전부 숫자인 것으로 봐서 우리는 해당 열이 float 자료형이라고 생각했을지도 모른다. 그러나 실제 dtypes를 이용해 확인해보면 object 인 것을 확인할 수 있다. 이는 해당 열에 누락값이 있음을 암시하는 것일수도 있다. 그럼 직접 확인해보자.

df['Total_Charges'].unique()

불행하게도 연속값이기 때문에 고유값으로 나열하기엔 너무나 많은 값들이 존재한다.

좀 더 확실하게 해당 열에 누락값이 있음을 확인사살하는 방법은 다음과 같은 메소드를 사용했을 때

df['Total_Charges'] = pd.to_numeric(df['Total_Charges'])

다음과 같이 " "값을 숫자 자료형으로 변환할 수 없음을 알리는 에러가 뜬다.

XGBoost는 누락 값에 대해서 어떻게 대응할 지 알아서 정하도록 설정되어 있다. 우리가 해줘야 되는 가공 작업은 단순히 누락값을 전부 0으로 바꿔주는 것이다. 

다만 혹시라도 누락값이 너무나 많으면 XGBoost이 자동으로 해결하는 것도 한계가 있을 수도 있으니 확인해보는 것도 좋다.

#누락된 값이 있는 행의 개수 = 누락 데이터 개수
len(df.loc[df['Total_Charges'] == ' '])
#누락된 행 직접보기
df.loc[df['Total_Charges'] == ' ']

누락된 데이터는 총 11개로 나온다. 그럼 누락값이 심각하게 많지는 않으니 전부 0으로 설정하도록 한다. 누락된 행들을 직접 확인해보면 Tenure_Months 열의 값이 0인 것을 확이할 수 있다. 서비스 제공을 시작한 지 얼마 되지도 않은 고객임을 의미하기 때문에 총 청구 금액도 당연히 누락될 수 밖에 없을 것이다. 앞서 제거한 Churn Value 열의 값 또한 갓 가입한 고객이기 때문에 0으로 설정되어 있는 것을 확인할 수도 있다.

이를 확인하고 우리에게는 두 가지 선택지가 주어진다. 누락된 값을 0으로 설정하거나 이들 행을 통째로 제거할 수도 있다. 해당 가이드에서는 0으로 설정하도록 하겠다. 

df.loc[(df['Total_Charges'] == ' '), 'Total_Charges'] = 0

제대로 0으로 변환되었는지 확인하고자 한다면 앞서 확인한 Tenure_Months 가 0이었다는 점을 이용해 다음 코드를 돌리면 된다. 이는 Total_Charges 열에 서비스를 받는 동안 정말 한푼도 내지 않았기 때문에 청구 금액이 0인 사람들도 있을 수 있기 때문이다.

df.loc[df['Tenure_Months'] == 0]

마지막으로 Total_Charges 열이 아직도 Object 자료형이란 사실을 상기하자. XGBoost는 int, float, 혹은 boolean 자료형 입력값만 받아들이기 때문에 바꿔줘야 한다. 이를 위해서 누락값들을 전부 변환시켜줬으니 to_numeric() 만 이용해 마무리 작업만 해주면 된다.

df['Total_Charges'] = pd.to_numeric(df['Total_Charges'])
#확인 작업
df.dtypes

숫자 자료형 데이터에서 누락값을 제거했으니 이제 스트링 자료형이 들어있는 띄어쓰기 빈칸을 전부 '_'로 교체할 수 있다.

df.replace(' ', '_', regex=True, inplace=True)
df.head()

4. 독립 변수와 의존 변수로 나누기

이제 데이터에서 누락값과 형식 정리를 완료했으니 분류(Classification)을 위해 쓸 독립 변수(independent variable)들과 예측하고자 하는 목표인 의존 변수(dependent variable)들로 열들을 나눠줘야 한다.

통상적으로 쓰는 X로 독립 변수 행렬을 표기하고 y로 의존 변수 벡터를 표기하도록 하겠다. 우리가 예측하고자 하는 것은 Churn Value 열의 값이다. 이는 고객이 이탈할 것인지 이탈하지 않을 것이지 0과 1로 분류한 값이다.

원본 데이터를 변형시키지 않도록 copy() 메소드를 이용해 X와 y 변수에 저장해준다. 데이터를 조작하다가 실수를 하면 해당 코드들을 다시 돌려서 원상 복귀시키면 된다.

#다른 방법으로는 X = df.iloc[:,:-1]. 마지막 열 빼고 전부 다 가져오기
X = df.drop('Churn_Value', axis =1).copy()
X.head()
y = df['Churn Value'].copy()
y.head()

이제 X에 있는 데이터들을 XGBoost에 적절한 형태로 변환시켜주자.

5. 원핫 인코딩

X 행렬 안에 들어있는 열들의 내용물들을 살펴보자. float 혹은 범주형 자료(Categorical Data) 둘 중 하나일 것이다.

  • City (도시 이름) - 범주형
  • Zip Code (우편번호)- 범주형
  • Latitude (위도)- Float
  • Longitude (경도)- Float
  • Gender (성별, 남성/여성)- 범주형
  • Senior Citizen (고령층여부, 예/아니오) - 범주형
  • Partner (배우자여부, 예/아니오) - 범주형
  • Dependents (자녀 혹은 부양자 여부, 예/아니오) - 범주형
  • Tenure Months (서비스 이용 개월) - Float
  • Phone Service (전화 서비스 이용중, 예/아니오) - 범주형
  • Multiple Lines (전화선 2개 이상 사용중, 예/아니오) - 범주형
  • Internet Service (인터넷 서비스 있음, 아니오/DSL/광케이블) - 범주형
  • Online Security (온라인 보안 서비스 있음, 예/아니오) - 범주형
  • Online Backup (온라인 백업 서비스 있음, 예/아니오) - 범주형
  • Device Protection (기기 보호 서비스 있음, 예/아니오) - 범주형
  • Tech Support (기술적 지원 A/S 있음, 예/아니오) - 범주형
  • Streaming TV (TV 스트림 서비스 있음, 예/아니오) - 범주형
  • Streaming Movies (영화 스트림 서비스 있음, 예/아니오) - 범주형
  • Contract (계약 기간, 월 단위/1년/2년) - 범주형
  • Paperless Billing (전자 청구서, 예/아니오) - 범주형
  • Payment Method(지불 방식, 우편 체크/전자 체크 등등) - 범주형
  • Monthly Charges (월 청구액)- Float
  • Total Charges (총 청구액) - Float

여기서 연속형 데이터인 Float 자료형은 XGBoost가 지원하나 object 자료형인 Phone Service 같은 Yes/No 같은 두 가지 이상의 값들로 이루어져 있는 범주형 데이터를 이해하지 못하기 때문에 한 개의 열을 여러 개의 이진법(binary) 열들로 변환시켜 주는 원핫 인코딩을 해줘야 한다. 예를 들어 [빨강, 초록, 초록, 파랑] 같은 데이터가 들어있는 열이 있다면 [1, 0, 0, 0], [0, 1, 1, 0], [0, 0, 0, 1]로 변환시켜주는 것이다.

누락된 값이 0으로 설정된 열이라면 다음과 같이 원핫 인코딩이 진행된다.

0으로 설정된 누락값에 대해 원핫 인코딩이 어떻게 적용되는지 보여준 예

그럼 여기서 질문. 선호하는 색이 알려지지 않은 관측 개체 5와 6은 파란색을 선호하는 사람들과 군집될까 아니면 초록색을 선호하는 사람들과 군집될까? XGBoost은 이 두 가지를 전부 시험해본 뒤 정확도 향상에 제일 도움이 되는 군집을 선택한다. 

왜 연속형 데이터처럼 취급하면서 간편하게 Yes/No를 1, 2로 변환시키면 안되는 것일까? 간단한 예를 들어 설명하겠다. Payment Method 열을 보면 다음과 같은 (앞서 스트링을 가공한) 고유값들이 있는 것을 확인할 수 있다.

  1. Mailed_Check

  2. Electronic_Check

  3. Bank_Tranfer

  4. Credit_Card

이 범주형 데이터를 각각 1, 2, 3, 4로 변환한 뒤 연속형 데이터로 취급한다면 우리는 숫자상 3인 계좌 이체가 숫자상 4인 신용 카드 결제에 1과 2보다 더 가깝다고 여길 수 밖에 없을 것이다. 마찬가지로 XGBoost 또한 이런식으로 데이터를 해석해 실제로는 특별한 관련이 없는 별개의 결제 방법들임에도 불구하고 3과 4에 해당하는 사람들을 같이 군집할 확률이 1과 4에 해당하는 사람들에 대해서보다 높을 것이다. 따라서 이 대신 원핫 인코딩을 사용해 변환시킨다면 1과 4에 해당하는 사람들과 3과 4에 해당하는 사람들을 군집할 확률은 동일할 것이다. 

파이썬에서 원핫 인코딩을 하는 방법은 여러가지가 존재한다. 가장 인기가 많은 둘은 sklearn에 들어있는ColumnTransformer()와 pandas에 들어있는 get_dummies()다. ColumnTransformer()는 XGBoost 모델을 구축할 때 썼었던 범주형 데이터의 고유값들을 기억하는 특별한 기능이 있다. 그러니까 예를 들어 모델을 만들 때 고유값들이 Red, Green, Blue만 있었는데 새로 들어온 데이터에 Orange 라는 엉뚱한 값이 들어있으면 ColumnTransformer()는 에러 메세지를 내거나 다른 방법으로 문제를 해결할 수 있다. 다만 ColumnTransformer()는 열 표기 이름을 잃어버리는 단점이 존재하기 때문에 제대로 변환했는지 확인하기 어려운 점이 있다. 해당 가이드에서는 원핫 인코딩이 어떻게 작동하는지 보여주기 위해 get_dummies()를 쓰겠다. 그러나 뭔지 알고 있다면 ColumnTranformer()를 사용하는 것을 추천한다.

일단 맛보기로 다음 코드를 돌려보자.

pd.get_dummies(X, columns = ['Payment_Method'].head()

변환된 열들은 오른쪽으로 붙는다. 원래 열 이름(Payment_Method)이 보존되고 그 옆에 어느 값에 해당되는지 알려주는 추가 인식표도 붙는 것을 확인할 수 있을 것이다.

이제 모든 Object 자료형 열들에 들어있는 고유값들이 타당한지 확인을 했다는 가정 하에 (개인이 알아서 꼭 확인 작업을 수동으로 해야 한다.) 다음 코드를 돌리면 원핫 인코딩 작업이 완료된다.

X_인코딩 = pd.get_dummies(X, columns = ['City',
					'Gender',
                                        'Senior_Citizen',
                                        'Partner',
                                        'Dependents',
                                        'Multiple_Lines',
                                        'Internet_Service',
                                        'Online_Security',
                                        'Online_Backup',
                                        'Device_Protection',
                                        'Tech_Support',
                                        'Streaming_TV',
                                        'Streaming_Movies',
                                        'Contract',
                                        'Paperless_Billing',
                                        'Payment_Method'])
X_인코딩.head()                                        

총 1178개의 열로 늘어난 것을 확인할 수 있을 것이다.

본격적으로 XGBoost 모델을 만들기 전에 존속변수 y 열도 고유값들이 제대로 0과 1로 이루어져 있는지 확인하라.

y.unique()

6. XGBoost 모델 기본 틀 만들기

이제 데이터 자체는 XGBoost에 집어넣을 수 있는 상태가 되었으나 아직 훈련(training) 집합과 시험(testing) 집합으로 나누어진 상태가 아니다. 일단 주어진 예들의 균형을 확인하자. 그러니까 주어진 관측 개체 중에 이탈 고객의 비율이 어떤지 확인하는 것이다.

sum(y)/len(y)

y 열이 1과 0으로 이루어져 있다는 사실을 이용해 양성 개체 (그러니까 이탈 고객) 수를 세보면 데이터에 있는 27%의 고객만 이탈했음을 확인할 수 있다. 이 비율이 훈련 데이터와 시험 데이터에 걸쳐 유지되면서 나눠지도록 해주자. 

#일정한 결과를 보장하고자 한다면 random_state = 42 로 추가하라.
X_train, X_test, y_train, y_test = train_test_split(X_인코드, y, stratify=y)

stratify 인수(argument)가 제대로 작동했는지 확인하고자 한다면 다음 코드들을 돌리면 된다.

sum(y_train)/len(y_train)
sum(y_test)/len(y_test)

그럼 둘다 대략 0.27인 것을 확인할 수 있을 것이다.

이제 XGBoost 모델을 구축할 준비가 되었다.

#일정한 결과를 얻고자 한다면 seed = 42 인자 추가
clf_xgb = xgb.XGBClassifier(objective='binary:logistic', missing=None)
clf_xgb.fit(X_train,
	   y_train,
           verbose=True,
           early_stopping_rounds=10,
           eval_metric='aucpr',
           eval_set=[(X_test, y_test)])

XGBClassifier에 들어가는 인자들은 전부 기본 설정 값이다. clf_xgb는 XGB 분류기(Classifier)의 줄인말이라고 보면 된다. 해당 변수는 XGB 트리들로 형성된 숲을 담고 있다면 보면 된다.

fit 메서드에서 early_stopping_rounds는 알고리즘이 트리를 만들다가 예측 점수가 나아지질 않을 때 몇 개의 트리를 더 만들고 안나아지는지 확인한 후 끝내는지 설정한다. 

aucpr는 수신자 조작 특성(ROC) 곡선 아래 면적인 AUC를 분류 성능 평가 지표로 설정함을 의미한다. eval_set은 몇 개의 트리들을 만들지 평가할 때 사용할 데이터(시험 데이터)를 설정한다. 

다음은 45번째 트리까지 성능 증가가 없었음을 의미한다.

이제 XGBoost 모델을 완성했으니 시험 데이터 집합에 대한 성능을 보고 혼동행렬을 완성하자.

plot_confusion_matrix(clf_xgb,
		    X_test,
                    y_test,
                    values_format='d',
		    display_labels=["Did not leave", "Left"])
                    

혼동행렬을 보면 실제로 이탈하지 않은 고객 1,294명 중 1,178명(91%)이 제대로 분류된 것을 확이할 수 있다. 반면, 이탈한 고객 467명 중 오직 239명(51%)이 제대로 분류되었다. 기업의 입장에서 보면 XGBoost 딱히 특출한 성능을 보이고 있질 않다. 이 상황에서 기업에게 제일 중요한 건 떠날만한 고객을 제대로 예상하는 모델을 구축하고 그에 걸맞는 대응을 마련하는 것이니 말이다. 원인은 앞서 확인한 양성 개체와 음성 개체의 비율 불균형이다. 다행이 XGBoost에 이 불균형에 대비한 scale_pos_weight가 있다. 이는 상대적으로 적은 클래스를 잘못 분류하는 것에 대한 패널티를 부여해서 모델이 이탈 고객에 대한 성능이 더 높아지도록 도와준다. 이에 더해 교차검증도 이용해 XGBoost의 성능을 끌어올리자.

7. 초매개변수 튜닝 (교차검증과 그리드 서치)

교차검증 글

XGBoost에는 알고리즘 자체가 결정할 수 없고 우리가 수동으로 설정해야 되는 수많은 매개변수들이 존재한다. 예를 들어 트리의 최대 깊이를 조절하는 max_depth, 학습률을 조정하는 learning_rate, 가지치기를 조정하는 gamma, 정규화 변수 람다를 조정하는 reg_lambda가 존재한다. 해당 초매개변수(Hyperparameter)를 튜닝해보도록 하자.

초매개변수가 많으므로 GridSearchCV()를 사용하도록 하겠다. 양성 개체와 음성 개체의 불균형이 있는 상황에서 XGBoost 매뉴얼에 따르면 전체적인 성능 평가 점수가 중요하다면 양성 개체와 음성 개체의 균형을 scale_pos_weight로 맞춘 뒤 평가지표를 AUC로 설정하라고 되어있다. 초매개변수의 빠른 튜닝을 위해 가능한 모든 매개변수 집합말고 그 하위 집합을 돌려본다.

매뉴얼에서는 기본으로 scale_pos_weight를 음성개체수/양성개체수로 설정하도록 지시하고 있다.

일단 매개변수 그리드를 다음과 같이 설정한 뒤 최적화를 시도했다. 만약 해당 그리드말고 다른 매개변수 값들을 시험해봐야겠다고 생각할 시 최적화된 변수들은 한개만 남기고 바꾸고자하는 매개변수들에 새 배열을 넣어봐서 최적화하면 된다. 보통 배열 중간쯤 있는 변수가 최적화 변수면 멈춘다.

교차검증 속도를 높이기 위해 subsample를 통해 샘플 데이터의 90%만 무작위로 사용하고 colsample_bytree를 통해 트리당 사용 열 샘플을 50%만 무작위로 사용하도록 했다.

param_grid={
	'max_depth':[3,4,5],
        'learning_rate':[1, 0.5, 0.1, 0.01, 0.05],
        'gamma':[0, 0.25, 1.0],
        'reg_lambda': [0, 1.0, 10.0, 20, 100],
        'scale_pos_weight': [1, 3, 5]
       }
       
optimal_params = GridSearchCV(
      estimator=xgb.XGBClassifier(objective='binary:logistic',
    				    subsample=0.9,
                          	    colsample_bytree=0.5),
      param_grid=param_grid,
      scoring='roc_auc',
      verbose=0, #뭐하는지 알고 싶으면 2로 설정해볼 것
      n_jobs=10,
      cv=3
   )

초매개변수를 최적화하면 이제 XGBoost 모델을 완성할 수 있다.

clf_xgb = xgb.XGBClassifier(objective = 'binary:logistic',
			    gamma=0.25,
                            learn_rate=0.1,
                            max_depth=4,
                            reg_lambda=10,
                            scale_pos_weight=3,
                            subsample=0.9,
                            colsample_bytree=0.5)
clf_xgb.fit(X_train,
	    y_train,
            verbose=True,
            early_stopping_rounds=10,
            eval_metric='aucpr',
            eval_set=[(X_test, y_test)])

다시 혼동행렬을 그려보면 이탈한 고객 분류를 더 잘하는 것을 확인할 수 있다. 그러나 이탈할 의사가 없는 고객 분류 성능을 대가로 향상이 이뤄지는 것을 확인할 수 있다. 따져보면 이탈할 사람을 상대로 벌어지는 잡아두기 전략이 떠날 의사가 없는 고객을 상대로 적용되서 나쁠 건 없기도 하다.

8. 트리 그려보기

clf_xgb = xgb.XGBClassifier(objective = 'binary:logistic',
			    gamma=0.25,
                            learn_rate=0.1,
                            max_depth=4,
                            reg_lambda=10,
                            scale_pos_weight=3,
                            subsample=0.9,
                            colsample_bytree=0.5,
                            n_estimators=1)
clf_xgb.fit(X_train, y_train)

xgb.to_graphviz(clf_xgb, num_tress=0, size="10,10",
		condition_node_params=node_params,
                leaf_node_params=lear_params)

[Copyright ⓒ 블로그채널 무단전재 및 재배포 금지]