이전 내용
다항 회귀 이해
- 다항 (Polynomial) 회귀: 회귀가 독립변수의 단항식이 아닌 2차, 3차 방정식과 같은 다항식으로 표현되는 것. 회귀에서 선형 회귀/비선형 회귀를 나누는 기준은 회귀 계수가 선형/비선형에 따른 것이지 독립변수의 선형/비선형 여부와는 무관하므로 다항 회귀 역시 선형 회귀이다.
사이킷런은 다항 회귀를 위한 클래스를 명시적으로 제공하지 않는 대신, 비선형 함수를 선형 모델엘 적용시키는 방법을 사용해 구현한다. 이를 위해 사이킷런은 PolynomialFeatures 클래스를 통해 피처를 다항식 피처로 변환한다.
[PolynomialFeatures 클래스]
PolynomialFeatures 클래스는 degree 파라미터를 통해 입력받은 단항식 피처를 degree 에 해당하는 다항식 피처로 변환하는데, fit(), transform() 메서드를 통해 이 같은 변환 작업을 수행한다.
1) PolynomialFeatures를 이용해 단항값을 2차 다항값으로 변환
# 단항값을 다항값으로 변환하는 예제
from sklearn.preprocessing import PolynomialFeatures
import numpy as np
# 다항식으로 변환한 단항식 생성, [[0, 1], [2, 3]]의 2*2 행렬 생성
X = np.arange(4).reshape(2,2)
print('일차 단항식 계수 피처:\n', X)
# degree = 2인 2차 다항식으로 벼환하기 위해 PolynomialFeatures를 이용해 변환
poly = PolynomialFeatures(degree=2)
poly.fit(X)
poly_ftr = poly.transform(X)
print('변환된 2차 다항식 계수 피처:\n', poly_ftr)
2) 3차 다항 계수를 이용해 3차 다항 회귀 함수식 유도 - PolynomialFeatures와 LinearRegression 클래스 이용
# 3차 다항 계수를 이용해 3차 다항 회귀 함수식 유도
from sklearn.preprocessing import PolynomialFeatures
import numpy as np
def polynomial_func(X):
y = 1 + 2*X[:, 0] + 3*X[:, 0]**2 + 4*X[:, 1]**3
return y
X = np.arange(4).reshape(2,2)
print('일차 단항식 계수 피처:\n', X)
y = polynomial_func(X)
print('삼차 다항식 결정값: \n', y)
3) 다항 회귀 구현 - 일차 단항식 계수를 삼차 다항식 계수로 변환하고, 이를 선형 회귀에 적용
# 다항 회귀 구현
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
import numpy as np
X = np.arange(4).reshape(2,2)
# 3차 다항식 변환
poly_ftr = PolynomialFeatures(degree=3).fit_transform(X)
print('3차 다항식 계수 피처: \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)
▶ 피처 변환과 선형 회귀 적용을 각각 별도로 하는 것보다는 사이킷런의 Pipeline 객체를 이용해 한 번에 다항 회귀를 구현하는 것이 코드를 더 명료하게 작성하는 방법이다.
※ Pipeline은 데이터 전처리에서 학습까지의 과정을 하나로 연결해주는 것이다.
Pipeline을 사용하는 경우 Pipeline에 작업(변수 선택, 표준화, 모형 클래스)을 등록만 해주면 기존 과정을 한 번에 처리할 수 있다. Pipeline에 작업을 등록하는 것은 어렵지 않기 때문에 모형 학습에 있어서 여러가지 과정을 거쳐야 한다면 확실히 Pipeline을 사용하는 것이 유리하다.
# 파아프라인 객체를 이용한 다항 회귀 구현
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
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))
다항 회귀를 이용한 과소적합 및 과적합 이해
다항 회귀는 피처의 직선적 관계가 아닌 복잡한 다항 관계를 모델링할 수 있으며, 다항식의 차수가 높아질수록 매우 복잡한 피처 간의 관계까지 모델링이 가능하다. 그러나 다항 회귀의 차수(degree)를 높일수록 학습 데이터에만 너무 맞춘 학습이 이뤄져서 정작 테스트 데이터 환경에서는 예측 정확도가 떨어지는 과적합의 문제가 크게 발생한다.
[다항 회귀의 차수를 변화시키면서 그에 따른 회귀 예측 곡선과 예측 정확도 비교]
학습 데이터는 30개의 임의의 데이터인 X, X의 코사인 값에서 약간의 잡음 변동 값을 더한 target인 y로 구성
# 다항 회귀의 차수를 변화시키면서 그에 따른 회귀 예측 곡선과 예측 정확도 비교
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score
%matplotlib inline
# 임의의 값으로 구성된 X값에 대해 코사인 변환 값 반환
def true_fun(X):
return np.cos(1.5 * np.pi * X)
# X는 0부터 1까지 30개의 임의의 값을 순서대로 샘플링한 데이터
np.random.seed(0)
n_samples = 30
X = np.sort(np.random.rand(n_samples))
# y 값은 코사인 기반의 true_fun()에서 약간의 노이즈 변동 값을 더한 값
y = true_fun(X) + np.random.randn(n_samples) * 0.1
- 예측 결과를 비교할 다항식 차수를 각각 1, 4, 15로 변경하면서 예측 결과 비교
- 다항식 차수별로 학습을 수행한 뒤 cross_val_score() 로 MSE 값을 구해 차수별 예측 성능 평가
- 0부터 1까지 균일하게 구성된 100개의 테스트용 데이터 세트를 이용해 차수별 회귀 예측 곡선 그리기
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)
# pipline 구성하는 세부 객체를 접근하는 name_steps['객체명']을 이용해 회귀계수 추출
coefficients = pipeline.named_steps['linear_regression'].coef_
print('\nDegree {0} 회귀 계수는 {1} 입니다.'.format(degrees[i], np.round(coefficients, 2)))
print('Degree {0} MSE는 {1} 입니다.'.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()
▶
- 실선으로 표현된 예측 곡선: 다항 회귀 예측 곡선
- 점선으로 표현된 곡선: 실제 데이터 세트 X, Y의 코사인 곡선
- 학습 데이터: 0부터 1까지의 30개의 임의의 X값과 그에 따른 코사인 Y 값에 잡음을 변동 값으로 추가해 구성
- MSE(Mean Squared Error, 평균 제곱 오차) 평가: 학습 데이터를 10개의 교차 검증 세트로 나누어 측정해서 평균
<Degree 1 예측 곡선>
- 단순한 직선으로 선형 회귀와 같다
- 실제 데이터 세트인 코사아니 데이터 세트를 직선으로 예측하기에는 단순
- 예측 곡선이 학습 데이터의 패턴을 제대로 반영하지 못하고 있는 과소 적합 모델
- MSE 값은 약 0.41
- 고편향(High Bias) 성 가짐
<Degree 4 예측 곡선>
- 실제 데이터 세트와 유사한 모습
- 변동하는 잡음을 예측하지는 못했으나, 학습 데이터 세트를 비교적 잘 반영해 코사인 곡선 기반으로 테스트 데이터를 잘 예측한 곡선을 가진 모델
- MSE 값은 약 0.04로 가장 뛰어난 예측 성능
- 학습 데이터의 패턴을 잘 반영하면서도 복잡하지 않은 균형 잡힌(Balanced) 모델
<Degree 15 예측 곡선>
- MSE 값은 터무니 없는 오류값 발생
- 예측 곡선을 보면 데이터 세트의 변동 잡음 값까지 지나치게 반영
▶ 학습 데이터 세트만 정확히 예측하고, 테스트 값의 실제 곡선과는 완전히 다른 형태의 예측 곡선 생성
- 학습 데이터에 너무 충실하게 맞춘 과적합이 심한 모델
- 고분산(High Variance) 성 가짐
편향-분산 트레이드오프(Bias-Variance Trade off)
- 고편향성을 가진 모델: 지나치게 한 방향성으로 치우친 경향이 있는 모델
- 고분산성을 가진 모델: 학습 데이터 하나 하나의 특성을 반영하여 지나치게 높은 변동성을 가지게 된 매우 복잡한 모델
편향-분산 트레이드오프(Bias-variance tradeoff) (또는 딜레마(dilemma))는 지도 학습 알고리즘이 트레이닝 셋의 범위를 넘어 지나치게 일반화하는 것을 예방하기 위해 두 종류의 오차(편향, 분산)를 최소화 할 때 겪는 문제이다.
편향은 학습 알고리즘에서 잘못된 가정을 했을 때 발생하는 오차이다. 높은 편향값은 알고리즘이 데이터의 특징과 결과물과의 적절한 관계를 놓치게 만드는 과소적합(underfitting) 문제를 발생 시킨다.
분산은 트레이닝 셋에 내재된 작은 변동(fluctuation) 때문에 발생하는 오차이다. 높은 분산값은 큰 노이즈까지 모델링에 포함시키는 과적합(overfitting) 문제를 발생 시킨다.
편향-분산 분해는 학습 알고리즘의 기대 오차를 분석하는 한 가지 방법으로, 오차를 편향, 분산, 그리고 데이터 자체가 내재하고 있어 어떤 모델링으로도 줄일수 없는 오류의 합으로 본다. 편향-분산 트레이드 오프는 분류(classification), 회귀분석 그리고 구조화된 출력 학습 출력학습(structed output learning) 등 모든 형태의 지도 학습에 응용된다. 또한 사람의 학습에서 직관적 판단 오류(heuristics)의 효과성을 설명하기 위해 언급되기도 한다.
편향과 분산의 고/저 | 특징 |
저편향/저분산 | 예측 결과가 실제 결과에 매우 잘 근접하면서도 예측 변동이 크지 않고 특정 부분에 집중돼 있는 아주 뛰어난 성능을 보여줌 |
저편향/고분산 | 예측 결과가 실제 결과에 비교적 근접하지만, 예측 결과가 실제 결과를 중심으로 꽤 넓은 부분에 분포. |
고편향/저분산 | 정확한 결과에서 벗어나면서도 예측이 특정 부분에 집중 |
고편향/고분산 | 정확한 예측 결과를 벗어나면서도 넓은 부분에 분포 |
- 일반적으로 편향과 분산은 한쪽이 높으면 한쪽이 낮아지는 경향을 보인다.
- 편향이 높으면 분산은 낮아지고(과소적합), 반대로 분산이 높으면 편향은 낮아진다. (과대적합)
- 편향이 너무 높으면 전체 오류가 높고, 편향을 점점 낮추면 동시에 분산이 높아지고 전체 오류도 낮아지게 된다.
- 편향을 낮추고 분산을 높이면서 전체 오류가 가장 낮아지는 '골디락스' 지점을 통과하면서 분산을 지속적으로 높이면 전체 오류 값이 오히려 증가하면서 예측 성능이 다시 저하된다.
편향과 분산이 서로 트레이드오프를 이루면서 오류 Cost 값이
최대로 낮아지는 모델을 구축하는 것이 가장 효율적인 머신러닝 예측 모델을 만드는 방법이다.
다음 내용
[출처]
파이썬 머신러닝 가이드
부자되고픈 꽁냥이 - T스토리 블로그
위키백과
'[파이썬 Projects] > <파이썬 머신러닝>' 카테고리의 다른 글
[머신러닝] 로지스틱 회귀 (3) | 2024.10.23 |
---|---|
[머신러닝] 회귀 - 규제 선형 모델: 릿지, 라쏘, 엘라스틱넷 (1) | 2024.10.22 |
[머신러닝] 회귀 - LinearRegression 클래스 (2) | 2024.10.21 |
[머신러닝] 경사 하강법(GD, gradient descent) (3) | 2024.09.22 |
[머신러닝] 회귀(Regression) (2) | 2024.09.21 |