# 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']
# 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()
# 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
# 결측치 유무 확인 (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)
선형 회귀 모델의 경우, 일반적으로 feature와 target 간에 선형관계가 있다고 가정하면서 결과를 예측한다. 그래서 선형 회귀 모델의 경우 feature와 target의 정규분포 형태를 아주 선호 한다.
그래서 각 feature와 target 분포가 치우쳐져있는 왜곡된 형태라면 예측에 부정적 영향을 미친다.
따라서 회귀 모델을 적용하기전에 일반적으로 데이터 스케일/정규화 작업을 필수적으로 진행한다.
하지만 이러한 일련의 과정이 꼭 예측 성능 향상에 직결된 것은 아니다.
또 하지만, 심하게 왜곡된 경우에는 이 일련의 과정의 여부가 영향을 미치기도 한다.
그리고 일반적으로 feature값과 target 데이터에 스케일/정규화 작업을 수행하는 방법이 조금 다르다.
보통 feature 데이터에 변환 작업은 다음과 같다.
2.은 1번 방법에 예측 성능 향상이 없을 경우 보통 사용
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)
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 ... ... ...
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))
# 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개월 동안의 고된 작업은 나 스스로 부족함도 더 많이 일깨워준 기간이었던 것 같다. 그리고 나의 이 일련의 작업들이 많은 분들께 도움이 되었으면 하고, 머신러닝에 대한 흥미와 관심이 더 높아졌으면 한다. 마지막으로 하나의 사진과 인용 글로 "기초부터 쌓아가는 머신러닝" 시리즈를 마치겠다.
" 대부분의 사람들에게 가장 위험한 일은 목표를 너무 높게 잡고 거기에 이르지 못하는 것이 아니라, 목표를 너무 낮게 잡고 거기에 쉽게 도달하는 것이다. - 미켈란젤로 - "