이전 경사 하강법은 선형 모델을 기준으로 생각했습니다. 그러나, 데이터가 단순한 선형보다 복잡한 형태를 가정해봅니다.
비선형 데이터를 학습하는 데 선형 모델을 사용할 수 있는데, 이렇게 하는 간단한 방법은 각 특성의 거듭제곱을 새로운 특성으로 추가하고, 이 확장된 특성을 포함한 데이터셋에 선형 모델을 훈련시키는 것입니다. 이런 기법을 다항 회귀(Polynomial Regression)이라 합니다.
먼저, 가장 간단한 2차방정식(quadratic equation)으로 비선형 데이터를 생성하겠습니다. (약간의 노이즈 포함)
m = 100
X = 6 * np.random.rand(m, 1)-3
y = 0.5 * X**2 + X + 2 + np.random.randn(m,1)
plt.plot(X, y, 'b.')
plt.xlabel('$x_1$', fontsize=18)
plt.ylabel('$y$', rotation=0, fontsize=18)
plt.axis([-3,3, 0, 10])
plt.show()
이렇게 보면, 선형은 아님을 알 수 있습니다. 이를 사이킷런의 PolynomialFeatures를 사용해 훈련 데이터를 변환해봅니다.
훈련 세트에 있는 각 특성을 제곱하여 새로운 특성으로 추가합니다.
from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree=2, include_bias = False)
X_poly = poly_features.fit_transform(X)
print (X[0], X_poly[0])
[2.38942838] [2.38942838 5.709368 ]
X_poly는 원래의 특성과 특성의 제곱을 포함합니다.
확장된 훈련 데이터에 LinearRegression을 적용해봅니다.
lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)
lin_reg.intercept_, lin_reg.coef_
(array([1.9735233]), array([[0.95038538, 0.52577032]]))
#numpy.linspace( *, *, *) 은 -3에서 3사이의 일정한 간격의 값을 가진 100개의 array를 만듭니다.
#이를 100행 1열로 reshape로 만듭니다.
X_new = np.linspace(-3, 3, 100).reshape(100, 1)
X_new_poly = poly_features.transform(X_new)
y_new = lin_reg.predict(X_new_poly)
plt.plot(X, y, 'b.')
plt.plot(X_new, y_new, 'r-', linewidth=2, label='Prediction')
plt.xlabel('$x_1$', fontsize=18)
plt.ylabel('$y$', rotation=0, fontsize=18)
plt.legend(loc='upper left', fontsize=14)
plt.axis([-3, 3, 0, 10])
plt.show()
실제 원래 함수가 이고,
예측된 모델은 입니다.
특성이 여러 개 일때 다항 회귀는 이 특성 사이의 관계를 찾을 수 있습니다. (일반적 선형 모델에서는 하지 못합니다.)
PolynomialFeatures가 주어진 차수까지 특성 간의 모든 교차항을 추가하기 때문입니다.
예를 들어, 두 개의 특성 a, b가 있을 때, degree=3으로 적용하면 뿐만 아니라 도 추가합니다.
PolynomialFeatures(degree=d)는 특성이 n개인 배열을 특성이 개인 배열로 변환합니다.
따라서 특성 수가 교차항을 포함해 엄청 늘어날 수 있음을 유의해야 합니다.
< 학습 곡선 >
고차 다항 회귀를 적용하면, 보통의 선형 회귀에서보다 훨씬 더 훈련 데이터에 잘 맞추려 할 것입니다.
예를 들면 300차 다항 회귀 모델을 이전의 훈련 데이터에 적용해보면, 훈련 샘플에 가능한 가까이 가는 구불구불한 모습이 됩니다.
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
for style, width, degree in (('g-', 1, 300), ('b--', 2, 2), ('r-+', 2, 1)):
polybig_features = PolynomialFeatures(degree = degree, include_bias = False)
std_scaler = StandardScaler()
lin_reg = LinearRegression()
polynomial_regression = Pipeline([
('poly_features', polybig_features),
('std_scaler', std_scaler),
('lin_reg', lin_reg)
])
polynomial_regression.fit(X,y)
y_newbig = polynomial_regression.predict(X_new)
plt.plot(X_new, y_newbig, style, label=str(degree), linewidth=width)
plt.plot(X,y, 'b.', linewidth=3)
plt.legend(loc='upper left')
plt.xlabel('$x_1$', fontsize=18)
plt.ylabel('$y$', rotation=0, fontsize=18)
plt.axis([-3, 3, 0, 10])
plt.show()
선형, 2차, 300차 회귀를 보면 차이를 알 수 있습니다.
당연하게, 이 고차 다항 회귀 모델은 심각한 과대적합입니다. 반면에 선형은 과소적합입니다.
이 경우 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, random_state=10)
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='validation set')
plt.legend(loc='upper right', fontsize=14)
plt.xlabel('train set volumn', fontsize=14)
plt.ylabel('RMSE', fontsize=14)
lin_reg = LinearRegression()
plot_learning_curves(lin_reg, X, y)
plt.axis([0, 80, 0, 3])
plt.show()
그래프가 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)
plt.axis([0, 80, 0, 3])
plt.show()
이 학습 곡선은 이전과 비슷해 보일 수 있지만, 매우 중요한 두 가지 차이점이 있습니다.
1) 훈련 데이터 오차가 선형 회귀 모델보다 훨씬 낮습니다.
2) 두 곡선 사이에 공간이 있습니다. 이 말은 훈련 데이터에서의 모델 성능이 검증 데이터에서보다 훨씬 낫다는 뜻이고,
이는 과대적합 모델의 특징입니다. 그러나 더 큰 훈련 세트를 사용하면 두 곡선이 점점 가까워집니다.
** 편향/분산 트레이드오프 **
통계학과 머신러닝에서 나온 중요한 이론 하나는 모델의 일반화 오차는 세 가지 다른 종류의 오차의 합으로 표현할 수 있다는 것입니다.
1) 편향
-일반화 오차 중에서 편향은 잘못된 가정으로 인한 것입니다. 예를 들어 데이터가 2차인데 선형으로 가정하는 경우입니다.
-편향이 큰 모델은 훈련 데이터에 과소적합되기 쉽습니다.
2) 분산
-분산(variance)은 훈련 데이터에 있는 작은 변동에 모델이 과도하게 민감하게 나타납니다. 자유도가 높은 모델(예를 들면 고차 다항 회귀 모델)이 높은 분산을 가지기 쉬워 훈련 데이터에 과대적합되는 경향이 있습니다.
3) 줄일 수 없는 오차
-줄일 수 없는 오차(irreducible error)는 데이터 자체에 있는 노이즈 때문에 발생합니다. 이 오차를 줄일 수 있는 유일한 방법은 데이터에서 노이즈를 제거하는 것입니다. (데이터 소스를 고치거나 이상치 감지하여 제거)
모델의 복잡도가 커지면, 통상적으로 분산이 늘어나고 편향은 줄어듭니다.
반대로 모델의 복잡도가 줄어들면, 편향이 커지고 분산이 작아집니다. 그래서 이 둘을 Trade-off 관계라 합니다.
여기서 주의해야 할 점은, 편향의 선형 모델의 절편과 혼동하지 말아야 한다는 점입니다.
편향(Bias)란 용어가 주로 사용되는 곳이 편향/분산 트레이드 오프, 선형 모델의 상수항, 신경망 모델의 상수항입니다.
이 책에서는 선형모델과 신경망 모델을 설명할 때, 혼동을 피하기 위해 절편이란 용어 대신 모두 편향이라고 옮깁니다.
편향/분산 트레이드 오프의 편향은 항상 분산과 같이 등장하므로 쉽게 구별이 가능합니다.
블로그
출처
이 글의 상당 부분은 [핸즈온 머신러닝, 한빛미디어/오렐리앙 제롱/박해선] 서적을 참고하였습니다.
나머지는 부수적인 함수나 메서드에 대해 부족한 설명을 적어두었습니다.
학습용으로 포스팅 하는 것이기 때문에 복제보다는 머신러닝에 관심이 있다면 구매해보시길 추천합니다.
도움이 되셨다면 로그인 없이 가능한
아래 하트♥공감 버튼을 꾹 눌러주세요!
'## 오래된 게시글 (미관리) ## > Python (Linux)' 카테고리의 다른 글
32. Python - 로지스틱 회귀 (0) | 2019.01.28 |
---|---|
31. Python - 규제(릿지 회귀, 라쏘 회귀, 엘라스틱넷, 조기종료) (0) | 2019.01.27 |
29. Python - 미니배치 경사 하강법 (0) | 2019.01.25 |
28. Python - 확률적 경사 하강법 (0) | 2019.01.25 |
27. Python - 배치 경사 하강법 (0) | 2019.01.24 |