- 미션 : 보험사에서 제공한 고객 데이터를 활용해 운전자가 보험을 청구할 확률 예측
- 문제유형 : 이진분류
- 평가지표 : 정규화된 지니계수
- 사용 모델 : LightGBM
- 캐글 노트북 : https://www.kaggle.com/jinkwonskk/iii-ensemble-lgb-xgb
#코드
# 안전 운전자 예측 경진대회 성능 개선 III : LightGBM과 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) + '_'
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
# LightGBM용 gini() 함수
def gini_lgb(preds, dtrain):
labels = dtrain.get_label()
return 'gini', eval_gini(labels, preds), True
# XGBoost용 gini() 함수
def gini_xgb(preds, dtrain):
labels = dtrain.get_label()
return 'gini', eval_gini(labels, preds)
from sklearn.model_selection import StratifiedKFold
# 층화 K 폴드 교차 검증기 생성
folds = StratifiedKFold(n_splits=5, shuffle=True, random_state=1991)
max_params_lgb = {
'bagging_fraction': 0.6213108174593661,
'feature_fraction': 0.608712929970154,
'lambda_l1': 0.7040436794880651,
'lambda_l2': 0.9832619845547939,
'min_child_samples': 9,
'min_child_weight': 36.10036444740457,
'num_leaves': 40,
'objective': 'binary',
'learning_rate': 0.005,
'bagging_freq': 1,
'force_row_wise': True,
'random_state': 1991
}
import lightgbm as lgb
# OOF 방식으로 훈련된 모델로 검증 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_val_preds_lgb = np.zeros(X.shape[0])
# OOF 방식으로 훈련된 모델로 테스트 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_test_preds_lgb = 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] # 검증용 데이터
# LightGBM 전용 데이터셋 생성
dtrain = lgb.Dataset(X_train, y_train) # LightGBM 전용 훈련 데이터셋
dvalid = lgb.Dataset(X_valid, y_valid) # LightGBM 전용 검증 데이터셋
# LightGBM 모델 훈련
lgb_model = lgb.train(params=max_params_lgb, # 최적 하이퍼파라미터
train_set=dtrain, # 훈련 데이터셋
num_boost_round=2500, # 부스팅 반복 횟수
valid_sets=dvalid, # 성능 평가용 검증 데이터셋
feval=gini_lgb, # 검증용 평가지표
early_stopping_rounds=300, # 조기종료 조건
verbose_eval=100) # 100번째마다 점수 출력
# 테스트 데이터를 활용해 OOF 예측
oof_test_preds_lgb += lgb_model.predict(X_test)/folds.n_splits
# 모델 성능 평가를 위한 검증 데이터 타깃값 예측
oof_val_preds_lgb[valid_idx] += lgb_model.predict(X_valid)
# 검증 데이터 예측확률에 대한 정규화 지니계수
gini_score = eval_gini(y_valid, oof_val_preds_lgb[valid_idx])
print(f'폴드 {idx+1} 지니계수 : {gini_score}\n')
max_params_xgb = {
'colsample_bytree': 0.8843124587484356,
'gamma': 10.452246227672624,
'max_depth': 7,
'min_child_weight': 6.494091293383359,
'reg_alpha': 8.551838810159788,
'reg_lambda': 1.3814765995549108,
'scale_pos_weight': 1.423280772455086,
'subsample': 0.7001630536555632,
'objective': 'binary:logistic',
'learning_rate': 0.02,
'random_state': 1991
}
import xgboost as xgb
# OOF 방식으로 훈련된 모델로 검증 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_val_preds_xgb = np.zeros(X.shape[0])
# OOF 방식으로 훈련된 모델로 테스트 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_test_preds_xgb = 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_xgb,
dtrain=dtrain,
num_boost_round=2000,
evals=[(dvalid, 'valid')],
maximize=True,
feval=gini_xgb,
early_stopping_rounds=200,
verbose_eval=100)
# 모델 성능이 가장 좋을 때의 부스팅 반복 횟수 저장
best_iter = xgb_model.best_iteration
# 테스트 데이터를 활용해 OOF 예측
oof_test_preds_xgb += xgb_model.predict(dtest,
iteration_range=(0, best_iter))/folds.n_splits
# 모델 성능 평가를 위한 검증 데이터 타깃값 예측
oof_val_preds_xgb[valid_idx] += xgb_model.predict(dvalid,
iteration_range=(0, best_iter))
# 검증 데이터 예측확률에 대한 정규화 지니계수
gini_score = eval_gini(y_valid, oof_val_preds_xgb[valid_idx])
print(f'폴드 {idx+1} 지니계수 : {gini_score}\n')
print('LightGBM OOF 검증 데이터 지니계수 :', eval_gini(y, oof_val_preds_lgb))
print('XGBoost OOF 검증 데이터 지니계수 :', eval_gini(y, oof_val_preds_xgb))
## 앙상블 수행
oof_test_preds = oof_test_preds_lgb * 0.5 + oof_test_preds_xgb * 0.5
## 예측 및 결과 제출
submission['target'] = oof_test_preds
submission.to_csv('submission.csv')