본문 바로가기
CS/머신러닝

혼자 공부하는 머신러닝 + 딥러닝 - Ch 3-3

by jaehoonChoi 2023. 1. 11.

Ch 3-3에서는 다중회귀와 과적합에 대해 공부합니다. 

 

여러 특성을 사용한 선형 회귀를 다중회귀라고 부릅니다. 

특성 1개에 target 1개면 회귀는 직선을 학습하지만, 특성 2개에 target 1개면, 

회귀는 평면을 학습하게 됩니다. 이렇게 2개 이상의 feature에 대한 회귀를 공부합니다.

이번 실습에서는 물고기의 길이 뿐 아니라 높이와 두께 데이터도 주어졌을 때, 

이들을 모두 이용하여 훈련하는 다중회귀를 학습합니다. 

 

1. 데이터 준비

판다스라는 데이터 분석 라이브러리에서 데이터를 불러옵시다. 판다스에서 데이터를 읽는 방식은

data= pd.read_csv(주소) 로 쉽게 할 수 있습니다. 

data=pd.read_csv('https://bit.ly/perch_csv_data')
fish_data=data.to_numpy()
#print(fish_data)
#fish_data.shape # 56*3 행렬
fish_target=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])
X_train, X_test, y_train, y_test=train_test_split(
    fish_data, fish_target, random_state=42)

 

 

2. 사이킷런 변환기

사이킷런의 PolynomialFeatures 클래스는 feature를 새로운 특성들로 변환해줍니다.

다중회귀는 선형회귀와 마찬가지로 데이터들의 선형결합으로 이루어진 식입니다. 

데이터 \(x_1 , ..., x_D\) 가 있을 때, 다중회귀 모델 \(\hat{y}\)는

$$ \hat{y}=w_0+w_1x_1+...+w_Dx_D $$ 

가 됩니다. 

 

우리가 손실함수를 최소화한다고 할 때, 

$$  \sum_{i=1}^{D}(y_i-(w_0+w_1x_1+...+w_Dx_D))^2    $$

를 살펴보면, \(x_i\) 와 \(x_ix_j \) 형태로 이루어져 있습니다. 이 식을 훈련하는 것입니다.

사이킷런 변환기는 데이터 \(x_1 , ..., x_D\)로부터 \(x_ix_j\)들을 만들어줍니다.

확인해봅시다.

# 변환기 
from sklearn.preprocessing import PolynomialFeatures
X=PolynomialFeatures(include_bias=False) # 다중회귀
X.fit(X_train) # 훈련
X_train = X.transform(X_train) # 특성 만들어 다시 만들어줌 
X_test = X.transform(X_test)

print(X.get_feature_names_out()) # 변수들
print(X_train.shape)
['x0' 'x1' 'x2' 'x0^2' 'x0 x1' 'x0 x2' 'x1^2' 'x1 x2' 'x2^2']
(42, 9)

결과를 통해 항들이 잘 만들어졌음을 알 수 있습니다. 

 

 

3. 훈련 

선형회귀 모델과 마찬가지로 coef_ 와 intercept_ 에 최적의 상수들이 구해져있습니다.

다중회귀를 수행하고 상수들과 모델성능을 검증합니다.

# 다중회귀 수행
lr=LinearRegression()
lr.fit(X_train, y_train)
print("coef_: ", lr.coef_)
print("intercept_: ", lr.intercept_)
print("train_score: ", lr.score(X_train, y_train))
print("test_score: ", lr.score(X_test, y_test))
coef_:  [  34.80604039  -88.68430232 -184.11606694   -2.2696004     8.74890226
    9.41670602   27.76120631 -119.89306061   93.68198334]
intercept_:  124.06521772230127
train_score:  0.9903183436982124
test_score:  0.9714559911594134

훌륭합니다!

 

4. 과대적합 

그럼 특성을 더 늘려보면 어떻게 될까요? \(x^3\)이나 \(x_{1}x_{2}x_{3}\) 같이 3항 이상의

특성들도 추가한 식을 만든다면 효과가 더 좋아질 것 같은데요? 

Polynominal 클래스에선 degree를 정하여 더 많은 특성값들을 만들 수 있습니다.

차수를 5로 하고 돌려보겠습니다. 

X=PolynomialFeatures(degree=5, include_bias=False) 
X.fit(X_train) 
X_train = X.transform(X_train) 
X_test = X.transform(X_test)
lr=LinearRegression()
lr.fit(X_train, y_train)
print("train_score: ", lr.score(X_train, y_train))
print("test_score: ", lr.score(X_test, y_test))

 

train_score:  0.9999999999999952
test_score:  -8023.135431047218

 

학습데이터 성능은 매우 정확한데 테스트 성능은 음수가 나와버립니다. 어떻게 된 걸까요?

이 그림을 봅시다. 

 

 

Overfitted가 된 경우가 지금 상태입니다. 과적합 상태란 학습데이터만 너무 구체적으로 학습한 나머지,

그와 다른 비슷한 데이터가 들어오면 오히려 오차가 심해지는 경우를 의미합니다. 

이러한 과적합 방지를 위한 몇가지 해결책이 있습니다. 대표적인 것이 규제(Regularization) 입니다.

말그대로 너무 과도한 학습을 막기 위해 적절한 항을 만들어 그 식도 고려를 하게끔 하는 것입니다.

대표적으로 라쏘 회귀와 릿지 회귀가 있습니다.

이 정규화에 관한 설명은 이론을 더 공부하여 따로 포스팅하겠습니다.

 

이번 실습의 교훈은 다중회귀의 높은 성능과 Overfitting이었습니다.

다중회귀는 여러 특성이 있을 때 이를 토대로 선형회귀와 똑같이 훈련하는 것입니다.

대표적인 degree=2 정도만 되어도 좋은 성능을 낼 수 있습니다.

반면 너무 차수를 높인다면 과적합이 일어나 성능저하가 생깁니다.  뭐든 적당한게 좋습니다. 

오늘 실습의 전체 코드입니다.

 

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression

data=pd.read_csv('https://bit.ly/perch_csv_data')
fish_data=data.to_numpy()
#print(fish_data)
#fish_data.shape # 56*3 행렬
fish_target=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])

X_train, X_test, y_train, y_test=train_test_split(
    fish_data, fish_target, random_state=42)

# 변환기 
from sklearn.preprocessing import PolynomialFeatures
X=PolynomialFeatures(include_bias=False) # 다중회귀
X.fit(X_train) # 훈련
X_train = X.transform(X_train) # 특성 만들어 다시 만들어줌 
X_test = X.transform(X_test)

print(X.get_feature_names_out()) # 변수들
print(X_train.shape)

# 다중회귀 수행
lr=LinearRegression()
lr.fit(X_train, y_train)
print("coef_: ", lr.coef_)
print("intercept_: ", lr.intercept_)
print("train_score: ", lr.score(X_train, y_train))
print("test_score: ", lr.score(X_test, y_test))

# 과적합 
X=PolynomialFeatures(degree=5, include_bias=False) 
X.fit(X_train) 
X_train = X.transform(X_train) 
X_test = X.transform(X_test)
lr=LinearRegression()
lr.fit(X_train, y_train)
print("train_score: ", lr.score(X_train, y_train))
print("test_score: ", lr.score(X_test, y_test))

 

 

 

댓글