💡규제 선형 모델
- 회귀 모델은 적절하게 데이터에 적합하면서도 회귀 계수가 기하급수적으로 커지는 것을 제어할 수 있어야한다. 이전까지는 선형 모델의 비용 함수는 실제 값과 예측 값의 차이를 최소화하는 것만 고려하였다. 그러다보니 과적합이 발생하여 회귀 계수가 쉽게 커졌다.
- 비용함수는 학습 데이터의 잔차 오류값을 최소로 하는 RSS 최소화 방법과 과적합을 방지하기 위해 회귀 계수 값이 커지지 않도록 하는 방법이 서로 균형을 이뤄야 한다.
- 비용 함수 목표 식
- alpha값을 크게하면 비용함수는 회귀 계수 W의 값을 작게 해 과적합을 개선할 수 있고,
- alpha값을 작게하면 회귀 계수 W의 값이 커져도 어느 정도 상쇄가 가능하므로 학습 데이터 적합을 더 개선할 수 있다.
즉, alpha를 0에서부터 지속적으로 값을 증가시키면 회귀 계수 값의 크기를 감소시킬 수 있다.
이처럼 비용함수에 alpha값으로 페널티를 부여해 회귀 계수 값의 크기를 감소시켜 과적합을 개선하는 방식을 규제(Regularization)라고 부른다.
규제는 크게 L2방식과 L1방식으로 구분된다.
1. 릿지 회귀 : L2 규제
- L2규제는 W의 제곱에 대해 페널티를 부여하는 방식이다.
- L2규제를 적용한 회귀를 릿지(Ridge)회귀라고 한다.
- 라쏘와 달리 가중치를 0으로 만들지는 않지만, 중요하지 않은 변수를 0과 가깝게 만들어줌으로써 과적합을 방지한다.
- 미분 값의 음수를 취하면 각 항의 부호가 바뀌기 때문에 가중치가 양수일 땐 L2 패널티가 음수, 반대로 가중치가 음수일 땐 L2 패널티가 양수로 되어 가중치를 0의 방향으로 잡아당기는 역할을 한다.
- 즉, 가중치의 절댓값을 가능한 작게 만들려는 작업을 함.
- 이를 weight decay라고 하며 weight decay는 특정 가중치가 비이상적으로 커지고 그것이 학습 효과에 큰 영향을 주는 것을 방지할 수 있음.
1-1. Ridge 회귀로 보스턴 주택가격 예측 / cross_val_score로 예측 성능 평가
보스턴 주택 가격 데이터 column 설명
더보기
- CRIM: 지역별 범죄 발생률
- ZN: 25,000평방피트를 초과하는 거주 지역의 비율
- NDUS: 비상업 지역 넓이 비율
- CHAS: 찰스강에 대한 더미 변수(강의 경계에 위치한 경우는 1, 아니면 0)
- NOX: 일산화질소 농도
- RM: 거주할 수 있는 방 개수
- AGE: 1940년 이전에 건축된 소유 주택의 비율
- DIS: 5개 주요 고용센터까지의 가중 거리
- RAD: 고속도로 접근 용이도
- TAX: 10,000달러당 재산세율
- PTRATIO: 지역의 교사와 학생 수 비율
- B: 지역의 흑인 거주 비율
- LSTAT: 하위 계층의 비율
- PRICE: 본인 소유의 주택 가격(중앙값) - 종속변수 (위의 건 독립변수)
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from scipy import stats
from sklearn.datasets import load_boston
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
# 보스턴 데이터 불러오기
boston = load_boston()
bostonDF = pd.DataFrame(boston.data, columns=boston.feature_names)
bostonDF['PRICE'] = boston.target # boston데이터셋의 target 배열은 주택가격
df = bostonDF
# feature, target 데이터 분리
y_target = df['PRICE'] # 레이블(종속변수)
X_data = df.drop(['PRICE'], axis=1, inplace=False) # 피처(독립변수)
from sklearn.linear_model import Ridge
from sklearn.model_selection import cross_val_score
# alpha=10으로 설정하고 ridge 회귀 수행
ridge = Ridge(alpha=10)
neg_mse_scores = cross_val_score(ridge, X_data, y_target,
scoring="neg_mean_squared_error", cv=5)
rmse_scores = np.sqrt(-1 * neg_mse_scores)
avg_rmse = np.mean(rmse_scores)
print('5 flods 의 개별 Negative MSE scores:', np.round(neg_mse_scores,3))
print('5 flods 의 개별 RMSE scores:', np.round(rmse_scores,3))
print('5 flods 의 평균 RMSE: {0:.3f}'.format(avg_rmse))
[out]
5 flods 의 개별 Negative MSE scores: [-11.422 -24.294 -28.144 -74.599 -28.517]
5 flods 의 개별 RMSE scores: [3.38 4.929 5.305 8.637 5.34 ]
5 flods 의 평균 RMSE: 5.518
- 릿지의 5개 폴드 세트의 평균 RMSE가 5.518 인것을 확인했다.
- 앞 예제의 규제가 없는 LinearRegression의 RMSE 평균인 5.829보다 더 뛰어난 예측 성능을 보여준다.
1-2. Ridge의 alpha값을 0, 0.1, 1, 10, 100으로 변환시키면서 RMSE와 회귀 계수 값의 변화 확인하기
# 릿지에 사용될 alpha 파라미터 값 정의
alphas = [0, 0.1, 1, 10, 100]
# alpha에 따른 평균 rmse 구하기
for alpha in alphas:
ridge = Ridge(alpha=alpha)
# cross_val_score로 5폴드의 평균 rmse 계산
neg_mse_scores = cross_val_score(ridge, X_data, y_target,
scoring="neg_mean_squared_error", cv=5)
avg_rmse = np.mean(np.sqrt(-1 * neg_mse_scores))
print('alpha {0}일때 5 folds의 평균 RMSE : {1:.3f}' .format(alpha, avg_rmse))
[out]
alpha 0일때 5 folds의 평균 RMSE : 5.829
alpha 0.1일때 5 folds의 평균 RMSE : 5.788
alpha 1일때 5 folds의 평균 RMSE : 5.653
alpha 10일때 5 folds의 평균 RMSE : 5.518
alpha 100일때 5 folds의 평균 RMSE : 5.330 # 가장 좋은 결과가 나왔다!
- alpha가 100일때 평균 RMSE가 5.330으로 가장 좋다.
1-3. alpha값의 변화에 따른 피처의 회귀 계수 값을 가로 막대 그래프로 시각화
# 각 alpha에 따른 회귀 계수 값을 시각화하기 위해 5개의 열로 된 맷플롯립 축 생성
fig, axs = plt.subplots(figsize=(18,6), nrows=1, ncols=5)
# 각 alpha에 따른 회귀 계수 값을 데이터로 저장하기 위한 DataFrmae생성
coeff_df = pd.DataFrame()
# alphas = [0, 0.1, 1, 10, 100]
# 회귀 계수 값 시각화 및 데이터 저장. pos는 axis의 위치 지정
for pos, alpha in enumerate(alphas):
ridge = Ridge(alpha = alpha)
ridge.fit(X_data, y_target)
# alpha에 따른 피처별로 회귀 계수를 Series로 변환하고 이를 DataFrame의 칼럼으로 추가
coeff = pd.Series(data=ridge.coef_, index=X_data.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])
# alpha에 따른 피처별 회귀 계수를 DataFrmae으로 표시
plt.show()
- alpha 값을 증가시킬수록 회귀 계수 값은 지속적으로 작아짐을 알 수 있다.
- 특히 NOX(일산화질소 농도)피처의 경우 alpha 값을 계속 증가시킴에 따라 회귀 계수가 크게 작아지고 있다.
1-4. DataFrame에 저장된 alpha값의 변화에 따른 ridge 회귀계수 값 구하기
# DataFrame에 저장된 alpha값의 변화에 따른 ridge 회귀계수 값 구하기
ridge_alphas = [0,0.1,1,10,100]
sort_column = 'alpha:' + str(ridge_alphas[0])
coeff_df.sort_values(by=sort_column, ascending=False)
👉 alpha 값이 증가하면서 회귀 계수가 지속적으로 작아지고 있음을 알 수 있다.
하지만, Ridge 회귀는 회귀 계수를 0으로 만들지는 않는다.
2. 라쏘 회귀 : L1 규제
- L1규제 W의 절대값에 대해 페널티를 부여하는 방식이다.
- L1규제를 적용한 회귀를 라쏘(Lasso)회귀라고 한다.
- 변수를 0으로 만든다 → 필요 없는 변수를 제외시키기 위해서도 종종 사용
- 학습의 방향이 단순히 손실함수를 줄여나가는 것 뿐만 아니라 가중치(w)값들 또한 최소가 될 수 있도록 진행된다.
- w의 부호에 따라 상수값을 빼주는 방식
- 중요한 것은 알파 값. 사용자가 조정할 수 있는 하이퍼파라미터이기 때문이다.
- α 값을 높이면 (α=1) 어차피 전체 페널티의 값도 줄여야하니 |가중치|의 합도 줄이도록 학습한다.
- 계속해서 특정상수의 값을 빼나가는 것이므로 몇몇 가중치들은 0으로 수렴하고 이에 따라 feature의 수도 감소
- 즉, 구불구불한 그래프를 feature의 수를 감소시키면서 펴주고 일반화에 적합하도록 만드는 것
- 가중치가 0인 중요하지 않은 특성들을 제외해줌으로써 모델에서 중요한 특성이 무엇인지 알 수 있다.
- 반대로 α 값을 줄이면(α=0.0001) 상대적으로 많은 feature를 이용하게 되므로 과적합될 우려가 있다.
2-1. alpha값에 따라 회귀 모델의 평균 RMSE출력 및 회귀 계수값 DF로 반환하는 함수
# alpha값에 따른 회귀 모델의 폴드 평균 RMSE 출력, 회귀 계수값 DF로 반환
def get_linear_reg_eval(model_name, params=None, X_data_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)
neg_mse_scores = cross_val_score(model, X_data_n,
y_target_n, scoring="neg_mean_squared_error", cv = 5)
avg_rmse = np.mean(np.sqrt(-1 * neg_mse_scores))
print('alpha {0}일 때 5 폴드 세트의 평균 RMSE: {1:.3f} '.format(param, avg_rmse))
# cross_val_score는 evaluation metric만 반환하므로 모델을 다시 학습하여 회귀 계수 추출
model.fit(X_data_n , y_target_n)
if return_coeff:
# alpha에 따른 피처별 회귀 계수를 Series로 변환하고 이를 DataFrame의 컬럼으로 추가.
coeff = pd.Series(data=model.coef_ , index=X_data_n.columns )
colname='alpha:'+str(param)
coeff_df[colname] = coeff
return coeff_df
# alpha값 변화에 따른 RMSE와 회귀계수 출력
# 라쏘에 사용될 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_data_n=X_data, y_target_n=y_target)
[out]
####### Lasso #######
alpha 0.07일 때 5 폴드 세트의 평균 RMSE: 5.612
alpha 0.1일 때 5 폴드 세트의 평균 RMSE: 5.615
alpha 0.5일 때 5 폴드 세트의 평균 RMSE: 5.669
alpha 1일 때 5 폴드 세트의 평균 RMSE: 5.776
alpha 3일 때 5 폴드 세트의 평균 RMSE: 6.189
# 반환된 coeff_lasso_df를 첫번째 컬럼순으로 내림차순 정렬하여 회귀계수 DataFrame출력
sort_column = 'alpha:'+str(lasso_alphas[0])
coeff_lasso_df.sort_values(by=sort_column, ascending=False)
💡 alpha 값이 증가함에 따라 일부 feature의 회귀 계수는 0으로 바뀌는 것을 볼 수 있다. 이렇게 회귀 계수가 0인 feature가 회귀 식에서 제외되면서 L1 규제를 이용하여 feature 선택의 효과를 얻을 수 있다.
3. 엘라스틱 넷 회귀
- 엘라스틱넷(Elastic Net) 회귀는 L2 규제와 L1 규제를 결합한 회귀이다.
- 라쏘 회귀는 서로 상관관계가 높은 feature들 중에서 중요 feature 만을 셀렉션하고 다른 feature들은 모두 회귀 계수를 0으로 만드는 성향이 강하다. 특히 이러한 성향으로 인해 alpha 값에 따라 회귀 계수의 값이 급격히 변동할 수도 있다. 엘라스틱넷 회귀는 이러한 점을 완화하기 위해 L2 규제를 라쏘 회귀에 추가한 것이다.
- 반대로 엘라스틱넷 회귀의 단점은 L1과 L2 규제가 결합된 규제로 인해 수행시간이 상대적으로 오래 걸리는 것이다.
- 사이킷런은 ElasticNet 클래스를 통해 엘라스틱넷 회귀를 구현할 수 있고, 주요 생성 파라미터는 alpha와 l1_ratio이다. alpha는 Ridge와 Lasso 클래스의 alpha와는 다르다. 엘라스틱넷의 규제는 aL1 + bL2로 정의할 수 있는데 여기서 alpha는 a+b이다.
- 그리고 l1_ratio는 a/(a+b)이다. l1_ratio가 0이면 a가 0이 되므로 L2 규제과 같고, l1_ratio가 1이면 b가 0이므로 L1 규제와 같다.
3-1. 위에서 만든 함수불러와서 엘라스틱넷 수행
# 엘라스틱넷에 사용될 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_data_n=X_data, y_target_n=y_target)
[out]
####### ElasticNet #######
alpha 0.07일 때 5 폴드 세트의 평균 RMSE: 5.542
alpha 0.1일 때 5 폴드 세트의 평균 RMSE: 5.526
alpha 0.5일 때 5 폴드 세트의 평균 RMSE: 5.467
alpha 1일 때 5 폴드 세트의 평균 RMSE: 5.597
alpha 3일 때 5 폴드 세트의 평균 RMSE: 6.068
# 반환된 coeff_elastic_df를 첫번째 컬럼순으로 내림차순 정렬하여 회귀계수 DataFrame출력
sort_column = 'alpha:'+str(elastic_alphas[0])
coeff_elastic_df.sort_values(by=sort_column, ascending=False)
💡 L1_ratio는 0.7로 고정하고 alpha에 따라 어떻게 변화하는지 살펴보았다. alpha가 0.5일 때 RMSE가 5.468로 가장 좋은 예측 성능을 보이고, 이전의 라쏘보다는 상대적으로 0이 되는 값이 적다는 것을 알 수 있다.
'Data Analytics > MachineLearning' 카테고리의 다른 글
[ML] 비지도학습(Unsupervised Learning)의 종류 알아보기 / 군집화, 차원축소, 연관규칙 (0) | 2022.11.08 |
---|---|
[ML] 시계열(Timeseries)이란? / AR , MA, ARIMA 알아보기 (0) | 2022.11.07 |
[ML]LinearRegression을 이용해서 보스턴 주택가격 예측해보기 / knn 모델과 비교해보기 (0) | 2022.11.07 |
[ML] 머신러닝 모델의 성능을 향상시키는 방법 GridSearchCV (0) | 2022.11.07 |
[ML] 머신러닝 최적화 방법, 경사하강법(Gradient Descent) 알아보기 / 확률적 경사하강법 (0) | 2022.11.07 |
댓글