본문 바로가기
공부/머신러닝

Ridge regression (릿지 회귀) python

by signature95 2022. 2. 22.
728x90
반응형

릿지 회귀분석은 선형회귀분석의 과대적합 문제를 해소하기 위해 L2 규제를 적용하는 방식을 사용합니다.

 

과대 적합은 다음과 같은 표로 해석할 수 있습니다.

전체 Error는 분산과 편향의 제곱 합으로 표시할 수 있습니다. 

즉 전체 Error를 최소화하는 회귀분석이 Least Square Method, 즉 OLS인 것입니다.

 

밑에 식을 한번 보겠습니다.

 

  • N은 데이터의 개수입니다. 만약 1000개의 샘플데이터가 있다면 N=1000이 되는 것이죠.
  • P는 feature의 개수입니다. 단순선형회귀인 경우에는 P=1이 되고 다중회귀에서는 P가 2이상인 값을 가집니다.
  • y는 실제 target의 값입니다.
  • ß는 가중치로서 OLS의 feature 계수라고 보시면 됩니다.
  • 람다는 ridge 모형의 하이퍼파라미터로 alpha값을 의미합니다. (릿지 회귀분석에서는 알파를 사용자가 임의로 설정해야 합니다)
  • 첫째 항은 편차의 제곱합을 의미합니다. 이는 OLS에서 feature 계수 ß를 도출하는 식입니다.
  • 둘째 항이 의미하는 것이 L2 규제입니다. 

이를 시각적으로 표현하면 다음과 같습니다.

만약 ß1, ß2를 추정할 경우, 일반적인 OLS는 초록색 원반을 기준으로 합니다.

하지만 L2규제를 적용하면, ß의 제곱합으로 하는 특성으로 인해 원형으로 이를 그릴 수 있습니다. 

따라서 이 L2규제와 OLS 추정으로 도출한 도형에서의 접점이 곧 ß1, ß2가 되는 것입니다. 

 

참고로 원래 OLS의 경우, 첫째 항의 값을 최소화하는 과정으로 회귀식을 적합시키고 ß를 도출합니다. 

하지만, 이 방법을 사용하는 경우, feature가 많아질수록 OLS의 결정계수는 높아지지만, 결국 개별 변수의 p값은 유의하지 않는 다중공선성 문제를 발생시키기도 합니다. 

그렇기에 다중선형회귀분석을 시행할때는 다중공선성 문제를 해결하기위해 VIF로 변수간 다중공선성을 유발하는 feature를 제거하기도 합니다.

 

VIF에 대해서는 다음 포스트를 참고하시면 됩니다.

2022.01.11 - [공부/통계학] - VIF (분산확장요인, python)

 

VIF (분산확장요인, python)

Feature selection 방법은 크게 3가지로 나뉜다. Filter Method (Feature간 상관성 기반) Wrapper Method (Feature를 조정하며 모형을 형성하고 예측 성능을 참고하여 Feature 선택) Embedded Method (예측 모형..

signature95.tistory.com

 

하지만 Ridge는 ß의 절대값 합에 람다(alpha)를 곱하고 편차제곱합과 더하여 전체 COST를 구하게 됩니다.

따라서 alpha=0일 때는 ridge 결과가 OLS와 동일하게 됩니다.

 

Ridge의 문제는 회귀계수가 0에 근접할 뿐, 결국 0으로 수렴하진 않습니다. 따라서 alpha를 아무리 늘려도 계수의 절대값이 감소하지만 0으로 되지 않기에 Shrinkage method라고 불립니다.

 

그럼 코드로 한번 이를 살펴보겠습니다.

 

데이터는 Sklearn의 데이터셋인 보스턴 집값을 사용하였습니다.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 한글폰트 출력 가능 코드
from matplotlib import rc
rc('font', family='AppleGothic') # Mac Os
#rc('font', family='NanumGothic') # Windows Os
plt.rcParams['axes.unicode_minus'] = False

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error


# 데이터셋 불러오기
X,y = load_boston(return_X_y=True)

# train, test 데이터 구분
X_train, X_test, y_train, y_test = train_test_split(X,y, random_state=2022)\

# 릿지 회귀분석 시행 (alpha의 기본값은 1로 설정되어 있습니다.)
ridge = Ridge().fit(X_train,y_train)
y_pred = ridge.predict(X_test)

print(f'훈련 데이터 점수 : {ridge.score(X_train, y_train) : .4f}')
print(f'검증 데이터 점수 : {ridge.score(X_test, y_test) : .4f}')
print(f'MSE : {mean_squared_error(y_test, y_pred) : .4f}')

>>>

훈련 데이터 점수 :  0.7428
검증 데이터 점수 :  0.7010
MSE :  24.3200

 

(참고)

여기서 Ridge의 설명을 보면 다음과 같이 설명합니다.

Linear least squares with l2 regularization.

Minimizes the objective function::

||y - Xw||^2_2 + alpha * ||w||^2_2

This model solves a regression model where the loss function is
the linear least squares function and regularization is given by
the l2-norm. Also known as Ridge Regression or Tikhonov regularization.
This estimator has built-in support for multi-variate regression
(i.e., when y is a 2d-array of shape (n_samples, n_targets)).

Read more in the :ref:`User Guide <ridge_regression>`.

 

이를 좀더 구체화하기 위해 함수를 형성하였습니다.

# 함수화 작업
def ridge_regression(data, target_col, plot=True):
    # 라이브러리 호출 
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.metrics import mean_squared_error
    from sklearn.linear_model import Ridge
    from sklearn.model_selection import train_test_split

    # feature, target 분리
    feature = data.drop(columns=target_col)
    target = data[target_col]

    # 훈련, 검정셋 분리
    X_train, X_test, Y_train, Y_test = train_test_split(feature, target, test_size = 0.2, random_state = 2022)

    # 빈 리스트 셍성
    train_score = []
    test_score = []
    r_squared = []
    intercept = []
    mse_train = []
    mse_test = []
    # 하이퍼파라미터 적용
    alpha_list = [0.0001, 0.001, 0.01, 0.1, 1, 10, 100]
    # 라소 회귀 요약 데이터 형성
    ridge_data = pd.DataFrame()

    for alpha in alpha_list:
        # 라소 회귀 시행
        ridge = Ridge(alpha= alpha, max_iter = 10000).fit(X_train, Y_train)
        # 결정계수 및 절편 리스트
        train_score.append(np.round(ridge.score(X_train, Y_train), 4))
        test_score.append(np.round(ridge.score(X_test, Y_test), 4))
        intercept.append(np.round(ridge.intercept_, 4))
        # 라소 회귀에서 도출된 각 feature의 계수
        ridge_data = pd.concat([ridge_data, pd.DataFrame(np.round(ridge.coef_, 4), ridge.feature_names_in_)], axis=1)
        # 훈련, 검정셋에서 y 값 예측 (기댓값 도출)
        Y_pred_train = ridge.predict(X_train)
        Y_pred = ridge.predict(X_test)
        # 훈련, 검정셋에서 MSE 도출 
        mse_train.append(np.round(mean_squared_error(Y_train,Y_pred_train),4))
        mse_test.append(np.round(mean_squared_error(Y_test,Y_pred),4))
        
        # 도식화 여부
        if plot == True:
            plt.subplot(2,1,1)
            plt.scatter(Y_train,Y_pred_train, label = '(실제값, 예측값)', alpha=.5)
            plt.plot(np.linspace(0,50,200), np.linspace(0,50,200), color = 'green', label = "45°(실제값 = 예측값)")
            plt.xlabel("실제 Price: $Y_i$")
            plt.ylabel("예측 Price : $\hat{Y}_i$")
            plt.title(f'ridge regression when a = {alpha}')
            plt.legend()
            plt.text(y=5,x=30, s=f'Train MSE 값 : {mean_squared_error(Y_train,Y_pred_train) : .4f}\nTrain Score 값 : {ridge.score(X_train,Y_train) : .4f}')
            plt.subplot(2,1,2)
            plt.scatter(Y_test,Y_pred, label = '(실제값, 예측값)', alpha=.5)
            plt.plot(np.linspace(0,50,200), np.linspace(0,50,200), color = 'green', label = "45°(실제값 = 예측값)")
            plt.xlabel("실제 Price: $Y_i$")
            plt.ylabel("예측 Price: $\hat{Y}_i$")
            plt.legend(loc='upper left')
            plt.text(y=5,x=30, s=f'Test MSE 값 : {mean_squared_error(Y_test,Y_pred) : .4f}\nTest Score 값 : {ridge.score(X_test,Y_test) : .4f}')
            plt.show()
        else:
            pass
    
    # 결정계수 출력
    plt.plot(np.log10(alpha_list), train_score, label ='$R^2$ of train')
    plt.plot(np.log10(alpha_list), test_score, label ='$R^2$ of test')
    plt.title('$R^2$ of ridge regreesion')
    plt.xlabel('log value of alpha')
    plt.ylabel('R-square')
    plt.legend()
    plt.show()
    
    # 라소 회귀 요약 데이터 형성
    ridge_data.columns = [f'alpha = {i}' for i in alpha_list]
    score = pd.DataFrame({'R^2 of Train' : train_score,
                        'R^2 of Test' : test_score,
                        'intercept' : intercept,
                        'mse_train' : mse_train,
                        'mse_test' : mse_test}).transpose()
    score.columns= [f'alpha = {i}' for i in alpha_list]
    ridge_summary = pd.concat([ridge_data, score], axis=0)

    return ridge_summary

# 데이터는 pd.DataFrame, concat을 사용하여 따로 data를 만들어 주었음
subx = pd.DataFrame(X, columns=load_boston().feature_names)
suby = pd.DataFrame(y, columns=['target'])

data = pd.concat([subx,suby], axis=1)

# 위의 함수 시행
ridge_regression(data,'target')

결과 출력

              alpha = 0.0001  alpha = 0.001  alpha = 0.01  alpha = 0.1  \
CRIM                 -0.1094        -0.1094       -0.1093      -0.1089   
ZN                    0.0418         0.0418        0.0418       0.0420   
INDUS                 0.0492         0.0492        0.0488       0.0452   
CHAS                  2.7548         2.7546        2.7527       2.7344   
NOX                 -13.7415       -13.7318      -13.6355     -12.7426   
RM                    4.7124         4.7125        4.7129       4.7163   
AGE                  -0.0120        -0.0120       -0.0121      -0.0128   
DIS                  -1.4352        -1.4351       -1.4337      -1.4207   
RAD                   0.2987         0.2987        0.2985       0.2967   
TAX                  -0.0124        -0.0124       -0.0124      -0.0125   
PTRATIO              -0.8955        -0.8954       -0.8944      -0.8854   
B                     0.0103         0.0103        0.0104       0.0104   
LSTAT                -0.5211        -0.5212       -0.5213      -0.5231   
R^2 of Train          0.7561         0.7561        0.7561       0.7560   
R^2 of Test           0.6226         0.6226        0.6224       0.6209   
intercept            27.6143        27.6079       27.5442      26.9563   
mse_train            21.0580        21.0580       21.0580      21.0612   
mse_test             26.9846        26.9858       26.9970      27.1026   

              alpha = 1  alpha = 10  alpha = 100  
CRIM            -0.1069     -0.1049      -0.1026  
ZN               0.0431      0.0460       0.0526  
INDUS            0.0252      0.0002      -0.0147  
CHAS             2.5936      1.8878       0.5502  
NOX             -7.6989     -1.5490      -0.1629  
RM               4.7167      4.3718       2.4276  
AGE             -0.0166     -0.0181      -0.0004  
DIS             -1.3480     -1.2663      -1.1861  
RAD              0.2878      0.2904       0.3460  
TAX             -0.0130     -0.0142      -0.0166  
PTRATIO         -0.8355     -0.7987      -0.8718  
B                0.0106      0.0108       0.0100  
LSTAT           -0.5344     -0.5741      -0.7169  
R^2 of Train     0.7547      0.7497       0.7299  
R^2 of Test      0.6120      0.6081       0.6352  
intercept       23.7988     22.8655      36.8806  
mse_train       21.1746     21.6094      23.3180  
mse_test        27.7380     28.0156      26.0791

확인해보면, 알파값이 증가할수록 L2규제가 더 많이 적용되므로 ß값이 점차 0으로 감소하는 것을 확인할 수 있습니다.

하지만, ß는 0으로 수렴하진 않기에 feature가 target에 미치는 영향을 줄여주지만 계수간 상관성은 존재할 수 있다는 것이 문제입니다.

따라서 많은 사람들이 feature selection까지 같이 수행할 수 있는 Lasso 회귀를 더 많이 사용하는 추세입니다.

728x90

'공부 > 머신러닝' 카테고리의 다른 글

Lasso Feature Selection (Embedded method) python  (0) 2022.04.19
모델링 공부  (0) 2021.11.14

댓글