- 미션 : 보험사에서 제공한 고객 데이터를 활용해 운전자가 보험을 청구할 확률 예측
- 문제유형 : 이진분류
- 평가지표 : 정규화된 지니계수
- 사용 모델 : LightGBM
- 캐글 노트북 : https://www.kaggle.com/jinkwonskk/ii-xgb-modeling
안전 운전자 예측 경진대회 성능 개선 II / XGB Modeling
Explore and run machine learning code with Kaggle Notebooks | Using data from Porto Seguro’s Safe Driver Prediction
www.kaggle.com
# 코드
# 안전 운전자 예측 경진대회 성능 개선 II : XGBoost 모델
import pandas as pd
# 데이터 경로
data_path = '/kaggle/input/porto-seguro-safe-driver-prediction/'
train = pd.read_csv(data_path + 'train.csv', index_col='id')
test = pd.read_csv(data_path + 'test.csv', index_col='id')
submission = pd.read_csv(data_path + 'sample_submission.csv', index_col='id')
## 피처 엔지니어링
### 데이터 합치기
all_data = pd.concat([train, test], ignore_index=True)
all_data = all_data.drop('target', axis=1) # 타깃값 제거
all_features = all_data.columns # 전체 피처
### 명목형 피처 원-핫 인코딩
from sklearn.preprocessing import OneHotEncoder
# 명목형 피처
cat_features = [feature for feature in all_features if 'cat' in feature]
# 원-핫 인코딩 적용
onehot_encoder = OneHotEncoder()
encoded_cat_matrix = onehot_encoder.fit_transform(all_data[cat_features])
### 파생 피처 추가
# '데이터 하나당 결측값 개수'를 파생 피처로 추가
all_data['num_missing'] = (all_data==-1).sum(axis=1)
# 명목형 피처, calc 분류 피처를 제외한 피처
remaining_features = [feature for feature in all_features
if ('cat' not in feature and 'calc' not in feature)]
# num_missing을 remaining_features에 추가
remaining_features.append('num_missing')
# 분류가 ind인 피처
ind_features = [feature for feature in all_features if 'ind' in feature]
is_first_feature = True
for ind_feature in ind_features:
if is_first_feature:
all_data['mix_ind'] = all_data[ind_feature].astype(str) + '_'
is_first_feature = False
else:
all_data['mix_ind'] += all_data[ind_feature].astype(str) + '_'
all_data['mix_ind']
cat_count_features = []
for feature in cat_features+['mix_ind']:
val_counts_dict = all_data[feature].value_counts().to_dict()
all_data[f'{feature}_count'] = all_data[feature].apply(lambda x:
val_counts_dict[x])
cat_count_features.append(f'{feature}_count')
cat_count_features
### 필요 없는 피처 제거
from scipy import sparse
# 필요 없는 피처들
drop_features = ['ps_ind_14', 'ps_ind_10_bin', 'ps_ind_11_bin',
'ps_ind_12_bin', 'ps_ind_13_bin', 'ps_car_14']
# remaining_features, cat_count_features에서 drop_features를 제거한 데이터
all_data_remaining = all_data[remaining_features+cat_count_features].drop(drop_features, axis=1)
# 데이터 합치기
all_data_sprs = sparse.hstack([sparse.csr_matrix(all_data_remaining),
encoded_cat_matrix],
format='csr')
### 데이터 나누기
num_train = len(train) # 훈련 데이터 개수
# 훈련 데이터와 테스트 데이터 나누기
X = all_data_sprs[:num_train]
X_test = all_data_sprs[num_train:]
y = train['target'].values
### 정규화 지니계수 계산 함수
import numpy as np
def eval_gini(y_true, y_pred):
# 실제값과 예측값의 크기가 같은지 확인 (값이 다르면 오류 발생)
assert y_true.shape == y_pred.shape
n_samples = y_true.shape[0] # 데이터 개수
L_mid = np.linspace(1 / n_samples, 1, n_samples) # 대각선 값
# 1) 예측값에 대한 지니계수
pred_order = y_true[y_pred.argsort()] # y_pred 크기순으로 y_true 값 정렬
L_pred = np.cumsum(pred_order) / np.sum(pred_order) # 로렌츠 곡선
G_pred = np.sum(L_mid - L_pred) # 예측 값에 대한 지니계수
# 2) 예측이 완벽할 때 지니계수
true_order = y_true[y_true.argsort()] # y_true 크기순으로 y_true 값 정렬
L_true = np.cumsum(true_order) / np.sum(true_order) # 로렌츠 곡선
G_true = np.sum(L_mid - L_true) # 예측이 완벽할 때 지니계수
# 정규화된 지니계수
return G_pred / G_true
# XGBoost용 gini() 함수
def gini(preds, dtrain):
labels = dtrain.get_label()
return 'gini', eval_gini(labels, preds)
## 하이퍼파라미터 최적화
### 데이터셋 준비
import xgboost as xgb
from sklearn.model_selection import train_test_split
# 8:2 비율로 훈련 데이터, 검증 데이터 분리 (베이지안 최적화 수행용)
X_train, X_valid, y_train, y_valid = train_test_split(X, y,
test_size=0.2,
random_state=0)
# 베이지안 최적화용 데이터셋
bayes_dtrain = xgb.DMatrix(X_train, y_train)
bayes_dvalid = xgb.DMatrix(X_valid, y_valid)
### 하이퍼파라미터 범위 설정
# 베이지안 최적화를 위한 하이퍼파라미터 범위
param_bounds = {'max_depth': (4, 8),
'subsample': (0.6, 0.9),
'colsample_bytree': (0.7, 1.0),
'min_child_weight': (5, 7),
'gamma': (8, 11),
'reg_alpha': (7, 9),
'reg_lambda': (1.1, 1.5),
'scale_pos_weight': (1.4, 1.6)}
# 값이 고정된 하이퍼파라미터
fixed_params = {'objective': 'binary:logistic',
'learning_rate': 0.02,
'random_state': 1991}
### (베이지안 최적화용) 평가지표 계산 함수 작성
def eval_function(max_depth, subsample, colsample_bytree, min_child_weight,
reg_alpha, gamma, reg_lambda, scale_pos_weight):
'''최적화하려는 평가지표(지니계수) 계산 함수'''
# 베이지안 최적화를 수행할 하이퍼파라미터
params = {'max_depth': int(round(max_depth)),
'subsample': subsample,
'colsample_bytree': colsample_bytree,
'min_child_weight': min_child_weight,
'gamma': gamma,
'reg_alpha':reg_alpha,
'reg_lambda': reg_lambda,
'scale_pos_weight': scale_pos_weight}
# 값이 고정된 하이퍼파라미터도 추가
params.update(fixed_params)
print('하이퍼파라미터 :', params)
# XGBoost 모델 훈련
xgb_model = xgb.train(params=params,
dtrain=bayes_dtrain,
num_boost_round=2000,
evals=[(bayes_dvalid, 'bayes_dvalid')],
maximize=True,
feval=gini,
early_stopping_rounds=200,
verbose_eval=False)
best_iter = xgb_model.best_iteration # 최적 반복 횟수
# 검증 데이터로 예측 수행
preds = xgb_model.predict(bayes_dvalid,
iteration_range=(0, best_iter))
# 지니계수 계산
gini_score = eval_gini(y_valid, preds)
print(f'지니계수 : {gini_score}\n')
return gini_score
### 최적화 수행
from bayes_opt import BayesianOptimization
# 베이지안 최적화 객체 생성
optimizer = BayesianOptimization(f=eval_function,
pbounds=param_bounds,
random_state=0)
# 베이지안 최적화 수행
optimizer.maximize(init_points=3, n_iter=6)
### 결과 확인
# 평가함수 점수가 최대일 때 하이퍼파라미터
max_params = optimizer.max['params']
max_params
# 정수형 하이퍼파라미터 변환
max_params['max_depth'] = int(round(max_params['max_depth']))
# 값이 고정된 하이퍼파라미터 추가
max_params.update(fixed_params)
max_params
## 모델 훈련 및 성능 검증
from sklearn.model_selection import StratifiedKFold
# 층화 K 폴드 교차 검증기 생성
folds = StratifiedKFold(n_splits=5, shuffle=True, random_state=1991)
# OOF 방식으로 훈련된 모델로 검증 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_val_preds = np.zeros(X.shape[0])
# OOF 방식으로 훈련된 모델로 테스트 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_test_preds = np.zeros(X_test.shape[0])
# OOF 방식으로 모델 훈련, 검증, 예측
for idx, (train_idx, valid_idx) in enumerate(folds.split(X, y)):
# 각 폴드를 구분하는 문구 출력
print('#'*40, f'폴드 {idx+1} / 폴드 {folds.n_splits}', '#'*40)
# 훈련용 데이터, 검증용 데이터 설정
X_train, y_train = X[train_idx], y[train_idx]
X_valid, y_valid = X[valid_idx], y[valid_idx]
# XGBoost 전용 데이터셋 생성
dtrain = xgb.DMatrix(X_train, y_train)
dvalid = xgb.DMatrix(X_valid, y_valid)
dtest = xgb.DMatrix(X_test)
# XGBoost 모델 훈련
xgb_model = xgb.train(params=max_params,
dtrain=dtrain,
num_boost_round=2000,
evals=[(dvalid, 'valid')],
maximize=True,
feval=gini,
early_stopping_rounds=200,
verbose_eval=100)
# 모델 성능이 가장 좋을 때의 부스팅 반복 횟수 저장
best_iter = xgb_model.best_iteration
# 테스트 데이터를 활용해 OOF 예측
oof_test_preds += xgb_model.predict(dtest,
iteration_range=(0, best_iter))/folds.n_splits
# 모델 성능 평가를 위한 검증 데이터 타깃값 예측
oof_val_preds[valid_idx] += xgb_model.predict(dvalid,
iteration_range=(0, best_iter))
# 검증 데이터 예측 확률에 대한 정규화 지니계수
gini_score = eval_gini(y_valid, oof_val_preds[valid_idx])
print(f'폴드 {idx+1} 지니계수 : {gini_score}\n')
print('OOF 검증 데이터 지니계수 :', eval_gini(y, oof_val_preds))
## 예측 및 결과 제출
submission['target'] = oof_test_preds
submission.to_csv('submission.csv')
# 새롭게 알게된 점
1. 모델에 따라서 반영해줘야 하는 하이퍼파라미터도 다르고, 성능에 치명적인 하이퍼파라미터도 다르다.
또한 기능이 같은데도 이름이 다를 수 있다.
2. xgb는 예측을 수행할때 iteration_range를 따로 변수에 넣어줘야한다. 그래야 최적 반복 및 횟수로 훈련된 모델을 활용해 예측한다.