본문 바로가기
데이터 분석/데이터 분석

[데이터 분석] 선형회귀분석 Linear Regression

by thomasito 2021. 7. 13.
반응형

 머신러닝의 가장 기본이 되는 개념 중 하나인 선형회귀분석(Linear Regression)에 대하여 다루어 보겠다. 머신러닝이란 일반적으로 Target Data (결과값)이 주어지는 지도학습(Supervised Learning)과 Target Data가 주어지지 않는 비지도학습(Unsupervised Learning)으로 나누어진다. 그리고 이 지도학습의 가장 큰 줄기가 되는 개념이 바로 회귀(Regression)와 분류(Classification)다. 

 

 오늘은 그 회귀 중 가장 기본이 되는 선형회귀분석에 대하여 다루어 보겠다. 선형회귀분석은 최소자승법(OLS : Ordinary Least Squares)을 기초로 하며 예측값과 관측값의 차이, 즉 오차(RSS : Residual Sum of Squares)를 최소화시키는 1차 선형함수를 만드는 것이다.

 

1. 데이터 세트 불러온 후 데이터 프레임 생성

 우선 sklearn.datsets 에 내재되어 있는 보스턴 주택 가격 예측 데이터를 불러온다.

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
%mamtplotlib inline

boston = load_boston()

print(boston.feature_names)
print(boston.data)

 내부의 key 값을 보니 'data'에 데이터들이 들어있으며, 'features_names'에 변수들이 나열되어 있다. 각자 어떻게 생긴 데이터들인지 간단히 출력을 해서 확인한다.

 

# 데이터 세트 DF 로 변환 (데이터 입력, 컬럼 입력)
boston_df = pd.DataFrame(boston.data, columns = boston.feature_names)

# 데이터프레임의 가격데이터를 target 으로 설정
boston_df['PRICE'] = boston.target
print('Boston 데이터 세트 크기:', boston_df.shape)
boston_df.head()

 데이터는 14개의 컬럼과 506개의 로우로 구성되어 있다. 범죄율(CRIM), 거주지 비율(ZN) 등 다양한 Features (독립변수라고 보면 된다.)로 이루어져 있다. 이렇게 데이터 프레임으로 보니까 이쁘다.

 

2. Seaborn 으로 regplot 그려보기

# subplot 으로 2개 행, 4개 열을 가진 subplot 그리기

# fig, axs 크기 (20,10), 컬럼 4개 X 로우 2개 짜리 데이터 프레임 생성
fig, axs = plt.subplots(figsize=(20,10), ncols=4, nrows=2)
lm_features = ['RM','ZN','INDUS','NOX','AGE','PTRATIO','LSTAT','RAD']

for i, feature in enumerate(lm_features):

    print(i,feature)
    row = int(i/4) # 0~3 까지는 0번 행,4~7 까지는 1번 행
    col = i%4 # 0~3까지, 4~7까지 각각 1,2,3,4 번 열
    
    # 시본의 regplot 을 이용해 산점도와 선형 회귀 직선을 함께 표현
    sns.regplot(x=feature, y='PRICE', data=boston_df, ax=axs[row][col])

 enumerate 는 리스트 내의 번호와 변수를 출력하는 반복문이다. lm_featrues의 경우 i, features 가 각각 0 : RM, 1 : ZN 와 같은 식으로 번호와 변수가 짝으로 돌면서 나온다. 

 

 seaborn 의 regplotregression 과 plot을 합친 말로 데이터 산점도와 선형회귀직선을 각각 보여준다. 이렇게 features가 여러개이고 target 이 1개인 경우에 적절한 시각화 방식이다. 딱 눈으로 봐도 RM(1번째), LSTAT(7번째)의 산점도가 밀집되어 있고 회귀선도 적절해 보인다.

 

 3. 훈련, 테스트 데이터 분리하여 RSS(오차) 값 관찰

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

# y 에 타겟데이터도 PRICE 넣음
# X 에 PRICE 컬럼(axis=1) 제외하고 넣음
y_target = boston_df['PRICE']
X_data = boston_df.drop(['PRICE'], axis=1, inplace=False)

# train_test_split(X값,Y값,테스트 데이터 비중)
X_train, X_test, y_train, y_test = train_test_split(X_data, y_target, test_size = 0.3,
                                                   random_state = 156)
                                                   
print(X_data.head())
print(y_target.head())
X_train, X_test, y_train, y_test = train_test_split(X 값, y값, 테스트 데이터 비중 = 0.3)

 train_test_split 모듈은 훈련 데이터와 테스트 데이터로 데이터를 배분해주는 역할을 한다. 데이터를 출력하여 회귀모델을 훈련할 독립변수(X_train)와 타겟변수(y_target)가 어떻게 생겼는지를 관찰해본다.

 

4. 훈련 데이터로 학습시킨 후 모델 도출

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('MGR : {0:.3f}, RMSE : {1:.3f}'.format(mse, rmse))
print('r2_score : {0:.3f}'.format(r2_score(y_test, y_preds)))

 선형회귀분석 모델에 X_train, y_train 데이터를 학습시킨 후 X_test 데이터를 넣어주고 RMSE 를 계산해본다. RMSE 는 우리가 잘 아는 표준편차를 의미하고 4.1 정도가 나왔다.

# 절편 값과 회귀 계수값 구해보기
print('절편 값', lr.intercept_)
print('회귀 계수값', np.round(lr.coef_,1))

# 회귀 계수를 큰 값 순으로 정렬하기 위하여 Series로 생성한후 내림차순 정렬
coeff = pd.Series(data=np.round(lr.coef_,1), index=X_data.columns)
coeff.sort_values(ascending=False)

 회귀계수가 아래와 같이 도출되었다. 식으로 표현하자면 y = 3.4*RM + 3.0*CHAS + ... + (-19.8)*NOX 이렇게 표현할 수 있겠다. 이 모델을 아래에서 검증해본다. 

 

5. 검증 데이터로 모델 검증 (5-fold 모델)

 데이터를 각각 다섯 개로 잘라서 검증을 해본다. 쉽게 말하면 처음 20%를 테스트 데이터로 나머지를 훈련 데이터로 검증하고, 그 다음 20%를 테스트 데이터 나머지를 훈련 데이터로 검증하는 방법을 5번 반복하는 것이다.

# 5개의 폴드 세트에서 cross_val_score()을 이용한 교차검증 진행

from sklearn.model_selection import cross_val_score

X_target = boston_df['PRICE']
X_data = boston_df.drop(['PRICE'], axis=1, inplace=False)
lr = LinearRegression()

# cross_val_score() 로 5 폴드 세트로 MSE 구한 뒤 이를 기반으로 다시 RMSE 구함
neg_mse_scores = cross_val_score(lr, X_data, y_target, scoring="neg_mean_squared_error", cv=5)
rmse_scores = np.sqrt(-1*neg_mse_scores)
avg_rmse = np.mean(rmse_scores)
neg_mse_scores = cross_val_score(머신러닝모델, 훈련데이터, 타겟데이터, 반환방법, 교차검증 분할횟수)
# cross_val_score(scoring='neg_mse_scores')로 반환된 값은 모두 음수
print('5 folds 의 개별 Negative MSE scores:', np.round(neg_mse_scores, 2))
print('5 folds 의 개별 RMSE scores:', np.round(rmse_scores, 2))
print('5 folds 의 평균 RMSE:{0:.3f}'.format(avg_rmse))

 5 folds 의 평균 RMSE는 5.8로 생각보다 RMSE 값이 들쑥날쑥하다. 어쨌든 오차 최소화의 측면에서 RMSE 라는 지표를 눈여겨 볼 필요가 있다. 물론 이것이 전부는 아니지만 어쨌든 회귀를 이해하는 바탕이 되는 개념이다.

반응형

댓글