프로그래밍/인공지능

다중 회귀(multiple regression)

austag 2024. 2. 5. 20:49

이번엔 다중 회귀 입니다. 특성이 1개만 있지 않으니까 이 회귀도 꼭 필요합니다.

* 다항 회귀랑 헷갈리지 않게 주의해 주세요..! *

선형회귀는 특성을 1개만 사용했습니다. 오늘은 여러 특성을 사용하는 선형 회귀인 다중회귀를 공부해보겠습니다.


1. 다중 회귀와 특성 공학

선형 회귀를 다시 기억해 볼까요? 1개의 특성으로 산점도와 그래프를 그려보면 직선으로 되었습니다. 2개의 특성으로 그리면 평면이 그려집니다.  그럼 방정식은 $(target) = a × (feature 1) + b × (feature 2) + (intercept) $ 이 됩니다.

 

다시 농어의 데이터를 가져올게요.. ㅋㅋ 이번엔 길이 뿐만 아니라 높이와 두께도 추가되었습니다.

데이터를 전처리 할 때, 2가지 특성을 만들 것 입니다. 특성들을 각각 제곱하여 추가하기, 특성을 서로 곱해서 추가하기(ex : 높이 × 길이) 를 해줄 것입니다.

이런 과정을 통해 기존의 특성을 사용하여 새로운 특성을 만드는 것을 특성 공학(feature engineering)이라고 합니다. 

 

2. 데이터 준비

일단 데이터를 불러와서 넘파이로 바꿔줄게요.

import pandas as pd

df = pd.read_csv('https://bit.ly/perch_csv_data')
perch_full = df.to_numpy()
print(perch_full)

 

왼쪽부터 길이, 높이, 두께 입니다.

 

타깃인 무게인 데이터도 넘파이 배열로 준비하고, 데이터들을 학습셋과 테스트셋으로 나눠줍니다.

import numpy as np

perch_weight = np.array([5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0, 110.0,
       115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0, 130.0,
       150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0, 197.0,
       218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0, 514.0,
       556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0, 820.0,
       850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0, 1000.0,
       1000.0])
from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
    perch_full, perch_weight, random_state=42
)

 

그리고 새 특성을 만들어 주어야 하는데 이를 위해 변환기를 공부하고 가겠습니다.

 

3. 변환기

사이킷런에선 특성을 만들거나 처리할 수 있는 클래스인 변환기(transformer)가 있습니다.

먼저 사용할 변환기를 불러와 주겠습니다.

from sklearn.preprocessing import PolynomialFeatures

음.. 일단 익혀봅시다.

poly = PolynomialFeatures()
poly.fit([[2,3]])
print(poly.transform([[2,3]]))

2개의 특성 2, 3으로 이루어진 샘플을 바꿔볼게요. 

fit()은 새롭게 만들 특성 조합을 찾고, transform()은 실제로 데이터를 변환합니다.

출력 결과는 다음과 같이 6개의 특성을 가진 샘플로 나오게 되었습니다.

위에서 특성을 새로 만들 때, 특성들을 곱한다고 했습니다.

2, 3은 그대로 있고, 2를 제곱한 4, 2와 3을 곱한 6, 3을 제곱한 9가 특성이 되었군요. 

흠.. 그럼 1은 갑자기 어디서 나왔을까...

$$ weight = a × (length) + b × (height) + c × (width) + d × 1 $$

이라는 식에서 뒤에 1이 보이나요? 이렇게 절편은 1인 특성과 곱해지는 계수라고 할 수 있습니다.

사이킷런이 절편을 자동으로 추가해서 1이 나오게 된 것이죠.

그러면 절편은 필요가 없으니 제거해주겠습니다.

poly = PolynomialFeatures(include_bias = False)
poly.fit([[2,3]])
print(poly.transform([[2,3]]))

짜잔

 

4. 변환기 적용

자 이런 방식으로 진행되고, 데이터에 적용시켜 보겠습니다.

poly = PolynomialFeatures(include_bias=False)
poly.fit(train_input)
train_poly = poly.transform(train_input)
print(train_poly.shape)

아하. 9개의 특성이 있군요 . 특성이 어떤 것이 있는지 확인해 볼까요?

print(poly.get_feature_names_out())

이런 식으로 어떻게 조합되었는지 알 수 있습니다.

 

5. 모델 훈련

이제 훈련을 해봅시다. 

from sklearn.linear_model import LinearRegression

lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))

오! 굉장히 높네요. 학습셋의 정확도를 봤으니 테스트셋의 정확도도 봐야겠죠.

poly.fit(test_input)
test_poly = poly.transform(test_input)
print(lr.score(test_poly, test_target))

흠 좀 낮네요. 특성을 더 만들면 어떻게 될까요?

5제곱까지 만들어서 해봅시다.

poly = PolynomialFeatures(degree=5, include_bias=False)
poly.fit(train_input)
train_poly = poly.transform(train_input)
test_poly = poly.transform(test_input)
print(train_poly.shape)

우와... 55까지 늘어났습니다. 바로 정확도를 출력해보죠.

lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))

print(lr.score(test_poly, test_target))

예? 오.... 매우 큰 음수..

 

특성이 많아질수록 강력해지는건 맞지만, 너무 과대적합이 되어버립니다.

그러면 과대적합을 줄이는 방법을 다음 글에서 알아봅시다.


 

 

참고 : 혼공머신