본문 바로가기
Data Analytics/MachineLearning

[ML]LinearRegression을 이용해서 보스턴 주택가격 예측해보기 / knn 모델과 비교해보기

by Istj_eff 2022. 11. 7.

column 설명

더보기
  • CRIM: 지역별 범죄 발생률
  • ZN: 25,000평방피트를 초과하는 거주 지역의 비율
  • NDUS: 비상업 지역 넓이 비율
  • CHAS: 찰스강에 대한 더미 변수(강의 경계에 위치한 경우는 1, 아니면 0)
  • NOX: 일산화질소 농도
  • RM: 거주할 수 있는 방 개수
  • AGE: 1940년 이전에 건축된 소유 주택의 비율
  • DIS: 5개 주요 고용센터까지의 가중 거리
  • RAD: 고속도로 접근 용이도
  • TAX: 10,000달러당 재산세율
  • PTRATIO: 지역의 교사와 학생 수 비율
  • B: 지역의 흑인 거주 비율
  • LSTAT: 하위 계층의 비율
  • PRICE: 본인 소유의 주택 가격(중앙값) - 종속변수 (위의 건 독립변수)

 

1. 데이터 살펴보기

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
import warnings
warnings.filterwarnings('ignore')

%matplotlib inline

# 보스턴 데이터 불러오기
boston = load_boston()
bostonDF = pd.DataFrame(boston.data, columns=boston.feature_names)

bostonDF['PRICE'] = boston.target # boston데이터셋의 target 배열은 주택가격
print(bostonDF.shape) # (506, 14)

 

bostonDF.info()

 

# 보스턴 데이터 시각화
# 4개의 열, 2개의 행
fig, axs = plt.subplots(figsize=(16,8), ncols=4, nrows=2)

lm_features = ['RM','ZN','INDUS','NOX','AGE','PTRATIO','LSTAT','RAD']

# i에는 인덱스가 feature에는 RM ~ RAD까지 순차적으로 들어감
for i, feature in enumerate(lm_features):
    row = int(i/4) # 2행
    col = i%4

    # sns.regplot : 산점도와 선형 회귀 직선을 함께 표현
    sns.regplot(x=feature, y='PRICE', data=bostonDF, ax=axs[row][col])

  • 집 값 상승 : RM(방 갯수)양 방향의 선형성(Positive)이 가장 크다. 즉, 방 크기가 클수록 집값 상승
  • 집 값 하락 : LSTAT(하위계층비율)음 방향의 선형성(Negative)이 가장 크다.

 

2. train / test 분리해서 학습과 예측을 수행

  • 506개 데이터 -> 7:3 train/test 데이터 분리
  • train : 학습 -> linear regression 학습/모델링 수행 -> 모델(W) 생성
  • test : 평가(validation)-> 평가지표 (MSE, RMSE, ... )
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
        # m_s_e, r2(선형회귀모델 적합도 : 분산값, 1에 가까울수록 적합도 높음)
    
df = bostonDF

# feature, target 데이터 분리
y_target = df['PRICE'] # 레이블(종속변수)
X_data = df.drop(['PRICE'], axis=1, inplace=False) # 피처(독립변수)

# train, test 데이터 분리
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()

# fit 메소드 학습 : 주어진 데이터로 estimator(사이킷런이 제공) 알고리즘 학습
lr.fit(X_train, y_train)

 

print(X_train.shape, X_test.shape)

[out]
(354, 13) (152, 13)

 

# predict 메소드 : 학습된 모델로 예측을 수행
y_preds = lr.predict(X_test)
y_preds[0:5]

[out]
array([23.15424087, 19.65590246, 36.42005168, 19.96705124, 32.40150641])

 

# rmse를 활용한 평가
mse = mean_squared_error(y_test, y_preds)
rmse = np.sqrt(mse)

print(f'MSE : {mse:.3f}, RMSE: {rmse:.3f}')
print(f'Variance score : {r2_score(y_test, y_preds):.3f}')

[out]
MSE : 17.297, RMSE: 4.159
Variance score : 0.757

 

3. 주택 가격 모델의 intercept(절편)coefficients(회귀 계수)값 확인

print("절편 값:", lr.intercept_) # y축 절편값

# 회귀 계수(coefficient) : 독립변수의 변화에 따라 종속변수에 미치는 영향력이 크기
print("회귀계수:", np.round(lr.coef_,1))

[out]
절편 값: 40.995595172164336
회귀계수: [ -0.1   0.1   0.    3.  -19.8   3.4   0.   -1.7   0.4  -0.   -0.9   0.
  -0.6]

 

# 회귀계수 정렬 (내림차순, 큰 값부터)
coeff = pd.Series(data=np.round(lr.coef_, 1), index=X_data.columns)
coeff.sort_values(ascending=False)

[out]
RM          3.4
CHAS        3.0
RAD         0.4
ZN          0.1
INDUS       0.0
AGE         0.0
TAX        -0.0
B           0.0
CRIM       -0.1
LSTAT      -0.6
PTRATIO    -0.9
DIS        -1.7
NOX       -19.8
dtype: float64
RM의 양의 절대값이 제일 크고, NOX(일산화질소 농도)가 음의 절대값이 너무 크다.

 

4. cross_val_score를 이용해 교차검증

  • 사이킷런의 지표 평가 기준은 높은 지표값일수록 좋은 모델이지만, 일반적으로 회귀는 MSE값이 낮을수록 좋은 회귀 모델이다.
  • RMSE를 구하기 위해선 MSE 값에 -1을 곱한 후 평균을 내면 된다.
    • 왜? scoring함수 호출하면 MSE값에 -1을 곱해서 반환하기 때문에
      다시!! -1을 곱해야 양수인 원래 모델에서 계산된 MSE값이 된다.
from sklearn.model_selection import cross_val_score

# features, target 데이터 정의
y_target = df['PRICE']
X_data = df.drop(['PRICE'], axis=1)

# 선형회귀 객체 생성
lr = LinearRegression()

# 5 folds의 개별 Negative MSE scores (음수로 만들어 작은 오류 값이 더 큰 숫자로 인식됨)
neg_mse_scores = cross_val_score(lr, X_data, y_target, scoring="neg_mean_squared_error", cv = 5)

# RMSE를 구하기 위해선 MSE 값에 -1을 곱한 후 평균을 내면 된다
# 왜? scoring함수 호출하면 MSE값에 -1을 곱해서 반환하기때문에 
rmse_scores =  np.sqrt(-1*neg_mse_scores) # 그래서 다시!! -1을 곱해야 양수인 원래 모델에서 계산된 MSE값이 된다.

# 5 fold 의 평균 RMSE
avg_rmse = np.mean(rmse_scores)
# cross_val_score(scoring="neg_mean_squared_error")로 반환된 값은 모두 음수
print(' 5 folds 의 개별 Negative MSE scores: ', np.round(neg_mse_scores, 2))
print(' 5 folds 의 개별 RMSE scores : ', np.round(rmse_scores, 2))
print(f' 5 folds 의 평균 RMSE : {avg_rmse:.3f}')

[out]
 5 folds 의 개별 Negative MSE scores:  [-12.46 -26.05 -33.07 -80.76 -33.31]
 5 folds 의 개별 RMSE scores :  [3.53 5.1  5.75 8.99 5.77]
 5 folds 의 평균 RMSE : 5.829

 

보스턴 주택가격 예측 RMSE값은 5.829가 나왔다.

 


5. KNN 모델과 비교해보기

from sklearn.neighbors import KNeighborsRegressor
from sklearn.linear_model import LinearRegression

knn_model = KNeighborsRegressor(n_neighbors=4).fit(X_train, y_train)
lr_model = LinearRegression().fit(X_train, y_train)
print('KNN 학습평가:', knn_model.score(X_train,y_train))
print('LR 학습평가:', lr_model.score(X_train,y_train))

[out]
KNN 학습평가: 0.7494788081365981
LR 학습평가: 0.7273505005599823

 

print('KNN 테스트평가:', knn_model.score(X_test,y_test))
print('LR 테스트평가:', lr_model.score(X_test,y_test))

[out]
KNN 테스트평가: 0.499306667207422
LR 테스트평가: 0.7572263323138921

 

학습평가 점수는 비슷하지만, 테스트 평가 점수는 Linear Regression모델이 0.75로 더욱 잘 나왔다.

 


  • RMSE를 구하기 위해서 MSE 값에 -1을 곱한 후 평균을 내는 이유
    scoring함수 호출하면 MSE값에 -1을 곱해서 반환하기 때문에
    다시 -1을 곱해야 양수인 원래 모델에서 계산된 MSE값이 된다.

댓글