사이킷런 찍먹시리즈(1) - 보스턴 주택가격 예측모델

6 분 소요

서론

본 글은 작성자의 공부를 위해 쓰는 게시물입니다.

아래 작성하는 글은 틀린내용이 있을수도 있으니 참고하시기 바라며,

책은 핸즈온 머신러닝 2판을 사용하여 공부하고 있습니다.

네이버 블로그에서 이전하여 URL로 남깁니다.

선형회귀

선형 회귀는 회귀의 가장 기본적인 형태로,

분산된 여러가지의 데이터가 하나의 직선과 비슷한 함수의 특성을

가진다고 생각하고 데이터 사이에 직선을 그어

새로운 데이터가 들어왔을때 이러한 직선에 해당 데이터를 x값으로 집어넣어

이 함수에 해당하는 y값으로 예측해보자 하는것이 선형회귀의 이론이다.

요즘은 딥러닝과 같은 복잡한 모델이 인기를 끌고있는데, 사실 대부분의 데이터들은 그렇게까지 복잡한 모델을 적용하면 오히려 성능이 떨어진다. 단순한 데이터는 단순한 모델로 접근하는 것이 더 좋은 성능을 낼 수 있는데, 특히 선형 회귀는 단순하지만 머신러닝의 가장 기본이 되는 중요한 모델이다.

image1

위 그림처럼 데이터의 산점도 모양이 어느정도 선형의 구조를 가진다고 할때,우리는 선형회귀를 이용하여 이러한 데이터의 함수를 대략 예측할 수 있다.

이러한 직선을 우리가 긋는다고 할때, 어떻게 해야 가장 모델을 예측하기 적합한 함수의 직선을 그을 수 있을까?

선이 실제 데이터와 얼마나 정확한지는 각 점에서 선을 그어 선까지의 길이를 측정하여,

예측함수 값과 실제 데이터의 값의 차이를 알아 볼 수 있을것이다.

​각 점에서 선에 그은 선의 길이를 모두 합하여 평균을 내어 보면,가장 그 평균이 적은 기울기의 함수를 찾을 수 있을 것이고 그 함수가 위의 데이터를 나타내기에 가장 적합한 함수라고 예측 해볼 수있을것이다.

하지만, 실제 머신러닝에서는 이 합을 평균을 내어본 값을 그대로 사용하기보다는, 점의값과 선의 값의 차를 모두 제곱해주어, 제곱된값을 평균을 내어 사용한다.

이는 절대값을 씌워주는 효과와 동시에 오차가 평균에 미치는 영향을 더욱 극대화 시켜주어,우리가 잡아내야할 오차를 더욱 과대하게 나타내준다.

이러한 지표를 우리는 MSE, Mean Squared Error, 평균 제곱오차라고 한다.

image1

위 수식에서 y는 이 함수가 예측한 예측값이고, t는 실제 데이터의 값이다.

이렇게 우리는 우리가 예측한 모델의 정확도의 점수를 알아 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np
def mean_squared_error(y, t):
    return 0.5 * np.sum((y-t)**2)

t=[0,0,1,0,0,0,0,0,0,0]

#예제 1
y=[0.1,0.05,0.6,0.05,0.1,0,0.1,0,0,0]
print(mean_squared_error(np.array(y),np.array(t)))

#예제 2
y=[0.1,0.05,0,0.05,0.1,0,0.1,0,0,0.6]
print(mean_squared_error(np.array(y),np.array(t)))

위의 코드에서 실제 데이터값은 [0,0,1,0,0,0,0,0,0,0] 이므로,

만약 우리가 예측한 값이 이 데이터 값과 동일하다면 MSE는 0이 되게 된다.

하지만 예제 1이나 예제 2처럼 예측한 데이터가 실제데이터 값과 차이가 있을경우, MSE를 측정하면 각각의 답은 다음과 같다.

예제 1 = 0.09750000000000003

예제 2 = 0.6975

예제 1이 예제 2의 MSE보다 낮은 값을 가지므로,예제1이 실제 데이터값과의 오차가 예제 2보다 적음을 알 수 있다.

하지만 이러한 데이터의 오차를 얻기 위해, 우리가 모든 기울기에 대한 함수의 MSE를 구할 수는 없을 것이다.

기울기는 연속적인 값이므로 우리가 빠르게 최적의 함수값을 찾아내기 위해서는 조금 다른 방법이 필요하다.

이것을 위해 우리는 MSE의 그래프를 그려 그 미분값이 우리가 예측한 1차함수의 기울기 라는 사실을 이용하여, 모든 예측데이터 모델 함수의 기울기 값에 대한 오차의 그래프를 그려 볼 것이다

​오차의 값을 제곱했으니 이 함수는 이차함수의 모양을 갖게 될것인데, 우리는 이를 MSE 손실함수라 한다.

image1

이 함수는 우리가 예측한 데이터 모델에 대한 MSE를 나타낸 함수이기 때문에,이 함수의 기울기가 0에 가까울 수록 우리가 갖게될 MSE값은 줄어들게 된다.

따라서 이 2차함수의 꼭짓점에 해당하는 0과 맞닿는 부분의 기울기 값을 찾아내므로써 가장 적은 오차를 갖는 MSE값을 알아 낼수 있다.

​또, 이러한 함수에 루트를 쓰운 다른 오차 측정 함수를 그려 볼 수 있는데, 이는 RMSE, Root Mean Squared Error, 평균 제곱근 오차라 한다.

image1

​이렇게 루트를 씌워주게 되면, 기존에 제곱을 해주었던 값보다 더욱 실제값과 유사한 단위로 다시 변환되기 때문에 해석이 MSE보다 직관적이 된다.

또, 이러한 RMSE에 log를 씌워 만든 평가지표도 있는데, 이는 RMSLE, Root Mean Squared Logarithmic Error, 평균 제곱 로그 오차가 있다.

image1

위의 y+1은 log(0)이 되는것을 방지하기 위해 추가 해준 것이다.

이러한 RMSLE는 세가지 특징을 갖는데, 이 특징은 아래와 같다.

1.아웃라이어에 덜 민감하다.

​기존 데이터와 스케일이 크게 다른 특이치의 데이터가 들어왔을때, RMSLE는 다른 오차값보다

훨씬 덜 증가한다. 따라서 큰 특이치의 데이터가 섞여있어도 그 결과를 둔감하게 반응한다.

2. 상대적 Error를 측정해준다.

다른 손실함수들은 절대적 크기에 따라 변하지만, RMSLE는 상대적 크기가 동일하다면, RMSLE의 값도 동일하다.

ex) 예측값 = 100, 실제데이터 = 90 일때, RMSLE = 1.1053, RMSE = 10

ex) 예측값 = 10,000, 실제데이터 = 90,000 일때, RMSLE = 1.1053, RMSE = 1000

​이처럼 오차의 스케일이 커져도, 그 상대적인 크기가 동일하다면 RMSLE는 그 차이를 반영하지 않는다.

3. Under Estimation에 큰 패널티를 부여한다.

image1

​위의 그래프는 RMSLE 그래프의 한 예시이다.

이처럼 예측값이 실제값보다 작을 경우 패널티를 극대화하여 더 큰 패널티를 부여한다.

​따라서 예측값이 실제 데이터보다 작으면 작을수록 더 오차가 커지기 때문에 예측값이 실제데이터보다 작아서는 안되는 데이터 예측에 용이하다.

예를들어 배달어플의 배달 도착시간을 예측해주는 머신러닝 프로그램을 제작한다고 했을 때, 만약 예상 도착시간이 실제 도착시간보다 큰 경우는 소비자가 불만을 크게 표출하지 않겠지만, 예상 도착시간이 실제 도착시간보다 적게 예측된 경우에는 소비자로 하여금 불만을 표출하게 할 수있다.

따라서 이렇게 예측시간이 실제값보다 작아서는 안되는 데이터의 경우 에는 RMSLE로 오차를 측정하면 좋은 결과를 얻을 수 있다.

​위의 데이터들은 각각의 장단점이 있으며, 어떤 것이 절대적으로 좋은 평가지표라고 할 순 없다.

따라서 데이터를 분석해보고, 가장 오차를 적게 나타내줄 수 있는 평가지표를 선별하여 잘 활용해야 할것이다.

이제 사이킷런을 이용해서 간단한 선형 모델을 적용하고 이를 분석해보자

1
from sklearn.datasets import load_boston

사이킷런 데이터셋에 있는 예제 데이터인 보스턴의 주택의 가격책정에 대한 데이터를

활용하여, 위에서 설명한 선형회귀를 적용시켜 예측값을 측정해보겠다.

1
2
3
4
5
from sklearn.datasets import load_boston

Boston = load_boston()

print(Boston.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

위와같은 결과가 출력되는데, 이렇게 .DESCR을 통해 이 데이터가 갖고있는 특성들의 의미를 알아 볼 수 있다.

이러한 특성들을 선형회귀로 학습시켜, 결과적으로 마지막에 있는 MEDV, 지역의 주택의 중간가치를 예측해보는 프로그램을 작성해 볼 것이다.

그러기 위해서는 이 주택가격을 이루는 데이터들의 특성이 어떤 의미를 갖는지 확인하는 것이 아주 중요하다.

image1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
​import pandas as pd
import numpy as np

boston_df = pd.DataFrame(boston.data, columns= boston.feature_names)
print(Boston_df)
​```
​​
​
​​![image1](/assets/images/knowledge/machinelearning/2021-05-04-사이킷런-보스턴주택가격예측모델/image8.PNG)
​​

​​```python
​​boston_df['Price'] = boston.target
​​```
​
​여기에 보스턴 데이터의 타겟, 즉 우리가 예측하고자 하는 값인 MEDV를 우리가 식별하기 쉬운 'Price' 라는 값으로 추가하여 데이터프레임 맨 끝자리에 추가해준다.

각각의 데이터 특성과 우리가 예측할 데이터의 상관관계를 알아보기위해, 데이터를 시각화하여 살펴보도록 하자.

```python
import matplotlib.pyplot as plt

for i in range(0,13):
    plt.figure(figsize = (8,5))
    plt.scatter(boston_df.iloc[:,i],boston_df['Price'],alpha=0.5)
    plt.xlabel(boston_df.columns[i], size = 14)
    plt.ylabel('Price', size = 12)

​​image1
​​
​​​​image1
​​​​
​​​​이렇게 시각화한 데이터를 보면 어느정도 우리가 예측하고자 하는 값과 각 특성과의 연관성을 눈으로 알 수 있다.

예를들어 RM특성이나 LSTAT 특성은 눈에 띄게 선형적인 관계를 나타내는 것을 쉽게 관측가능하다.

​사실 지금 제공된 보스턴주택가격 데이터는 어느정도 전처리가 이루어진 데이터라 오랜시간을 들여야하는 전처리 과정이 생략되어 있지만, 전처리가 전혀되니 않은 날것의 데이터의 경우는 특성에 섞여있는 이상치를 잡아내거나 어떠한 관계도 없어 보이는 데이터는 삭제하는식의 전처리가 필요한데, 이럴때에 시각화로 데이터를 나타내보는것은 아주 유용하다.

다음의 특성들을 확인하였으니, 한번 위의 데이터들을 사이킷런을 통하여 선형회귀를 적용했을때 얼마의 정확도를 가지는지 알아보도록 하겠다.

1
2
3
4
5
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split

linear = LinearRegression(normalize = True)
X_train, X_test, y_train, y_test = train_test_split(boston.data, boston.target, test_size= 0.2)

사이킷런에서 선형회귀를 불러온뒤, 위의 데이터를 트레인 셋과 테스트 셋으로 0.8 : 0.2의 비율로 먼저 나눠주겠다. 이렇게 나눈 트레인셋을 학습시킨 후, 테스트셋을 예측해 봄으로써, 이 모델에서 선형회귀가 얼마나 적합한지 알아볼것이다.

1
2
3
4
linear.fit(X_train,y_train)
print(linear.score(X_train, y_train))
print(linear.score(X_test, y_test))


0.7347422985410577

0.7504655806536701

트레이닝 셋을 선형회귀 모델에 학습시킨후, 학습데이터 점수와 평가데이터 점수를 출력하게 해보았다.

위 결과를 보니, 학습데이터에서는 약 73퍼센트의 정확도, 평가데이터에서는 약 75퍼센트의 정확도를 보였다.

​위에서 진행한 선형회귀 모델을 시각화하여, 어떠한 직선을 그엇는지 확인해보면,

1
2
3
4
X_pred= linear.predict(X_test)

plt.scatter(X_pred,y_test)
plt.plot([5,50], [5,50], '--r')

​​​​image1
​​​​
​​​​위와같은 그래프를 얻을 수 있다.
​​​​
그래프를 보면, 데이터의 산점도에 맞는 선이라고 보기에는 선과 점이 조금 동떨어져있다.

테스트 결과의 정확도가 약 75퍼센트밖에 안되기 때문에 이 모델로 주택의 가격을 예측하기에는 실제값과 동떨어진 느낌이 있다.

물론 전처리를 하나도 안하고 그대로 선형회귀 모델에 학습시킨 것 치고는 높은 수치이지만, 실제 주택가격 예측에 이 모델을 사용하기에는 낮은 정확도일것이다.

​그렇다면 우리는 정확도를 높이기 위해서 어떤 것을 해볼 수 있을까?

데이터의 전처리를 조금 더 꼼꼼히 해보거나, 또 다른 유의미한 특성을 조사하여 추가하거나,가격과 직접적으로 연관이 없는 데이터의 영향력을 줄이거나, 아니면 선형회귀모델이 아닌 아예 다른 모델을 적용해보거나 하는 방법을 통해 이 모델의 정확도를 높일 수 있을 것이다.

댓글남기기