본문 바로가기

Data Science/Machine Learning 구현

[ML] Regression metric과 Polynominal Regression 구현하기

반응형

이번 포스팅에서는 회귀분석(Regression) 모델의 성능 평가에 이용되는 평가지표(metric)Polynominal Regression(다항 회귀분석)를 Python으로 구현하는 방법에 대해 알아보려고 한다. 목차는 다음과 같다.

 

1. Metrics for regression

2. Polynominal Regression

 

Linear Regression

1. Metrics for regression

회귀분석은 기본적으로 연속적인 실수값을 예측하는 모델이다. 따라서 회귀분석 모델의 성능을 평가할 때에는 분류 모델의 성능을 평가할 때 사용했던 정확도, 정밀도, 재현율 등 Confusion Matrix같은 지표를 사용해선 안 된다. Regression의 성능을 평가하기 위한 방법으로는 보통 Loss(Error라고도 부르며 예측값에서 실제값을 뺀 차이를 말한다.)의 연속적인 실수값을 살펴본다. 이 Loss(Error)를 평가하기 위한 지표들도 다양하게 존재하는데 하나하나씩 살펴보자.

 

  • MAE(Mean Absolute Error) : 실제값과 예측값의 차이를 절댓값으로 변환해 평균한 수치
  • MSE(Mean Sqaured Error) : 실제값과 예측값의 차이를 제곱하여 평균한 수치
  • RMSE(Root Mean Squared Error) : MSE에 루트(제곱근)을 씌운 수치. 실제 오류 평균보다 더 커지는 특성이 있으므로 이를 방지해준다.
  • MSLE(Mean Squared Log Error) : MSE에 Log(로그)를 취한 수치. 결정값이 클수록 오류값도 커지기 때문에 일부 큰 오류값들로 인해 전체 오류값이 커지는 것을 방지해준다.
  • RMSLE(Root Mean Sqaured Log Error) : RMSE에 Log(로그를) 취한 수치. 결정값이 클수록 오류값도 커지기 때문에 일부 큰 오류값들로 인해 전체 오류값이 커지는 것을 방지해준다.
  • R2 Score(결정계수) : 분산 기반으로 예측 성능을 평가한다. 실제 값의 분산 대비 예측값의 분산 비율을 지표로 하며, 1에 가까울수록 예측 정확도가 높다. 

위와 같은 여러가지 평가지표들은 친절하게도 Scikit-learn에서 라이브러리 형태로 제공을 하고 있다. 하지만 RMSE같은 경우는 직접적으로 제공하지 않아 MSE지표에서 직접 Sqrt()를 씌워주어야 하는 약간의 수고(?)가 필요하다.

 

또한 Scikit-learn에서 제공하는 교차검증도 해주며 모델 성능평가를 해주는 cross_val_score나 GridSearchCV 라이브러리도 위와 같은 회귀분석 평가지표를 제공한다. 하지만 한 가지 주의해야 할 점이 있다. 일반적으로 Scikit-learn 라이브러리에서 제공해주는 평가지표를 출력해주는 함수들은 기본적으로 Loss의 최소값을 출력해준다. 다음 코드를 살펴보자.

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

y_target = boston_df['PRICE']
x_feature = boston_df.drop(['PRICE'], axis=1, inplace=False)

x_train, x_test, y_train, y_test = train_test_split(x_feature,
                                                   y_target,
                                                   test_size=0.3,
                                                   random_state=42)

lr = LinearRegression()
lr.fit(x_train, y_train)
y_pred = lr.predict(x_test)

mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_test, y_pred)
print(f"MSE score: {mse}")
print(f"RMSE scroe : {rmse}")
print(f"결정 계수 : {r2}")

코드의 결과물은 다음과 같다. 결과물을 살펴보면 MSE, RMSE값이 최소의 값으로 출력된다.

결과 화면

하지만 cross_val_score같은 경우, 인자에 'neg'라는 단어를 붙여주어야 하는데 이는 Negative(음수)를 뜻한다. 왜 음수를 붙여주는 걸까? 그 이유는 바로 cross_val_score나 GridSearchCV 함수의 경우 score를 출력해줄 때 가장 높은 수치의 score를 출력하도록 되어있기 때문이다. 예를 들어, cross_val_score로 교차검증을 3번 실시한 후 각각의 MSE값이 [3, 5, 7] 이라는 결과값이 나왔다. 이 때 cross_val_score의 가장 높은 수치의 score를 출력하는 특성대로 한다면 MSE값은 낮을수록 예측성능이 좋다는 의미임에도 불구하고 '7'이라는 가장 안좋은 MSE값을 출력하게 된다. 따라서 결과값들의 순위를 바꿔주기 위해서 neg(negative=음수)를 붙여 -1을 곱해주게 되는 것이다. 

 

방금 든 예시로 든다면 [3, 5, 7] 에다가 각각 -1을 곱해주어 [-3, -5, -7]이 되면서 이 세개의 수치 중 가장 큰 값은 -3이므로 cross_val_score는 -3이라는 결과물을 출력하게 된다.(그래서 명확한 metric 지표를 출력하려면 다시 -1을 곱해주어 출력시켜야 한다.)

 

cross_val_score를 이용한 코드는 다음과 같다.

# cross_val_score로 회귀모델 평가 지표 출력해보기
# GridSearchCV도 마찬가지만 해당 라이브러리들은 높은 score 출력이 목적이기 때문에 평가지표 score에 음수(negative)가 붙여서나옴
# 따라서 실질적인 score는 반환값에다가 음수(-)인 -1을 곱해주어서 출력해야 함
from sklearn.model_selection import cross_val_score

y_target = boston_df['PRICE']
x_feature = boston_df.drop(['PRICE'], axis=1, inplace=False)

# cross_val_score는 자체적으로 교차검증해주기 때문에 train_test_split함수 할 필요 없음!
lr = LinearRegression()

nmse = cross_val_score(lr, x_feature, y_target,
                      scoring='neg_mean_squared_error',
                      cv=5)
mse = -1 * nmse
rmse = np.sqrt(mse)
avg_rmse = np.mean(rmse)

print(f"cross_val_score가 반환하는 negative mse:{np.round(nmse,2)}")
print(f"실질적인 mse:{np.round(mse, 2)}")
print(f"RMSE :{np.round(rmse, 2)}")
print(f"평균 RMSE :{avg_rmse :.4f}")

결과값은 다음과 같다.

결과 화면

2. Polynominal Regression(다중 회귀)

다중 회귀도 기본적으로는 선형 회귀이다. 쉽게 헷갈릴 수 있는데 회귀에서 선형회귀/비선형 회귀를 나누는 기준은 회귀 계수가 선형인지 비선형인지에 따른 것이지 독립변수(feature들)의 선형/비선형 여부와는 관계가 없는 것이다.

 

http://jacob-yo.net/tag/udemy/

 

아쉽게도 Scikit-learn에서는 다중 회귀를 직접적으로 호출하는 API는 제공되지 않는다. 하지만 Scikit-learn에서 제공하는 PolynominalFeatures와 LinearRegression 라이브러리를 결합하여 다항회귀를 구현할 수 있다. 그래서 이 2개를 하나의 흐름으로 제공하는 Pipeline 라이브러리를 이용할 수 있다. PolynominalFeatures에 대해 간단하게 설명하자면 원본의 단항 feature들을 다항 feature들로 변환하는 역할을 한다. 그리고 이렇게 변환된 것을 LinearRegression으로 학습시키게 되면 다항 회귀를 구현할 수 있는 것이다.

 

다음은 sklearn에서 제공하는 내장된 boston 데이터를 갖고 다항회귀를 구현해보는 코드이다. 

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error , r2_score
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from sklearn.datasets import load_boston
import pandas as pd
import numpy as np

# boston 데이타셋 로드
boston = load_boston()

# boston 데이타셋 DataFrame 변환 
bostonDF = pd.DataFrame(boston.data , columns = boston.feature_names)

# boston dataset의 target array는 주택 가격임. 이를 PRICE 컬럼으로 DataFrame에 추가함. 
bostonDF['PRICE'] = boston.target
print('Boston 데이타셋 크기 :',bostonDF.shape)

y_target = bostonDF['PRICE']
X_data = bostonDF.drop(['PRICE'],axis=1,inplace=False)


X_train , X_test , y_train , y_test = train_test_split(X_data , y_target ,test_size=0.3, random_state=156)

# pipeline이용해 다항회귀 구현
# include_bias : 절편값 포함 여부
p_model = Pipeline([('poly',PolynomialFeatures(degree=2,
                                              include_bias=False)),
                   ('linear',LinearRegression())])
p_model.fit(X_train, y_train)
y_preds = p_model.predict(X_test)
# metric
mse = mean_squared_error(y_test, y_preds)
rmse = np.sqrt(mse)
r2_score = r2_score(y_test, y_preds)

print(f"MSE : {mse:.4f}")
print(f"RMSE : {rmse:.4f}")
print(f"Variance Score(R2 score) {r2_score:.4f}")

결과값은 다음과 같다.

결과 화면

위 코드에서 PolynomialFeatures 함수 인자에서 degree(차수)의 숫자를 하나만 올리면 다음과 같은 엄청난 오버피팅(과적합)이 발생한다. 따라서 다항회귀식에서 차수가 올라갈수록 항상 좋은 성능 결과값을 내는 것은 절대 아님을 알아두자.

degree=3일때 결과값

 

#더 공부하기 -  선형회귀에서의 다중공산성 문제

다중공산성이란, 회귀분석에서 독립변수들 간의 강한 상관관계가 존재할 때를 말한다. 독립변수들(feature들)간의 상관관계가 큰 경우 분산이 매우 커져서 오류에 매우 민감해진다. 따라서 이럴 때는 일반적으로 상관관계가 높은 feature가 많은 경우 독립적인 중요한 feature만 제외하고 제거하거나 상관관계가 높은 feature에 규제(Regularization)를 적용해야 한다.

반응형