본문 바로가기

ML with SckitLearn

선형 회귀 분석 - LinearRegression 및 보스턴 주택 가격 회귀 구현

반응형

LinearRegression 선형 회귀 분석 - LinearRegression 


LinearRegression 클래스는 예측값과 실제 값의 RSS(Residual Sum of Squares)를 최소화해 OLS(Ordinary Least Squares)추정 방식으로 구현한 클래스입니다.

 

OLS기반의 회귀 계수 계산은 입력 피처의 독립성에 많은 영향을 받습니다. 피처간의 상관관계가 매우 높은 경우 분산이 매우 커져서 오류에 매우 민감해집니다. 이러한 현상을 다중공선정(Multi-Collinearity)문제라고 합니다. 일반적으로 상관관계가 높은 피처가 많은 경우 독립적인 중요한 피처만 제거하거나 규제를 적용합니다.

 

또한 매우 많은 피처가 다중 공전성 문제를 가지고 있다면 PCA를 통해 차원 축소를 수행하는 것도 고려해 볼 수 있습니다.

 


회귀 평가 지표

 

회귀의 성능을 평가하는 지표는 다음과 같습니다.

평가지표 설명
MAE Mean Absolute Error(MAE)이며 실제값예측값의 차이를 절댓값으로 변환해 평균한 것입니다.
MSE Mean Squared Error(MSE)이며 실제 값과 예측값의 차이를 제곱해 평균한 것입니다.
RMSE MSE값은 오류의 제곱을 구하므로 실제 오류 평균보다 더 커지는 특성이 있으므로 MSE에 루트를 씌운 것이 RMSE입니다. 

숫자가 작을수록 성능이 좋다는 의미
R2 분산 기반으로 예측 성능을 평가합니다. 실제 값의 분산 대비 예측값의 분산 비율을 지표로 하며, 1에 가까울수록 예측 정확도가 높습니다.

 

각 평가 방법에 대한 사이킷런의 API 및 cross_val_score나 GridSearchCV에서 평가 시 사용되는 scoring파라미터의 적용 값입니다.

평가방법 사이킷런 평가 지표 API Soring 함수 적용 값
MAE metrics.mean_absoulte_error 'neg_mean_absolute_error'
MSE metrics.mean_squared_error 'neg_mean_squared_error'
R2 metrics.r2_score 'r2'

cross_val_score,  GridSearchCV와 같은 Scoring함수에 회귀 평가 지표를 적용할 때 한 가지 유의할 점이 있습니다. 예를 들어, MAE의 scoring파라미터 값을 살펴보면 'neg_mean_absolute_error'와 같이 'neg_'라는 접두어가 붙어 있습니다. 이는 Negative(음수)값을 가진다는 의미인데, MAE는 절댓값의 합이기 때문에 음수가 될 수 없습니다. 

 

Scoring함수에 'neg_mean_absolute_error'를 적용해 음수값을 반환하는 이유는 사이킷런의 Scoring함수가 score값이 클수록 좋은 평가 결과로 자동 평가하기 때문입니다. (특히 GridSearchCV의 경우 가장 좋은 Evaluation값을 가지는 하이퍼 파라미터로 Estimator를 학습까지 자동으로 시킬 수 있습니다.) 

 

그런데 실제 값과 예측값의 오류 차이를 기반으로 하는 회귀 평가 지표의 경우 값이 커지면 오히려 나쁜 모델이라는 의미이므로 이를 사이킷런의 Scoring함수에 일반적으로 반영하려면 보정이 필요합니다.

 

 


LinearRegression을 이용해 보스턴 주택 회귀 구현

 

보스톤 주택 가격의 회귀 구현을 위해 load_boston()을 호출합니다. 아래 결과를 확인하면 RM과 LSTAT의 PRICE의 영향이 가장 크게 보입니다.

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from scipy import stats
from sklearn.datasets import load_boston

boston = load_boston()
bostonDF = pd.DataFrame(boston.data, columns=boston.feature_names)
bostonDF['PRICE'] = boston.target

print("#bostonDF\n", bostonDF)
print("#bostonDF.info()\n", bostonDF.info())

fig, axs = plt.subplots(figsize=(16,8), ncols=5, nrows=2)
lm_features = ['ZN', 'INDUS', 'NOX', 'RM', 'AGE', 'RAD','PTRATIO', 'LSTAT','CHAS','B']

for i, feature in enumerate(lm_features):
    row = int(i//5)
    col = i%5
    sns.regplot(x=feature, y='PRICE', data=bostonDF, ax=axs[row][col])
plt.show()

 

seaborn의 regplot() API는 X, Y축 값의 산점도와 함께 선형 회귀 직선을 그려줍니다. matplotlib.subplots()를 이용해 각 ax마다 칼럼과 PRICE의 관계를 표현합니다. 

 

아래 표를 보면 RM(방 개수)은 양 방향의 선형성(Positive Linearity)이 가장 큽니다. 즉, 방의 크기가 클수록 가격이 증가하는 모습을 확연히 보여줍니다 LSTAT는 음 방향의 선형성이 가장 큽니다. LSTAT이 적을 수록 PRICE가 증가하는 모습이 확연히 나타납니다.


LinearRegression : ML

from sklearn.datasets import load_boston
from sklearn.model_selection import cross_val_score, train_test_split
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score

boston = load_boston()
bostonDF = pd.DataFrame(boston.data, columns=boston.feature_names)
bostonDF['PRICE'] = boston.target

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)

#선형 회귀 OLS로 학습/예측/평가 수행
lr = LinearRegression()
lr.fit(X_train, y_train)
y_preds = lr.predict(X_test)
mse = mean_squared_error(y_test, y_preds)
rmse = np.sqrt(mse)
print('\nMSE : {0:.3f}, RMSE : {1:.3F}\n'.format(mse, rmse))
print('\nr2 Variance score  {0:.3F}\n'.format(r2_score(y_test, y_preds)))

print('\n절편 값:\n', lr.intercept_)
print('\n회귀 계수값:\n\n', np.round(lr.coef_, -1))

# 회귀 계수를 큰 값 순으로 정렬하기 위해 Series로 생성, 인덱스 칼럼명에 유의
coeff = pd.Series(data=np.round(lr.coef_,1), index=x_data.columns)
print('\ncoeff.sort_values(ascending=False)\n', coeff.sort_values(ascending=False))

# cross_val_score()로 5폴드 세트로 MSE를 구한 뒤 이를 기반으로 다시 RMSE구함
neg_mse_scores = cross_val_score(lr, x_data, y_target, scoring="neg_mean_squared_error", cv=5)
print('\nneg_mse_scores\n', neg_mse_scores)
rmse_scores = np.sqrt(-1*neg_mse_scores)
print('\nrmse_scores\n', rmse_scores)
avg_rmse = np.mean(rmse_scores)

# neg_mean_squared_error로 반환된 값은 모두 음수
print('\n5 folds의 개별 Negative MSE scores: \n', np.round(neg_mse_scores,2))
print('\n5 folds의 개별 RMSE scores :', np.round((rmse_scores)))
print('\n5 folds의 평균 RMSE : {0:.3f}\n'.format(avg_rmse))

LinearRegression클래스는 fit()메서드로 X,y배열을 입력 받으면 회귀 계수(Coefficients)인 W를 coef_ 속성에, intercept_속성에 절편을 저장합니다. 

 

train_test_split()을 이용해 학습과테스트 데이터 세트를 분리해 학습과 예측을 수행합니다. 그리고 mean_squared_error()와 r2_score() API를 이용해 MSE와 R2 Score를 측정합니다.

 

coef_ 속성은 회귀 계수 값만 가지고 있으므로 이를 피처별 회귀 계수 값으로 다시 매핑하고, 높은 값순으로 출력으 하기 위해 판다시의 Series의 sort_values()함수를 이용합니다.

 

#coeff.sort_values(ascending=False)

RM          3.4
CHAS        3.0
RAD         0.4
ZN          0.1
B           0.0
TAX        -0.0
AGE         0.0
INDUS       0.0
CRIM       -0.1
LSTAT      -0.6
PTRATIO    -0.9
DIS        -1.7
NOX       -19.8
dtype: float64

결과를 보면 RM이 양의 값으로 회귀 계수가 가장 크다는 것을 알 수 있습니다. 

 

마지막으로 cross_val_score()을 이용해 교차 검증으로 MSE와 RMSE를 측정해 봅니다. 사이킷런은 cross_val_score()의 인자로 scoring='neg_mean_squared_error'를 지정하면 반환되는 수치 값은 음수 값입니다. 앞에서도 설명했듯이 사이킷런의 지표 평가 기준은 높은 지표 값일수록 좋은 모델인 데 반해, 일반적으로 회귀는  MSE값이 낮을수록 좋은 회귀 모델입니다. 

 

사이킷런의 metric평가 기준에 MSE를 부합시키기 위해서 soring='neg_mean_squared_error'로 사이킷런의 Scoring함수를 호출하면 모델에서 계산된 MSE값에 -1을 곱해서 반환합니다. 따라서 cross_val_score()에서 반환된 값에 다시 -1을 곱해서 양의 값인 원래 모델에서 계산된 MSE값이 됩니다.

반응형