(220929) 다층 퍼셉트론을 이용한 회귀 문제 분석(보스턴 지역 주택가격 및 자동차 연비 예측)

이은경·2022년 10월 3일
0

1. 보스턴 지역 주택가격 예측하기

1) 문제 정의

  • 1970년대 보스턴 지역의 주택 가격을 예측하는 회귀(연속적인 수치 값) 문제

2) 데이터 준비하기

from tensorflow.keras.datasets.boston_housing import load_data

(X_train, y_train), (X_test, y_test) = load_data(path='boston_housing.npz', 
                                                 test_split=0.2, seed=777)

3) 데이터 확인하기

X_train[0]
  • 결과값: array([2.5199e-01, 0.0000e+00, 1.0590e+01, 0.0000e+00, 4.8900e-01,
    5.7830e+00, 7.2700e+01, 4.3549e+00, 4.0000e+00, 2.7700e+02,
    1.8600e+01, 3.8943e+02, 1.8060e+01])
  • 케라스 자료의 feature값에 대한 설명 확인 필요

4) 데이터 전처리 및 검증 데이터 분리하기

import numpy as np

# 1) feature 전처리 -> 정규화 
     -> 표준화(Standardization 실제 값에서 평균(mean)값을 빼서 표준편차(std)로 나누기)
mean = np.mean(X_train, axis = 0) # 모든 row 를 반영
std = np.std(X_train, axis = 0)

X_train = (X_train-mean) / std 
X_test = (X_test-mean) / std 
# 0-1의 구간은 아니고 평균의 범위일 것

from sklearn.model_selection import train_test_split

# 2) 훈련/검증 데이터 셋 분리
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.33, 
                                                  random_state=777)

print(X_train.shape) # 훈련데이터 270건
print(X_val.shape) # 검증데이터 134건
  • X_train의 평균과 표준편차로 같이 정규화
  • X_test의 평균과 표준편차를 별도로 만들면, 훈련과 테스트 데이터를 정규화하는 기준이 달라져버림
  • 가장 좋은 것은 X 전체 데이터에 정규화 하는 것이 좋음

5) 모델 구성, 설정 및 학습하기

from keras.models import Sequential
from keras.layers import Dense

# 3개의 레이어로 모델 구성하기
model = Sequential()
model.add(Dense(64, activation ='relu', input_shape=(13,)))
model.add(Dense(32, activation ='relu'))
model.add(Dense(1)) 
# 하나의 출력 값, 활성화 함수 설정하지 않은면 default값은 linear임

# 모델 설정하기
model.compile(optimizer = 'adam', loss='mse', metrics=['mae', 'mse']) 
# 실제 오차율인 mae(absolute error)과 제곱 오차값 mse 둘다 볼 수 있음
# 오차율이 줄이는 것이 목표

# 모델 학습하기
history = model.fit(X_train, y_train, epochs=300, validation_data=(X_val, y_val))

6) 모델 결과 그리기

import matplotlib.pyplot as plt

his_dict = history.history
mse = his_dict['mse']
val_mse = his_dict['val_mse'] # 검증 데이터가 있는 경우 ‘val_’ 수식어가 붙습니다.

epochs = range(1, len(mse) + 1)
fig = plt.figure(figsize = (20, 10))

# 훈련 및 검증 손실 그리기
ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(epochs, mse, color = 'blue', label = 'train_mse')
ax1.plot(epochs, val_mse, color = 'orange', label = 'val_mse')
ax1.set_title('train and val mse')
ax1.set_xlabel('epochs')
ax1.set_ylabel('mse')
ax1.legend()

mae = his_dict['mae']
val_mae = his_dict['val_mae']

# 훈련 및 검증 정확도 그리기
ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, mae, color = 'blue', label = 'train_mae')
ax2.plot(epochs, val_mae, color = 'orange', label = 'val_mae')
ax2.set_title('train and val mae')
ax2.set_xlabel('epochs')
ax2.set_ylabel('mae')
ax2.legend()

plt.show()

7) 모델 평가하기

model.evaluate(X_test, y_test)
  • 결과값: 4/4 - 0s 3ms/step - loss: 8.8172 - mae: 2.1957 - mse: 8.8172
  • 2,195.7달러 차이

8) 모델 예측 결과 그리기

test_predictions = model.predict(X_test).flatten()

plt.scatter(y_test, test_predictions)
plt.xlabel('True Values [Price]')
plt.ylabel('Predictions [Price]')
plt.axis('equal')
plt.axis('square')
plt.xlim([0,plt.xlim()[1]])
plt.ylim([0,plt.ylim()[1]])
_ = plt.plot([-100, 100], [-100, 100])

  • 우상향 하는 선보다 아래쪽에 찍힌 점들이 많은 것으로 보아 우리 모델이 주택가격을 실제보다 다소 저렴하게 예측하는 경향이 있다.
  • feature의 특성을 보고 예측한 주택가격이 실제보다 조금 저렴하게 예측한 집들만 뽑아서 특성을 다시 확인거나 외부 요인이 있는지 집 구조 자체에 문제가 있는지 추가 확인 필요
  • 반대 방향으로 주택 자체 feature에 더해서 물가나 다른 요인들에 의해 가격이 결정되었다라고 결론을 내릴 수 있음
  • 그래서 무드, 여론, 정책, 대출 등과 같은 third part요인도 같이 분석해야 한다.
  • 실제로, 분석과 상이한 이유를 찾기 위해 주택가격에 영향을 주는 요인들을 추가해서 반복 분석한다.

9) K-폴드 이용하여 향상시키기

from tensorflow.keras.datasets.boston_housing import load_data
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

import numpy as np
from sklearn.model_selection import KFold

(x_train, y_train), (x_test, y_test) = load_data(path='boston_housing.npz',
                                                 test_split=0.2,
                                                 seed=777)

# 데이터 표준화
mean = np.mean(x_train, axis = 0)
std = np.std(x_train, axis = 0)

# 여기까진 전부 동일
x_train = (x_train - mean) / std
x_test = (x_test - mean) / std

#----------------------------------------
# K-Fold를 진행해봅니다.
k = 3

# 주어진 데이터셋을 k만큼 등분
# 여기서는 3이므로 훈련 데이터셋(404개)를 3등분하여
# 1개는 검증셋으로, 나머지 2개는 훈련셋으로 활용
kfold = KFold(n_splits=k)

# 재사용을 위해 모델을 반환하는 함수를 정의합니다.

def get_model():
    model = Sequential()
    model.add(Dense(64, activation = 'relu', input_shape = (13, )))
    model.add(Dense(32, activation = 'relu')) 
    model.add(Dense(1))   

    model.compile(optimizer = 'adam', loss = 'mse', metrics = ['mae'])
    
    return model

mae_list = [] # 테스트셋을 평가한 후 결과 mae를 담을 리스트를 선언합니다.

# k번 진행합니다.
for train_index, val_index in kfold.split(x_train):
    # 해당 인덱스는 무작위로 생성됩니다.
    # 무작위로 생성해주는 것은 과대적합을 피할 수 있는 좋은 방법입니다.
    x_train_fold, x_val_fold = x_train[train_index], x_train[val_index]
    y_train_fold, y_val_fold = y_train[train_index], y_train[val_index]
    
    # 모델 불러오기
    model = get_model()
    
    model.fit(x_train_fold, y_train_fold, epochs = 300, 
              validation_data = (x_val_fold, y_val_fold))
    
    _, test_mae = model.evaluate(x_test, y_test)
    mae_list.append(test_mae)

print(np.mean(mae_list)) 
  • 결과값: 4/4 - 0s 3ms/step - loss: 8.8942 - mae: 2.1067
    2.137338320414225
  • 종전에는 검증 데이터를 별도로 분리하여 학습하지 못했는데 모델이 검증 데이터도 학습할 수 있게 됨에 따라 학습 데이터가 많아져 정확도가 높아질 수 있다.(하지만 굉장히 미묘함)

2. 자동차 연비(MPG) 예측하기

1) 문제 정의

  • 1970년대 후반과 1980년대 초반의 자동차 연비를 예측하는 회귀(연속적인) 문제

2) 데이터 준비하기

#라이브러리 임포트
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

import tensorflow as tf
import keras
from keras.models import Sequential
from keras.layers import Dense
from tensorflow.keras.optimizers import RMSprop

import warnings
warnings.filterwarnings('ignore')


dataset_path = tf.keras.utils.get_file("auto-mpg.data", "https://archive.ics.uci.edu/
                                  ml/machine-learning-databases/auto-mpg/auto-mpg.data")

column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight', 
                'Acceleration', 'Model Year', 'Origin']               
                
#연비, 실린더, 배수량, 마력, 중력, 가속, 연식, 제조국(1;USA, 2;Europe, 3;Japan)

raw_dataset = pd.read_csv(dataset_path, names = column_names, 
                          na_values='?', comment = '\t', sep=' ', skipinitialspace=True) 

dataset = raw_dataset.copy() # 복사본 만들기
  • 읽어들여올 때, '?'를 NaN 값으로 변경해줌(이 데이터 셋을 알고 있기 때문에 가능)
  • '\t' 탭키 다음에 나오는 문자부터는 데이터 아니야, 주석으로 처리해서 읽어오지 않음
  • sep=' ' = 공백 기준으로 데이터를 분리해서 가져와줘.
  • skipinitialspace = 데이터 앞 공백을 제거해줘

3) 데이터 전처리

# NaN데이터 포함 행 삭제하고 다시 dataset에 담기
dataset = dataset.dropna()

# Origin(제조국): 수치형 -> 범주형; 원핫인코딩(수동)

origin = dataset.pop('Origin') # pop = 데이터 프레임에서 잘라내고 복사하기

dataset['USA'] = (origin==1) * 1.0   # 조건 만족하지 않으면 0
dataset['Europe'] = (origin==2) * 1.0
dataset['Japan'] = (origin==3) * 1.0

4) 훈련 및 테스트 데이터로 분리

train_dataset = dataset.sample(frac=0.8, random_state=0) # 80%를 임의 추출
test_dataset = dataset.drop(train_dataset.index) 
# 80%를 임의추출한 index 삭제하고 남은 20% 담기

5) X(feature, 독립변수, 문제지), y(label, 종속변수, 정답지)로 분리

train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')

6) 데이터(X) 정규화 -> 표준화

mean=np.mean(train_dataset, axis = 0)
std=np.std(train_dataset, axis = 0)

train_dataset = (train_dataset - mean) / std
test_dataset = (test_dataset - mean) / std

7) 학습 모델 구성, 설정 및 학습하기

np.random.seed(7)

# 모델 구성하기
model = Sequential()
model.add(Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]))
# train_dataset.keys() = X의 feature의 개수
model.add(Dense(64, activation='relu'))
model.add(Dense(1))

# 모델 설정하기
model.compile(optimizer=RMSprop(0.001), loss='mse', metrics=['mae', 'mse'])

# 모델 학습하기
history = model.fit(train_dataset, train_labels, epochs = 500)

8) 모델 결과 그려보기

import matplotlib.pyplot as plt

his_dict = history.history
mse = his_dict['mse']

epochs = range(1, len(mse) + 1)
fig = plt.figure(figsize = (10, 5))

# 훈련 및 검증 손실 그리기
ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(epochs, mse, color = 'blue', label = 'train_mse')
ax1.set_title('train mse')
ax1.set_xlabel('epochs')
ax1.set_ylabel('mse')
ax1.legend()

mae = his_dict['mae']

# 훈련 및 검증 정확도 그리기
ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, mae, color = 'blue', label = 'train_mae')
ax2.set_title('train mae')
ax2.set_xlabel('epochs')
ax2.set_ylabel('mae')
ax2.legend()

plt.show()

9) 모델 평가하기

# (1) 리스트로 담아 줌
model.evaluate(test_dataset, test_labels) 
  • 3/3 - 1s 5ms/step - loss: 6.0058 - mae: 1.8943 - mse: 6.0058
    [6.005845069885254, 1.8943045139312744, 6.005845069885254]
# (2) 변수별로 각각 담아줌
loss, mae, mse = model.evaluate(test_dataset, test_labels) 

print('테스트 세트의 손실점수: {:5.2f} MPG'.format(loss))
print('테스트 세트의 평균 절대 오차: {:5.2f} MPG'.format(mae))
print('테스트 세트의 평균 제곱 오차: {:5.2f} MPG'.format(mse))
  • 3/3 - 0s 7ms/step - loss: 6.0058 - mae: 1.8943 - mse: 6.0058
    테스트 세트의 손실점수: 6.01 MPG
    테스트 세트의 평균 절대 오차: 1.89 MPG
    테스트 세트의 평균 제곱 오차: 6.01 MPG

10) 모델 예측결과 그려보기

test_predictions = model.predict(test_dataset).flatten()

plt.scatter(test_labels, test_predictions)
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
plt.axis('equal')
plt.axis('square')
plt.xlim([0,plt.xlim()[1]])
plt.ylim([0,plt.ylim()[1]])
_ = plt.plot([-100, 100], [-100, 100])

0개의 댓글