<머신러닝 • 딥러닝 문제해결 전략> 8장의 내용을 실습했습니다.
# 훈련/ 테스트/ 제출 데이터 불러오기
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')
1. 피처엔지니어링
데이터 합치기
훈련 & 테스트 데이터 합친 뒤 합친 데이터에서 타깃값을 제거한 데이터 프레임 => all_data
여기에 있는 모든 피처 all_features 변수에 저장합니다.
all_data = pd.concat([train, test], ignore_index=True)
all_data = all_data.drop('target', axis=1) # 타깃값 제거
all_features = all_data.columns # 전체 피처
all_features
명목형 피처 원-핫 인코딩
명목형 데이터는 고유값 별 순서가 따로 없기 때문에 모든 명목형 피처에 원핫 인코딩을 적용합니다.
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])
encoded_cat_matrix
필요없는 피처 제거
제거할 피처는 calc 분류에 속하는 20개 피처와 그 외 6가지의 피처입니다.
원핫 인코딩에 사용한 명목형 피처, calc 분류의 피처, 추가 제거할 6개를 제외한 나머지 피처들을 remaining_features에 저장합니다.
# 추가로 제거할 피처
drop_features = ['ps_ind_14', 'ps_ind_10_bin', 'ps_ind_11_bin',
'ps_ind_12_bin', 'ps_ind_13_bin', 'ps_car_14']
# '1) 명목형 피처, 2) calc 분류의 피처, 3) 추가 제거할 피처'를 제외한 피처
remaining_features = [feature for feature in all_features
if ('cat' not in feature and
'calc' not in feature and
feature not in drop_features)]
인코딩과 피처제거를 완료한 뒤 방금 구한 remaining_features를 포함하는 데이터와 앞서 명목형 피처를 원핫 인코딩한 encoded_cat_matrix를 합칩니다.
from scipy import sparse
all_data_sprs = sparse.hstack([sparse.csc_matrix(all_data[remaining_features]),
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
2. 평가지표 계산 함수 작성
정규화 지니계수라는 평가지표는 파이썬/싸이킷런에서 기본으로 제공하지 않기 때문에 직접 사용자 정의함수를 만들어야합니다.
정규화 지니계수는 값이 0에 가까울 수록 성능이 나쁘고, 1에 가까울 수록 좋다는 의미가 됩니다.
정규화 지니계수는 = 예측값에 대한 지니계수(예측값과 실제값으로 구한 지니계수) / 예측이 완벽할 때의 지니계수 (예측값과 실제값이 같다)
실제 타깃값(y_true)와 예측값(y_pred)를 입력받아 정규화 지니계수를 반환하도록 구현했습니다.
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(preds, dtrain):
labels = dtrain.get_label() #
return 'gini', eval_gini(labels, preds), True #반환값
# return 평가지표이름, 평가점수, true(평가점수가 높을수록 좋은지 여부)
3. 모델 훈련 및 성능 검증
OOF 예측 방식
k 폴드 교차 검증을 수행하면서 각 폴드마다 테스트 데이터로 예측하는 방식. 훈련된 모델로 마지막에 한 번만 예측하는 것이 아닌 각 폴드 별로 여러번 예측해 평균을 내는 방식이다.
OOF 방식으로 LightGBM 훈련
본 경진 대회의 타깃값이 불균형하기 때문에 층화 k폴드를 수행합니다. 층화 k폴드는 타깃값이 균등하게 배치되게 폴드하는 방식으로 StratifiedKFold()로 층화 k 폴드 교차 검증기를 생성할 수 있습니다.
from sklearn.model_selection import StratifiedKFold
# 층화 K 폴드 교차 검증기
folds = StratifiedKFold(n_splits=5, shuffle=True, random_state=1991)
- n_split : 전달한 수만큼 폴드를 나눔
- shuffle=True : 폴드를 나눌 때 데이터를 섞어줌
# LightGBM 하이퍼파라미터 설정
params = {'objective': 'binary', # 이진분류 문제여서 binary로 설정
'learning_rate': 0.01, # 학습률 0.01
'force_row_wise': True,
'random_state': 0} # 랜덤시드값 0
# 1차원 배열 2가지 만들기
# OOF 방식으로 훈련된 모델로 검증 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_val_preds = np.zeros(X.shape[0])
# OOF 방식으로 훈련된 모델로 테스트 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_test_preds = np.zeros(X_test.shape[0])
oof_val_preds는 검증 데이터를 활용해 예측한 확률값을 저장하는 배열, 따라서 배열의 크기는 훈련데이터와 같아야 한다.
oof_test_preds는 테스트 데이터를 활용해 예측한 확률값을 저장하는 배열로, 배열의 크기는 테스트 데이터와 같아야한다.
lightgbm 모델을 훈련하면서 oof 예측도 같이 수행해보는 코드입니다. oof 예측 시 자주 사용하는 패턴 중 하나이다.
import lightgbm as lgb
# OOF 방식으로 모델 훈련, 검증, 예측
for idx, (train_idx, valid_idx) in enumerate(folds.split(X, y)): # 1
# 각 폴드를 구분하는 문구 출력
print('#'*40, f'폴드 {idx+1} / 폴드 {folds.n_splits}', '#'*40)
# 훈련용 데이터, 검증용 데이터 설정 # 2 훈련 & 검증용으로 나눔
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=params, # 훈련용 하이퍼파라미터
train_set=dtrain, # 훈련 데이터셋
num_boost_round=1000, # 부스팅 반복 횟수
valid_sets=dvalid, # 성능 평가용 검증 데이터셋
feval=gini, # 검증용 평가지표
early_stopping_rounds=100, # 조기종료 조건
verbose_eval=100) # 100번째마다 점수 출력
# 테스트 데이터를 활용해 OOF 예측 # 5
oof_test_preds += lgb_model.predict(X_test)/folds.n_splits
# 모델 성능 평가를 위한 검증 데이터 타깃값 예측 # 6
oof_val_preds[valid_idx] += lgb_model.predict(X_valid)
# 검증 데이터 예측 확률에 대한 정규화 지니계수 # 7
gini_score = eval_gini(y_valid, oof_val_preds[valid_idx])
print(f'폴드 {idx+1} 지니계수 : {gini_score}\n')
# 1: folds.split(X, y)를 호출해 데이터를 k개로 나눔, enumerate()를 적용해 인덱스와 k폴드로 나뉜 훈련데이터 인덱스, 검증 데이터 인덱스를 사용할 수 있게 함.
# 5: 훈련된 모델이 테스트 데이터를 주어 타깃확률값을 예측함.
# 6 : 모델 성능 평가를 위해 검증 데이터를 활용해 예측하는 코드로 맨 처음 oof_val_preds의 모든 값이 0으로 초기화되어 있다. 여기서 valid_idx(검증 데이터 인덱스)에 해당하는 값만 검증 데이터 예측 확률로 업데이트 해준다. 폴드가 5번 반복되면 oof_val_preds 내 모든 값이 검증 데이터 예측 확률로 업데이트 된다. 최종 업데이트된 값과 실제 타깃값 y를 비교해 지니계수를 계산하면 모델 성능을 검증할 수 있다.
# 7: 현재 폴드에서 정규화 지니계수를 계산. 폴드마다 계산되는 중간 점검용 점수라 보면 된다.
print('OOF 검증 데이터 지니계수:', eval_gini(y, oof_val_preds))
검증 데이터로 예측한 확률을 실제 타깃값과 비교해 지니 계수를 출력해보았습니다.
4. 예측 및 결과 제출
submission['target'] = oof_test_preds
submission.to_csv('submission.csv')
'Data > MLDL' 카테고리의 다른 글
안전 운전자 예측 경진대회: 성능개선2(XGBoost 모델) (0) | 2022.11.14 |
---|---|
안전 운전자 예측 경진대회: 성능개선1(lightGBM 모델) (0) | 2022.11.14 |
범주형 경진대회 이진분류 (0) | 2022.10.31 |
자전거 대여 수요 예측(2) (0) | 2022.09.26 |
자전거 수요 예측 경진대회 (0) | 2022.09.26 |