편향과 분산
직선 모델은 너무 간단해서 복잡한 곡선 관계를 학습하지 못한다는 한계가 있습니다.
모델이 너무 간단해서 데이터의 관계를 잘 학습하지 못하는 경우
그 모델은 편향이 높다 라고 합니다.
모델의 복잡도를 높여서 트레이닝 데이터의 관계를 완벽히 학습했다면
그 모델은 편향이 낮다 라고 할 수 있습니다.
그렇다면, 편향이 낮은 모델은 항상 편향이 높은 모델보다 좋을까요?
꼭 그렇지만은 않습니다.
각 모델이 처음 접하는 데이터인 테스트 데이터 셋에 대해서 모델의 성능을 평가해보면,
트레이닝 데이터의 관계를 완벽하게 나타내는 모델은 너무 트레이닝 데이터에 딱 맞춰서 학습되어있기때문에
처음 보는 테스트 데이터 셋에 대해서는 편향이 높은 모델보다 성능이 더 안좋을 수 있습니다.
이렇게 데이터셋별로 얼마나 일관된 성능을 보여주는지를 분산(Variance)라고 합니다.
다양한 데이터셋 간에 비슷한 성능을 보여주면 분산이 낮다고 하고,
성능 차이가 많이 나면 분산이 높다고 합니다.
위의 경우에는 편향이 너무 낮아 트레이닝 데이터 셋에 너무 딱 맞아 떨어지는 모델은 분산이 높고,
편향이 높은 모델은 분산이 낮다고 말할 수 있습니다.
편향-분산 트레이드오프 (Bias-variance tradeoff)
편향을 높이면 학습이 잘 안되고,
편향이 낮으면 분산이 높아진다고 하고 둘 다 안좋은 것 아닌가..라는 생각이 들 수도 있습니다.
어떻게 해야하는지 설명하기 전에
두 가지 용어만 알아보고 가겠습니다.
과소적합 (underfitting)
과소적합은 모델이 너무 단순해서 데이터의 내재적인 구조를 학습하지 못할 때 발생합니다.
아래 사진과 같은 경우가 과소적합이 일어난 경우라고 할 수 있습니다. 이미지 출처
해결 방법은 다음과 같습니다.
- 모델 파라미터가 더 많은 강력한 모델을 선택합니다.
- 학습 알고리즘에 더 좋은 특성을 제공합니다.
- 모델의 제약을 줄입니다.
과대적합 (overfitting)
과대적합은 훈련 데이터에 있는 잡음의 양에 비해 모델이 너무 복잡할 때 일어납니다.
아래 사진과 같은 경우가 과대적합이 일어난 경우라고 할 수 있습니다. 이미지 출처
해결 방법은 다음과 같습니다.
- 파라미터 수가 적은 모델을 선택하거나, 훈련 데이터에 있는 특성 수를 줄이거나, 모델에 제약을 가하여 단순화시킵니다.
- 훈련 데이터를 더 많이 모읍니다.
- 훈련 데이터의 잡음을 줄입니다.(예를 들면 오류 데이터 수정과 이상치 제거)
일반적으로 편향과 분산은 하나가 줄어들수록 다른 하나는 늘어나는 경향이 있습니다.
따라서 둘 중 하나를 줄이기 위해서는 다른 하나를 포기해야 하는 관계입니다.
이러한 관계를 편향-분산 트레이드오프 라고 부릅니다.
편향-분산 트레이드오프 문제는 머신 러닝 프로그램들의 성능과 밀접한 관계가 있기 때문에
편향과 분산의 적당한 밸련스를 찾아내야합니다.
위의 과소적합 그래프도, 과대적합 그래프도 아닌
아래와 같은 그래프를 찾아야 한다는 것입니다. 이미지 출처
scikit-learn으로 과소적합 방지해보기
우리의 목적은 과소적합도, 과대적합도 되지 않은 모델을 찾는 것입니다.
과소적합은 충분히 복잡한 모델을 쓰는 것으로 간단하게 해결이 가능합니다.
scikit-learn으로 복잡한 회귀 모델을 만들어 과소적합을 한 번 방지해보겠습니다.
정규화 (Regularization)
위의 코드에서 과소적합은 방지됐지만 과대적합이 발생했는데요,
모델 과대적합을 방지해주는 방법 중 하나인 정규화에 대해서 살펴보겠습니다.
과대적합이 되면 가설 함수의 그래프가 데이터에 거의 딱 맞게 그려져서 굴곡이 많고 급격하게 변화하게 됩니다.
함수가 급격하게 변하는 것은 가설 함수의 계수들, 즉 \(\theta\)값이 크기 때문입니다.
정규화는 모델을 학습시킬 때 이 \(\theta\)값들이 너무 커지는 걸 방지하는 방법입니다.
\(\theta\)값들이 너무 커지는 걸 방지하면 트레이닝 데이터에 대한 오차는 조금 커질 수 있어도
위아래로 급격히 변하는 가설 함수를 좀 더 완만하게 만들 수 있습니다.
그렇게 하면 여러 데이터 셋에 대해 좀 더 일관된 성능을 보이기 때문에 과적합을 막을 수 있습니다.
L1, L2 정규화
머신 러닝에서 정규화는 손실 함수에 정규화 항이라는 것을 더해서 \(\theta\)값들이 너무 커지는 것을 방지하는 방법입니다.
여태까지의 좋은 가설 함수를 평가할 때
손실 함수의 아웃풋이 더 작을수록 좋은 가설 함수,
손실 함수의 아웃풋이 클수록 안좋은 가설 함수라고 평가해왔는데요,
과적합을 방지하기 위해서 가설 함수의 평가 기준을 아래와 같이 바꿔주겠습니다.
트레이닝 데이터에 대한 오차도 작고 \(\theta\) 값들도 작아야 좋은 가설 함수이다.
이런 조건을 충족시키기 위한 두 가지 방법을 소개하겠습니다.
L1 정규화
L1 정규화를 적용할 때의 손실 함수의 수식을 보겠습니다.
\[ \displaystyle J(\theta) = \frac{1}{m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)}) - y^{(i)})^2 + \sum_{i=1}^{n} \vert \theta_{i} \vert \]
이렇게 \(\theta\) 값들의 절댒값을 손실 함수에 모두 더해주면 됩니다.
그런데 이 때, \(\theta_{0}\)은 과적합과 상관이 없기 때문에 \(\theta_{0}\)은 더해주지 않습니다.
그런데 실제로는 정규화 항에 \(\alpha\)도 곱해주는데, \(\theta\)값이 커지는 것에 대해 얼마나 페널티를 줄지를 정해주는 것입니다.
\[ \displaystyle J(\theta) = \frac{1}{m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)}) - y^{(i)})^2 + \alpha\sum_{i=1}^{n} \vert \theta_{i} \vert \]
예를 들어 \(\alpha\)값이 크면 \(\theta\)값들이 조금만 커져도 손실 함수가 굉장히 커지므로 \(\theta\)를 줄이는 게 우선이고,
\(\alpha\)값이 작으면 \(\theta\)값이 커져도 손실 함수가 별로 안커지므로 MSE를 줄이는 것이 우선입니다.
L1 정규화를 적용하는 회귀 모델을 Lasso Regression 또는 Lasso model 이라고 얘기합니다.
L2 정규화
L2 정규화도 L1 정규화와 거의 유사한데 \( \vert \theta \vert \)값들을 모두 더하는 대신에 \(\theta^{2}\)의 값들을 모두 더해주면 됩니다.
\[ \displaystyle J(\theta) = \frac{1}{m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)}) - y^{(i)})^2 + \alpha\frac{1}{2}\sum_{i=1}^{n}\theta_{i}^{2} \]
L2 정규화를 적용하는 회귀 모델을 Ridge Regression 또는 Ridge model 이라고 얘기합니다.
scikit-learn으로 과대적합 방지해보기
L1, L2 정규화 일반화
위에서는 계속 예시로 다항 회귀 모델을 사용했는데,
다른 모델들에도 정규화는 똑같이 적용할 수 있습니다.
앞서 다뤘던 모델들을 대상으로는
LinearRegression 대신 Lasso나 Ridge를 쓰면 되고,
LogisticRegression에는 penalty라는 옵셔널 파라미터를 전달해주면 됩니다. 참고 링크
(SGDRegressor같은 다른 여러 모델에도 penalty에 'l1' 또는 'l2'를 전달해주면 됩니다.)
나중에 다룰 딥러닝 모델도 손실 함수를 최소화하는 알고리즘입니다.
딥러닝 모델도 과적합되는 경우가 많아서 딥러닝 할 때에도 정규화는 중요합니다.
딥러닝 모델의 파라미터는 보통 \(\theta\)대신 \(w\)로 나타냅니다.
딥러닝 모델에 정규화를 적용하려면 모델의 손실 함수에 정규화 항으로 아래 둘 중 하나를 더해주면 됩니다.
\[ \displaystyle \alpha\sum_{i=1}^{n}\vert w_{i} \vert, \alpha\frac{1}{2}\sum_{i=1}^{n}w_{i}^{2} \]
L1, L2 정규화의 차이
L1 정규화는 여러 \(\theta\)값들을 0으로 만듭니다.
덜 중요한 특성의 가중치를 제거한다는 것입니다.
즉, 자동으로 특성 선택을 하고 희소 모델을 만듭니다.(0이 아닌 특성의 가중치가 적습니다.)
라쏘 모델의 비용 함수는 \(\theta_{i} = 0\hbox{, i=1, 2, 3, } \dots \hbox{, n}\)일 때 미분 불가능합니다.
이 때 서브그레이디언트 벡터 \(g\)를 사용하면 경사 하강법을 적용하는 데 문제가 없습니다.
아래 식은 라쏘 비용 함수에 사용할 수 있는 서브그레이디언트 벡터 공식입니다.
\[ \displaystyle g(\theta, J) = \frac{\partial}{\partial\theta}J(\theta) + \alpha sign(\theta), sign(\theta) = \begin{cases} -1, \theta < 0 \\ 0, \theta = 0 \\ +1, \theta > 0 \end{cases} \]
L2 정규화는 \(\theta\)값들을 0으로 만들기보다는 조금씩 줄여줍니다. 모델에 사용되는 속성을 없애지는 않습니다.
(이렇게 되는 이유가 궁금하다면 여기를 참고하세요)
L1 정규화는 어떤 모델에 쓰이는 속성 또는 변수의 개수를 줄이고 싶을 때 사용합니다.
속성이 너무 많으면 과적합 뿐만이 아니라 모델을 학습시킬 때 많은 자원을 소모할 수 있습니다.
이럴 때 L1 정규화를 사용하면 사용되는 속성의 개수를 많이 줄일 수 있습니다.
반대로 딱히 속성의 개수를 줄일 필요가 없다고 생각되면 L2 정규화를 사용하면 됩니다.
엘라스틱넷
엘라스틱넷은 릿지 회귀와 라쏘 회귀를 절충한 모델입니다.
규제항은 릿지와 회귀의 규제항을 단순히 더해서 사용하고,
혼합 정도는 혼합 비율 \(r\)을 사용해 조절합니다.
\[ \displaystyle J(\theta) = MSE(\theta) + r\alpha\sum_{i=1}^{n} \vert \theta_{i} \vert + \frac{1 - r}{2}\alpha\sum_{i=1}^{n}\theta_{i}^{2} \]
라쏘, 릿지, 엘라스틱넷 이렇게 세 개나 있으니까
언제 뭘 써야할지 어지러울 수 있습니다.
적어도 규제가 약간 있는 것이 대부분의 경우에 좋으므로 일반적으로 평범한 선형 회귀는 피해야 합니다.
릿지가 기본이 되지만 쓰이는 특성이 몇 개뿐이라고 의심되면 라쏘나 엘라스틱넷이 낫습니다.
특성 수가 훈련 샘플보다 많거나 특성 몇 개가 강하게 연관되어 있을 때에는
보통 라쏘가 문제를 일으키므로 라쏘보다는 엘라스틱넷이 낫습니다.
아래는 ElasticNet을 사용한 간단한 예제입니다.
조기 종료
경사 하강법과 같은 반복적 학습 알고리즘을 규제하는 색다른 방식은
검증 에러가 최솟값에 도달하면 바로 훈련을 중지시키는 것입니다.
이를 조기 종료 방식이라 합니다.
이 방식에서는 에포크가 진행됨에 따라 알고리즘이 점차 학습되어
훈련 세트에 대한 RMSE와 검증 세트에 대한 RMSE가 줄어다가
검증 세트에 대한 RMSE가 멈췄다가 다시 상승할 때
모델이 훈련 데이터에 과대적합되기 시작한다는 것을 의미하므로
검증 에러가 최소에 도달하는 즉시 훈련을 멈춥니다.
이 규제 테크닉은 아주 효과적이고 간단해서
제프리 힌턴이 훌륭한 공짜 점심 이라고 불렀습니다.
다음은 조기 종료를 위한 기본적인 구현 코드입니다.
from copy import deepcopy
poly_scaler = Pipeline([
("poly_features", PolynomialFeatures(degree=90, include_bias=False)),
("std_scaler", StandardScaler())
])
X_train_poly_scaled = poly_scaler.fit_transform(X_train)
X_val_poly_scaled = poly_scaler.transform(X_val)
sgd_reg = SGDRegressor(max_iter=1, tol=-np.infty, warm_start=True,
penalty=None, learning_rate="constant", eta0=0.0005, random_state=42)
minimum_val_error = float("inf")
best_epoch = None
best_model = None
for epoch in range(1000):
sgd_reg.fit(X_train_poly_scaled, y_train) # 중지된 곳에서 다시 시작합니다
y_val_predict = sgd_reg.predict(X_val_poly_scaled)
val_error = mean_squared_error(y_val, y_val_predict)
if val_error < minimum_val_error:
minimum_val_error = val_error
best_epoch = epoch
best_model = deepcopy(sgd_reg)
'DATA > 머신 러닝' 카테고리의 다른 글
[머신 러닝] 그리드 서치 (Grid search) (0) | 2022.01.07 |
---|---|
[머신 러닝] 테스트와 검증 (0) | 2022.01.07 |
[머신 러닝] 더 빠르게, 더 정확하게 (0) | 2022.01.06 |
[머신 러닝] 로지스틱 회귀 (Logistic Regression) (0) | 2022.01.05 |
[머신 러닝] 다항 회귀 (Polynomial Regression) (0) | 2022.01.04 |