1. 다항 회귀
특성의 수가 많은 비선형 데이터의 경우, 선형 모델을 활용하여 학습할 수 있다. 이렇게 하는 간단한 방법은 각 특성의 거듭제곱을 새로운 특성으로 추가하고, 이 확장된 특성을 포함한 데이터셋에 선형 모델을 훈련시키는 것이다. 이런 기법을 다항 회귀라고 한다.
import numpy as np
m = 100
X = 6 * np.random.rand(m,1)-3
y = 0.5 * X**2 + X + 2 + np.random.randn(m,1)
import matplotlib.pyplot as plt
plt.plot(X, y, 'bo')
plt.xlabel('X1')
plt.ylabel('y', rotation=0)
plt.show()
직선은 이 데이터에 잘 맞지 않을 것이므로 사이킷런의 PolynomialFeatures 를 사용해 훈련 데이터를 변환한다. (해당 메서드의 매개변수 degree 의 기본값은 2이고 include_bias의 기본값은 True (편향을 위한 특성 x0 = 1이 추가됨) 이다.)
from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)
print('X[0] :', X[0])
print('X_poly[0] :', X_poly[0])
여기서 PolynomialFeature의 매개변수인 degree를 통해 2차, 3차... 고차 방정식으로 변환이 가능하다. 예를 들어, degree = 3 인 경우, x^2, x^3, y^2, y^3 뿐만 아니라 ab, (a^2)b, a(b^2) 특성도 추가된다.
이제 확장된 훈련 데이터에 선형회귀 함수를 적용시킨다.
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)
lin_reg.intercept_, lin_reg.coef_
X_test = np.linspace(-3, 3, 100).reshape(100, 1)
#linspace : -3에서 3까지 동일한 간격으로 100등분
#reshape : 데이터를 100,1 행렬도 재배치
X_test_poly = poly_features.fit_transform(X_test)
y_pred = lin_reg.predict(X_test_poly)
plt.scatter(X, y)
plt.plot(X_test, y_pred, 'r')
plt.xlabel('X1')
plt.ylabel('y', rotation=0)
plt.show()
2. 학습 곡선
일반적으로 주어진 데이터가 어떤 함수 형태를 띄고 있는지 모른다. 마찬가지로 모델이 주어진 데이터에 과대적합 혹은 과소적합되었는지 여부도 알 수 없다. 이전에 살펴본 교차 검증을 활용하여 모델의 일반화 성능을 추정하는 방법 이외에도 학습 곡선을 살펴보면서 모델과 데이터를 비교하는 방법도 존재한다.
학습 곡선은 훈련 세트와 검증 세트의 모델 성능을 훈련 세트 크기 (또는 훈련 반복)의 함수로 나타낸다. 직관적인 이해를 위해 학습 곡선을 그리는 함수를 그려보겠다.
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
def plot_learning_curves(model, X, y):
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)
train_errors, val_errors = [], []
for m in range(1, len(X_train)):
model.fit(X_train[:m], y_train[:m])
y_train_predict = model.predict(X_train[:m])
y_val_predict = model.predict(X_val)
train_errors.append(mean_squared_error(y_train[:m], y_train_predict))
val_errors.append(mean_squared_error(y_val, y_val_predict))
plt.plot(np.sqrt(train_errors), "r-+", linewidth=2, label="train_set")
plt.plot(np.sqrt(val_errors), "b-", linewidth=3, label="val_set")
plt.axis([0, 80, 0, 3])
plt.xlabel('volume_train_set')
plt.ylabel('RMSE')
plt.legend()
lin_reg = LinearRegression()
plot_learning_curves(lin_reg, X, y)
+해석
훈련 데이터
- 그래프가 0에서 시작하므로 훈련 세트에 하나 혹은 두 개의 샘플이 있을 땐 모델이 완벽하게 작동함을 의미
- 훈련 세트에 샘플이 추가됨에 따라 잡음도 있고, 비선형이기 때문에 모델이 훈련 데이터를 완벽히 학습하는 것이 불가능해짐
- 곡선이 어느 정도 평편해질 때까지 오차 상승 (해당 위치에서는 훈련 샘플 추가되어도 평균 오차 차이없음)
검증 데이터
- 모델이 적은 수의 훈련 샘플로 훈련될 때는 제대로 일반화될 수 없어서 검증 오차가 초기에 매우 큼
- 샘플이 추가됨에 따라 학습이 되어 검증 오차가 점점 감소
- 하지만 선형 회귀의 직선은 주어진 데이터(다항)를 잘 모델링할 수 없으므로 오차의 감소가 완만해져서 훈련 세트의 그래프와 가까워짐
==> 과소적합 모델의 전형적인 모습 (수평한 구간, 꽤 높은 오차에서 근접)
from sklearn.pipeline import Pipeline
polynomial_regression = Pipeline([
("poly_features", PolynomialFeatures(degree=10, include_bias = False)),
("lin_reg", LinearRegression())
])
plot_learning_curves(polynomial_regression, X,y)
+해석
- 10차 다항식의 그래프
- 오차가 이전 그래프보다 낮음
- 두 곡선 사이의 공간 존재 --> 훈련 데이터에서의 모델 성능이 검증 데이터보다 훨씬 낮음 --> 과대적합
- 과대적합 해결하기 위해 데이터를 계속해서 추가해야한다.
편향 / 분산 트레이드 오프 : 모델의 일반화 오차는 세 가지 다른 종류의 오차의 합으로 표현이 가능하다.
- 편향 : 잘못된 가정으로 발생한 오차. (2차 데이터를 선형으로 가정하는 경우), 편향이 큰 모델은 과소적합되기 쉽다.
- 분산 : 훈련 데이터의 작은 변동에도 모델이 과도하게 민감하기 때문에 발생. (자유도가 높은 모델 등), 분산이 큰 모델은 과대적합되기 쉽다.
- 줄일 수 없는 오차 : 데이터 자체에 있는 잡음 때문에 발생. 해당 오차를 줄일 수 있는 유일한 방법은 데이터 내의 잡음을 제거하는 것
- 모델의 복잡도가 커지면 통상적으로 분산이 늘어나고 편향은 줄어든다. 반대로 모델의 복잡도가 줄어들면 편향이 커지고 분산이 작아진다. 이러한 현상을 편향 / 분산 트레이드 오프라고 한다.
3. 규제가 있는 선형 모델
모델의 규제 : 과대적합을 감소시키는 좋은 방법 (=모델 제한)
이렇게 모델의 자유도를 줄이면 과대적합을 방지할 수 있으며, 다항 회귀 모델에서 다항식의 차수를 감소시키는 등의 방법으로 모델의 자유도를 줄일 수 있다.
3-1 릿지 회귀 (Rigde or Tikhonov, 릿지 혹은 티호노프 규제)
- 규제가 추가된 선형 회귀
- L2-Norm을 사용한 회귀
- 일반적으로 영향을 거의 미치지 않는 특성에 대하여 0에 가까운 가중치를 부여한다.
- 규제항이 비용 함수에 추가 (학습 알고리즘을 데이터에 맞추는 효과 + 모델의 가중치가 가능한 한 작게 유지되도록)
- 이러한 규제항은 훈련하는 동안에만 비용 함수에 추가됨 (훈련 종료시, 규제가 없는 성능 지표로 모델의 성능 평가)
하이퍼 파라미터 α는 모델을 얼마나 많이 규제할지 조절한다. α = 0 이면 릿지 회귀는 선형 회귀와 같아진다. (MSE(Θ)) 반면에 α 가 아주 크면 모든 가중치가 거의 0에 가까워지고 결국 데이터의 평균을 지나는 수평선이 된다.
주의할 것은 편향 Θ0는 규제되지 않는다. (합 기호가 i = 0이 아니고 i = 1에서 시작하므로)
w를 특성의 가중치 벡터(Θ1~Θn)라고 정의하면 규제항은 1/2(||w||)^2과 같다. (l2-norm, 유클라디안 노름, 제곱항을 합한 것의 제곱근)
경사 하강법에 적용하려면 MSE 그레이디언트 벡터에 αw 를 더하면 된다.
- 릿지 회귀는 입력 특성의 스케일에 민감하기 때문에 수행 전 데이터의 스케일을 맞추는 작업이 중요하다.
- 릿지 회귀를 계산하기 위해서는 정규방정식을 사용할 수도 있고 경사 하강법을 사용할 수도 있다.
왜 훈련 동안에만 규제항 추가?
훈련에 사용되는 비용 함수는 최적화를 위해 미분이 가능해야 한다. 반면 테스트에 사용되는 성능 지표는 최종 목표에 가능한 한 가까워야 하기 때문에 규제항을 포함하지 않는다.
안드레 루이 숄레스키가 발견한 행렬 분해를 사용하여 변형된 정규 방정식을 사용한 릿지 회귀 수행
from sklearn.linear_model import Ridge
ridge_reg = Ridge(alpha=1, solver='cholesky')
ridge_reg.fit(X, y)
ridge_reg.predict([[1.5]])
확률적 경사 하강법을 사용한 릿지 회귀 수행
from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor(penalty='l2')
sgd_reg.fit(X, y.ravel())
#ravel() : Flatten 하게 변형시켜주는 함수
sgd_reg.predict([[1.5]])
3-2 라쏘 회귀 (least absolute shrinkage and selection operator, LASSO)
- 선형 회귀의 또 다른 규제된 버전
- 릿지 회귀처럼 비용 함수에 규제항을 더하지만 L2 노름의 제곱을 2로 나눈 것 대신에 '가중치 벡터의 L1 노름'을 사용
- 특성값의 계수가 매우 낮다면 0으로 수렴하게 하여 특성을 지워버림
오른쪽 그래프에서 녹색 점선 (10^(-7))은 2차 방정식처럼 보이며 거의 선형적이다. 차수가 높지만 가중치가 모두 0에 수렴하게 되어 2차 방정식처럼 보이는 것이다. 이처럼 라쏘 회귀는 자동으로 특성 선택을 하고 희소 모델을 만든다. (즉, 0이 아닌 특성의 가중치가 적다.)
위에서 설명한 내용을 보다 쉽게 이해하기 위해 해당 그림을 참고해보자.
- 두 축은 모델 파라미터 두 개를 나타내고 배경의 등고선은 각기 다른 손실 함수를 나타낸다.
- 왼쪽 위 그래프의 등고선은 L1 손실( |θ1|+|θ2| )을 나타낸다. 축에 가까워지면서 선형적으로 줄어든다. 예를 들어 모델 파라미터를 θ1 = 2, θ2 = 0.5로 초기화하고 경사 하강법을 실행하면 두 파라미터가 동일하게 감소될 것이다. (노란 선). 따라서 더 작은 θ2=0.5가 먼저 0에 도달한다. 그다음 경사 하강법이 θ1 = 0에 도달할 때까지 축을 따라 내려간다.
- 오른쪽 위 그래프의 등고선은 라쏘 손실 함수를 나타낸다. (L1 손실을 더한 MSE함수) 하얀 작은 원이 경사하강법이 θ1=0.25, θ2 = -1 로 초기화된 모델 파라미터를 최적화하는 과정을 나타낸 것이다. 여기에서도 θ2 = 0 으로 빠르게 줄어든 후, 전역 최소점인 빨간 지점에 도달한다. 여기서 α를 증가하면 전역 최적점이 노란 점선을 따라서 왼쪽으로 이동하게 된다. 그렇게 되면 시작했던 지점 (0.25, -1)에서 수직으로 내려서 도달할 수 있는 지점이 오게 된다. (수평) 반대로 α를 감소시키면 전역 최적점이 오른쪽으로 이동한다. (가중치 중 하나가 0이 된다.)
- 이번엔 L2 규제를 사용한 릿지 회귀에 대한 두 그래프 (아래)를 살펴보자. 왼쪽 아래 그래프에서 L2 손실은 원점에 가까울수록 줄어드는 것을 볼 수 있다. 따라서 경사 하강법이 원점까지 직선 경로를 따라 간다.
- 오른쪽 아래의 그래프는 L2 손실을 더한 MSE함수이다. (릿지 규제)라쏘와 다른 점은 두 가지인데, 첫 번째로, 첫째 파라미터가 전역 최적점에 가까워질수록 그레이디언트가 작아진다. 따라서 경사 하강법이 자동으로 느려지고 수렴에 도움이 된다. 두 번째로, α를 증가시킬수록 최적의 파라미터 (빨간 지점)가 원점에 더 가까워진다.
- 라쏘의 비용 함수는 θ = 0 일 때 미분 가능하지 않다. 하지만 θ = 0일 때 서브그레이디언트 벡터 G를 사용하면 경사 하강법을 적용할 수 있다.
from sklearn.linear_model import Lasso
lasso_reg = Lasso(alpha=0.1)
lasso_reg.fit(X,y)
lasso_reg.predict([[1.5]])
3-3 엘라스틱넷(elastic net)
- 릿지와 라쏘 회귀를 절충한 모델
- 규제항은 릿지와 라쏘의 규제항을 단순히 더해서 사용하며, 혼합 비율 r을 사용해 조절.
- r = 0 이면 엘라스틱넷은 릿지 회귀와 동일, r = 1 이면 라쏘 회귀와 동일
- 라쏘와 릿지 회귀의 최적화 지점이 서로 다르기 때문에 두 정규화 항을 합쳐서 조절한다는 의미
그렇다면 선형 회귀, 릿지, 라쏘, 엘라스틱넷은 각각 언제 사용해야 할까?
적어도 규제가 약간 있는 것이 대부분의 경우에 좋은 결과를 기대할 수 있으므로 일반적으로 평범한 선형 회귀는 피해야 한다. 릿지가 기본이 되지만 쓰이는 특성이 몇 개뿐이라고 의심되면 라쏘나 엘라스틱넷이 낫다. 특성 수가 훈련 샘플 수보다 많거나 특성 몇 개가 강하게 연관되어 있을 때는 보통 라쏘가 문제를 일으키므로 엘라스틱넷이 선호된다.
from sklearn.linear_model import ElasticNet
elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic_net.fit(X,y)
elastic_net.predict([[1.5]])
4. 조기 종료
학습 알고리즘을 규제하는 또 다른 방법은 검증 에러가 최솟값에 도달하면 바로 훈련을 중지시키는 것이다. 이를 조기 종료라고 한다.
위의 그림은 배치 경사 하강법으로 훈련시킨 복잡한 모델이다. (고차원 다항 회귀 모델) 에포크가 진행됨에 따라 알고리즘이 점차 학습되어 훈련 세트에 대한 예측 에러(RMSE)와 검증 세트에 대한 예측 에러가 줄어든다. 하지만 잠시 후 감소하던 검증 에러가 다시 상승한다. (모델의 과대적합) 조기 종료는 이렇게 검증 세트의 예측 에러가 최소에 도달하는 즉시 훈련을 멈추는 것이다.
(보통 검증 에러가 일정 시간 동안 최솟값보다 큰 경우 학습을 멈춘다)
from sklearn.base import clone
from sklearn.preprocessing import StandardScaler
#데이터 준비
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)
poly_scaled = 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_scaled.fit_transform(X_val)
sgd_reg = SGDRegressor(max_iter = 1, tol=-np.infty, warm_start = True,
penalty=None, learning_rate = "constant", eta0=0.0005)
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 = clone(sgd_reg)
warm_start = True로 지정하면 fit() 메서드가 호출될 때 처음부터 다시 시작하지 않고 이전 모델 파라미터에서 훈련을 이어간다.
'핸즈온머신러닝&딥러닝' 카테고리의 다른 글
서포트 벡터 머신 1 (1) | 2021.06.07 |
---|---|
모델 훈련 4(로지스틱 회귀) (0) | 2021.06.07 |
모델 훈련 2(경사 하강법) (0) | 2021.06.01 |
모델 훈련 (수정 필요) (0) | 2021.05.30 |
MNIST 활용, 분류 3 (0) | 2021.05.27 |