들어가며

지난글에서는 학습곡선을 통해서 모델 학습이 과대적합인지 과소적합인지 파악하여 모델을 결정하는방법에 대해서 알아보았습니다. 과소적합인 경우에는 더 좋은 모델과 하이퍼파라미터를 선택하면되지만 과대적합인 경우에는 어떻게 해결해야할까요? 이번 글에서는 비용함수에 변수를 추가하여 모델을 규제하여 과대적합을 방지하는 방법에 대해서 알아보겠습니다.

 

목차

1. 과대적합 발생이유

2. 과대적합 해소방법

    1.릿지 회귀(티호노프 규제)

    2.라쏘 회귀(Lasso Regression)

    3.엘라스틱넷

    4.조기종료

 

3.1 과대적합 발생이유

과대적합이 발생하는 이유는 각 파라미터 변수들의 절댓값이 크게 설정되기 때문입니다. 아래 그림은 이후에 설명할 규제의 정도에 따른 학습결과인데 미리 이를 예로 설명하겠습니다. 오른쪽 그림은 차수를 10차로 설정한 그림입니다. 파란색 곡선을 보면 다른 곡선보다 데이터를 따라가기 위해서 과하게 출렁이는 모습을 볼 수 있습니다. 이런 경우가 바로 파라미터(가중치, 각 항의 계수)들의 절댓값이 크기에 발생합니다. 

3.2. 과대적합 해소방법

위에서 파라미터의 절댓값이 크기에 과대적합이 발생하는 사실을 알았습니다. 그렇다면 과대적합을 해소하기 위해서는 인위적으로 파라미터의 절댓값을 감소시켜야합니다. 이를 위한 방법은 여러가지가 있습니다. 이를 차례대로 알아보도록 하겠습니다.

3.2.1 릿지회귀( 티호노프 회귀 )

첫 번째 방법은 학습에 사용하는 비용함수에 L2노름을 추가하는 것입니다. 이렇게 하면 편도함수를 구할 때 기존의 MSE 편도함수 뿐만아니라 알파와 해당 파라미터값을 곱한 값을 합하여 구하게 될 것입니다. 이는 곧 파라미터 갱신에 사용되므로 결과적으로 L2노름을 사용하지 않았을 때보다 파라미터의 절댓값 변화가 낮아지게되는 것입니다.

위 그림을 정확히 설명하자면 릿지 회귀에서 알파값에 따른 곡선의 변화를 나타냅니다. 알파값이 증가할 수록 각 파라미터가 낮아져 곡선의 복잡성이 낮아져 오버피팅이 줄어드는 것을 확인할 수 있습니다. 참고로 릿지 회귀는 특성의 범위에 민감하게 반응하므로 학습이전에 StandardScaling이 필요합니다.

코드구현

코드로는 sklearn.linear_model에서 직접 Ridge모듈을 선택하거나 SGDRegressor에서 penalty를 l2로 설정하는 두 가지 방법이 있습니다. Ridge에서 solver를 cholesky로 설정할 경우 정규방정식을 통해서 학습을 진행합니다.

1. Ridge

from sklearn.linear_model import Ridge
ridge_reg = Ridge(alpha=1, solver="cholesky", random_state=42)
ridge_reg.fit(X, y)
ridge_reg.predict([[1.5]])
#result = array([[1.55071465]])

2. SGDRegressor

sgd_reg = SGDRegressor(max_iter=50, tol=-np.infty, penalty="l2", random_state=42)
sgd_reg.fit(X, y.ravel())
sgd_reg.predict([[1.5]])
# resutl = array([1.49905184])

3.2.2 Lasso 회귀

Lasso는 least absolute shrinkage and selection operator의 줄임말로서 쉽게 해석하면 중요한 특성에 맞춰서 축약한다는 소리입니다. 우선 Lasso는 릿지회귀와 비슷하게 비용함수에 변수를 추가하는데 이 때는 L1노름을 추가합니다.

출처 : https://dojinkimm.github.io/ml/2019/11/10/handson-ch4.html

이제 위 비용함수가 어떻게 특성을 축약하는지 알아봅시다. 위 비용함수를 파라미터 갱신에 사용한다고 생각합시다. 편도함수를 구하면 새로 추가한 L1노름 부분은 알파값과 해당 파라미터의 양,음에 따라서 부호가 정해질 것입니다. 즉 갱신을 할 때 모든 파라미터들은 알파값이라는 동일한 값을 통해서 갱신이 진행됩니다. 따라서 초기값이 작은 파라미터의 경우 먼저 0으로 수렴하게 됩니다. 아래의 그림으로 더 쉽게 설명하겠습니다.

왼쪽 위의 경우에서 둥근 등고선은 MSE비용함수를 나타내고 삼각형은 L1노름을 나타냅니다. 이 부분에서 노란 삼각형은 초기에 (0.25,-1)부분에서 파라미터가 시작한 경우 갱신이 진행되는 과정을 설명하고있는 것입니다. L1노름의 경우에는 기울기의 절댓값이 1로 각 파라미터의 절댓값이 감소하는 것이 보입니다. (기울기가 1인 이유는 변수 알파에 의해서만 갱신이 되기 때문입니다.) 이렇게 진행되다가 세타1이 먼저 0으로 수렴하고 이후 다시 세타2가 0으로 수렴하고 있는 모습입니다. 즉 L1노름이 계속 갱신하게 되면 별로 중요하지 않은 파라미터는 먼저 0으로 수렴하여 삭제되고 이 후 살아남은 파라미터가 모델을 학습하는 모습을 볼 수 있습니다. 최종적으로 오른쪽 두 개의 그래프를 살펴보면 Lasso는 별로 중요하지 않은 세타2는 삭제한 반면 릿지는 모든 파라미터를 활용하여 최적화가되는 모습을 볼 수 있습니다.

 

이러한 특성때문에 중요한 몇개의 특성만으로 모델을 표현하고 싶은 경우(특성 축소)에 라쏘를 선택할 수 있지만 릿지만큼의 정확성을 기대하는것은 힘들 수 있습니다.

 

코드구현

라쏘 또한 릿지함수와 마찬가지로 sklearn.linear_model에서 Lasso모듈을 사용하여 구현할 수 있습니다.

 

from sklearn.linear_model import Lasso

plt.figure(figsize=(8,4))
plt.subplot(121)
plot_model(Lasso, polynomial=False, alphas=(0, 0.1, 1), random_state=42)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.subplot(122)
plot_model(Lasso, polynomial=True, alphas=(0, 10**-7, 1), tol=1, random_state=42)

save_fig("lasso_regression_plot")
plt.show()

3.2.3 엘라스틱넷

엘라스틱넷은 한 마디로 릿지와 Lasso회귀를 섞어놓은 것을 의미합니다.

출처 : https://dojinkimm.github.io/ml/2019/11/10/handson-ch4.html

릿지와 라쏘를 이해했다면 위 공식은 아주 쉬울것입니다. 혼합비율 상수 r을 사용하여 릿지와 라쏘를 조율하여 둘 다 사용하고있는 모습입니다. 보통 라쏘보다 엘라스틱넷이 더 선호되는데 그 이유는 만약 샘플수가 특성수 보다 많은 경우나 특성들이 강한 상관관계를 지닌경우 Lasso가 제대로 작동하지 않기 때문입니다.

 

코드구현

from sklear.linear_model import ElasticNet

elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic_net.fit(X,y)
elastic_net.predict([[1.5]])
# array([1.54333xxx])

3.2.4 조기종료 (early stopping)

조기종료는 말 그대로 학습을 진행하는 와중 학습을 종료하는 것입니다. 이는 아주 간단한 개념이면서도 좋은 결과를 얻을 수 있는데 점진적으로 학습이 진행될때 검증 세트의 결과값이 최소이며 앞으로 더 좋은 결과가 기대대지 않을경우 해당 최소 구간에서 학습을 종료하는 것을 의미합니다.

+ Recent posts