1. Polynomial Regression (다항회귀)
Polynomial Regression
회귀식이 독립변수의 단항식(일차식)이 아닌 2차, 3차 방정식과 같은 다항식으로 표현되는 것
- 데이터 세트에 대해서 Feature X에 대해 Target y값의 관계를 단순 선형 회귀 직선형으로 표현한 것보다 다항 회귀 곡선형
- 비선형함수(독립변수가 비선형)는 맞으나 선형 회귀 분석(회귀 계수가 선형적으로 결합)이다
선형 회귀와 다항 회귀의 구분
회귀에서 선형 / 비선형 회귀를 나누는 기준은:
회귀 계수들이 결합한 형태가 선형 / 비선형인지에 따른 것이지 독립변수의 선형 / 비선형 여부와는 무관함
단순 선형 회귀 : $y = W_{0} + W_{1}X_{1}$
다중 선형 회귀: $y = W_{0} + W_{1}X_{1} + W_{2}X_{2} + ......$
다항 회귀 (선형 회귀): $y = W_{0} + W_{1}X^{1} + W_{2}X^{2} + ...... + W_{d}X^{k}$
Scikit - Learn에서의 다항 회귀
사이킷런은 다항회귀를 바로 API로 제공하지 않음
다항회귀 역시 선형회귀이기에 비선형 함수를 선형 모델에 적용시키는 방법을 사용해 구현
대신 PolynomialFeatures 클래스로 원본 단항 피처들을 다항 피처들로 변환한 데이터 세트에 LinearRegression 객체를 적용하여 다항회귀 기능을 제공함
PolynomialFeatures
- 원본 Feature 데이터 세트를 기반으로 degree 차수에 따른 다항식을 적용하여 새로운 Feature들을 생성하는 Class Feature Engineering의 기법 중 하나임
- PolynomialFeatures(degree, include_bias)
- degree: 변환할 다항 피처의 차수
- include_bias: 0차항도 만들 것인지에 대한 여부 (True면 만듦)
Pipeline Class @ Scikit - Learn
사이킷런에서는 일반적으로 Pipeline 클래스를 이용하여
Pipeline 객체는 Feature 엔지니어링 변환과 모델 학습/예측을 순차적으로 결합해줌
PolynomialFeatures 변환과 LinearRegression으로 학습/예측을 결합하여 다항회귀를 구현
Pipeline( [ ( 'poly', PolynomialFeatures(degree)), ('linear', LinearRegression( ) ) ] )
- Pipeline( ) 안에 전체적으로 [ ] 을 치고 각 Pipe는 ( )의 tuple 형태로 관리한다
- ( )와 [ ]의 순서에 유의할 것
Pipeline.named_steps['Class_name']
- Pipeline을 구성하는 세부 객체에 접근함
- 학습 모델 LinearRegression( )의 회귀 계수 추출 가능
2. Polynomial Regression - Code 실습 (1)
PolynomialFeatures 클래스로 다항식 변환
- PolynomialFeatures 클래스로 원본 단항 피처들을 다항 피처들로 변환한 데이터 세트
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
- 각 $x_{i}$는 $X$의 i번째 column (i번째 Feature을 지닌 target값 모음)이라 생각
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]
# [0 1]를 넣으면 5가 나오고, [2 3]을 넣으면 125가 나온다
# 삼차 다항식 결정값:
# [ 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,)
Scikit - Learn 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]
3. Polynomial Regression - Code 실습 (2)
# 문제아님
np.random.seed(0)
# 코드를 실행하세요.
def true_func(X):
return np.cos(1.5*np.pi*X)
X = np.sort(np.random.rand(30))
y = true_func(X) + np.random.rand(30)*0.1
plt.scatter(X,y)
X = X.reshape(-1,1)
y = y.reshape(-1,1)
- degree가 2인 다항회귀 분석을 수행하고 그래프를 그려 확인한다
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
poly = PolynomialFeatures(degree = 2)
X_poly = poly.fit_transform(X)
- 모델의 학습 및 예측
# 선형회귀모델을 lr_d2 변수명으로 생성하고,
# X_poly 데이터를 활용하여 모델을 학습시키세요.
lr_d2 = LinearRegression()
lr_d2.fit(X_poly, y)
# 예측을 수행하고 결과를 y_pred 변수에 저장하세요.
y_pred = lr_d2.predict(X_poly)
# 모델의 회귀계수를 lr_coef 변수에 저장하고
# 코드를 실행하여 다항회귀식을 확인해보세요.
lr_coef = lr_d2.coef_
print(
"다항선형회귀식: y={}+({})x+({})x^2".format(
np.round(lr_d2.intercept_[0], 2),
np.round(lr_coef[0][1], 2),
np.round(lr_coef[0][2], 2),
)
)
# 다항선형회귀식: y=1.54+(-7.23)x+(5.56)x^2
- 그래프를 그린다
# 코드를 실행하여 그래프를 확인하세요. (그래프를 그리기 위한 코드)
X_for_plot = np.arange(0.02,0.99,0.001).reshape(-1,1)
X_for_plot_poly = poly.fit_transform(X_for_plot)
plt.scatter(X,y)
plt.plot(X_for_plot,lr_d2.predict(X_for_plot_poly),color='red')
- degree=16인 다항회귀 분석을 수행 (X, y 데이터 그대로 사용), Pipeline 클래스를 활용해 다항회귀 분석을 한 번에 구현하기
from sklearn.pipeline import Pipeline
# Pipeline객체를 생성하고 model 변수에 저장하세요.
model = Pipeline(
[("poly", PolynomialFeatures(degree=16)),
("linear", LinearRegression())]
)
# model 객체로 학습을 수행하세요.
model.fit(X, y)
- degree=16인 다항회귀식의 그래프를 확인하기
# 코드를 실행하여 degree=16인 다항회귀식의 그래프를 확인해보세요.
# 코드를 실행하여 그래프를 확인하세요. (그래프를 그리기 위한 코드)
plt.scatter(X,y)
plt.plot(X_for_plot,model.predict(X_for_plot),color='blue')
- Variance - Bias Trade off
from sklearn.metrics import r2_score
# 맨 위의 true_func() 함수가 실제 y값에 대한 함수이다.
# 아래 코드를 실행해 모든 그래프를 한 번에 확인해보세요.
X_true = np.arange(0.00, 1.0, 0.001).reshape(-1, 1)
y_true = true_func(X_true)
plt.scatter(X, y, label="train_data")
plt.plot(X_true, y_true, color="green", label="true function")
plt.plot(
X_for_plot,
model.predict(X_for_plot),
color="blue",
label="degree=16",
linestyle="--",
)
plt.plot(
X_for_plot,
lr_d2.predict(X_for_plot_poly),
color="red",
label="degree=2",
linestyle=":",
)
plt.legend(loc="best")
- 모델의 예측 및 평가
# 학습된 모델 lr_d2와 model을 사용하여 예측값을 저장합니다.
lr_d2_pred = lr_d2.predict(poly.fit_transform(X_true))
lr_d16_pred = model.predict(X_true)
# 각각의 R-squared 점수를 lr_d2_r2와 lr_d15_r2에 저장합니다.
lr_d2_r2 = r2_score(y_true, lr_d2_pred)
lr_d16_r2 = r2_score(y_true, lr_d16_pred)
# 결과 확인.
print("degree=2일 때 결정계수: ", lr_d2_r2)
print("degree=20일 때 결정계수: ", lr_d16_r2)
# degree=2일 때 결정계수: 0.9243043674691815
# degree=20일 때 결정계수: 0.6728611547034357
4. Polynomial Regression을 활용한 Boston 주택 가격 예측
- 사이킷런에서는 일반적으로 Pipeline 클래스를 이용하여:
- PolynomialFeatures 변환과 LinearRegression으로 학습/예측을 결합하여 다항회귀를 구현
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=2, 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 : 15.556 , RMSE : 3.944
# Variance score : 0.782
# degree = 3인 경우 Overfitting 발생
# Boston 데이타셋 크기 : (506, 14)
# MSE : 79625.594 , RMSE : 282.180
# Variance score : -1116.598
- degree가 높아질수록 overfitting할 가능성 증가
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))
5. Overfitting & Underfitting
Overfitting : Train data에 대해서 과도하게 학습 & Test data에 대해서는 잘 동작 X
모델이 훈련 데이터에만 특수한 성질을 과하게 학습해 일반화를 못해 결국 테스트 데이터에서 오차가 커지는 현상
- Overfitting은 학습 데이터(Training Set)에 대해 과하게 학습된 상황입니다. 따라서 학습 데이터 이외의 데이터(Test Set)에 대해선 모델이 잘 동작하지 못합니다.
- 학습 데이터가 부족하거나, 데이터의 특성에 비해 모델이 너무 복잡한 경우 발생합니다. Training Set에 대한 loss는 계속 떨어지는데, Test Set에 대한 loss는 감소하다가 다시 증가합니다.
Underfitting : Train data 조차도 학습 X 할 정도로 부족하게 학습
훈련데이터에 과적합도 못하고 일반화 성질도 학습하지 못해, 훈련/테스트 데이터 모두에서 오차가 크게 나는 경우
- Underfitting(과소적합)은 이미 있는 Train set도 학습을 하지 못한 상태를 의미합니다. Overfitting과 반대되는 상태를 의미합니다.
- Underfitting이 발생하는 이유는 아래와 같습니다.
- 학습 반복 횟수가 너무 적음
- 데이터의 특성에 비해 모델이 너무 간단함
- 데이터 양이 너무 적음
- Complex Model: Overfitting occurs, Variance 증가, Bias 감소
- Simple Model: Underfitting occurs, Variance 감소, Bias 증가
- Generalization Error를 줄이는 것이 목적
Polynomial Regression을 이용한 Underfitting, Overfitting의 이해
- 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)
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.98293000e+03 1.03899390e+05 -1.87416123e+06 2.03716219e+07
# -1.44873283e+08 7.09315363e+08 -2.47065792e+09 6.24561050e+09
# -1.15676510e+10 1.56894936e+10 -1.54006023e+10 1.06457264e+10
# -4.91377530e+09 1.35919645e+09 -1.70380786e+08] 입니다.
# Degree 15 MSE 는 181238256.56 입니다.
Bias - Variance Trade off
- Bias: 편향성 / 방향성
- Variance: 분산