Data Science/Pandas & Numpy&Scikit-learn

회귀모델에서 타겟(y)값의 정규화 방법 비교 실험

상어군 2022. 5. 27. 23:26
반응형

선형회귀에서는 타겟(y)값이 정규분포라는 가정을 한다.

문득, 실제로 모델에서 어떠한 성능적 영향을 미치는지 궁금증이 나타났다.

또한 트리기반의 모델에서는 타겟값의 정규분포가 영향을 미치는지 같이 실험을 진행한다.

 

※ 본 실험은 수학적 분석 기반이 아닌, 라이브러리를 활용한 단순 실험임을 밝힙니다.

    여러 지적사항 및 의견이 있으신분은 편하게 댓글로 말씀해주시기 바랍니다.

 

0. 적용 정규화 방법

1. Raw data

2. MinMaxScaler

3. StandardScaler

4. RobustScaler

5. Log Transfer

6. Log Transfer + StandardScaler

7. Boxcox Transfer

8. Boxcox Transfer + StandardScaler

 

1. 사용 데이터

오늘 사용할 데이터는 보스턴 지역의 집값 데이터로 다운로드 주소는 아래와 같다.

https://www.kaggle.com/competitions/house-prices-advanced-regression-techniques/data

 

House Prices - Advanced Regression Techniques | Kaggle

 

www.kaggle.com

 

2. 정규화 별 분포 그래프

타겟(y) 데이터는 약 0~800000 사이에 분포함을 알 수 있다.

MinMaxScaler, StandardScaler, RobustScaler 방법의 경우 분포(왜도,첨도)는 변하지 않고

범위에 대한 Scaling이 이루어진 것을 확인 할 수 있다.

Log Transfer, Boxcox Transfer의 경우 분포가 정규분포에 근사한 것을 확인 할 수 있다.

Log Transfer + StandardScaler, Boxcox Transfer + StandardScaler의 경우

각각 Transfer를 적용한 후 StandardScaler를 적용하였다.

 

3. 비교 실험

피처(x)는 수치형 값만 사용하였으며 StandardScaler를 적용하였다.

타겟(y)는 각 정규화를 적용하였다.

실험은 선형모델인 Multiple Linear Regression과 트리기반 모델인 XGBoost 모델을 기반으로 진행된다.

동일한 스케일을 통해 성능비교를 하기 위해서 각 산출된 예측결과(y')은 raw data로 역정규화를 진행했다.

역정규화된 예측결과를 R2와 RMSE를 통해 비교평가한다.

3-1. Multiple Linear Regression 예측 결과

정규화 방법 R2 RMSE
Normal (Raw Data) 0.8591 28633.8319
MinMaxScaler 0.859 28637.966
StandardScaler 0.859 28640.3746
RobustScaler 0.859 28646.3154
Log Transfer 0.8822 26180.119
Log Transfer + StandardScaler 0.8823 26168.4911
Boxcox Transfer 0.8815 26263.2542
Boxcox Transfer + StandardScaler -219.6484 1133053.942

3-2. XGBoost 예측 결과

정규화 방법 R2 RMSE
Normal (Raw Data) 0.9105 22813.6360
MinMaxScaler 0.9119 22643.7367
StandardScaler 0.9136 22416.1819
RobustScaler 0.9112 22725.5135
Log Transfer 0.9102 22852.5051
Log Transfer + StandardScaler 0.9127 22532.4878
Boxcox Transfer 0.9116 22684.0413
Boxcox Transfer + StandardScaler -219.6485 1133053.942

3-3. 결과해석

선형모델에서는 Scaler 방법들이 별다른 성능을 보여주지 못하였다.

반면 Transfer 모델의 경우 유의미한 성능향상을 보였다.

따라서 선형모델에서 타겟이 정규분포에 근사함이 유의미한 성능차이를 가져옴을 확인했다.

 

트리기반 모델에서는 유의미한 성능차이를 가져오지 못하였다.

 

Boxcox Transfer + StandardScaler 방법은 모든 실험에서 예측을 전혀 하지 못하는 결과를 가져왔다.

이는 어떠한 이유로 해당 현상이 발생했는지 추가적으로 공부하여 포스팅하겠다.

 

4. 코드

from google.colab import drive
drive.mount('/content/drive')

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler, StandardScaler, RobustScaler
from scipy import stats
from scipy.special import boxcox, inv_boxcox
from sklearn.linear_model import LinearRegression
from xgboost import XGBRegressor
from sklearn.metrics import r2_score
from sklearn.metrics import mean_squared_error
import warnings

warnings.filterwarnings('ignore')

###### 데이터 로드
path = "./drive/MyDrive/ColabNotebooks/"
df = pd.read_csv(path+"house_prices_train.csv")

df = df.sample(frac=1).reset_index(drop=True)

train = df.iloc[:1200]
test = df.iloc[1200:].reset_index(drop=True)

###### 피처 및 타겟 선정
x_col = [x for x in train.columns[1:] if (type(train[x][0]) != str) and (not pd.isna(train[x][0]))]
y_col = "SalePrice"

###### 기본 전처리
train_x = train[x_col]
test_x = test[x_col]

# Nan 값 제거
train_x = train_x.dropna()
test_x = test_x.dropna()

# Y 값 추출
train_y_normal = train_x[y_col].tolist()
test_y_normal = test_x[y_col].tolist()

# X 값에서 Y 값 제거
train_x.drop([y_col], axis=1, inplace=True)
test_x.drop([y_col], axis=1, inplace=True)

# X변수는 모두 STD 스케일링 적용
sc = StandardScaler()
train_x.loc[:,:] = sc.fit_transform(train_x)
test_x.loc[:,:] = sc.transform(test_x)

##### 타겟에 정규화 방법 적용
# normal
#train_y_normal

# MinMaxScaler
sc_mm = MinMaxScaler()
train_y_mm = sc_mm.fit_transform(pd.DataFrame(train_y_normal))

# StandardScaler
sc_std = StandardScaler()
train_y_std = sc_std.fit_transform(pd.DataFrame(train_y_normal))

# RobustScaler
sc_robust = RobustScaler()
train_y_robust = sc_robust.fit_transform(pd.DataFrame(train_y_normal))

# log Transfer
#sc_std_log = StandardScaler()
train_y_log = np.log1p(train_y_normal)
#train_y_log = sc_std_log.fit_transform(pd.DataFrame(train_y_log))

# log Transfer + StandardScaler
sc_log_std = StandardScaler()
train_y_log_std = np.log1p(train_y_normal)
train_y_log_std = sc_log_std.fit_transform(pd.DataFrame(train_y_log_std))

# BoxCox Transfer
train_y_boxcox, optimal_lambda_Bc = stats.boxcox(train_y_normal)

# BoxCox Transfer
sc_boxcox_std = StandardScaler()
train_y_boxcox_std, optimal_lambda_BcStd = stats.boxcox(train_y_normal)
train_y_boxcox_std = sc_boxcox_std.fit_transform(pd.DataFrame(train_y_boxcox_std))

##### 분포그래프 생성
fig, ax = plt.subplots(ncols=2, nrows=4, figsize=(15,15))

sns.distplot(train_y_normal, ax=ax[0,0])
sns.distplot(train_y_mm, ax=ax[0,1])
sns.distplot(train_y_std, ax=ax[1,0])
sns.distplot(train_y_robust, ax=ax[1,1])
sns.distplot(train_y_log, ax=ax[2,0])
sns.distplot(train_y_log_std, ax=ax[2,1])
sns.distplot(train_y_boxcox, ax=ax[3,0])
sns.distplot(train_y_boxcox_std, ax=ax[3,1])

ax[0,0].set_title("Normal")
ax[0,1].set_title("MinMax")
ax[1,0].set_title("STD")
ax[1,1].set_title("Robust")
ax[2,0].set_title("Log")
ax[2,1].set_title("Log + STD")
ax[3,0].set_title("Boxcox")
ax[3,1].set_title("Boxcox + STD")

##### MLR 실험
# Normal
mlr_normal = LinearRegression()
mlr_normal.fit(train_x,train_y_normal)
pred_y = mlr_normal.predict(test_x)

print("Normal : " ,r2_score(test_y_normal, pred_y),
      '\t',mean_squared_error(test_y_normal, pred_y)**0.5)

# MinMax
mlr_mm = LinearRegression()
mlr_mm.fit(train_x,train_y_mm)
pred_y = mlr_mm.predict(test_x)
y_pred_inverse = sc_mm.inverse_transform(pd.DataFrame(pred_y))

print("MinMax : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# STD
mlr_std = LinearRegression()
mlr_std.fit(train_x,train_y_std)
pred_y = mlr_std.predict(test_x)
y_pred_inverse = sc_std.inverse_transform(pd.DataFrame(pred_y))

print("S T D : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# Robust
mlr_robust = LinearRegression()
mlr_robust.fit(train_x,train_y_robust)
pred_y = mlr_robust.predict(test_x)
y_pred_inverse = sc_robust.inverse_transform(pd.DataFrame(pred_y))

print("Robust : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# Log
mlr_log = LinearRegression()
mlr_log.fit(train_x,train_y_log)
pred_y = mlr_log.predict(test_x)
y_pred_inverse = np.expm1(pred_y)

print("L o g : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# Log + STD
mlr_log_std = LinearRegression()
mlr_log_std.fit(train_x,train_y_log_std)
pred_y = mlr_log_std.predict(test_x)
y_pred_inverse = sc_log_std.inverse_transform(pd.DataFrame(pred_y))
y_pred_inverse = np.expm1(y_pred_inverse)

print("LogStd : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# Boxcox
mlr_boxcox = LinearRegression()
mlr_boxcox.fit(train_x,train_y_boxcox)
pred_y = mlr_boxcox.predict(test_x)
y_pred_inverse = inv_boxcox(pred_y,optimal_lambda_Bc)

print("Boxcox : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# Boxcox + STD
mlr_boxcox_std = LinearRegression()
mlr_boxcox_std.fit(train_x,train_y_boxcox)
pred_y = mlr_boxcox_std.predict(test_x)
y_pred_inverse = sc_boxcox_std.inverse_transform(pd.DataFrame(pred_y))
y_pred_inverse = inv_boxcox(y_pred_inverse,optimal_lambda_BcStd)

print("Boxcox : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)
      
##### XGB 실험
# Normal
xgb_normal = XGBRegressor(objective='reg:squarederror', n_estimators=100)
xgb_normal.fit(train_x,train_y_normal)
pred_y = xgb_normal.predict(test_x)

print("Normal : " ,r2_score(test_y_normal, pred_y),
      '\t',mean_squared_error(test_y_normal, pred_y)**0.5)

# MinMax
xgb_mm = XGBRegressor(objective='reg:squarederror', n_estimators=100)
xgb_mm.fit(train_x,train_y_mm)
pred_y = xgb_mm.predict(test_x)
y_pred_inverse = sc_mm.inverse_transform(pd.DataFrame(pred_y))

print("MinMax : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# STD
xgb_std = XGBRegressor(objective='reg:squarederror', n_estimators=100)
xgb_std.fit(train_x,train_y_std)
pred_y = xgb_std.predict(test_x)
y_pred_inverse = sc_std.inverse_transform(pd.DataFrame(pred_y))

print("S T D : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# Robust
xgb_robust = XGBRegressor(objective='reg:squarederror', n_estimators=100)
xgb_robust.fit(train_x,train_y_robust)
pred_y = xgb_robust.predict(test_x)
y_pred_inverse = sc_robust.inverse_transform(pd.DataFrame(pred_y))

print("Robust : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# Log
xgb_log = XGBRegressor(objective='reg:squarederror', n_estimators=100)
xgb_log.fit(train_x,train_y_log)
pred_y = xgb_log.predict(test_x)
y_pred_inverse = np.expm1(pred_y)

print("L o g : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# Log + STD
xgb_log_std = XGBRegressor(objective='reg:squarederror', n_estimators=100)
xgb_log_std.fit(train_x,train_y_log_std)
pred_y = xgb_log_std.predict(test_x)
y_pred_inverse = sc_log_std.inverse_transform(pd.DataFrame(pred_y))
y_pred_inverse = np.expm1(y_pred_inverse)

print("LogStd : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# Boxcox
xgb_boxcox = XGBRegressor(objective='reg:squarederror', n_estimators=100)
xgb_boxcox.fit(train_x,train_y_boxcox)
pred_y = xgb_boxcox.predict(test_x)
y_pred_inverse = inv_boxcox(pred_y,optimal_lambda_Bc)

print("Boxcox : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

# Boxcox + STD
xgb_boxcox_std = LinearRegression()
xgb_boxcox_std.fit(train_x,train_y_boxcox)
pred_y = xgb_boxcox_std.predict(test_x)
y_pred_inverse = sc_boxcox_std.inverse_transform(pd.DataFrame(pred_y))
y_pred_inverse = inv_boxcox(y_pred_inverse,optimal_lambda_BcStd)

print("Boxcox : " ,r2_score(test_y_normal, y_pred_inverse),
      '\t',mean_squared_error(test_y_normal, y_pred_inverse)**0.5)

 

마치며

포스팅에 대한 피드백과 의견 그리고 질문은 언제나 환영합니다.

감사합니다.

반응형