상세 컨텐츠

본문 제목

05. LSTM을 이용한 기온 예측 (다변량 시계열 데이터 예측)

데이터 분석/웹 스크래핑

by 알파고제로 2023. 12. 26. 11:17

본문

1. 데이터 다운로드

- 독일의 Max Planck Institute for Biogeochemistry에서 관측한 날씨 데이터셋

- 2003년 이후 10분 간격으로 관측된 온도, 대기압, 습도와 같은 14종류의 다양한 관측치를 포함
- 한 시간 동안 6개, 하루에 144개의 관측치를 포함

import tensorflow as tf
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd

mpl.rcParams['figure.figsize'] = (8, 6)
mpl.rcParams['axes.grid'] = False

# 인터넷 상의 orgin에 위치해 있는 파일을 fname 이름으로 다운로드 받는다.
zip_path = tf.keras.utils.get_file(
  origin='https://storage.googleapis.com/tensorflow/tf-keras-datasets/jena_climate_2009_2016.csv.zip',
  fname='jena_climate_2009_2016.csv.zip',
  extract=True)
# splitext: 파일 확장자 분리
csv_path, _ = os.path.splitext(zip_path)
print(csv_path)

2. 하이퍼 파리미터 설정

TRAIN_SPLIT = 300000
EVALUATION_INTERVAL = 200
EPOCHS = 10
tf.random.set_seed(13)

3. 데이터 셋 확인

df = pd.read_csv(csv_path)
print(df.shape)
print(df.head())
print(df.columns)

4. 분석 데이터 컬럼 추출

- 14개의 특성값 중 3개(기압, 온도, 공기 밀도)만 특성값으로 추출

features_considered = ['p (mbar)', 'T (degC)', 'rho (g/m**3)']
features = df[features_considered]
features.index = df['Date Time']
print(features.head())

5. 데이터 표준화

dataset = features.values
data_mean = dataset[:TRAIN_SPLIT].mean(axis=0)
data_std = dataset[:TRAIN_SPLIT].std(axis=0)
dataset = (dataset-data_mean)/data_std

print(dataset.shape)
print(dataset)

6. 타임 스텝 별 데이터 분리 함수

# dataset: 학습 데이터 셋
# target: 종속변수
# start_index: 시작 인덱스
# end_index: 끝 인덱스
# history_size: 과거 데이터의 크기
# target_size: 예측해야 할 레이블의 크기(즉, 얼마 동안의 미래를 예측할지를 의미)
# step: 샘플링 간격
# single_step: 주어진 과거의 자료로부터 하나의 포인트를 예측할지 결정

def multivariate_data(dataset, target, start_index, end_index, history_size, target_size, step, single_step=False):
    data = []
    labels = []

    start_index = start_index + history_size
    # end_index를 지정하지 않으면(즉, 데이터셋의 끝까지 추출) end_index는 예측하고자하는 미래 시간만큼을 차감(데이터셋 내에 미래시간이 없기 때문에)
    if end_index is None:
        end_index = len(dataset) - target_size
    
    for i in range(start_index, end_index):
        # ex) 1st data indices: [0,6,12,18,...714, 720] (step값 만큼 추출 데이터 건너 뜀)
        indices = range(i - history_size, i, step)
        data.append(dataset[indices])

        if single_step:
            labels.append(target[i+target_size])
        else:
            labels.append(target[i:i + target_size])
    return np.array(data), np.array(labels)

7. 모델 학습 및 검증 손실 변화 시각화 함수

def plot_train_history(history, title):
    loss = history.history['loss']
    val_loss = history.history['val_loss']

    epochs = range(len(loss))

    plt.figure()

    plt.plot(epochs, loss, 'b', label='Training loss')
    plt.plot(epochs, val_loss, 'r', label='Validation loss')
    plt.title(title)
    plt.legend()

    plt.show()

8. 싱글 스텝 예측

- 주어진 과거의 자료로부터 미래의 단일 지점을 예측

8.1 타임 스텝별 독립변수와 종속변수 추출

past_history = 720 # 지난 5일간의 데이터 (720 = 5일 * 6개(한시간 관측 개수) * 24시간(하루))
future_target = 72 # 12시간 이 후 온도 예측 (6개*12시간)
STEP = 6 # 60분 내에 기온의 급격한 변화가 예상되지 않으므로 샘플링은 1시간마다 수행


x_train_single, y_train_single = multivariate_data(dataset, dataset[:, 1], 0, TRAIN_SPLIT, past_history, future_target, STEP, single_step=True)

x_val_single, y_val_single = multivariate_data(dataset, dataset[:, 1], TRAIN_SPLIT, None, past_history, future_target, STEP, single_step=True)

# 720개의 과거 데이터를 6단계로 추출하여 120개의 레코드 생성
# 120: 5일 * 24시간
# 3: 3개의 특성
print(x_train_single.shape)
print(y_train_single.shape)
print(f'Single window of past history : {x_train_single[0].shape}')
print(x_train_single[0])

8.2 학습 및 평가 데이터 추출

[참고] from_tensor_slices() 함수의 이해
- tf.data는 데이터 입력 파이프 라인 빌드를 위한 텐서플로우의 서브패키지이다. 로컬 파일이나 메모리에 올려져 있는 데이터를 모델에 집어넣기 적합한 텐서로 변환하는 작업을 한다.
- from_tensor_slices()는 주어진 텐서들을 첫 번째 차원을 따라 슬라이스한다. 
- 모든 입력 텐서는 첫번째 차원과 같은 크기를 가져야한다.
- cache()는 데이터셋을 캐시, 즉 메모리 또는 파일에 보관한다. 따라서 두번째 이터레이션부터는 캐시된 데이터를 사용한다.
- shuffle()는 데이터셋을 임의로 섞어준다. BUFFER_SIZE개로 이루어진 버퍼로부터 임의로 샘플을 뽑고, 뽑은 샘플은 다른 샘플로 대체한다. 완벽한 셔플을 위해서 전체 데이터셋의 크기에 비해 크거나 같은 버퍼 크기가 요구된다.
- batch()는 데이터셋의 항목들을 하나의 배치로 묶어준다.
- 배치 작업이 이루어진 후 데이터의 구성

BATCH_SIZE = 256
BUFFER_SIZE = 10000

train_data_single = tf.data.Dataset.from_tensor_slices((x_train_single, y_train_single))
train_data_single = train_data_single.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat()

val_data_single = tf.data.Dataset.from_tensor_slices((x_val_single, y_val_single))
val_data_single = val_data_single.batch(BATCH_SIZE).repeat()
for i in train_data_single:
    print(i)

8.3. LSTM 모델 구축 후 학습

single_step_model = tf.keras.models.Sequential()
# x_train_single.shape: (299280, 120, 3)
single_step_model.add(tf.keras.layers.LSTM(32, input_shape=x_train_single.shape[-2:]))
single_step_model.add(tf.keras.layers.Dense(1))
single_step_model.compile(optimizer=tf.keras.optimizers.RMSprop(), loss='mae')

# (256, 1): Time Step을 만들 때 BATCH_SIZE를 256개로 지정. 즉, 256개의 타임 스텝에 대해 12시간 뒤의 단일 포인트에 대한 예측 값
for x, y in val_data_single.take(1):
    print(single_step_model.predict(x).shape)

# 200 단계 훈련, 50 단계 검증, 10회 반복
single_step_history = single_step_model.fit(train_data_single, epochs=EPOCHS, steps_per_epoch=EVALUATION_INTERVAL, validation_data=val_data_single, validation_steps=50)
plot_train_history(single_step_history,
                   'Single Step Training and Validation Loss')

8.4. 예측 결과 시각화 함수

# 그래프의 x축 좌표 값 생성
def create_time_steps(length):
    return list(range(-length, 0))

# plot_data: [5일간 기온 데이터, 12시간 후 기온 데이터, 예측 값]
# delta: 미래 예측 지점
# title: 그래프 제목
def show_plot(plot_data, delta, title):
    labels = ['History', 'True Future', 'Model Prediction']
    marker = ['.-', 'rx', 'go']
    time_steps = create_time_steps(plot_data[0].shape[0])
    if delta:
        future = delta
    else:
        future = 0

    plt.title(title)
    for i, x in enumerate(plot_data):
        if i:
            plt.plot(future, plot_data[i], marker[i], markersize=10, label=labels[i])
        else:
            plt.plot(time_steps, plot_data[i].flatten(), marker[i], label=labels[i])
    plt.legend()
    plt.axis('auto')
    plt.xlim([time_steps[0], (future+5)*2])
    plt.xlabel('Time-Step')
    return plt

8.5. 예측 결과 시각화

# 검증 데이터 중 3개의 데이터를 가져와 256개로 묶인(batch)데이터 중 0번째 타임 스텝 데이의 12시간 후 단일 지점의 예측 값과 실제 값 확인
for x, y in val_data_single.take(3):
    plot = show_plot([x[0][:, 1].numpy(), y[0].numpy(),
                      single_step_model.predict(x)[0]], 12,
                     'Single Step Prediction')
    plot.show()

9. 멀티 스텝 예측

- 과거 히스토리가 주어지면 미래의 값 범위를 예측
- Multi Step Model은 미래의 시퀀스를 예측한다.

9.1. 타임 스텝별 독립변수와 종속변수 추출

past_history = 720 # 지난 5일간의 데이터 (720 = 5일 * 144개, 144 = 6개 * 24시간)
future_target = 72 # 12시간 이 후 온도 예측 (60분*12)
STEP = 6 # 60분 내에 기온의 급격한 변화가 예상되지 않으므로 샘플링은 1시간마다 수행

x_train_multi, y_train_multi = multivariate_data(dataset, dataset[:, 1], 0, TRAIN_SPLIT, past_history, future_target, STEP)

x_val_multi, y_val_multi = multivariate_data(dataset, dataset[:, 1], TRAIN_SPLIT, None, past_history, future_target, STEP)

# 720개의 과거 데이터를 6단계로 추출하여 120개의 레코드 생성
# 120: 5일 * 24시간
# 3: 3개의 특성
print(x_train_single.shape)
print('Single window of past history : {}'.format(x_train_multi[0].shape))
print('\n Target temperature to predict : {}'.format(y_train_multi[0].shape))

9.2. 학습 및 평가 데이터 추출

BATCH_SIZE = 256
BUFFER_SIZE = 10000

train_data_multi = tf.data.Dataset.from_tensor_slices((x_train_multi, y_train_multi))
train_data_multi = train_data_multi.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat()

val_data_multi = tf.data.Dataset.from_tensor_slices((x_val_multi, y_val_multi))
val_data_multi = val_data_multi.batch(BATCH_SIZE).repeat()

9.3. LSTM 모델 구축 및 학습

multi_step_model = tf.keras.models.Sequential()
multi_step_model.add(tf.keras.layers.LSTM(32, return_sequences=True, input_shape=x_train_multi.shape[-2:]))
# 싱글 스텝 예측보다는 복잡하기 때문에 LSTM을 두 개의 층으로 구성
multi_step_model.add(tf.keras.layers.LSTM(16, activation='relu'))
multi_step_model.add(tf.keras.layers.Dense(72))
multi_step_model.compile(optimizer=tf.keras.optimizers.RMSprop(clipvalue=1.0), loss='mae')

# (256, 72): Time Step을 만들 때 BATCH_SIZE를 256개로 지정. 즉, 256개의 타임 스텝에 대해 12시간(6*12) 뒤의 멀티 포인트에 대한 예측 값
for x, y in val_data_multi.take(1):
    print(multi_step_model.predict(x).shape)

# 200 단계 훈련, 50 단계 검증, 10회 반복
multi_step_history = multi_step_model.fit(train_data_multi, epochs=EPOCHS, steps_per_epoch=EVALUATION_INTERVAL, validation_data=val_data_multi, validation_steps=50)
plot_train_history(multi_step_history,
                   'Multi Step Training and Validation Loss')

9.4. 예측 결과 시각화 함수

# 그래프의 x축 좌표 값 생성
def create_time_steps(length):
    return list(range(-length, 0))

def multi_step_plot(history, true_future, prediction):
    plt.figure(figsize=(12, 6))
    num_in = create_time_steps(len(history))
    num_out = len(true_future)
    
    # history[:, 1] : 3개의 독립변 수 중 2번째 컬럼이 기온 데이터
    plt.plot(num_in, np.array(history[:,1]), label='History')
    plt.plot(np.arange(num_out)/STEP, np.array(true_future), 'bo', label='True Future')
    if prediction.any():
        plt.plot(np.arange(num_out)/STEP, np.array(prediction), 'ro', label='Predicted Future')
    plt.legend()
    plt.show()

9.5. 예측 결과 시각화

for x, y in val_data_multi.take(3):
    multi_step_plot(x[0], y[0], multi_step_model.predict(x)[0])

관련글 더보기

댓글 영역