MLOps 부트캠프 by 한경+토스뱅크/Machine Learning

다항회귀(Polynomial Regression)

나니니 2024. 9. 10. 00:28

다항회귀란?

선형 회귀는 학습 데이터에 가장 잘 맞는 일차식을 찾아서 예측을 하는 알고리즘인 반면, 다항 회귀는 고차식을 찾아서 예측하는 알고리즘이다. 

 

집 값 예측 문제를 생각해보자.

 

항상 집의 크기라는 변수가 주어진다는 가정을 했고 이걸 하나의 속성으로 이용했는데 사용하는 데이터가 항상 이렇게 하나만 사용해도 의미가 있는 완벽한 변수일 수는 없다.

 

예를 들어 집이 사각형이라고 가정하고 집 크기 대신 집의 높이와 너비 데이터만 있다고 하자.

사실 아무리 너비가 커도 높이가 작거나, 높이만 크고 너비만 작으면 크기가 작고 구조가 효율적이지 않기 때문에 집 값이 높지 않다. 사실 이 두 변수들보다 집값을 예측하는 데 훨씬 좋은 수치는 이 둘을 곱한 값, 집의 넓이다.

단순 선형 회귀를 사용하면 이 두 변수가 서로 독립적이기 때문에 프로그램은 “높이와 너비가 같이 커야지만 집 값도 커진다”라는 관계를 학습할 수 없다.

쉽게 생각하면 "높이"라는 속성이 집 값에 미치는 영향, 그리고 "너비"라는 속성이 집 값에 미치는 영향을 따로 찾아내는 것이다.

너비와 높이처럼 이렇게 딱 맞아떨어지지는 않더라도, 집이 아무리 커도, 높은 층이 아니거나 지하철에서 아무리 가까워도 지은지 20년이 넘었으면 집 값이 낮을 수 있다. 이런 경우들에도 마찬가지로 단순 선형 회귀를 쓰면 속성 하나하나가 다 독립적이기 때문에 이런 변수들 사이의 관계를 최종 예측 결과에 반영하지 못하는 것이다.

속성들을 서로 곱해서 차항을 높여주면, 그러니까 선형 회귀 문제를 다항 회귀 문제로 만들어주면 속성들 사이에 있을 수 있는 복잡한 관계들을 프로그램에 학습시킬 수 있다.

 

 

예를 들어서 집 크기가 집 가격과 어떤 관계인지 이런 식으로 데이터가 있다고 하자.

이 데이터에 가장 잘 맞는 직선을 찾으면, 대충 이렇게 생겼다. 

직선을 최대한 잘 그린 것 같은데도, 이 직선이 데이터를 잘 표현한 것 같진 않다. 이걸 어떻게 개선할 수 있을까?

직선이 아닌 곡선을 사용해보자. 

그러면 이 곡선을 어떻게 찾을 수 있을까요? 챕터 1에서 선형 회귀를 할 때 가설 함수는 이렇게 생겼다.

이 가설 함수는 직선이니까, 가설 함수를 곡선으로 바꾸면 된다.

예를 들어서, 데이터에 가장 잘 맞는 곡선이 이차 함수일 것 같으면 이런 식으로...

이차 함수
이차 가설 함수

가설 함수를 쓸 수 있다.

만약 데이터에 가장 잘 맞는 곡선이 이차 함수가 아니라 삼차 함수일 것 같으면...

삼차 함수
3차 가설 함수

이렇게 가설 함수를 정하면 된다. 

 

마찬가지로 4차 함수는 가설 함수가 이렇게 생겼고,

4차 가설 함수

5차 함수는 가설 함수가 이렇게 생겼습니다.

5차 가설 함수

함수의 차수가 더 높을수록 더 굴곡이 많은 곡선인데, 우리는 데이터를 보고 적합할 것 같은 모양을 골라야 한다. 

 

이런 식으로 데이터에 잘 맞는 일차 함수나 직선을 구하는 게 아니라 다항식이나 곡선을 구해서 학습하는 걸 '다항 회귀', 영어로는 'polynomial regression'이라고 부릅니다

 

단일 속성 다항 회귀

다항 회귀도 두 가지로 나뉜다.

속성이 하나인 경우랑, 속성이 많은 경우. 속성이 많고 다항 회귀를 하면, '다중 다항 회귀'라고 부르는데, 먼저 속성이 하나인 경우부터 보자. 

 

크기를 이용해서 집 가격을 예측한다고 합시다. 선형 회귀에서 가설 함수는 이렇게 생겼습니다.

그리고 우리가 선형 회귀를 통해 하려는 건, 학습 데이터에 가장 잘 맞는 $θ_0$ 과 $θ_1$을 찾아내는 것이다. 

선형 회귀가 아니라 다항 회귀를 한다면 가설 함수가 어떻게 생겼을까? 가설 함수가 이차 함수라면, 이렇게 생겼다.

선형 회귀랑 조금 다르게 생긴 것 같지만, 사실 거의 똑같다.

선형 회귀를 할 때랑 마찬가지로, 다항 회귀에서 하려는 건 학습 데이터에 가장 잘 맞는 $θ_0, θ_1$, 그리고 $θ_2$ 를 찾아내는 것이다.

만약 가설 함수가 삼차 함수면 이렇게 생겼을 것이다.

그러면 이번에도 그냥 학습 데이터에 가장 잘 맞는 $θ_0, θ_1, θ_2$, 그리고 $θ_3$을 찾아내면 된다. 

그런데 이걸 어떻게 할 수 있을까?

사실 잘 생각해 보면, 이건 입력 변수가 3개인 다중 선형 회귀랑 굉장히 유사한 모습이다.

여기서 위에는 다항 회귀의 가설 함수고, 밑에 있는 건 입력 변수가 3개인 다중 선형 회귀의 가설 함수인데 거의 비슷하게 생겼다.

우리가 만약 이 $x, x^2, x^3$을 밑에 있는 $x_1, x_2, x_3$처럼 취급할 수 있다면, 사실상 똑같은 형태라고 할 수 있다. 

 

예를 들면, 
이런 식으로 데이터가 있다고 해보자.

이 테이블에 가상의 열 두 개를 추가하는데 지금 크기에다가 크기를 제곱한 것과 크기를 세제곱한 걸 넣는다.

그러면 이제 이 데이터를 마치 입력 변수가 3개인 것처럼 취급할 수 있고, 

이제 이 데이터를 갖고 다중 선형 회귀를 하면 되는 것이다. 

다중 다항 회귀

다항 회귀를 하는 방법은 입력 변수를 제곱하고 세제곱한 값들로 가상의 열을 추가하고, 이 새로운 열들을 마치 입력 변수처럼 취급하는 것이다. 그러면 입력 변수가 여러 개 있는 다중 선형 회귀랑 똑같은 방식으로 하면 된다. 

 

그런데 위의 집 크기로 집 가격을 예측하는 예시에서는 입력 변수가 하나밖에 없었다. 만약 입력 변수가 많은데 다항 회귀를 하려고 하면, '다중 다항 회귀'라고 하는데 이걸 어떻게 할 수 있을까?

아래 표를 보자. 

 

 

입력 변수가 집 크기만 있는 게 아니라, 방 수, 지하철역까지의 거리도 있다.

각각 $x_1, x_2, x_3$이라고 이름을 붙여 보자. 그리고 목표 변수는 똑같이 집의 가격이다. 

가설 함수가 이차 함수라고 가정해보자. 

이차 함수니까 제일 큰 항이 이차항이어야 하므로 가설 함수에 상수항, 일차항, 이차항... 이렇게 있는 것이다. 만약 가설 함수가 삼차 함수라면 삼차항까지 있을 것이다. 

아무튼 일단은 이차항까지만 있다고 가정하는데, 이차항이란 뭘까? 이차항은 변수 두 개가 곱해진 것이다. 예를 들어서 $x1$ 제곱도 이차항이고, $x1$과 $x2$를 곱해도 이차항이다. 이런 식으로 우리 3개 변수 로 가능한 모든 이차항을 보면...

이렇게 6개이고, 가설 함수는 아래와 같다. 

맨 앞에 상수항이 있고... 그 다음은 일차항들이 있고... 그 다음은 이차항들이 있다. 

이렇게 보면 복잡해 보이지만... 이번에도 이 6가지에 대해서 가상의 열을 추가해 보자.

그러면 이제 이 문제를 입력 변수가 9개인 다중 선형 회귀라고 생각할 수 있다.

다항 회귀라는 생각을 하지 않고, 그냥 다중 선형 회귀인 것처럼 취급을 하고 문제를 풀면 되는 것이다. 

 

예제 - 다항회귀로 당뇨병 예측하기

더보기

당뇨병 데이터를 다항 회귀 문제로 변환했는데, 이를 사용해서 다항 회귀를 직접 해보자.

아래 순서대로 진행해 볼게요.
1. 데이터를 training/test set으로 나눈다.
2. 선형 회귀 모델을 가져와서, training set 데이터를 사용해서 학습시킨다.
3. test set 데이터를 이용해서 학습시킨 모델로 예측한다.

조건
- train_test_split 함수의 옵셔널 파라미터는 test_size=0.2, random_state=5 이렇게 설정해 주세요.
- 예측 값 벡터 변수 이름은 꼭 y_test_predict를 사용하세요!
- 정답 확인은 모델의 성능으로 합니다. (템플렛 가장 아래 줄에 출력 코드 있음)

# 필요한 라이브러리 import
from sklearn import datasets
from sklearn.preprocessing import PolynomialFeatures

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

import pandas as pd  

diabetes_dataset = datasets.load_diabetes()  # 데이터 셋 갖고오기

polynomial_transformer = PolynomialFeatures(2)  # 2 차식 변형기를 정의한다
polynomial_features = polynomial_transformer.fit_transform(diabetes_dataset.data)  # 당뇨 데이터를 2차항 문제로 변환

features = polynomial_transformer.get_feature_names(diabetes_dataset.feature_names)  # 입력 변수 이름들도 맞게 바꾼다

X = pd.DataFrame(polynomial_features, columns=features)

# 목표 변수
y = pd.DataFrame(diabetes_dataset.target, columns=['diabetes'])

# train_test_split를 사용해서 주어진 데이터를 학습, 테스트 데이터로 나눈다
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state=5)

# 선형 회귀 모델을 가지고 오고 
linear_regression_model = LinearRegression()
linear_regression_model.fit(X_train, y_train)  # 학습 데이터를 이용해서 모델을 학습 시킨다

# 평균 제곱 오차의 루트를 통해서 테스트 데이터에서의 모델 성능 판단
y_test_predict = linear_regression_model.predict(X_test)
mse = mean_squared_error(y_test, y_test_predict)

mse ** 0.5



#실습 결과
57.877049027249242