ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 회귀모델에서 타겟(y)값의 정규화 방법 비교 실험
    Data Science/Pandas & Numpy&Scikit-learn 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)

     

    마치며

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

    감사합니다.

    반응형

    댓글

Designed by Tistory.