[Python] Kaggle 데이터 소개 및 분석

거친코딩·2021년 6월 2일
0
post-thumbnail

기초부터 쌓아가는 머신러닝 #8

8주차 : Kaggle 데이터 소개 및 분석


🚩 Predict the value of transactions for potential customers

  • You are provided with an anonymized dataset containing numeric feature variables, the numeric target column, and a string ID column.

🚩 import required library for analysis

# pip install catboost

import os
import numpy as np
import pandas as pd
import gc # garbage collector

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

from sklearn import model_selection
from sklearn.model_selection import train_test_split

# 1	StandardScaler	기본 스케일. 평균과 표준편차 사용
# 2	MinMaxScaler	최대/최소값이 각각 1, 0이 되도록 스케일링
# 3	MaxAbsScaler	최대절대값과 0이 각각 1, 0이 되도록 스케일링
# 4	RobustScaler	중앙값(median)과 IQR(interquartile range) 사용. 아웃라이어의 영향을 최소화
from sklearn.preprocessing import RobustScaler

import lightgbm as lgb
import xgboost as xgb
from catboost import CatBoostRegressor

# Allows the use of display() for DataFrames
from IPython.display import display

import warnings
warnings.filterwarnings('ignore')

# check where data exists
os.listdir('./drive/MyDrive/machine_learning_data')
>['train.csv', 'test.csv']

🚩 Load Train and Test Data

# Read train and test files
train_df = pd.read_csv('./drive/MyDrive/machine_learning_data/train.csv')
test_df = pd.read_csv('./drive/MyDrive/machine_learning_data/test.csv')

# check data size
print('train_df.shape : ',train_df.shape)
>train_df.shape :  (4459, 4993)

# test data misses target data
print('test_df.shape : ',test_df.shape) 
>test_df.shape :  (49342, 4992)

train_df.head()


🚩 Train and Test Data Info

# data size = 3807
# column = 4993, ID to 9fc776466
# column data type = float64(3673), int64(1319), object(1)
train_df.info()
> <class 'pandas.core.frame.DataFrame'>
RangeIndex: 3807 entries, 0 to 3806
Columns: 4993 entries, ID to 9fc776466
dtypes: float64(3673), int64(1319), object(1)
memory usage: 145.0+ MB

🚩 Data Preprocessing

# 결측치 유무 확인 (train)
# null 값 0
print(train_df.drop(결측 Id,axis=0).isnull().sum().sum())
train_df = train_df.drop(213,axis=0)


# null 값 0
print(test_df.drop(결측 Id,axis=0).isnull().sum().sum())
test_df = test_df.drop(48,axis=0)



# 모델 학습에 의미 없는 상수 컬럼 삭제
colsToRemove = []
for col in train_df.columns:
  if col != 'ID' and col != 'target':
    if train_df[col].std() == 0:
      colsToRemove.append(col)

# colsToRemove에 들어있는 columns drop
train_df.drop(colsToRemove, axis=1, inplace=True)
test_df.drop(colsToRemove, axis=1, inplace=True)

print("삭제된 컬럼 수 : ",len(colsToRemove))
print("삭제된 컬럼 : ",colsToRemove)

>삭제된 컬럼 수 :  256
삭제된 컬럼 :  ['d5308d8bc', 'c330f1a67', 'eeac16933', '7df8788e8', ......


># 중복 컬럼 삭제 (중복 row 아님)
groups = train_df.columns.to_series().groupby(train_df.dtypes).groups

dup_col = []

for key,value in groups.items():
  df_col = train_df[value].columns
  df = train_df[value]
  l_df_col = len(df_col)

  for i in range(l_df_col):
    i_df = df.iloc[:,i].values
    for j in range(i+1,l_df_col):
      j_df = df.iloc[:,j].values
      if np.array_equal(i_df,j_df):
        dup_col.append(df_col[i])
        break
      
print(dup_col)

> ['34ceb0081', '8d57e2749', '168b3e5bc', 'a765da8bc', 'acc5b709d']
train_df = train_df.drop(dup_col, axis=1)
test_df = test_df.drop(dup_col, axis=1)


# 희소한 데이터 삭제
temp_col = [x for x in train_df.columns if not x in ['ID','target']]

for temp in temp_col:
  if len(np.unique(train_df[temp]))<2:
    train_df.drop(temp,axis=1,inplace=True)
    test_df.drop(temp,axis=1,inplace=True)


# 불필요한 메모리 소모 요소 제거
gc.collect()

print("Train set size: {}".format(train_df.shape))
>Train set size: (4459, 4732)

print("Test set size: {}".format(test_df.shape))
>Test set size: (49342, 4731)

🚩 Prepare Data for Training & Test

  • 선형 회귀 모델의 경우, 일반적으로 feature와 target 간에 선형관계가 있다고 가정하면서 결과를 예측한다. 그래서 선형 회귀 모델의 경우 feature와 target의 정규분포 형태를 아주 선호 한다.

  • 그래서 각 feature와 target 분포가 치우쳐져있는 왜곡된 형태라면 예측에 부정적 영향을 미친다.

  • 따라서 회귀 모델을 적용하기전에 일반적으로 데이터 스케일/정규화 작업을 필수적으로 진행한다.

  • 하지만 이러한 일련의 과정이 꼭 예측 성능 향상에 직결된 것은 아니다.

  • 또 하지만, 심하게 왜곡된 경우에는 이 일련의 과정의 여부가 영향을 미치기도 한다.

  • 그리고 일반적으로 feature값과 target 데이터에 스케일/정규화 작업을 수행하는 방법이 조금 다르다.

  • 보통 feature 데이터에 변환 작업은 다음과 같다.

      1. StandardScaler, MinMaxScaler 등 스케일/정규화 작업 실시
      1. 스케일/정규화 수행한 데이터셋에 다항 특성 등을 적용하여 데이터 변환
    • 2.은 1번 방법에 예측 성능 향상이 없을 경우 보통 사용

      1. 원래 값에 log 함수를 적용하면 보다 정규 분포에 가까운 형태로 분포 시킨다.
      1. 이러한 변환을 로그 변환이라 부른다.
      1. 1,2번 보다 로그 변환이 훨씬 많이 사용되는 변환 방법
      1. 그 이유는 1 방법은 예측 성능 향상에 크게 기대하기 어려움
      1. 2번 방법은 feature의 개수가 기하급수적으로 늘어날 수 있음
  • target 데이터에 변환 작업은 다음과 같다.

    • 일반적으로 로그 변환을 적용한다.
    • target값을 정규분포나 다른 정규값으로 변환하면 변환된 값을 다시 복원하기 힘듦
    • 무엇보다, 타켓 로그 변환을 통해서 예측 성능 향상의 사례가 많이 보고 됨
x_train = train_df.drop(["ID", "target"], axis=1)
# np.log1p는 np.log와 다르다.
# 기본적으로 np.log1p(t) => t = x+1
y_train = np.log1p(train_df["target"].values)

x_test = test_df.drop(["ID"], axis=1)

dev_x, val_x, dev_y, val_y = train_test_split(x_train, y_train, test_size = 0.2, random_state = 2021)

🚩 Build LightGBM Model & Predict

params = {
    'objective' : 'regression',
    'metric' : 'rmse',
    'num_leaves' : 40,
    'learning_rate' : 0.004,
    'bagging_fraction' : 0.6,
    'feature_fraction' : 0.6,
    'bagging_frequnecy' : 6,
    'bagging_seed' : 2021,
    'verbosity' : -1,
    'seed' : 2021
}

lgtrain = lgb.Dataset(dev_x,dev_y)
lgval = lgb.Dataset(val_x,val_y)
evals_result = {}
model = lgb.train(params = params,
                  train_set = lgtrain,
                  num_boost_round = 5000,
                  valid_sets = [lgtrain,lgval],
                  early_stopping_rounds = 100,
                  verbose_eval = 150,
                  evals_result = evals_result)
>Training until validation scores don't improve for 100 rounds.
[150]	training's rmse: 1.5053	valid_1's rmse: 1.5553
[300]	training's rmse: 1.34576	valid_1's rmse: 1.47217
[450]	training's rmse: 1.23447	valid_1's rmse: 1.43298
[600]	training's rmse: 1.15071	valid_1's rmse: 1.4148
[750]	training's rmse: 1.08428	valid_1's rmse: 1.40695
[900]	training's rmse: 1.03057	valid_1's rmse: 1.40509
[1050]	training's rmse: 0.986511	valid_1's rmse: 1.40492
Early stopping, best iteration is:
[972]	training's rmse: 1.00853	valid_1's rmse: 1.40452

# np.log1p()를 다시 되돌리는 exp => np.expm1()
pred_test_y = np.expm1(model.predict(x_test,num_iteration=model.best_iteration))
print("LightGBM Training Completed...")
>LightGBM Training Completed...

# np.log1p()를 다시 되돌리는 exp => np.expm1()
pred_test_y = np.expm1(model.predict(x_test,num_iteration=model.best_iteration))

# feature importance
print("Features Importance...")
gain = model.feature_importance('gain')
featureimp = pd.DataFrame({
    'feature':model.feature_name(),
    'split':model.feature_importance('split'),
    'gain':100*gain/gain.sum()
}).sort_values('gain',ascending=False)
print(featureimp[:50])
>Features Importance...
        feature  split      gain
4130  f190486d6    882  9.849580
2375  58e2e02e6    849  5.811981
4020  15ace8c9f    512  3.433981
3465  eeb9cd3aa    581  2.989733
2614  9fd594eec    355  2.526893
3661  491b9ee45    342  2.146805
3571  58232a6fb    399  1.496084
1457  b43a7cfd5    396  1.309112
834   6eef030c1    342  1.245391
3722  d6bb78916    354  1.120091
8     20aa07010    332  1.115102
...
...
...

🚩 Build XGBoost Model & Predict

params = {
    'objective' : 'reg:linear',
    'eval_metric' : 'rmse',
    'eta' : 0.001,
    'max_depth' : 10,
    'subsample' : 0.6,
    'colsample_bytree' : 0.6,
    'alpha' : 0.001,
    'random_state' : 2021,
    'silent' : True
}

tr_data = xgb.DMatrix(dev_x,dev_y)
va_data = xgb.DMatrix(val_x,val_y)

watchlist = [(tr_data,'train'), (va_data,'valid')]

model_xgb = xgb.train(
    params = params,
    dtrain = tr_data,
    num_boost_round = 2000,
    evals = watchlist,
    maximize = False,
    early_stopping_rounds = 100,
    verbose_eval = 100
)
>[0]	train-rmse:14.0775	valid-rmse:14.1177
Multiple eval metrics have been passed: 'valid-rmse' will be used for early stopping.
Will train until valid-rmse hasn't improved in 100 rounds.
[100]	train-rmse:12.7592	valid-rmse:12.7986
[200]	train-rmse:11.5678	valid-rmse:11.6066
[300]	train-rmse:10.4921	valid-rmse:10.5303
[400]	train-rmse:9.52113	valid-rmse:9.55966
[500]	train-rmse:8.6446	valid-rmse:8.6842
[600]	train-rmse:7.85334	valid-rmse:7.89461
[700]	train-rmse:7.13903	valid-rmse:7.18295
[800]	train-rmse:6.49464	valid-rmse:6.54228
[900]	train-rmse:5.91326	valid-rmse:5.96601
[1000]	train-rmse:5.38919	valid-rmse:5.44703
[1100]	train-rmse:4.91681	valid-rmse:4.98084
[1200]	train-rmse:4.49123	valid-rmse:4.56162
[1300]	train-rmse:4.10815	valid-rmse:4.18668
[1400]	train-rmse:3.76306	valid-rmse:3.85015
[1500]	train-rmse:3.45344	valid-rmse:3.55035
[1600]	train-rmse:3.17498	valid-rmse:3.2822
[1700]	train-rmse:2.92547	valid-rmse:3.04332
[1800]	train-rmse:2.70234	valid-rmse:2.83169
[1900]	train-rmse:2.50294	valid-rmse:2.64422
[1999]	train-rmse:2.3263	valid-rmse:2.48055

dtest = xgb.DMatrix(x_test)
xgb_pred_y = np.expm1(model_xgb.predict(dtest,ntree_limit=model_xgb.best_ntree_limit))
print("XGB Training Completed...")
>XGB Training Completed...
---
### 🚩 Build Catboost Model & Predict
cb_model = CatBoostRegressor(
    iterations = 500,
    learning_rate = 0.05,
    depth = 10,
    eval_metric = 'RMSE',
    random_seed = 2021,
    bagging_temperature = 0.2,
    od_type = 'Iter',
    metric_period = 50,
    od_wait = 20
)

cb_model.fit(
    dev_x,dev_y,
    eval_set = (val_x,val_y),
    use_best_model = True,
    verbose = 50
)
>Warning: Overfitting detector is active, thus evaluation metric is calculated on every iteration. 'metric_period' is ignored for evaluation metric.
0:	learn: 1.7489421	test: 1.7216299	best: 1.7216299 (0)	total: 3.96s	remaining: 32m 55s
50:	learn: 1.4888660	test: 1.5406275	best: 1.5406275 (50)	total: 3m 3s	remaining: 26m 56s
100:	learn: 1.3800745	test: 1.4993093	best: 1.4993093 (100)	total: 6m 3s	remaining: 23m 54s
150:	learn: 1.3099000	test: 1.4801917	best: 1.4800142 (149)	total: 9m 2s	remaining: 20m 53s
200:	learn: 1.2439396	test: 1.4686800	best: 1.4686800 (200)	total: 12m 1s	remaining: 17m 52s
250:	learn: 1.1701638	test: 1.4587038	best: 1.4585832 (249)	total: 15m	remaining: 14m 52s
300:	learn: 1.1084993	test: 1.4524398	best: 1.4524156 (299)	total: 18m	remaining: 11m 54s
350:	learn: 1.0686589	test: 1.4473458	best: 1.4473458 (350)	total: 21m	remaining: 8m 54s
400:	learn: 1.0293033	test: 1.4444994	best: 1.4441743 (398)	total: 24m	remaining: 5m 55s
Stopped by overfitting detector  (20 iterations wait)

bestTest = 1.444174333
bestIteration = 398

Shrink model to first 399 iterations.

pred_test_cat = np.expm1(cb_model.predict(x_test))

🚩 Compare Loss between LightGB vs XGBoost vs Catboost

# Combine Predictions
total_predict = pd.DataFrame()

total_predict["lgb_pred"] = pred_test_y

total_predict["xgb_pred"] = xgb_pred_y

total_predict["cat_pred"] = pred_test_cat

# 각 모델의 loss sum을 해보면, 각 모델의 성능을 비교해볼 수 있다.
# 하지만.. 모델의 파라미터 최적화는 따로 시키지 않았기 때문에 지금의 모델 비교는 약간 의미가 없긴 하다.
# "이렇게 모델을 비교할 수도 있구나" 정도로만 봐주면 좋겠다.
total_predict
>	lgb_pred	xgb_pred	cat_pred
0	2.001453e+06	262068.765625	1.804198e+06
1	1.701793e+06	290694.156250	1.844829e+06
2	1.546757e+06	270940.937500	1.444750e+06
3	4.322790e+06	485094.062500	3.118294e+06
4	1.979771e+06	292952.312500	1.873694e+06
...	...	...	...
49337	1.082067e+06	197993.281250	1.093863e+06
49338	8.161375e+06	888732.500000	6.891485e+06
49339	8.882229e+05	215466.062500	8.839863e+05
49340	5.010181e+05	169397.390625	7.046141e+05
49341	1.939021e+06	293184.843750	1.816532e+06
49342 rows × 3 columns

길면 길고 짧으면 짧았던 3개월간 머신러닝 블로그와 유튜브 강의.. 3개월 동안의 고된 작업은 나 스스로 부족함도 더 많이 일깨워준 기간이었던 것 같다. 그리고 나의 이 일련의 작업들이 많은 분들께 도움이 되었으면 하고, 머신러닝에 대한 흥미와 관심이 더 높아졌으면 한다. 마지막으로 하나의 사진과 인용 글로 "기초부터 쌓아가는 머신러닝" 시리즈를 마치겠다.

" 대부분의 사람들에게 가장 위험한 일은 목표를 너무 높게 잡고 거기에 이르지 못하는 것이 아니라, 목표를 너무 낮게 잡고 거기에 쉽게 도달하는 것이다. - 미켈란젤로 - "

profile
데이터 분석 유튜버 "거친코딩"입니다.

0개의 댓글