[Python 머신러닝] 05-5 다항 회귀와 과(대)적합/과소적합 이해
회귀
다항 회귀와 과(대)적합/과소적합 이해
다항 회귀 이해
다항 회귀는 $y = w_0 + w_1 \times x_1 + w_2 \times x_2 + w_3 \times x_1 \times x_2 + w_4 \times {x_1}^2 + w_5 \times {x_2}^2$과 같이 회귀식이 독립변수의 단항식이 아닌 2차, 3차 방정식과 같은 다항식으로 표현되는 것을 지칭한다.
데이터 세트에 대해서 피처 X에 대해 Target Y 값의 관계를 단순 선형 회귀 직선형으로 표현한 것보다 다항 회귀 곡선형으로 표현한 것이 더 예측 성능이 높다.
선형 회귀와 비선형 회귀의 구분
-
선형 회귀
$y = w_0 + w_1 \times x_1 + w_2 \times x_2 + w_3 \times x_1 \times x_2 + w_4 \times {x_1}^2 + w_5 \times {x_2}^2$새로운 변수인 Z를 z = [$x_1$, $x_2$, $x_1\times x_2$, ${x_1}^2$, ${x_2}^2$]로 한다면,
$y = w_0 + w_1 \times z_1 + w_2 \times z_2 + w_3 \times z_3 + w_4 \times z_4 + w_5 \times z_5$ -
비선형 회귀 $Y = w_1 \times cos(X + w_4) + w_2 \times cos(2\times X + w_4) + w_3$
$Y = w_1 \times X^{w2}$
사이킷런에서의 다항 회귀
사이킷런은 다항회귀를 바로 API로 제공하지 않고 PolynomialFeatures 클래스로 원본 단항 피처들을 변환한 데이터 세트에 LinearRegression 객체를 적용하여 다항 회귀 기능을 제공한다.
PolynomialFeatures: 원본 피처 데이터 세트를 기반으로 degree 차수에 따른 다항식을 적용하여 새로운 피처들을 생성하는 클래스 피처 엔지니어링의 기법 중의 하나
PolynomialFeatures 변환 (단항 피처 [$x_1, x_2$]를 2차 다항 피처 [$1, x_1, x_2, x_1x_2, {x_1}^2, {x_2}^2$]로 변경) -> LinearRegression 학습 (PolynomialFeatures로 변환된 X 피처들을 LinearRegression 객체로 학습)
-> 사이킷런에서는 일반적으로 Pipeline 클래스를 이용하여 PolynomialFeatures 변환과 LinearRegression 학습/예측을 결합하여 다항 회귀를 구현한다.
<실습>실습>
PolynomialFeatures 클래스로 다항식 변환
- 1차 단항 피처들의 값이 [$x_1, x_2$] = [0 1]일 경우
2차 다항 피처들의 값은 [$1, x_1=0, x_2=1, x_1x_2=0, {x_1}^2=0, {x_2}^2=1$] 형태인 [0, 1, 0, 0, 1]로 변환
from sklearn.preprocessing import PolynomialFeatures
import numpy as np
# 다항식으로 변환한 단항식 생성, [[0,1],[2,3]]의 2X2 행렬 생성
X = np.arange(4).reshape(2,2)
print('일차 단항식 계수 feature:\n',X )
# degree = 2 인 2차 다항식으로 변환하기 위해 PolynomialFeatures를 이용하여 변환
poly = PolynomialFeatures(degree=2)
poly.fit(X)
poly_ftr = poly.transform(X)
print('변환된 2차 다항식 계수 feature:\n', poly_ftr)
일차 단항식 계수 feature:
[[0 1]
[2 3]]
변환된 2차 다항식 계수 feature:
[[1. 0. 1. 0. 0. 1.]
[1. 2. 3. 4. 6. 9.]]
- 3차 다항식 결정값을 구하는 함수 polynomial_func(X) 생성. 즉 회귀식은 결정값 y = 1+ 2x_1 + 3x_1^2 + 4x_2^3
def polynomial_func(X):
y = 1 + 2*X[:,0] + 3*X[:,0]**2 + 4*X[:,1]**3
print(X[:, 0])
print(X[:, 1])
return y
X = np.arange(0,4).reshape(2,2)
print('일차 단항식 계수 feature: \n' ,X)
y = polynomial_func(X)
print('삼차 다항식 결정값: \n', y)
일차 단항식 계수 feature:
[[0 1]
[2 3]]
[0 2]
[1 3]
삼차 다항식 결정값:
[ 5 125]
- 3차 다항식 계수의 피처값과 3차 다항식 결정값으로 학습
# 3 차 다항식 변환
poly_ftr = PolynomialFeatures(degree=3).fit_transform(X)
print('3차 다항식 계수 feature: \n',poly_ftr)
# Linear Regression에 3차 다항식 계수 feature와 3차 다항식 결정값으로 학습 후 회귀 계수 확인
model = LinearRegression()
model.fit(poly_ftr,y)
print('Polynomial 회귀 계수\n' , np.round(model.coef_, 2))
print('Polynomial 회귀 Shape :', model.coef_.shape)
3차 다항식 계수 feature:
[[ 1. 0. 1. 0. 0. 1. 0. 0. 0. 1.]
[ 1. 2. 3. 4. 6. 9. 8. 12. 18. 27.]]
Polynomial 회귀 계수
[0. 0.18 0.18 0.36 0.54 0.72 0.72 1.08 1.62 2.34]
Polynomial 회귀 Shape : (10,)
사이킷런 파이프라인(Pipeline)을 이용하여 3차 다항회귀 학습
- 사이킷런의 Pipeline 객체는 Feature 엔지니어링 변환과 모델 학습/예측을 순차적으로 결합해줍니다.
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
import numpy as np
def polynomial_func(X):
y = 1 + 2*X[:,0] + 3*X[:,0]**2 + 4*X[:,1]**3
return y
# Pipeline 객체로 Streamline 하게 Polynomial Feature변환과 Linear Regression을 연결
model = Pipeline([('poly', PolynomialFeatures(degree=3)),
('linear', LinearRegression())])
X = np.arange(4).reshape(2,2)
y = polynomial_func(X)
model = model.fit(X, y)
print('Polynomial 회귀 계수\n', np.round(model.named_steps['linear'].coef_, 2))
Polynomial 회귀 계수
[0. 0.18 0.18 0.36 0.54 0.72 0.72 1.08 1.62 2.34]
다항 회귀를 이용한 보스턴 주택가격 예측
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error , r2_score
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
import numpy as np
# boston 데이타셋 로드
boston = load_boston()
# boston 데이타셋 DataFrame 변환
bostonDF = pd.DataFrame(boston.data , columns = boston.feature_names)
# boston dataset의 target array는 주택 가격임. 이를 PRICE 컬럼으로 DataFrame에 추가함.
bostonDF['PRICE'] = boston.target
print('Boston 데이타셋 크기 :',bostonDF.shape)
y_target = bostonDF['PRICE']
X_data = bostonDF.drop(['PRICE'],axis=1,inplace=False)
X_train , X_test , y_train , y_test = train_test_split(X_data , y_target ,test_size=0.3, random_state=156)
## Pipeline을 이용하여 PolynomialFeatures 변환과 LinearRegression 적용을 순차적으로 결합.
p_model = Pipeline([('poly', PolynomialFeatures(degree=3, include_bias=False)),
('linear', LinearRegression())])
p_model.fit(X_train, y_train)
y_preds = p_model.predict(X_test)
mse = mean_squared_error(y_test, y_preds)
rmse = np.sqrt(mse)
print('MSE : {0:.3f} , RMSE : {1:.3F}'.format(mse , rmse))
print('Variance score : {0:.3f}'.format(r2_score(y_test, y_preds)))
Boston 데이타셋 크기 : (506, 14)
MSE : 79625.594 , RMSE : 282.180
Variance score : -1116.598
X_train_poly= PolynomialFeatures(degree=2, include_bias=False).fit_transform(X_train, y_train)
X_train_poly.shape, X_train.shape
((354, 104), (354, 13))
다항 회귀를 이용한 과소적합 및 과적합 이해
<실습>실습>
- 사이킷런의 아래 문서에서 예제를 차용함
- https://scikit-learn.org/stable/auto_examples/model_selection/plot_underfitting_overfitting.html
cosine 곡선에 약간의 Noise 변동값을 더하여 실제값 곡선을 만듬
import numpy as np
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score
%matplotlib inline
# random 값으로 구성된 X값에 대해 Cosine 변환값을 반환.
def true_fun(X):
return np.cos(1.5 * np.pi * X)
# X는 0 부터 1까지 30개의 random 값을 순서대로 sampling 한 데이타 입니다.
np.random.seed(0)
n_samples = 30
X = np.sort(np.random.rand(n_samples))
# y 값은 cosine 기반의 true_fun() 에서 약간의 Noise 변동값을 더한 값입니다.
y = true_fun(X) + np.random.randn(n_samples) * 0.1
plt.scatter(X, y)
<matplotlib.collections.PathCollection at 0x171cb68bfd0>
plt.figure(figsize=(14, 5))
degrees = [1, 4, 15]
# 다항 회귀의 차수(degree)를 1, 4, 15로 각각 변화시키면서 비교합니다.
for i in range(len(degrees)):
ax = plt.subplot(1, len(degrees), i + 1)
plt.setp(ax, xticks=(), yticks=())
# 개별 degree별로 Polynomial 변환합니다.
polynomial_features = PolynomialFeatures(degree=degrees[i], include_bias=False)
linear_regression = LinearRegression()
pipeline = Pipeline([("polynomial_features", polynomial_features),
("linear_regression", linear_regression)])
pipeline.fit(X.reshape(-1, 1), y)
# 교차 검증으로 다항 회귀를 평가합니다.
scores = cross_val_score(pipeline, X.reshape(-1,1), y,scoring="neg_mean_squared_error", cv=10)
coefficients = pipeline.named_steps['linear_regression'].coef_
print('\nDegree {0} 회귀 계수는 {1} 입니다.'.format(degrees[i], np.round(coefficients, 2)))
print('Degree {0} MSE 는 {1:.2f} 입니다.'.format(degrees[i] , -1*np.mean(scores)))
# 0 부터 1까지 테스트 데이터 세트를 100개로 나눠 예측을 수행합니다.
# 테스트 데이터 세트에 회귀 예측을 수행하고 예측 곡선과 실제 곡선을 그려서 비교합니다.
X_test = np.linspace(0, 1, 100)
# 예측값 곡선
plt.plot(X_test, pipeline.predict(X_test[:, np.newaxis]), label="Model")
# 실제 값 곡선
plt.plot(X_test, true_fun(X_test), '--', label="True function")
plt.scatter(X, y, edgecolor='b', s=20, label="Samples")
plt.xlabel("x"); plt.ylabel("y"); plt.xlim((0, 1)); plt.ylim((-2, 2)); plt.legend(loc="best")
plt.title("Degree {}\nMSE = {:.2e}(+/- {:.2e})".format(degrees[i], -scores.mean(), scores.std()))
plt.show()
Degree 1 회귀 계수는 [-1.61] 입니다.
Degree 1 MSE 는 0.41 입니다.
Degree 4 회귀 계수는 [ 0.47 -17.79 23.59 -7.26] 입니다.
Degree 4 MSE 는 0.04 입니다.
Degree 15 회귀 계수는 [-2.98294000e+03 1.03899850e+05 -1.87416981e+06 2.03717199e+07
-1.44874017e+08 7.09319141e+08 -2.47067173e+09 6.24564702e+09
-1.15677216e+10 1.56895933e+10 -1.54007040e+10 1.06457993e+10
-4.91381016e+09 1.35920643e+09 -1.70382078e+08] 입니다.
Degree 15 MSE 는 182581084.83 입니다.
편향-분산 트레이드오프(Bias-Variance Trade off)
- 편향이 높으면 분산은 낮아진다. (과소적합)
- 분산이 높으면 편향이 낮아진다. (과대적합)