다항회귀에서 degree1의 경우 지나치게 예측 곡선을 단순화해 과소적합 모델이 만들어졌고, degree15의 경우 지나치게 모든 학습 데이터에 적합한 회귀식을 만들기 위해서 다항식이 복잡해지고 회귀계수가 매우 크게 설정되면서 테스트 데이터 세트에 대해서 형편없는 예측 성능을 보이는 과적합 모델이 만들어졌다.

→ 회귀모델은 적절히 데이터에 적합하면서도 회귀계수가 기하급수적으로 커지는 것을 제어해야 함

 

 

이전까지 선형 모델의 비용함수는 RSS를 최소화하는, 즉 실제값과 예측값의 차이를 최소화 하는 것만 고려해 학습 데이터에 지나치게 맞춰지고 회귀계수가 쉽게 커져 과적합이 발생하는 경향이 있음

→ 위의 문제를 해결하기 위해 비용함수는 RSS 최소화 방법과 과적합을 방지하기 위해 회귀계수 값이 커지지 않도록 하는 방법이 서로 균형을 이루어야 함(학습 데이터의 잔차 오류와 회귀계수의 값이 함께 최소화하는 것을 목표로 함)

alpha는 학습 데이터 적합 정도와 회귀계수 값의 크기를 제어하는 파라미터로, 적절하게 설정해야한다.

○ alpha가 0(또는 매우 작은 값)이라면 Min(RSS(W) + 0)이 되면서 기존과 동일하게 RSS를 최소화하는 것만 고려하는 것과 다를게 없고

○ alpha가 무한대(또는 매우 큰 값)이라면 RSS(W)에 비해 alpha * llWll22 값이 너무 커지게 되므로

즉, alpha를 작게 하면 회귀계수 W의 값이 커져도 어느 정도 상쇄가 가능하므로 학습 데이터 적합을 더 개선할 수 있고, alpha를 크게 하면 회귀계수 W의 값을 작게 해 과적합을 개선할 수 있음

 

이처럼 비용함수에 alpha 값으로 페널티를 부여해 회귀계수 값의 크기를 감소시켜 과적합을 개선하는 방식을 규제(Regularization)라고 함


릿지 회귀(L2 Norm)

W의 제곱에 대해 페널티를 부여하는 방식. 회귀계수의 크기를 감소시키는 규제 모델

비용함수는

이고, 이 식을 최소화하는 W를 찾는 것이 목표이다.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.datasets import load_boston
%matplotlib inline

boston = load_boston()

bostonDF = pd.DataFrame(boston.data, columns=boston.feature_names)

bostonDF['PRICE'] = boston.target

y_target = bostonDF['PRICE']
X_features = bostonDF.drop(['PRICE'], axis=1, inplace=False)
from sklearn.linear_model import Ridge
from sklearn.model_selection import cross_val_score

# alpha=10으로 설정해 릿지 회귀 수행
ridge = Ridge(alpha=10)
mse_scores = -cross_val_score(ridge, X_features, y_target, scoring='neg_mean_squared_error', cv=5)
rmse_scores = np.sqrt(mse_scores)
avg_rmse = np.mean(rmse_scores)

print('5fold의 개별 mse scores:', np.round(mse_scores, 3))
print('5fold의 개별 rmse scores:', np.round(rmse_scores, 3))
print('5fold의 평균 rmse:{0:.3f}'.format(avg_rmse))

alpha 값에 따른 rmse와 회귀계수를 살펴보자

alphas = [0, 0.1, 1, 10, 100]

for alpha in alphas:
    ridge = Ridge(alpha=alpha)
    
    # cross_val_score를 이용해 5 fold의 평균 rmse 계산
    mse_scores = -cross_val_score(ridge, X_features, y_target, scoring='neg_mean_squared_error', cv=5)
    avg_rmse = np.mean(np.sqrt(mse_scores))
    print('alpha {0} 일 때 5 folds의 평균 rmse : {1:.3f}'.format(alpha, avg_rmse))

# alpha 값에 따른 회귀계수 시각화
fig, axs = plt.subplots(figsize=(18,6), nrows=1, ncols=5)
# alpha 값에 따른 회귀계수를 데이터로 저장하기 위한 DataFrame 생성
coeff_df = pd.DataFrame()

# alphas 리스트 값을 차례로 입력해 회귀계수 시각화 및 데이터 저장. pos는 axis의 위치 지정
for pos, alpha in enumerate(alphas):
    ridge = Ridge(alpha=alpha)
    ridge.fit(X_features, y_target)
    # alpha에 따른 피처별로 회귀계수를 Series로 변환하고 이를 DataFrame의 컬럼으로 추가
    coeff = pd.Series(ridge.coef_, index=X_features.columns)
    colname = 'alpha:'+str(alpha)
    coeff_df[colname] = coeff
    # 막대그래프로 alpha 값에 따른 회귀계수 시각화. 내림차순
    coeff = coeff.sort_values(ascending=False)
    axs[pos].set_title(colname)
    axs[pos].set_xlim(-3,6)
    sns.barplot(x=coeff.values, y=coeff.index, ax=axs[pos])
    
plt.show()

coeff_df.sort_values(by='alpha:0', ascending=False)

릿지 회귀는 alpha 값이 커질수록 회귀계수 값을 작게 만든다

 

라쏘 회귀(L1 Norm)

W의 절대값에 대해 페널티를 부여하는 방식. 불필요한 회귀계수를 급격하게 감소시켜 0으로 만드는 규제 모델

→ 이러한 측면에서 적절한 피처만 회귀에 포함시키는 피처 선택의 특성을 가지고 있음

비용함수는

이고, 이 식을 최소화하는 W를 찾는 것이 목표이다.

 

alpha 값에 따른 rmse와 회귀계수를 살펴보자. (alpha 값을 변화시키면서 결과를 출력하는 별도의 함수를 만들것)

from sklearn.linear_model import Lasso, ElasticNet

# alpha 값에 따른 회귀모델의 폴드 평균 rmse를 출력, 회귀계수 값들을 DataFrame으로 반환
def get_linear_reg_eval(model_name, params=None, X_features_n=None, y_target_n=None,
                       verbose=True, return_coeff=True):
    coeff_df = pd.DataFrame() # 빈 데이터프레임 생성
    if verbose : print('#######', model_name, '#######')
    for param in params:
        if model_name == 'Ridge':
            model = Ridge(alpha=param)
        elif model_name == 'Lasso':
            model = Lasso(alpha=param)
        elif model_name == 'ElasticNet':
            model = ElasticNet(alpha=param, l1_ratio=0.7) # l1_ratio는 0.7로 고정시킴
        
        mse_scores = -cross_val_score(model, X_features_n, y_target_n, scoring='neg_mean_squared_error', cv=5)
        avg_rmse = np.mean(np.sqrt(mse_scores))
        print('alpha {0}일 때 5 폴드 세트의 평균 rmse:{1:.3f}'.format(param, avg_rmse))
        
        # cross_val_score는 evaluation metric만 반환하므로 모델을 다시 학습하여 회귀계수 추출
        model.fit(X_features_n, y_target_n)
        if return_coeff:
            # alpha에 따른 피처별 회귀계수를 Series로 변환하고 이를 DataFrame의 컬럼으로 추가
            coeff = pd.Series(model.coef_, index=X_features_n.columns)
            colname = 'alpha:'+str(param)
            coeff_df[colname] = coeff
            
    return coeff_df
# 라쏘에 사용될 alpha 파라미터의 값을 정의하고 get_linear_reg_eval() 함수 호출
lasso_alphas = [0.07, 0.1, 0.5, 1, 3]
coeff_lasso_df = get_linear_reg_eval('Lasso', params=lasso_alphas, X_features_n=X_features, y_target_n=y_target)

# 반환된 coeff_lasso_df를 첫 번째 컬럼순으로 내림차순 정렬해 회귀계수 DataFrame 출력
coeff_lasso_df.sort_values(by='alpha:'+str(lasso_alphas[0]), ascending=False) # by='alpha:0.07'

NOX 피처는 alpha가 0.07일 때부터 회귀계수가 0이다.

라쏘 회귀는 alpha의 크기가 증가함에 따라 일부 피처의 회귀계수는 아예 0으로 바뀌고 있다. 회귀계수가 0인 피처는 회귀식에서 제외되면서 피처 선택의 효과를 얻을 수 있다.

 

엘라스틱넷 회귀(L2 Norm+L1 Norm)

L2 규제와 L1 규제를 결합한 회귀.

○ 엘라스틱넷은 라쏘 회귀가 서로 상관관계가 높은 피처들의 경우에 이들 중에서 중요 피처만을 셀렉션하고 다른 피처들은 모두 회귀계수를 0으로 만드는 성향이 강하다. 특히 이러한 성향으로 인해 alpha값에 따라 회귀계수의 값이 급격히 변동할 수도 있는데, 엘라스틱넷 회귀는 이를 완화하기 위해 L2 규제를 라쏘 회귀에 추가한 것

○ 주요 생성 파라미터는 alpha, l1_ratio

비용함수는

이고, 이 식을 최소화하는 W를 찾는 것이 목표이다.

 

alpha 값에 따른 rmse와 회귀계수를 살펴보자.(코드에 l1_ratio를 0.7로 고정시켰는데, 이는 단순히 alpha값의 변화만 살피기 위해 미리 고정했음)

# 엘라스틱넷에 사용될 alpha 파라미터의 값들을 정의하고 get_linear_reg_eval() 함수 호출
# l1_ratio는 0.7로 고정
elastic_alphas = [0.07, 0.1, 0.5, 1, 3]
coeff_elastic_df = get_linear_reg_eval('ElasticNet', params=elastic_alphas, X_features_n=X_features, y_target_n=y_target)

# 반환된 coeff_elastic_df를 첫 번째 컬럼순으로 내림차순 정렬해 회귀계수 DataFrame 출력
coeff_elastic_df.sort_values(by='alpha:'+str(elastic_alphas[0]), ascending=False) # by='alpha:0.07'

alpha값에 따른 피처들의 회귀계수들 값이 라쏘보다는 상대적으로 0이 되는 값이 적음을 알 수 있다.

 

 

 

Reference)

https://dsbook.tistory.com/206

+ Recent posts