본문 바로가기

머신러닝

10. 지도 (분류, 회귀) - XGBoost : 커플 성사 여부 예측하기

[공부 자료 : Must Have 데싸노트의 실전에서 통하는 머신러닝]

# XGBoost

- 부스팅

랜덤 포레스트 : 각 트리를 독립적으로 만드는 알고리즘

부스팅 : 순차적으로 트리를 만들어 이전 트리보다 더 나은 트리를 만들어내는 알고리즘

      부스팅 알고리즘은 트리 모델을 기반으로 한 최신 알고리즘 중 하나

      랜덤 포레스트보다 더 빠른 속도좋은 예측 능력을 보여준다

부스팅 알고리즘 종류

      1. XG부스트

      2. 라이트GBM

      3. 캣 부스트

- 트리 모델의 진화 과정

1. 결정 트리 : 가장 기본적인 트리 모델

2. 배깅 : 부스트랩 훈련셋을 사용하는 트리 모델

      부스트랩 : 데이터의 일부분무작위로 반복 추출하는 방법 (복원 추출 허용)

      부스트랩으로 추출한 데이터의 여러 부분집합을 사용하여 여러 트리를 만든다 → 오버피팅 방지 

      이 때, 각 트리는 독립적으로 학습

      각각의 트리들이 모여 하나의 모델 역할을 한다

3. 랜덤 포레스트 : 데이터와 변수의 일부를 사용하여 여러 트리를 만드는 모델

4. 부스팅 : 이전 트리의 학습 내용다음 트리에 반영하여 여러 트리를 만드는 방법

      랜덤 포레스트에서는 각 트리가 독립적이지만, 부스팅에서는 독립적이지 않다

            랜덤 포레스트 : 각 트리를 만들 때 이전에 만든 트리와 상관 없이 새로운 데이터 부분집합과 변수 부분집합을 이용

            부스팅 : 각 트리르 순차적으로 만들며 이전 트리의 정보를 이용 (이전 트리의 예측 결과를 반영)

      에이다 부스트 (AdaBoost) : 부스팅의 대표 알고리즘

            단계적으로 트리를 만들 때 이전 단계에서의 분류 결과에 따라 각 데이터에 가중치를 부여 및 수정

            이전 트리에서 가중치가 덜 부여되고 잘못 분류된 데이터들에 더 높은 가중치와 우선순위를 부여

5. 경사 부스팅 : 부스팅에 경사 하강법을 적용

      경사 부스팅은 경사 하강법을 이용하여 이전 모델의 에러를 기반으로 다음 트리를 만든다

      경사 부스팅으로 구현한 알고리즘 : XGBoost, LightGBM, Catboost

6. XG 부스팅 : 경사 부스팅에서 계산 속도알고리즘 효율을 개선한 모델

      기존의 경사 부스팅 대비 계산 성능 최적화와 알고리즘 개선

      1) XG 부스트 이전 부스팅 모델은 트리를 순차적으로 만들기 때문에 모델링 속도가 느리다

            XG 부스트도 트리를 순차적으로 만들지만 병렬화, 분산 컴퓨팅, 캐시 최적화 등을 활용해 계산 속도를 향상

      2) 기존 경사 하강법의 알고리즘은 접점의 기울기를 계산하고 매개변수를 이동

            XG 부스트는 2차 도함수를 활용해 더 적절한 이동 방향과 이동 크기를 찾아내어 더 빨리 전역 최소값에 도달

      3) 또한 XG 부스트는 정규화 하이퍼 파라미터를 지원

            트리 모델은 진화할수록 더 좋은 예측 성능을 보이는 동시에 오버 피팅 문제가 증가

            XG 부스트는 부작용을 막기 위해 Lasso(L1) 정규화Ridge(L2) 정규화 하이퍼 파라미터를 지원

      4) 애매하게 예측된 관측치높은 가중치를 부여하는 가중치 분위수 스케치 (Weighted Quantile Sketch)  

            최적 분할을 찾기 위해 각 변수에 대한 히스토그램을 만든다

            히스토그램의 기둥의 경계는 최상의 분할지점을 찾기 위한 후보로 사용된다

            가중 분위스 스케치에서는 각 기둥이 동일한 가중치를 갖도록 만들어진다

      5) 결측치를 유연하게 처리해내는 희소성 인식 (Sparsity Aware)

- XGBoost

XGBoost (Extra Gradient Boost)

      가장 먼저 개발되었으며, 가장 널리 사용되는 부스팅 알고리즘

      손실 함수 뿐만 아니라, 모형 복잡도까지 고려

      캐글 컴피티션 우승장가 많이 사용하는 성능이 검증된 부스팅 모델

XGBoost 장점

      예측 속도가 상당히 빠르고, 예측력이 우수

      변수 종류가 많고, 데이터가 클수록 뛰어난 성능

      종속변수가 연속형 데이터이든 범주형 데이터이든 사용 가능

      이미지나 자연어가 아닌, 표로 정리된 데이터라면 거의 모든 경우에 사용 가능

XGBoost 단점

      복잡한 모델인 만큼, 해석에 어려움이 있음

      더 나은 성능을 위한 하이퍼파라미터 튜닝이 까다로움

- 경사 하강법 (Gradient Descent)

경사 하강법 : 머신러닝 학습 시 최소의 오차를 찾는 방법

      오차 함수(y)에 대한 경사도(미분계수, 접선의 기울기)를 기준으로, 매개변수(x)를 이동해 최소 오차를 찾는다

      기울기가 0인 지점에서 최소 오차를 갖는다

경사 하강법은 매개변수의 이동 방향을 미분계수의 부호와 크기로 판단

      미분계수가 양수이면 매개변수를 왼쪽으로 이동

      미분계수가 음수이면 매개변수를 오른쪽으로 이동

실제로 경사 하강법은 오차의 최소값을 정확히 찾아내는 것이 아니다

      미분계수가 최대한 0에 가깝도록 계속 움직여 오차의 최소값의 근사값에 다다르는 방법

지역 최소값과 전역 최소값

      지역(local) 최소값 : 미분계수가 0이지만, 에러 함수 전체에서 최소값이 아닌 값

      전역(globa) 최소값 : 미분계수가 0인 값 중, 에러 함수 전체에서 최소값

      경사 하강법에서 지역 최소값에 갇히지 않고 벗어나 전역 최소값을 찾기 위해선 학습률을 충분히 크게 해줘야 한다

 

# XGBoost - 커플 성사 여부 예측하기

- 1. 데이터 분석하기

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

file_url = 'https://media.githubusercontent.com/media/musthave-ML10/data_source/main/dating.csv'
data = pd.read_csv(file_url)

data.info()
RangeIndex: 8378 entries, 0 to 8377
Data columns (total 39 columns):
#  #   Column                         Non-Null Count  Dtype  
# ---  ------                         --------------  -----  
#  0   has_null                       8378 non-null   int64 - null값(무응답)이 있는가  
#  1   gender                         8378 non-null   object - 본인 성별
#  2   age                            8283 non-null   float64 - 본인 나이
#  3   age_o                          8274 non-null   float64 - 상대 나이
#  4   race                           8315 non-null   object - 본인 인종
#  5   race_o                         8305 non-null   object - 상대 인종
#  6   importance_same_race           8299 non-null   float64 - 동일 인종 선호
#  7   importance_same_religion       8299 non-null   float64 - 동일 종교 선호
#  8   pref_o_attractive              8289 non-null   float64 - 상대가 매력을 얼마나 중요
#  9   pref_o_sincere                 8289 non-null   float64 - 상대가 성실을 얼마나 중요
#  10  pref_o_intelligence            8289 non-null   float64 - 상대가 지식을 얼마나 중요
#  11  pref_o_funny                   8280 non-null   float64 - 상대가 유머를 얼마나 중요
#  12  pref_o_ambitious               8271 non-null   float64 - 상대가 야망을 얼마나 중요
#  13  pref_o_shared_interests        8249 non-null   float64 - 상대가 공통을 얼마나 중요
#  14  attractive_o                   8166 non-null   float64 - 상대가 평가한 본인 매력
#  15  sincere_o                      8091 non-null   float64 - 상대가 평가한 본인 성실
#  16  intelligence_o                 8072 non-null   float64 - 상대가 평가한 본인 지식
#  17  funny_o                        8018 non-null   float64 - 상대가 평가한 본인 유머
#  18  ambitous_o                     7656 non-null   float64 - 상대가 평가한 본인 야망
#  19  shared_interests_o             7302 non-null   float64 - 상대가 평가한 본인 공통
#  20  attractive_important           8299 non-null   float64 - 본인이 매력을 얼마나 중요
#  21  sincere_important              8299 non-null   float64 - 본인이 성실을 얼마나 중요
#  22  intellicence_important         8299 non-null   float64 - 본인이 지식을 얼마나 중요
#  23  funny_important                8289 non-null   float64 - 본인이 유머를 얼마나 중요
#  24  ambtition_important            8279 non-null   float64 - 본인이 야망을 얼마나 중요
#  25  shared_interests_important     8257 non-null   float64 - 본인이 공통을 얼마나 중요
#  26  attractive_partner             8176 non-null   float64 - 본인이 평가한 상대 매력
#  27  sincere_partner                8101 non-null   float64 - 본인이 평가한 상대 성실
#  28  intelligence_partner           8082 non-null   float64 - 본인이 평가한 상대 지식
#  29  funny_partner                  8028 non-null   float64 - 본인이 평가한 상대 유머
#  30  ambition_partner               7666 non-null   float64 - 본인이 평가한 상대 야망
#  31  shared_interests_partner       7311 non-null   float64 - 본인이 평가한 상대 공통
#  32  interests_correlate            8220 non-null   float64 - 관심사 연관도
#  33  expected_happy_with_sd_people  8277 non-null   float64 - 행복 기대치
#  34  expected_num_interested_in_me  1800 non-null   float64 - 나에게 관심 기대치
#  35  like                           8138 non-null   float64 - 파트너가 마음에 들었는가
#  36  guess_prob_liked               8069 non-null   float64 - 나를 마음에 들어할지 예상
#  37  met                            8003 non-null   float64 - 상대를 이전에 만난 적이 있나
#  38  match                          8378 non-null   int64 - 매칭 여부

row : 총 8378개

colum : 총 39개

Pandas는 기본적으로 20개 column까지만 보여주고 나머지는 ... 처리

      pd.options.display.max_colums : 보고 싶은 colum의 개수를 설정

pd.options.display.max_columns = 40

data.head()

통계 정보 확인

round(data.describe(), 2)

max 값 분석

      평가 관련 변수는 0점 ~ 10점

            상대 → 본인 : attractive_o, sincere_o, intelligence_o, funny_o, ambition_o, shared_interests_o

            본인 → 상대 : attractive_p, sincere_p, intelligence_p, funny_p, ambition_p, shared_interests_p

      중요도 관련 변수는 총 합이 100점이 되어야 한다

            상대가 얼마나 중요하게 : pref_o_attractive / sincere / intelligence / funny / ambition / shared_interest

            본인이 얼마나 중요하게 : attractive / sincere / intelligence / funny / ambition / shared_interests_important

- 2. 결측치 처리

data.isna().mean()
# has_null                         0.000000
# gender                           0.000000
# age                              0.011339
# age_o                            0.012413
# race                             0.007520
# race_o                           0.008713
# importance_same_race             0.009429
# importance_same_religion         0.009429
# pref_o_attractive                0.010623
# pref_o_sincere                   0.010623
# pref_o_intelligence              0.010623
# pref_o_funny                     0.011697
# pref_o_ambitious                 0.012772
# pref_o_shared_interests          0.015397
# attractive_o                     0.025304
# sincere_o                        0.034256
# intelligence_o                   0.036524
# funny_o                          0.042970
# ambitous_o                       0.086178
# shared_interests_o               0.128432
# attractive_important             0.009429
# sincere_important                0.009429
# intellicence_important           0.009429
# funny_important                  0.010623
# ambtition_important              0.011817
# shared_interests_important       0.014443
# attractive_partner               0.024111
# sincere_partner                  0.033063
# intelligence_partner             0.035331
# funny_partner                    0.041776
# ambition_partner                 0.084984
# shared_interests_partner         0.127357
# interests_correlate              0.018859
# expected_happy_with_sd_people    0.012055
# expected_num_interested_in_me    0.785152
# like                             0.028646
# guess_prob_liked                 0.036882
# met                              0.044760
# match                            0.000000

대부분의 변수에서 결측치가 보이지만, 대체로 5% 미만

      XGBoost 알고리즘도 기본적으로 트리 베이스 모델로, 결측치를 채우는 것이 까다롭지 않다

1. 중요도(pref, important)와 관련된 변수들은 결측치를 제거

      피처 엔지니어링에서 중요도 x 점수로 계산을 진행할 예정

data = data.dropna(subset = ['pref_o_attractive', 'pref_o_sincere', 'pref_o_intelligence', 'pref_o_funny', 'pref_o_ambitious', 'pref_o_shared_interests', 
                             'attractive_important', 'sincere_important', 'intellicence_important', 'funny_important', 'ambtition_important', 'shared_interests_important'])

2. 나머지 변수들은 결측치를 -99(데이터에 등장할 가능성이 없는 숫자)로 채운다

      선형 모델에서는 -99가 아웃라이어로써 작용

      트리 모델에서는 -99로 분류하는 노드가 생겨서 결측치로 자동 처리해준다

data = data.fillna(-99)

- 3. 피쳐 엔지니어링

1. 본인과 상대방의 나이 → 나이 차이 변수를 만들기 + 성별을 나이에 반영하기

이 때, 본인 or 상대방의 나이가 -99(결측치)라면, 나이 차가 아닌 -99(결측치)를 반환

또한, 남자가 연상일 경우 나이 차를 (+)로, 여자가 연상일 경우 나이 차를 (-)로 반환 : 남자 나이 - 여자 나이

def age_gap(x):
    if x['age'] == -99: # 본인 나이가 -99 (결측치)
        return -99
    elif x['age_o'] == -99: # 상대 나이가 -99 (결측치)
        return -99
    elif x['gender'] == 'female': 
        return x['age_o'] - x['age']
        # 본인이 여성일 경우, 상대 나이 (남) - 본인 나이 (여)
    else:
        return x['age'] - x['age_o']
        # 본인이 남성일 경우, 본인 나이 (남) - 상대 나이 (여)

정의한 함수를 DataFrame에 적용하기 : age_gap 변수를 새로 만들기

data['age_gap'] = data.apply(age_gap, axis = 1)

age_gap 변수에는 (+)와 (-)로 성별 데이터도 포함되어 있다

하지만 절대적인 나이 차이도 중요한 변수가 될 수 있다

age_gap에 절대값을 씌운 age_gap_abs 변수를 생성

data['age_gap_abs'] = abs(data['age_gap'])

2. 인종 → 본인과 상대의 인종이 같으면 1, 다르면 -1

race와 race_o의 결측치에도 -99를 주었기 때문에, 값이 -99일 경우 -99를 반환

def same_race(x):
    if x['race'] == -99:
        return -99
    elif x['race_o'] == -99:
        return -99
    elif x['race'] == x['race_o']: # 동일 인종
        return 1
    else: # 다른 인종
        return -1

함수를 DataFrame에 반영하여 same_race 변수 만들기

data['same_race'] = data.apply(same_race, axis = 1)

인종 관련 변수에는 importance_same_race 변수도 있다

same_race와 importance_same_race 변수를 곱한다

      동일 인종이면 (+), 다른 인종이면 (-)

      동일 인종을 중요시 할수록 절대값이 크다

단, same_race의 값이 -99일 경우엔 그대로 -99를 반영 (결측치)

def same_race_point(x):
    if x['same_race'] == -99:
        return -99
    else:
        return x['same_race'] * x['importance_same_race']

함수를 DataFrame에 적용하여 same_race_point 변수 만들기

data['same_race_point'] = data.apply(same_race_point, axis = 1)

※ 왜 same_race 변수를 구할 때, 다른 인종을 0이 아닌 -1로 했을까?

same_race를 importance_same_race와 곱하기 때문

      인종을 전혀 중요하게 생각하지 않는 사람(importance_same_race = 0)도 same_race_point = 0

      다른 인종(same_race = 0)도 same_race_point = 0

      변수가 변별력을 잃는다

3. attractive, sincere, intelligence, funny, ambitious, shared_interests에 대한 평가 점수와 중요도

점수(score)와 중요도(importance)를 곱해서 새로운 변수를 만들기

      중요도(importance)가 -99(결측치)일 경우, -99 반환

      점수(score)가 -99(결측치)일 경우, -99 반환

def rating(data, importance, score):
    if data[importance] == -99:
        return -99
    elif data[score] == -99:
        return -99
    else:
        return data[importance] * data[score]

상대가 생각하는 중요도 (pref_o_) : col #8 ~ col #13

상대가 한 평가 (_o) : col #14 ~ col #19

본인이 생각하는 중요도 (_important) : col #20 ~ col #25

본인이 한 평가 (_partner) : col #26 ~ col #31

partner_imp = data.columns[8:14] # 상대가 생각하는 중요도
partner_rate_me = data.columns[14:20] # 상대가 한 평가

my_imp = data.columns[21:26] # 본인이 생각하는 중요도
my_rate_partner = data.columns[27:32] # 본인이 한 평가

각각의 항목에 대해서, 상대가 준 점수 * 상대의 중요도 변수 만들기 (반환 값을 받을 변수)

각각의 항목에 대해서, 본인이 준 점수 * 본인의 중요도 변수 만들기 (반환 값을 받을 변수)

new_label_partner = ['attractive_p', 'sincere_partner_p', 'intelligence_p', 'funny_p', 'ambition_p', 'shared_interests_p']
new_label_me = ['attractive_m', 'sincere_partner_m', 'intelligence_m', 'funny_m', 'ambition_m', 'shared_interests_m']

zip( ) : for문에 리스트 3개를 동시에 사용할 수 있다

for i, j, k in zip(new_label_partner, partner_imp, partner_rate_me):
    print(i, '&', j, '&', k)
# attractive_p & pref_o_attractive & attractive_o
# sincere_partner_p & pref_o_sincere & sincere_o
# intelligence_p & pref_o_intelligence & intelligence_o
# funny_p & pref_o_funny & funny_o
# ambition_p & pref_o_ambitious & ambitous_o
# shared_interests_p & pref_o_shared_interests & shared_interests_o

rating( ) 함수를 이용하여 라벨 = 중요도 x 평가 계산하기

      data 전체에 대하여 apply( )를 활용하여 rating( ) 함수를 사용

            x : DataFrame

            i : 라벨 변수 (중요도 x 평가)

            j : 중요도 변수

            k : 평가 변수

      lamda x : 해당 변수 i의 한 줄 한 줄의 데이터가 x로 받아져서 rating( ) 함수에 사용된다

for i, j, k in zip(new_label_partner, partner_imp, partner_rate_me): # 상대
    data[i] = data.apply(lambda x: rating(x, j, k), axis = 1)
for i, j, k in zip(new_label_me, my_imp, my_rate_partner): # 본인
    data[i] = data.apply(lambda x: rating(x, j, k), axis = 1)

4. 문자 데이터를 원-핫 인코딩

data.info()
# 1   gender                         8130 non-null   object 
# 4   race                           8130 non-null   object 
# 5   race_o                         8130 non-null   object

문자 데이터 변수 : gender, race, race_o

data = pd.get_dummies(data, columns = ['gender', 'race', 'race_o'], drop_first = True)

- 4. 모델링 및 평가

from sklearn.model_selection import train_test_split

X = data.drop('match', axis = 1)
y = data['match']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 100)
import xgboost as xgb

xgb_mdl = xgb.XGBClassifier(n_estimators = 500, max_depth = 5, random_state = 100) # 모델 객체 생성
xgb_mdl.fit(X_train, y_train) # 학습
pred = xgb_mdl.predict(X_test) # 예측
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report

accuracy_score(y_test, pred)
# 0.8628536285362853

정확도 86%가 낮은 정확도인 이유

data['match'].describe()
# count    8130.000000
# mean        0.164822
# std         0.371042
# min         0.000000
# 25%         0.000000
# 50%         0.000000
# 75%         0.000000
# max         1.000000

종속변수 match의 평균 값은 0.165 (16.5%)

      매칭된 경우(1)가 16.5%

      매칭되지 않은 경우(0)가 83.5%

주어진 데이터는 모델링 없이 모든 경우를 0(매칭되지 않음)으로만 예측해도 84%는 맞출 수 있는 편향된 데이터

      학습을 통해 얻은 86%의 정확도는 학습을 안한 것보다 약간 나은 수준

confusion_matrix(실제값, 예측값) : 혼동 행렬

print(confusion_matrix(y_test, pred))
# [[1298   67]
#  [ 156  105]]
    예측값
    0 (매칭 X) 1 (매칭 O)
실제값 0 (매칭 X) 1298 (TN) 67 (FP)
1 (매칭 O) 156 (FN) 105 (TP)

실제값 0 - 예측값 0 : 1298 (TN)

실제값 0 - 예측값 1 : 67 (FP, 1종 오류)

실제값 1 - 예측값 0 : 156 (FN, 2종 오류)

실제값 1 - 예측값 1 : 105 (TP)

 

classification_report(실제값, 예측값) : 오류 유형에 따른 분석

print(classification_report(y_test, pred))
#               precision    recall  f1-score   support

#            0       0.89      0.95      0.92      1365 - 0에 대한 정밀도, 재현율, F1 점수, 해당 갯수
#            1       0.61      0.40      0.48       261 - 1에 대한 정밀도, 재현율, F1 점수, 해당 갯수

#     accuracy                           0.86      1626
#    macro avg       0.75      0.68      0.70      1626
# weighted avg       0.85      0.86      0.85      1626

대부분의 경우, 예측하고자 하는 경우를 1로 두기 때문에 1에 대한 정보를 해석한다 (0은 필요에 따라 해석 진행)

      목표값의 대부분이 0(84%)이기 때문에 0에 대한 정밀도, 재현율, F1 점수가 0.9 이상

정밀도 (precision) : 1(양성, 매칭 O)로 예측한 경우 중, 얼마만큼 실제로 1인가

      TP / (TP + FP)

      FP(1종 오류)가 커지면 정밀도는 낮아진다

재현율 (recall) : 실제로 1(양성, 매칭 O) 중 얼마만큼을 1로 예측했는가

      TP / (TP + FN)

      FN(2종 오류)가 커지면 재현율은 낮아진다

F-1 점수 : 정밀도와 재현율의 조화평균

      2 x (정밀도 x 재현율) / (정밀도 + 재현율) 

      정밀도와 재현율이 높으면 F-1 점수는 높아진다

      정밀도와 재현율이 비슷할수록 F-1 점수는 높아진다

어떤 오류값을 봐야 하는가?

      1종 오류가 중요하다면 정밀도에 신경을 써야 한다

      2종 오류가 중요하다면 재현율에 신경을 써야 한다

      특별히 더 중요한 오류 유형이 없다면 F-1 점수를 신경 써야 한다

- 5. 하이퍼 파라미터 튜닝 : 그리드 서치

하이퍼 파라미터 튜닝 : 임의의 값을 넣어 더 나은 결과를 찾는다

      좋은 결과가 나올 때 까지 하나하나 값을 넣어줘 비교해야 한다

그리드 서치 : 한 번 시도로 수백 가지 하이퍼 파라미터 값을 시도할 수 있다

      각각의 하이퍼 파라미터에 대해서 조사하고 싶은 모든 값을 넘겨주면 조합을 만들어 각각 모델링 한다

      또한, 그리드 서치에는 K-폴드 교차검증도 함께 사용한다

ex) 파라미터 A = [1, 2, 3], 파라미터 B = [4, 5], K-폴드 = 5

      모델링 횟수 = A x B x K = 30

from sklearn.model_selection import GridSearchCV # 그리드 서치 모듈

그리드 서치에 넣을 매개변수를 딕셔너리 형태로 입력

      learning_rate (학습률) : 경사하강법에서 매개변수를 얼마만큼 이동해가며 최소 오차를 찾을지 보폭의 크기를 결정

            너무 작은 학습률 : 지역 최소값에서 빠져나오지 못한다

            너무 큰 학습률 : 최소값을 찾지 못한다

      max_depth : 각 트리의 깊이를 제한

      subsample : 모델을 학습시킬 때 일부 데이터만 사용하여 각 트리를 만든다

            subsample = 0.5 : 데이터의 절반씩만 랜덤 추출하여 트리를 만든다

            오버 피팅을 방지하는데 도움

      n_estimators : 전체 나무의 개수

parameters = {'learning_rate':[0.01, 0.1, 0.3],
              'max_depth':[5, 7, 10],
              'subsample':[0.5, 0.7, 1],
              'n_estimators':[300, 500, 1000]
              }

GridsearchCV(머신러닝 알고리즘 모델, 하이퍼 파라미터, n_jobs, scoring, cv)

      n_jobs : 사용할 코어 수

      scoring : 모델링 시 최적의 모델을 평가할 기준 (f1 = F1 점수)

      cv : K-폴드 값

xgb_mdl = xgb.XGBClassifier() # 머신러닝 알고리즘(XG부스트) 모델 객체 생성 (빈 모델)

gs_mdl = GridSearchCV(xgb_mdl, parameters, n_jobs = -1, scoring = 'f1', cv = 5) # 그리드 객체 생성

학습

      하이퍼 파라미터 4종류 * 3개씩 = 3 x 3 x 3 x 3 = 81번의 모델링

      교차검증 5회 

      → 총 405번의 모델링 작업

gs_mdl.fit(X_train, y_train) # 학습

best_params_ : 최적의 하이퍼 파라미터 조합을 출력

gs_mdl.best_params_
# {'learning_rate': 0.1, 'max_depth': 5, 'n_estimators': 1000, 'subsample': 0.7}

그리드 서치를 이용해서 예측하기

pred = gs_mdl.predict(X_test)

accuracy_score(y_test, pred) # 0.8628536285362853

분류 리포트

print(classification_report(y_test, pred))
#               precision    recall  f1-score   support

#            0       0.90      0.94      0.92      1365
#            1       0.60      0.44      0.51       261

#     accuracy                           0.86      1626
#    macro avg       0.75      0.69      0.71      1626
# weighted avg       0.85      0.86      0.85      1626

      정확도가 미세하게 상승

      F1 점수 상승

일반적으로 하이퍼 파라미터 튜닝은 예측에 큰 영향을 끼치지 못한다

      예측에 큰 영향을 끼치는 것은 피처 엔지니어링모델 알고리즘 선정

- 6. 중요 변수 확인

feature_importances_ : XGBoost는 변수의 중요도를 계산하여 알려주는 내장 함수를 제공

      단, 그리드 서치로 학습된 모델에서는 이 기능을 사용할 수 없다

      그리드 서치로 최적의 하이퍼 파라미터 확인 → XGBoost 모델에 해당 파라미터로 재학습

xgb_mdl = xgb.XGBClassifier(learning_rate = 0.3, max_depth = 5, n_estimators = 1000, subsamples = 0.5, random_state = 100) # XGBoost 모델 객체 생성

xgb_mdl.fit(X_train, y_train) # 학습

xgb_mdl.feature_importances_
# array([0.0065875 , 0.00997889, 0.01140108, 0.00906033, 0.01104698,
#        0.01227812, 0.00887271, 0.01624694, 0.01086972, 0.01259066,
#        0.01051758, 0.06942619, 0.01312322, 0.00967889, 0.04112966,
#        0.01354105, 0.02690756, 0.01647096, 0.01045718, 0.01864079,
#        0.01256743, 0.01054458, 0.00885988, 0.03031815, 0.00954538,
#        0.01477399, 0.03790702, 0.01048999, 0.00989513, 0.01038827,
#        0.01184977, 0.02534583, 0.08302046, 0.02302745, 0.02874302,
#        0.01195098, 0.01259892, 0.00569656, 0.01619294, 0.01123691,
#        0.00859949, 0.01123985, 0.01208494, 0.01120727, 0.00904968,
#        0.00980906, 0.01222008, 0.01272813, 0.01108321, 0.0093992 ,
#        0.01852996, 0.02193918, 0.01266858, 0.02828786, 0.02443564,
#        0.0191917 , 0.01165292, 0.01872558, 0.01336896], dtype=float32)

중요도는 Numpy 형태로 나온다

      X_train의 변수 이름을 사용해 feature_imp라는 DataFrame으로 만들기

feature_imp = pd.DataFrame({'features':X_train.columns, 'values':xgb_mdl.feature_importances_})

feature_imp.head()
# 	features			values
# 0	has_null			0.006587
# 1	age				0.009979
# 2	age_o				0.011401
# 3	importance_same_race		0.009060
# 4	importance_same_religion	0.011047

변수의 중요도를 내림차순으로 정리하기

바 그래프 형태로 보기

import matplotlib.pyplot as plt
import seaborn as sns

plt.figure(figsize = (20, 10)) # 그래프 크기
sns.barplot(x = 'values', y = 'features', data = feature_imp.sort_values(by = 'values', ascending = False).head(10))
# feature_imp를 'values' 변수를 기준으로 내림차순(큰 -> 작은) 정렬하고, 상위 10개 항목 그리기

선형 회귀로지스틱 회귀

      계수의 부호와 크기를 보고 변수가 얼마나 영향을 끼치는지, 긍정적 영향인지 부정적 영향인지 파악

결정 트리

      각 노드가 어떤 변수에서 어떤 값을 기준으로 다음 노드로 나누는지를 보여준다

XGBoost 

      values의 상대적인 중요도를 계산했을 뿐, 긍정적 영향인지 부정적 영향인지 알 수 없다