모도리는 공부중

21.03.11. 딥러닝 - 시계열 RNN 본문

K-디지털 빅데이터 분석서비스 개발자과정 20.11.02~21.04.12/PYTHON(웹크롤링, 머신·딥러닝)

21.03.11. 딥러닝 - 시계열 RNN

공부하는 모도리 2021. 3. 11. 18:09
728x90
반응형

저번시간 숙제

1. 시퀀스 길이 맞추기
2. 모델링
    - LSTM
    - Dropout
    - Embedding

 

 

선생님 풀이 갑니다.

..... 잘.. 자버렸다.. ;;;;;

 

 

 


에너지 데이터 예측하기(시계열, RNN)

시간 흐름에 따라 발생하는 에너지 데이터를 예측해보자.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

data = pd.read_csv('data/energy.csv')
data.head()

data.tail()

data.shape

out : (26304, 3)

 

2만 6천개의 데이터가 있음을 확인 완료.

 

시계열 데이터 파싱

# parse_dates : timestamp열을 그냥 글자 타입에서 date 날짜 타입으로 변경
# index_col : 특정 열을 인덱스로 지정
energy = pd.read_csv('data/energy.csv',
                     parse_dates=['timestamp'],
                     index_col='timestamp')
                     
energy.info()

 

energy.head()

 

1. load 데이터를 이용해서 다음 1시간의 load 데이터를 예측해보자.

del energy['temp'] # 온도 데이터 삭제
# 어떤 패턴을 가지고 있는지 시각화 - matplotlib 사용
plt.figure(figsize=(10,5)) # 가로, 세로
plt.plot(energy)
plt.show()

여름과 겨울에 패턴이 주기적으로 올라갔다 내려가는 모습을 보이고 있다.

RNN을 통해 학습해나가는 모델링을 진행하자.

  1. 데이터 스케일링
  2. 이터 셋 분리 (train, validation, test)
  3. RNN 학습을 위한 데이터 shape으로 변경 (samples, time steps, features)

데이터셋을 분리할 때 사진은 무작위 샘플링을 진행한다. 하지만 시간은 무작위로 진행해버리면 시간의 흐름이 망가지게 되므로 시계열데이터를 다룰 때는 데이터셋 분리를 뚝뚝 끊어내는 방식으로 진행을 한다.

이런 방식으로 끊어낸다.

MinMax 스케일링

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()

energy.head()
# 현재 scaler에는 load 자료밖에 없다.

energy['load'] = scaler.fit_transform(energy)
# 원래 무슨값이었든간에 1과 0 사이값을 찾아서 단위를 바꿔버리는 작업 진행

energy
# 최적화된 값 확인

데이터 셋을 RNN을 위한 형태로 변경

  • 데이터셋 분리 전에 RNN학습을 위한 shape로 변경을 먼저 진행한다.
  • 중복되는 코드를 여러번 입력하는 과정을 단축시키기 위한 순서 변경
energy_shifted = energy.copy()
energy_shifted

# 다음 한 시간 예측을 위해 정답 데이터를 먼저 추가
# shift() : load 데이터를 한 칸씩 위로(-1) 밀어낸다.
#     - 위는 -, 아래는 +.
#     - 시간 단위이므로 freq='H'
energy_shifted['y_t+1'] = energy_shifted['load'].shift(-1, freq='H')
energy_shifted

과거 사용량을 보며 몇 번이나 쓰게 할건지 작업한다. 예를 들어 과거 10시간을 토대로 예측하게 할 수 있다. 지금 우리는 과거 5시간을 토대로 예측하는 시스템을 만든다.

energy_shifted['load_t-5'] = energy_shifted['load'].shift(5, freq='H')
energy_shifted

과거 5번의 데이터는 없으므로 load_t-5의 0~4행은 NaN으로 비어있다.

energy_shifted['load_t-4'] = energy_shifted['load'].shift(4, freq='H')
energy_shifted['load_t-3'] = energy_shifted['load'].shift(3, freq='H')
energy_shifted['load_t-2'] = energy_shifted['load'].shift(2, freq='H')
energy_shifted['load_t-1'] = energy_shifted['load'].shift(1, freq='H')
energy_shifted['load_t-0'] = energy_shifted['load'].shift(0, freq='H') # 0은 현재를 의미

energy_shifted

2012-01-01 00:00:00 ~ 2012-01-01 04:00:00 의 시간은 NaN 데이터가 있어서 활용할 수 없으므로 데이터를 지워준다.

energy_shifted.dropna(inplace=True)
print(energy_shifted.shape)
energy_shifted

out :

데이터 셋 분리

  • 그림에 없는 부분은 전부 train으로 사용한다.
# 이제 데이터 셋 분리를 위한 날짜를 지정한다.
validation_start = '2014-09-01 00:00:00'
test_start = '2014-11-01 00:00:00'

# 날짜로 파싱했으므로 날짜 연산은 충분히 가능하다.
train = energy_shifted[energy_shifted.index < validation_start] # val보다 큰 경우
val = energy_shifted[(energy_shifted.index >= validation_start) &
                     (energy_shifted.index < test_start)]
test = energy_shifted[energy_shifted.index >= test_start] # test보다 작은 경우

train.shape, val.shape, test.shape

out : ((23371, 8), (1464, 8), (1463, 8))

plt.figure(figsize=(10,5))
plt.plot(train['load'])
plt.plot(val['load'])
plt.plot(test['load'])
plt.show()

# 문제와 답
# train.loc[행, 열] 
#     - loc 없이 하면 컬럼만 색인

X_train = train.loc[:, 'load_t-5':'load_t-0']
y_train = train['y_t+1']

X_val = val.loc[:, 'load_t-5':'load_t-0']
y_val = val['y_t+1']

X_test = test.loc[:, 'load_t-5':'load_t-0']
y_test = test['y_t+1']

print(X_train.shape, y_train.shape)
print(X_val.shape, y_val.shape)
print(X_test.shape, y_test.shape)

out :

(23371, 6) (23372,)

(1464, 6) (1464,)

(1463, 6) (1463,)

 

feature값은 1이며 현재 생략되어 있다. 이 생략된 feature값을 reshape를 통해 나타낸다.

# 이 셀을 두 번 실행시키면 values가 없다고 뜬다. 그럴 때는 위부터 다시 실행하고 와라.
X_train = X_train.values.reshape(23371,6,1)
X_val = X_val.values.reshape(1464,6,1)
X_test = X_test.values.reshape(1463,6,1)

모델링 진행

from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense, GRU

# LSTM은 내부 연산이 많이 들어간다.
# RNN계열의 GRU를 이용해보자.

model = Sequential()
# time_steps는 과거 6개의 데이터를 토대로 미래를 맞춘다.
# features는 에너지 사용량 하나만 쓰므로 1개이다.
model.add(GRU(32, input_shape=(6, 1)))
model.add(Dense(1))

model.compile(loss='mse', optimizer='Adam')

history = model.fit(X_train, y_train, validation_data=(X_val, y_val), epochs=20)
더보기
Train on 23371 samples, validate on 1464 samples
Epoch 1/20
23371/23371 [==============================] - 2s 75us/sample - loss: 0.0014 - val_loss: 9.5257e-04
Epoch 2/20
23371/23371 [==============================] - 2s 75us/sample - loss: 9.6280e-04 - val_loss: 7.4761e-04
Epoch 3/20
23371/23371 [==============================] - 2s 80us/sample - loss: 7.4246e-04 - val_loss: 5.4264e-04
Epoch 4/20
23371/23371 [==============================] - 2s 72us/sample - loss: 6.1394e-04 - val_loss: 5.0589e-04
Epoch 5/20
23371/23371 [==============================] - 2s 72us/sample - loss: 5.8653e-04 - val_loss: 5.0239e-04
Epoch 6/20
23371/23371 [==============================] - 2s 82us/sample - loss: 5.7243e-04 - val_loss: 5.7241e-04
Epoch 7/20
23371/23371 [==============================] - 2s 77us/sample - loss: 5.7133e-04 - val_loss: 5.5243e-04
Epoch 8/20
23371/23371 [==============================] - 2s 72us/sample - loss: 5.7570e-04 - val_loss: 5.3266e-04
Epoch 9/20
23371/23371 [==============================] - 2s 78us/sample - loss: 5.6388e-04 - val_loss: 4.9545e-04
Epoch 10/20
23371/23371 [==============================] - 2s 74us/sample - loss: 5.6693e-04 - val_loss: 5.4547e-04
Epoch 11/20
23371/23371 [==============================] - 2s 74us/sample - loss: 5.5968e-04 - val_loss: 4.9566e-04
Epoch 12/20
23371/23371 [==============================] - 2s 74us/sample - loss: 5.5062e-04 - val_loss: 4.8851e-04
Epoch 13/20
23371/23371 [==============================] - 2s 82us/sample - loss: 5.5253e-04 - val_loss: 4.8189e-04
Epoch 14/20
23371/23371 [==============================] - 2s 76us/sample - loss: 5.3797e-04 - val_loss: 4.7079e-04
Epoch 15/20
23371/23371 [==============================] - 2s 79us/sample - loss: 5.3923e-04 - val_loss: 4.6248e-04
Epoch 16/20
23371/23371 [==============================] - 2s 78us/sample - loss: 5.2006e-04 - val_loss: 4.6022e-04
Epoch 17/20
23371/23371 [==============================] - 2s 79us/sample - loss: 5.1791e-04 - val_loss: 4.3717e-04
Epoch 18/20
23371/23371 [==============================] - 2s 79us/sample - loss: 5.0288e-04 - val_loss: 4.1597e-04
Epoch 19/20
23371/23371 [==============================] - 2s 86us/sample - loss: 4.8905e-04 - val_loss: 3.9519e-04
Epoch 20/20
23371/23371 [==============================] - 2s 78us/sample - loss: 4.8019e-04 - val_loss: 3.7490e-04
plt.figure(figsize=(15,5))
plt.plot(history.history['loss'], label=['loss'])
plt.plot(history.history['val_loss'], label=['val_loss'])
plt.legend()
plt.show()

결과 시각화

result = test[['y_t+1']]
result

# 모델이 예측한 결과를 컬럼으로 새로 추가한다
result['predict'] = model.predict(X_test)
result

 

# scaler 적용 전 원래 단위로 변경
inverse_result = scaler.inverse_transform(result)
inverse_result

# val값과 test값이 얼마나 비슷하게 그려지는지 확인
plt.figure(figsize=(30,5))
plt.plot(inverse_result[:,0], label='actual')
plt.plot(inverse_result[:,1], label='predict')
plt.legend()
plt.show()

2. 과거 load와 temp를 활용해 다음 한 시간의 load를 예측해보자.

  • 온도와 사용량을 같이 고려하며 예측하는 모델링
energy = pd.read_csv('data/energy.csv',
                     parse_dates=['timestamp'],
                     index_col=['timestamp'])
energy.head()

데이터 스케일링

load_scaler = MinMaxScaler()
temp_scaler = MinMaxScaler()
energy['load'] = load_scaler.fit_transform(energy[['load']])
energy['temp'] = temp_scaler.fit_transform(energy[['temp']])

energy.head()

energy_shifted = energy.copy()
energy_shifted['y_t+1'] = energy_shifted['load'].shift(-1, freq='H')
energy_shifted

# 과거 6개의 시간을 shift
energy_shifted['load_t-5'] = energy_shifted['load'].shift(5, freq='H')
energy_shifted['temp_t-5'] = energy_shifted['temp'].shift(5, freq='H')
energy_shifted['load_t-4'] = energy_shifted['load'].shift(4, freq='H')
energy_shifted['temp_t-4'] = energy_shifted['temp'].shift(4, freq='H')
energy_shifted['load_t-3'] = energy_shifted['load'].shift(3, freq='H')
energy_shifted['temp_t-3'] = energy_shifted['temp'].shift(3, freq='H')
energy_shifted['load_t-2'] = energy_shifted['load'].shift(2, freq='H')
energy_shifted['temp_t-2'] = energy_shifted['temp'].shift(2, freq='H')
energy_shifted['load_t-1'] = energy_shifted['load'].shift(1, freq='H')
energy_shifted['temp_t-1'] = energy_shifted['temp'].shift(1, freq='H')
energy_shifted['load_t-0'] = energy_shifted['load'].shift(0, freq='H')
energy_shifted['temp_t-0'] = energy_shifted['temp'].shift(0, freq='H')
energy_shifted

energy_shifted.dropna(inplace=True)

데이터셋 나누기

train = energy_shifted[energy_shifted.index < validation_start] # val보다 큰 경우
val = energy_shifted[(energy_shifted.index >= validation_start) &
                     (energy_shifted.index < test_start)]
test = energy_shifted[energy_shifted.index >= test_start] # test보다 작은 경우

train.shape, val.shape, test.shape

out : ((23371, 15), (1464, 15), (1463, 15))

# 문제와 답
# train.loc[행, 열] 
#     - loc 없이 하면 컬럼만 색인
X_train = train.loc[:, 'load_t-5':'temp_t-0']
y_train = train['y_t+1']

X_val = val.loc[:, 'load_t-5':'temp_t-0']
y_val = val['y_t+1']

X_test = test.loc[:, 'load_t-5':'temp_t-0']
y_test = test['y_t+1']

# 총 6번의 순환 동안 2개씩 입력이 들어가야 한다.
X_train = X_train.values.reshape(23371,6,2)
X_val = X_val.values.reshape(1464,6,2)
X_test = X_test.values.reshape(1463,6,2)

모델링 및 시각화

 

..........하하...

728x90
반응형
Comments