본문 바로가기

데이터분석 및 프로젝트

선형회귀분석에 대해 알아보고 코드를 음미해봅시다:)

이번엔 선형회귀분석에 대해 알아보는 시간을 가져보겠습니다. 용어들과 코드 한 줄 한 줄 이해할 수 있도록 실습도 해봅시다.

 

먼저 회귀분석이란 두 변수(독립변수와 종속변수)사이의 함수적 관계를 기술하는 수학적 방정식을 구하는데 사용합니다. 무슨 말이냐면 변수들 사이에 어떠한 연관성을 발견하고 이를 통해 종속변수의 값을 추정하거나 예측하는데 사용하게 됩니다. 두 변수끼리 일종의 인과관계를 갖게 되는 것이죠. 여기서 선형회귀분석이란 말 그대로 모델이 Linear(선형)한 경우, y= ax + b 형태의 모델을 이용하여 예측 및 분석하는 방법입니다. 선형모델이기 때문에 복잡한 추세를 가지는 데이터일수록 오차가 크지만, 비교적 간단하게 계산할 수 있고, 대략적인 상관관계 및 그 정도를 유추할 수 있어 기본적인 분석방법으로 많이 사용됩니다. python에서는 아주 간편하게 회귀분석을 해볼 수 있는 sklearn패키지의 linear regression 함수를 제공합니다.

 

독립변수 : 다른 변수에 영향을 주고 그 변수의 값을 예측하려는 변수

종속변수 : 독립변수의 특정한 값에 따른 그의 값을 예측하고자 하는 변수

 

보통, 회귀분석을 할 때는 먼저 두 변수 사이의 관계를 대략적으로 알아보기 위해서 산포도를 그립니다. 일반적으로 x축에 독립변수, y축에 종속변수를 설정합니다. 이렇게 하면 두 변수간의 관련성과 상관분석이나 회귀분석을 해서 예측이 가능할지를 알아볼 수 있습니다.

 

예측하셨다시피 회귀모델은 독립변수와 종속변수간의 일정한 연관성이 있어서 일정한 추세를 갖게 되고, 이 변수들 사이에 선형 함수 관계가 존재한다고 생각하여 이것을 기준으로 예측을 하는 것입니다. 그렇기 때문에 수집된 데이터에 가장 적절한 회귀 직선을 구해야 합니다. 회귀직선이란 산포도에 산재해 있는 점들의 관계에 근거하여 그어지는 선입니다. 선이 그어지는 방법으로는 최소자승법이 사용됩니다.

 

최소자승법이랑 잔차를 자승(제곱)한 값들의 합이 최소가 되도록 표본회귀식을 구하는 방법입니다. 측정값을 기초로 적당한 제곱합을 만들고 그것을 최소로 하는 값을 구하여 측정결과를 처리하는 방법인데, 말이 좀 어렵습니다. 그냥 이런식으로 진행되는 구나 정도만 이해하시고 실습을 진행하시면 이해가 가실 것입니다.

 

이렇게 구해진 표본회귀식이 어느정도 예측을 정확하게 해낼 수 있는지의 검증이 필요하겠죠? 그래서 산포도를 기준으로 표본회귀선을 그었을 때 선 주위에 두 변수의 값들이 몰려 있으면 실제값과 예측값간의 차이가 크지 않다는 것이므로 예측의 정확도가 높아진다고 할 수 있습니다. 하지만 이렇게 눈으로 판단하는 것 외에 2가지의 적합도 검증 방법을 사용합니다.

 

첫 번째는 추정의 표준오차(standard error of esrimate)라고 합니다. 제가 위에 설명한 회귀선 주위로 얼마나 퍼져 있나를 수치로 나타내 주는 방법입니다.

이러한 식을 따른다 라는 것 정도만 알아도 충분합니다. 직접 손으로 구할 일은 없으니까요 하하.. 그런데 식에 SSE라는 값이 들어가 있습니다. 이것은 총변동(total variation)이라는 식으로 설명이 가능 합니다.

 

총제곱합은 (Sum od Squares Total : SST) = 회귀제곱합(Sun of Squares Regression : SSR) + 잔차제곱합 (Sun of Squares Error : SSE) 라는 의미입니다. 여기서 SST는 실제 값들이 이들의 평균으로부터 흩어진 정도이며 SSR은 예측치와 실제 값들의 평균의 차이의 제곱합, SSE는 예측치와 실제 값의 차이의 제곱합을 뜻합니다. 실제 이 값들을 구하여 결정계수를 구하므로 인지하고 계셔야 합니다.

 

그렇다면 결정계수란 제가 말씀드렸던 2가지의 적합도 검증 방법 중 나머지 한 가지입니다. 결정계수는 R^2로 표현되며, 0부터 1까지의 값을 가집니다. 구한 표본회귀선이 완벽하게 예측을 할 수 있다면 1의 값을 가지게 됩니다. SSR/SST로도 표현되면 이는 1-SSE/SST와도 같습니다. 예를 들어, 결정계수의 값이 0.98이라면 독립변수가 종속변수의 98%를 결정하고 다른 요인들이 나머지 2%의 영향을 미친다는 것을 알 수 있는 것입니다. 그리고 성능은 잔차와 MSE, RMSE로 평가하게 됩니다.

 

이제 sklearn에서 제공하는 데이터 셋을 가지고 실습을 진행해 보도록 하겠습니다.

 

 

 

 

 

 

 

 

 

단일선형회귀 분석 실습

In [48]:
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error
from math import sqrt
%matplotlib inline
matplotlib.style.use('ggplot')
 

먼저 필요한 도구들을 불러옵니다.

In [37]:
from sklearn import datasets
boston_house_prices = datasets.load_boston()
print(boston_house_prices.keys())
print(boston_house_prices.data.shape)
print(boston_house_prices.feature_names)
 
dict_keys(['data', 'target', 'feature_names', 'DESCR', 'filename'])
(506, 13)
['CRIM' 'ZN' 'INDUS' 'CHAS' 'NOX' 'RM' 'AGE' 'DIS' 'RAD' 'TAX' 'PTRATIO'
 'B' 'LSTAT']
 

sklearn에서 제공하는 데이터셋 boston을 불러와서 저장해줍니다. 그 후 데이터의 확인을 위해 key값, 행과 열의 길이, 컬럼의 이름들을 프린트해줍니다.

In [38]:
print(boston_house_prices.DESCR)
 
.. _boston_dataset:

Boston house prices dataset
---------------------------

**Data Set Characteristics:**  

    :Number of Instances: 506 

    :Number of Attributes: 13 numeric/categorical predictive. Median Value (attribute 14) is usually the target.

    :Attribute Information (in order):
        - CRIM     per capita crime rate by town
        - ZN       proportion of residential land zoned for lots over 25,000 sq.ft.
        - INDUS    proportion of non-retail business acres per town
        - CHAS     Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
        - NOX      nitric oxides concentration (parts per 10 million)
        - RM       average number of rooms per dwelling
        - AGE      proportion of owner-occupied units built prior to 1940
        - DIS      weighted distances to five Boston employment centres
        - RAD      index of accessibility to radial highways
        - TAX      full-value property-tax rate per $10,000
        - PTRATIO  pupil-teacher ratio by town
        - B        1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
        - LSTAT    % lower status of the population
        - MEDV     Median value of owner-occupied homes in $1000's

    :Missing Attribute Values: None

    :Creator: Harrison, D. and Rubinfeld, D.L.

This is a copy of UCI ML housing dataset.
https://archive.ics.uci.edu/ml/machine-learning-databases/housing/


This dataset was taken from the StatLib library which is maintained at Carnegie Mellon University.

The Boston house-price data of Harrison, D. and Rubinfeld, D.L. 'Hedonic
prices and the demand for clean air', J. Environ. Economics & Management,
vol.5, 81-102, 1978.   Used in Belsley, Kuh & Welsch, 'Regression diagnostics
...', Wiley, 1980.   N.B. Various transformations are used in the table on
pages 244-261 of the latter.

The Boston house-price data has been used in many machine learning papers that address regression
problems.   
     
.. topic:: References

   - Belsley, Kuh & Welsch, 'Regression diagnostics: Identifying Influential Data and Sources of Collinearity', Wiley, 1980. 244-261.
   - Quinlan,R. (1993). Combining Instance-Based and Model-Based Learning. In Proceedings on the Tenth International Conference of Machine Learning, 236-243, University of Massachusetts, Amherst. Morgan Kaufmann.

 

이 부분은 저희가 불러온 데이터 셋의 설명입니다. 해당 컬럼이 어떤 컬럼인지 설명도 나와있습니다. 참고하면 좋습니다.

In [39]:
data_frame = pd.DataFrame(boston_house_prices.data)
data_frame.tail()
Out[39]:
  0 1 2 3 4 5 6 7 8 9 10 11 12
501 0.06263 0.0 11.93 0.0 0.573 6.593 69.1 2.4786 1.0 273.0 21.0 391.99 9.67
502 0.04527 0.0 11.93 0.0 0.573 6.120 76.7 2.2875 1.0 273.0 21.0 396.90 9.08
503 0.06076 0.0 11.93 0.0 0.573 6.976 91.0 2.1675 1.0 273.0 21.0 396.90 5.64
504 0.10959 0.0 11.93 0.0 0.573 6.794 89.3 2.3889 1.0 273.0 21.0 393.45 6.48
505 0.04741 0.0 11.93 0.0 0.573 6.030 80.8 2.5050 1.0 273.0 21.0 396.90 7.88
 

불러온 데이터 셋에서 data에 해당하는 값들을 dataframe형태로 변경해줍니다.

In [40]:
data_frame.columns = boston_house_prices.feature_names
data_frame.tail()
Out[40]:
  CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT
501 0.06263 0.0 11.93 0.0 0.573 6.593 69.1 2.4786 1.0 273.0 21.0 391.99 9.67
502 0.04527 0.0 11.93 0.0 0.573 6.120 76.7 2.2875 1.0 273.0 21.0 396.90 9.08
503 0.06076 0.0 11.93 0.0 0.573 6.976 91.0 2.1675 1.0 273.0 21.0 396.90 5.64
504 0.10959 0.0 11.93 0.0 0.573 6.794 89.3 2.3889 1.0 273.0 21.0 393.45 6.48
505 0.04741 0.0 11.93 0.0 0.573 6.030 80.8 2.5050 1.0 273.0 21.0 396.90 7.88
 

이전의 결과는 컬럼이름이 숫자로 되어있는데 이것을 데이터 셋에 저장된 컬럼이름으로 바꾸는 과정입니다.

In [41]:
data_frame['Price'] = boston_house_prices.target
data_frame.tail()
Out[41]:
  CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT Price
501 0.06263 0.0 11.93 0.0 0.573 6.593 69.1 2.4786 1.0 273.0 21.0 391.99 9.67 22.4
502 0.04527 0.0 11.93 0.0 0.573 6.120 76.7 2.2875 1.0 273.0 21.0 396.90 9.08 20.6
503 0.06076 0.0 11.93 0.0 0.573 6.976 91.0 2.1675 1.0 273.0 21.0 396.90 5.64 23.9
504 0.10959 0.0 11.93 0.0 0.573 6.794 89.3 2.3889 1.0 273.0 21.0 393.45 6.48 22.0
505 0.04741 0.0 11.93 0.0 0.573 6.030 80.8 2.5050 1.0 273.0 21.0 396.90 7.88 11.9
 

데이터프레임에 price라는 컬럼을 추가하여 데이터는 target에 저장된 데이터를 사용합니다.

In [42]:
data_frame.plot(kind="scatter", x="RM", y="Price", figsize=(6,6),
                color="blue", xlim = (4,8), ylim = (10,45))
Out[42]:
<matplotlib.axes._subplots.AxesSubplot at 0x224f11aa438>
 
 

산점도로 나타내줍니다.

In [43]:
from sklearn import linear_model
linear_regression = linear_model.LinearRegression()
linear_regression.fit(X = pd.DataFrame(data_frame["RM"]), y = data_frame["Price"])
prediction = linear_regression.predict(X = pd.DataFrame(data_frame["RM"]))
print('a value = ', linear_regression.intercept_)
print('b balue =', linear_regression.coef_)
 
a value =  -34.67062077643857
b balue = [9.10210898]
 

데이터를 학습시킵니다. 선형회귀분석 모델을 저장하고, 예측값을 저장합니다. 그 후 선형회귀분석 모델에 맞게 학습하는 함수를 이용하여 새로운 값을 예측합니다. 그리고 선형회귀분석식의 a계수와 b계수를 출력해줍니다.

In [44]:
residuals = data_frame["Price"] - prediction
residuals.describe()
Out[44]:
count    5.060000e+02
mean     1.899227e-15
std      6.609606e+00
min     -2.334590e+01
25%     -2.547477e+00
50%      8.976267e-02
75%      2.985532e+00
max      3.943314e+01
Name: Price, dtype: float64
 

잔차를 구하여 적합도를 검증해주기 위한 작업입니다.

In [45]:
SSE = (residuals**2).sum()
SST = ((data_frame["Price"]-data_frame["Price"].mean())**2).sum()
R_squared = 1 - (SSE/SST)
print('R_squared = ', R_squared)
 
R_squared =  0.4835254559913341
 

SSE와 SST 값을 저장하고 결정계수 값을 계산합니다.

In [46]:
data_frame.plot(kind="scatter",x="RM",y="Price",figsize=(6,6),
                color="blue", xlim = (4,8), ylim = (10,45))

# Plot regression line
plt.plot(data_frame["RM"],prediction,color="red")
Out[46]:
[<matplotlib.lines.Line2D at 0x224f121e2e8>]
 
In [49]:
print('score = ', linear_regression.score(X = pd.DataFrame(data_frame["RM"]), y = data_frame["Price"]))
print('Mean_Squared_Error = ', mean_squared_error(prediction, data_frame["Price"]))
print('RMSE = ', mean_squared_error(prediction, data_frame["Price"])**0.5)
 
score =  0.4835254559913343
Mean_Squared_Error =  43.60055177116956
RMSE =  6.603071389222561
 

MSE를 구하는 모듈을 불러와 값을 구하고, score를 통해 예측한 값이 얼만큼의 성능을 보이는지 평가합니다.

 

다중선형회귀분석

 

지금까지 한 개의 독립변수와 하나의 종속변수의 관계를 분석하는 단일선형회귀분석을 배웠습니다. 아무래도 한 개의 독립변수로 종속변수의 값을 예측하는데는 무리가 있을 것으로 보입니다. 이럴 때는 다중선형회귀분석을 이용합니다. 다중회귀선형분석은 두 개 이상의 독립변수들이 하나의 종속변수의 관계를 분석하는 것인데요. 과정은 단일선형회귀분석과 비슷합니다.

 

다중선형회귀모델식을 이러합니다.

이 식을 통해 회귀계수를 추정하고 적절한 회귀 직선을 구하는 것입니다. 방법은 최소자승법을 이용하죠. 그 외 적합도 검증과정은 단일과 같습니다. 바로 실습진행해보죠.

 

 

단일선형회귀분석에서 이용했던 boston 데이터 셋을 이용하여 비교하기 쉽게 진행해보겠습니다.

In [50]:
X = pd.DataFrame(boston_house_prices.data)
X.tail()
Out[50]:
  0 1 2 3 4 5 6 7 8 9 10 11 12
501 0.06263 0.0 11.93 0.0 0.573 6.593 69.1 2.4786 1.0 273.0 21.0 391.99 9.67
502 0.04527 0.0 11.93 0.0 0.573 6.120 76.7 2.2875 1.0 273.0 21.0 396.90 9.08
503 0.06076 0.0 11.93 0.0 0.573 6.976 91.0 2.1675 1.0 273.0 21.0 396.90 5.64
504 0.10959 0.0 11.93 0.0 0.573 6.794 89.3 2.3889 1.0 273.0 21.0 393.45 6.48
505 0.04741 0.0 11.93 0.0 0.573 6.030 80.8 2.5050 1.0 273.0 21.0 396.90 7.88
In [51]:
X.columns = boston_house_prices.feature_names
X.tail()
Out[51]:
  CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT
501 0.06263 0.0 11.93 0.0 0.573 6.593 69.1 2.4786 1.0 273.0 21.0 391.99 9.67
502 0.04527 0.0 11.93 0.0 0.573 6.120 76.7 2.2875 1.0 273.0 21.0 396.90 9.08
503 0.06076 0.0 11.93 0.0 0.573 6.976 91.0 2.1675 1.0 273.0 21.0 396.90 5.64
504 0.10959 0.0 11.93 0.0 0.573 6.794 89.3 2.3889 1.0 273.0 21.0 393.45 6.48
505 0.04741 0.0 11.93 0.0 0.573 6.030 80.8 2.5050 1.0 273.0 21.0 396.90 7.88
 

단일선형회귀분석과 변수명만 다르고 과정은 동일합니다.

In [52]:
X['Price'] = boston_house_prices.target
y = X.pop('Price')
X.tail()
Out[52]:
  CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT
501 0.06263 0.0 11.93 0.0 0.573 6.593 69.1 2.4786 1.0 273.0 21.0 391.99 9.67
502 0.04527 0.0 11.93 0.0 0.573 6.120 76.7 2.2875 1.0 273.0 21.0 396.90 9.08
503 0.06076 0.0 11.93 0.0 0.573 6.976 91.0 2.1675 1.0 273.0 21.0 396.90 5.64
504 0.10959 0.0 11.93 0.0 0.573 6.794 89.3 2.3889 1.0 273.0 21.0 393.45 6.48
505 0.04741 0.0 11.93 0.0 0.573 6.030 80.8 2.5050 1.0 273.0 21.0 396.90 7.88
 

이번에는 변수에 전체 데이터 중 price에 해당하는 값만 데이터 프레임에서 제외 시키고 저장하였습니다.

In [53]:
linear_regression = linear_model.LinearRegression()
linear_regression.fit(X = pd.DataFrame(X), y = y)
prediction = linear_regression.predict(X = pd.DataFrame(X))
print('a value = ', linear_regression.intercept_)
print('b balue =', linear_regression.coef_)
 
a value =  36.459488385089855
b balue = [-1.08011358e-01  4.64204584e-02  2.05586264e-02  2.68673382e+00
 -1.77666112e+01  3.80986521e+00  6.92224640e-04 -1.47556685e+00
  3.06049479e-01 -1.23345939e-02 -9.52747232e-01  9.31168327e-03
 -5.24758378e-01]
 

데이터를 학습시켜 선형회귀분석의 계수들을 출력합니다.

In [54]:
residuals = y-prediction
residuals.describe()
Out[54]:
count    5.060000e+02
mean     2.924319e-15
std      4.683822e+00
min     -1.559447e+01
25%     -2.729716e+00
50%     -5.180489e-01
75%      1.777051e+00
max      2.619927e+01
Name: Price, dtype: float64
 

적합도 검증을 위해 잔차를 구해줍니다.

In [55]:
SSE = (residuals**2).sum()
SST = ((y-y.mean())**2).sum()
R_squared = 1 - (SSE/SST)
print('R_squared = ', R_squared)
 
R_squared =  0.7406426641094094
In [56]:
print('score = ', linear_regression.score(X = pd.DataFrame(X), y = y))
print('Mean_Squared_Error = ', mean_squared_error(prediction, y))
print('RMSE = ', mean_squared_error(prediction, y)**0.5)
 
score =  0.7406426641094095
Mean_Squared_Error =  21.894831181729202
RMSE =  4.679191295697281
 

이렇게 적합도검증과 성능평가까지 마쳤습니다. 결과를 보시면 단일선형회귀보다 다중선형회귀분석이 더 높은 스코어를 보이는 것을 알 수 있죠. 하지만 꼭 변수가 많다고 예측확률이 높은 것은 아닙니다. 적절히 전처리를 해주고 영향을 주는 변수들만 추려서 독립변수들로 사용한다면 더욱 높은 스코어를 기대할 수 있을 것 입니다.:)

In [ ]: