본문 바로가기

머신러닝

[머신러닝] 다중 선형 회귀(Multiple Linear Regression)

728x90
반응형

 

 

[목차]

 

1. 다중 선형 회귀

 

2. 다중 선형 회귀 실습

 

 

  1. 다중 선형 회귀

 

다중 선형 회귀(Multiple Linear Regression, MLR)여러 개의 독립 변수와 하나의 종속 변수의 선형 관계를 모델링하는 것이다. 

독립 변수들을 $x_i$라고 하고, 종속 변수를 $y$라고 하면 다중 선형 회귀 식은 다음과 같다. 

$y = W_1x_1 + W_2x_2 + ... + W_nx_n + b$ 

 

다중 선형 회귀를 사용할 땐 다음과 같은 가정들이 필요하다. 

 

1. 각각의 독립 변수는 종속 변수와의 선형 관계가 존재한다. ($y = W_ix_i$ 그래프를 그렸을 때 직선의 형태)

2. 독립 변수 사이에서는 높은 수준의 상관관계가 존재하지 않아야 한다. 만약 그렇지 않다면 관계를 분석하기가 어렵다. 

3. 추정된 종속 변수의 값과 실제 관찰된 종속 변수의 값과의 차이, 즉 잔차(residual)가 정규 분포를 이루어야 한다. 

 

 

  2. 다중 선형 회귀 실습

 

단순 선형 회귀에 비해 특성이 여러 개로 늘어났기 때문에 데이터를 직접 입력하기엔 불편함이 많다. 따라서 유명한 데이터 분석 라이브러리인 판다스(Pandas)를 사용해보자.

 

판다스를 이용하면 데이터를 인터넷에서 바로 다운로드하여 사용할 수 있다. 그리고 판다스에는 데이터프레임(Dataframe)이라는 데이터 구조를 갖는데, 넘파이 배열과 비슷하게 다차원 배열을 다룰 수 있고 더 많은 기능을 제공한다. 데이터프레임은 넘파이 배열로 바꿀 수도 있다. 

 

그러므로 판다스를 이용해 데이터를 내려받고, 넘파이 배열로 변환하여 모델을 훈련해보자. 

import pandas as pd
import numpy as np

df = pd.read_csv('https://bit.ly/perch_csv')
perch_full = df.to_numpy()
print(perch_full)
#[[ 8.4   2.11  1.41]
# [13.7   3.53  2.  ]
# [15.    3.82  2.43]
# [16.2   4.59  2.63]
# ...]

 

순서대로 길이(Length), 높이(Height), 너비(Width)를 의미하는 값이다.  

타깃 데이터는 이전과 동일하게 사용하고, 훈련 셋과 테스트 셋을 나눈다. 

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])

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

 

사이킷런에서는 변환기(Transformer)라고 불리는 클래스들을 제공한다. 변환기는 데이터셋을 만들거나 변환하거나 전처리하는 역할을 수행한다. 변환기 클래스는 모두 fit(), transform() 메서드를 제공한다. 

 

fit() 메서드는 새롭게 만들 특성 조합을 찾고 transform() 메서드는 실제로 데이터를 변환한다. 

입력 데이터를 변환하는 데는 타깃 데이터가 필요하지 않으므로 fit() 메서드에 입력 데이터만 전달한다. 두 메서드를 하나로 붙인 fit_transform() 메서드도 존재한다. 

 

우리는 PolynomialFeatures 클래스를 사용할 것이다. PolynomialFeatures 클래스는 입력 데이터 $x$를 $[1, x, x^2, x^3, ...] $과 같이 여러 다항식으로 변환한다.

만약 입력 데이터가 $x, y$이면, $[1, x, y, x^2, y^2, xy, x^3, y^3, x^2y, ...]$ 과 같이 변환된다. 따라서 PolynomialFeatures 클래스는 차수를 결정하는 degree상수항 생성 여부를 결정하는 include_bias를 매개변수로 갖는다. 

from sklearn.preprocessing import PolynomialFeatures

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

 

2개의 특성을 가진 샘플 [2, 3]이 5개의 특성을 가진 샘플 [2. 3. 4. 6. 9.]로 바뀐 것을 볼 수 있다. 

원래는 [1. 2. 3. 4. 6. 9.]로 생성되지만, 사이킷런의 선형 모델은 자동으로 절편을 추가하므로 상수항이 필요 없기 때문에 include_bias=False로 절편을 위한 항인 1을 제거했다. degree는 default값이 2다. 

(*사이킷런의 선형 모델은 자동으로 특성에 추가된 절편 항을 무시하기 때문에 꼭 include_bias=False로 지정하지 않아도 된다)

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

 

train_poly의 크기를 출력해보면 (42, 9)로 나온다. 이는 42개의 행9개의 열이 존재한다는 의미인데, 3개의 샘플(길이, 높이, 너비)이 9개로 변환된 것을 알 수 있다. 이처럼 기존의 특성을 사용해 새로운 특성을 뽑아내는 작업을 특성 공학(Feature engineering)이라고 한다. 

get_feature_names_out() 메서드를 호출하면 9개의 특성이 각각 어떤 입력의 조합으로 만들어졌는지 알려준다. 

print(poly.get_feature_names_out()) 
# ['x0' 'x1' 'x2' 'x0^2' 'x0 x1' 'x0 x2' 'x1^2' 'x1 x2' 'x2^2']

 

이제 모델을 훈련해보자. 

lr = LinearRegression()
lr.fit(train_poly, train_target) 
print(lr.score(train_poly, train_target)) # 0.9903183436982124
print(lr.score(test_poly, test_target)) # 0.97145599115942

다중 회귀 모델을 훈련한 결과 훈련 셋의 점수가 매우 높게 나온 것을 볼 수 있다. 테스트 셋의 점수는 높아지진 않았지만 과소 적합 문제는 해결되었다. 이처럼 특성이 늘어나면 선형 회귀의 능력이 강해진다는 것을 알 수 있다. 

 

만약 특성을 더 많이 추가하면 어떨까? degree = 3, 4, ... 로 차수를 늘려서 세제곱, 네제곱 항을 넣으면 점수가 더 높아질까? 

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) # (42, 55)

 

degree를 5로 높여보자. train_poly의 shape를 출력해보니 특성이 무려 55개로 늘어났다. 이를 이용하여 다시 선형 회귀 모델을 훈련한다. 

lr = LinearRegression()
lr.fit(train_poly, train_target) 
print(lr.score(train_poly, train_target)) # 0.9999999999997522
print(lr.score(test_poly, test_target)) # -144.40564433961768

 

훈련 셋에 대한 점수는 거의 완벽한 점수로 나온 반면, 테스트 셋의 점수는 아주 큰 음수인 결과가 나왔다.

즉, 특성의 개수를 크게 늘리면 선형 모델은 아주 강력해져서 훈련 셋에 대해 거의 완벽한 결과를 얻을 수 있지만, 너무 훈련 셋에 과대 적합되어 테스트 셋에서는 매우 낮은 점수를 얻게 된다. 

 

다음엔 이러한 과대 적합을 줄이는 방법을 알아보자. 

 

참고)

- https://gocardless.com/en-us/guides/posts/multiple-linear-regression-mlr-definition/

- 혼자 공부하는 머신러닝+딥러닝(박해선, 한빛미디어)

 

 

PC로 보시는 것을 권장합니다. 

피드백은 언제나 환영입니다. 댓글로 달아주세요 ^-^

 

 

728x90
반응형