4. LSTMΒΆ

Open In Colab

3μž₯μ—μ„œλŠ” Johns Hopkins Universityμ—μ„œ μ œκ³΅ν•˜λŠ” μ‹œκ³„μ—΄ 데이터λ₯Ό μ§€λ„ν•™μŠ΅μš© λ°μ΄ν„°λ‘œ λ³€ν™˜ν•˜λŠ” 과정을 μ‹€μŠ΅ν–ˆμŠ΅λ‹ˆλ‹€. 이번 μž₯μ—μ„œλŠ” LSTM을 μ‚¬μš©ν•˜μ—¬ μ•žμœΌλ‘œμ˜ λŒ€ν•œλ―Όκ΅­ μ½”λ‘œλ‚˜ ν™•μ§„μž 수λ₯Ό μ˜ˆμΈ‘ν•΄λ³΄λŠ” λͺ¨λΈμ„ κ΅¬μΆ•ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

4.1절과 4.2μ ˆμ—μ„œλŠ” λŒ€ν•œλ―Όκ΅­ μ½”λ‘œλ‚˜ λˆ„μ  ν™•μ§„μž 수 데이터λ₯Ό 뢈러온 ν›„, 데이터λ₯Ό ν›ˆλ ¨μš©, κ²€μ¦μš©, μ‹œν—˜μš© λ°μ΄ν„°λ‘œ λ‚˜λˆ„μ–΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. 4.3μ ˆμ—μ„œλŠ” LSTM λͺ¨λΈμ„ μ •μ˜ν•˜κ³  4.4μ ˆμ—μ„œλŠ” μ •μ˜ν•œ λͺ¨λΈμ„ ν•™μŠ΅μ‹œμΌœ λ³΄κ² μŠ΅λ‹ˆλ‹€. λ§ˆμ§€λ§‰μœΌλ‘œ μ½”λ‘œλ‚˜ ν™•μ§„μž μˆ˜μ— λŒ€ν•œ μ˜ˆμΈ‘κ°’μ„ ν™•μΈν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

μš°μ„  기본적인 λͺ¨λ“ˆλ“€μ„ import ν•΄μ€λ‹ˆλ‹€.

%matplotlib inline은 notebook을 μ‹€ν–‰ν•œ λΈŒλΌμš°μ €μ—μ„œ λ°”λ‘œ 그림을 λ³Ό 수 있게 ν•΄μ£ΌλŠ” 것, %config InlineBackend.figure_format='retina'λŠ” κ·Έλž˜ν”„μ˜ 해상도λ₯Ό λ†’μ—¬μ€λ‹ˆλ‹€.

import torch
import os
import numpy as np
import pandas as pd
from tqdm import tqdm
import seaborn as sns
from pylab import rcParams
import matplotlib.pyplot as plt
from matplotlib import rc
from sklearn.preprocessing import MinMaxScaler
from pandas.plotting import register_matplotlib_converters
from torch import nn, optim

%matplotlib inline
%config InlineBackend.figure_format='retina'
sns.set(style='whitegrid', palette='muted', font_scale=1.2)
rcParams['figure.figsize'] = 14, 10
register_matplotlib_converters()
RANDOM_SEED = 42
np.random.seed(RANDOM_SEED)
torch.manual_seed(RANDOM_SEED)
<torch._C.Generator at 0x7f773ce2bb88>

4.1 데이터 λ‹€μš΄λ‘œλ“œΒΆ

λͺ¨λΈλ§ μ‹€μŠ΅μ„ μœ„ν•΄ λŒ€ν•œλ―Όκ΅­ μ½”λ‘œλ‚˜ λˆ„μ  ν™•μ§„μž 데이터λ₯Ό λΆˆλŸ¬μ˜€κ² μŠ΅λ‹ˆλ‹€. 2.1μ ˆμ— λ‚˜μ˜¨ μ½”λ“œλ₯Ό ν™œμš©ν•˜κ² μŠ΅λ‹ˆλ‹€.

!git clone https://github.com/Pseudo-Lab/Tutorial-Book-Utils
!python Tutorial-Book-Utils/PL_data_loader.py --data COVIDTimeSeries
!unzip -q COVIDTimeSeries.zip
Cloning into 'Tutorial-Book-Utils'...
remote: Enumerating objects: 24, done.
remote: Counting objects: 100% (24/24), done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 24 (delta 6), reused 14 (delta 3), pack-reused 0
Unpacking objects: 100% (24/24), done.
COVIDTimeSeries.zip is done!

4.2 데이터 μ „μ²˜λ¦¬ΒΆ

3μž₯μ—μ„œ μ‹€μŠ΅ν•œ μ½”λ“œλ₯Ό ν™œμš©ν•΄ μ‹œκ³„μ—΄ 데이터λ₯Ό μ§€λ„ν•™μŠ΅μš© λ°μ΄ν„°λ‘œ λ³€ν˜•ν•œ ν›„ ν•™μŠ΅μš©, κ²€μ¦μš©, μ‹œν—˜μš© λ°μ΄ν„°λ‘œ λΆ„λ¦¬ν•˜κ² μŠ΅λ‹ˆλ‹€. 그리고 ν›ˆλ ¨μš© λ°μ΄ν„°μ˜ ν†΅κ³„λŸ‰μ„ ν™œμš©ν•΄ μŠ€μΌ€μΌλ§μ„ μ§„ν–‰ν•˜κ² μŠ΅λ‹ˆλ‹€.

#λŒ€ν•œλ―Όκ΅­ λ°μ΄ν„°λ§Œ μΆ”μΆœ ν›„ μΌμžλ³„ ν™•μ§„μž 수둜 λ³€ν™˜
confirmed = pd.read_csv('time_series_covid19_confirmed_global.csv')
confirmed[confirmed['Country/Region']=='Korea, South']
korea = confirmed[confirmed['Country/Region']=='Korea, South'].iloc[:,4:].T
korea.index = pd.to_datetime(korea.index)
daily_cases = korea.diff().fillna(korea.iloc[0]).astype('int')


def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

#μ§€λ„ν•™μŠ΅μš© λ°μ΄ν„°λ‘œ λ³€ν™˜
seq_length = 5
X, y = create_sequences(daily_cases, seq_length)

#ν•™μŠ΅μš©, κ²€μ¦μš©, μ‹œν—˜μš©μœΌλ‘œ 뢄리
train_size = int(327 * 0.8)
X_train, y_train = X[:train_size], y[:train_size]
X_val, y_val = X[train_size:train_size+33], y[train_size:train_size+33]
X_test, y_test = X[train_size+33:], y[train_size+33:]

MIN = X_train.min()
MAX = X_train.max()

def MinMaxScale(array, min, max):

    return (array - min) / (max - min)

#MinMax μŠ€μΌ€μΌλ§
X_train = MinMaxScale(X_train, MIN, MAX)
y_train = MinMaxScale(y_train, MIN, MAX)
X_val = MinMaxScale(X_val, MIN, MAX)
y_val = MinMaxScale(y_val, MIN, MAX)
X_test = MinMaxScale(X_test, MIN, MAX)
y_test = MinMaxScale(y_test, MIN, MAX)

#Tensor ν˜•νƒœλ‘œ λ³€ν™˜
def make_Tensor(array):
    return torch.from_numpy(array).float()

X_train = make_Tensor(X_train)
y_train = make_Tensor(y_train)
X_val = make_Tensor(X_val)
y_val = make_Tensor(y_val)
X_test = make_Tensor(X_test)
y_test = make_Tensor(y_test)
print(X_train.shape, X_val.shape, X_test.shape)
print(y_train.shape, y_val.shape, y_test.shape)
torch.Size([261, 5, 1]) torch.Size([33, 5, 1]) torch.Size([33, 5, 1])
torch.Size([261, 1]) torch.Size([33, 1]) torch.Size([33, 1])

4.3 LSTM λͺ¨λΈ μ •μ˜ΒΆ

LSTM λͺ¨λΈμ„ μƒμ„±ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. CovidPredictor ν΄λž˜μŠ€λŠ” κΈ°λ³Έ λ³€μˆ˜, layerλ₯Ό μ΄ˆκΈ°ν™” ν•΄μ£ΌλŠ” μƒμ„±μž, ν•™μŠ΅ μ΄ˆκΈ°ν™”λ₯Ό μœ„ν•œ reset_hidden_state ν•¨μˆ˜, 그리고 μ˜ˆμΈ‘μ„ μœ„ν•œ forward ν•¨μˆ˜λ‘œ ꡬ성돼 μžˆμŠ΅λ‹ˆλ‹€.

class CovidPredictor(nn.Module):
    def __init__(self, n_features, n_hidden, seq_len, n_layers):
        super(CovidPredictor, self).__init__()
        self.n_hidden = n_hidden
        self.seq_len = seq_len
        self.n_layers = n_layers
        self.lstm = nn.LSTM(
            input_size=n_features,
            hidden_size=n_hidden,
            num_layers=n_layers
        )
        self.linear = nn.Linear(in_features=n_hidden, out_features=1)
    def reset_hidden_state(self):
        self.hidden = (
            torch.zeros(self.n_layers, self.seq_len, self.n_hidden),
            torch.zeros(self.n_layers, self.seq_len, self.n_hidden)
        )
    def forward(self, sequences):
        lstm_out, self.hidden = self.lstm(
            sequences.view(len(sequences), self.seq_len, -1),
            self.hidden
        )
        last_time_step = lstm_out.view(self.seq_len, len(sequences), self.n_hidden)[-1]
        y_pred = self.linear(last_time_step)
        return y_pred

4.4 ν•™μŠ΅ΒΆ

4.3μ ˆμ—μ„œ μ •μ˜ν•œ CovidPredictor 클래슀λ₯Ό ν•™μŠ΅μ‹œν‚€κΈ° μœ„ν•΄ train_model ν•¨μˆ˜λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€. ν•™μŠ΅μš© 데이터와 κ²€μ¦μš© 데이터λ₯Ό μž…λ ₯으둜 λ°›μœΌλ©°, num_epochsλŠ” ν•™μŠ΅μ‹œν‚¬ epoch 횟수λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€. verboseλŠ” epoch을 verbose번째 λ§ˆλ‹€ 좜λ ₯ν•œλ‹€λŠ” λœ»μž…λ‹ˆλ‹€. patienceλŠ” κ²€μ¦μš© 손싀값(validation loss)을 patience번째 epochλ§ˆλ‹€ patience만큼의 이전 손싀값과 비ꡐ해 쀄어듀지 μ•ŠμœΌλ©΄ ν•™μŠ΅μ„ μ’…λ£Œ μ‹œν‚¬ λ•Œ μ‚¬μš©ν•˜λŠ” μΈμžμž…λ‹ˆλ‹€. PyTorchμ—μ„œλŠ” hidden_stateλ₯Ό λ³΄μ‘΄ν•˜κΈ° λ•Œλ¬Έμ— μƒˆλ‘œμš΄ μ‹œν€€μŠ€κ°€ μž…λ ₯될 λ•Œλ§ˆλ‹€ hidden_stateλ₯Ό μ΄ˆκΈ°ν™” μ‹œμΌœμ•Ό 이전 μ‹œν€€μŠ€μ˜ hidden_state둜 λΆ€ν„° 영ν–₯을 받지 μ•ŠμŠ΅λ‹ˆλ‹€.

def train_model(model, train_data, train_labels, val_data=None, val_labels=None, num_epochs=100, verbose = 10, patience = 10):
    loss_fn = torch.nn.L1Loss() #
    optimiser = torch.optim.Adam(model.parameters(), lr=0.001)
    train_hist = []
    val_hist = []
    for t in range(num_epochs):

        epoch_loss = 0

        for idx, seq in enumerate(train_data): 

            model.reset_hidden_state() # seq 별 hidden state reset

            # train loss
            seq = torch.unsqueeze(seq, 0)
            y_pred = model(seq)
            loss = loss_fn(y_pred[0].float(), train_labels[idx]) # 1개의 step에 λŒ€ν•œ loss

            # update weights
            optimiser.zero_grad()
            loss.backward()
            optimiser.step()

            epoch_loss += loss.item()

        train_hist.append(epoch_loss / len(train_data))

        if val_data is not None:

            with torch.no_grad():

                val_loss = 0

                for val_idx, val_seq in enumerate(val_data):

                    model.reset_hidden_state() # seq λ³„λ‘œ hidden state μ΄ˆκΈ°ν™” 

                    val_seq = torch.unsqueeze(val_seq, 0)
                    y_val_pred = model(val_seq)
                    val_step_loss = loss_fn(y_val_pred[0].float(), val_labels[val_idx])

                    val_loss += val_step_loss
                
            val_hist.append(val_loss / len(val_data)) # val hist에 μΆ”κ°€

            ## verbose 번째 λ§ˆλ‹€ loss 좜λ ₯ 
            if t % verbose == 0:
                print(f'Epoch {t} train loss: {epoch_loss / len(train_data)} val loss: {val_loss / len(val_data)}')

            ## patience 번째 λ§ˆλ‹€ early stopping μ—¬λΆ€ 확인
            if (t % patience == 0) & (t != 0):
                
                ## lossκ°€ μ»€μ‘Œλ‹€λ©΄ early stop
                if val_hist[t - patience] < val_hist[t] :

                    print('\n Early Stopping')

                    break

        elif t % verbose == 0:
            print(f'Epoch {t} train loss: {epoch_loss / len(train_data)}')

            
    return model, train_hist, val_hist
model = CovidPredictor(
    n_features=1,
    n_hidden=4,
    seq_len=seq_length,
    n_layers=1
)
model, train_hist, val_hist = train_model(
    model,
    X_train,
    y_train,
    X_val,
    y_val,
    num_epochs=100,
    verbose=10,
    patience=50
)
Epoch 0 train loss: 0.0846735675929835 val loss: 0.047220394015312195
Epoch 10 train loss: 0.03268902644807637 val loss: 0.03414301574230194
Epoch 20 train loss: 0.03255926527910762 val loss: 0.03243739902973175
Epoch 30 train loss: 0.032682761279652 val loss: 0.033064160495996475
Epoch 40 train loss: 0.0325928641549201 val loss: 0.032514143735170364
Epoch 50 train loss: 0.032316437919741904 val loss: 0.033000096678733826
Epoch 60 train loss: 0.03259847856704788 val loss: 0.03266565129160881
Epoch 70 train loss: 0.03220883647418827 val loss: 0.032897673547267914
Epoch 80 train loss: 0.03264666339685834 val loss: 0.032588861882686615
Epoch 90 train loss: 0.032349443449406844 val loss: 0.03221791982650757

train_hist와 val_hist에 μ €μž₯된 손싀값듀을 μ‹œκ°ν™” ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

plt.plot(train_hist, label="Training loss")
plt.plot(val_hist, label="Val loss")
plt.legend()
<matplotlib.legend.Legend at 0x7f76de333fd0>
../../_images/Ch4-LSTM_20_1.png

4.5 예츑¢

이번 μ ˆμ—μ„œλŠ” κ΅¬μΆ•ν•œ λͺ¨λΈμ„ ν™œμš©ν•΄ μƒˆλ‘œ λ“€μ–΄μ˜€λŠ” 데이터에 λŒ€ν•œ μ˜ˆμΈ‘μ„ μ§„ν–‰ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. ν˜„μž¬ κ΅¬μΆ•λœ λͺ¨λΈμ€ \(t-5\)μ‹œμ  λΆ€ν„° \(t-1\)μ‹œμ κΉŒμ§€μ˜ 데이터λ₯Ό ν™œμš©ν•΄ \(t\)μ‹œμ μ˜ ν™•μ§„μžλ₯Ό μ˜ˆμΈ‘ν•©λ‹ˆλ‹€. λ§ˆμ°¬κ°€μ§€λ‘œ μ‹€μ œ κ΄€μΈ‘λœ \(t-5\)λΆ€ν„° \(t-1\)κΉŒμ§€μ˜ μƒˆλ‘œμš΄ 데이터λ₯Ό μž…λ ₯ν•œλ‹€λ©΄ \(t\)μ‹œμ μ˜ ν™•μ§„μžμ— λŒ€ν•œ 예츑이 κ°€λŠ₯ν•  것이며, 이λ₯Ό One-Step예츑이라고 ν•©λ‹ˆλ‹€. κ³Όκ±° 데이터λ₯Ό ν™œμš©ν•΄ ν•œ λ‹¨μœ„ μ•žλ§Œ μ˜ˆμΈ‘ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€.

반면 κ³Όκ±° 데이터λ₯Ό ν™œμš©ν•΄ 두 λ‹¨μœ„, μ„Έ λ‹¨μœ„ λ“±μ˜ μ—¬λŸ¬ λ‹¨μœ„ μ•žμ„ μ˜ˆμΈ‘ν•˜λŠ” 것을 Multi-Step예츑이라고 ν•©λ‹ˆλ‹€. Multi-Stepμ˜ˆμΈ‘μ—λŠ” 크게 2가지 λ°©λ²•μœΌλ‘œ λ‚˜λ‰˜λŠ”λ°, μ•žμ„œ κ΅¬μΆ•ν•œ One-Stepμ˜ˆμΈ‘ν•˜λŠ” λͺ¨λΈμ„ ν™œμš©ν•˜λŠ” 방법과 seq2seqꡬ쑰의 λͺ¨λΈμ„ ν™œμš©ν•˜λŠ” 방법이 μžˆμŠ΅λ‹ˆλ‹€.

첫번째 방법은 One-Stepμ˜ˆμΈ‘ν•˜λŠ” λͺ¨λΈμ—μ„œ λ°˜ν™˜ν•œ \(t\)μ‹œμ μ˜ μ˜ˆμΈ‘κ°’ \(\hat{t}\)을 ν™œμš©ν•΄ \(t-4\), \(t-3\), \(t-2\), \(t-1\), \(\hat{t}\) κ°’μœΌλ‘œ \(t+1\)μ‹œμ μ˜ 값을 μ˜ˆμΈ‘ν•©λ‹ˆλ‹€. 이처럼 λͺ¨λΈμ—μ„œ λ‚˜μ˜¨ μ˜ˆμΈ‘κ°’μ„ λ‹€μ‹œ λͺ¨λΈ μž…λ ₯κ°’μœΌλ‘œ λ„£μ–΄ 반볡적으둜 μ˜ˆμΈ‘ν•  수 μžˆμ§€λ§Œ, μ˜ˆμΈ‘κ°’μ˜ μ˜€μ°¨κ°€ λˆ„μ λ˜μ–΄ 점차 예츑 μ„±λŠ₯이 λ–¨μ–΄μ§€λŠ” ν˜„μƒμ΄ λ°œμƒν•©λ‹ˆλ‹€.

λ‘λ²ˆμ§Έ 방법은 seq2seqꡬ쑰λ₯Ό ν™œμš©ν•΄ μ˜ˆμΈ‘ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. μ˜ˆμΈ‘ν•˜κ³ μž ν•˜λŠ” 미래 κΈ°κ°„ 만큼 decoder 길이λ₯Ό μ„€μ •ν•΄μ„œ μ˜ˆμΈ‘ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€. decoderλ„€νŠΈμ›Œν¬λ₯Ό 톡해 μΆ”κ°€ 정보λ₯Ό μ˜ˆμΈ‘κ°’ μ‚°μΆœ μ‹œ ν™œμš©ν•  수 μžˆλ‹€λŠ” μž₯점이 μžˆμ§€λ§Œ 예츑 μ‹œκ°„ λ‹¨μœ„κ°€ 고정돼야 ν•©λ‹ˆλ‹€.

이번 μ ˆμ—μ„œλŠ” One-Step예츑 λͺ¨λΈμ„ 반볡적으둜 ν™œμš©ν•΄ Multi-Stepμ˜ˆμΈ‘ν•˜λŠ” 것을 μ½”λ“œλ‘œ ν™•μΈν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

4.5.1 One-Step 예츑¢

μš°μ„  μ•žμ„œ λ§Œλ“  λͺ¨λΈμ— λŒ€ν•œ One-Stepμ˜ˆμΈ‘μ„ 진행해 λͺ¨λΈ μ„±λŠ₯을 ν™•μΈν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. κ΅¬μΆ•λœ μ‹œν—˜ 데이터에 λŒ€ν•œ μ˜ˆμΈ‘μ„ μ§„ν–‰ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. μ˜ˆμΈ‘ν•  λ•Œλ„ μƒˆλ‘œμš΄ μ‹œν€€μŠ€κ°€ μž…λ ₯될 λ•Œ λ§ˆλ‹€ hidden_stateλŠ” μ΄ˆκΈ°ν™”λ₯Ό ν•΄μ€˜μ•Ό 이전 μ‹œν€€μŠ€μ˜ hidden_stateκ°€ λ°˜μ˜λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. torch.unsqueeze ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ μž…λ ₯ λ°μ΄ν„°μ˜ 차원을 늘렀 λͺ¨λΈμ΄ μ˜ˆμƒν•˜λŠ” 3차원 ν˜•νƒœλ‘œ λ§Œλ“€μ–΄μ€λ‹ˆλ‹€. 그리고 예츑된 데이터 내에 μ‘΄μž¬ν•˜λŠ” μŠ€μΉΌλΌκ°’λ§Œ μΆ”μΆœν•˜μ—¬ preds λ¦¬μŠ€νŠΈμ— μΆ”κ°€ν•©λ‹ˆλ‹€.

pred_dataset = X_test

with torch.no_grad():
    preds = []
    for _ in range(len(pred_dataset)):
        model.reset_hidden_state()
        y_test_pred = model(torch.unsqueeze(pred_dataset[_], 0))
        pred = torch.flatten(y_test_pred).item()
        preds.append(pred)

λͺ¨λΈμ΄ μ˜ˆμΈ‘ν•œ κ°’κ³Ό μ‹€μ œκ°’κ³Ό 비ꡐλ₯Ό μ§„ν–‰ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. y_test에 μ‹€μ œκ°’μ΄ μ €μž₯돼있으며 ν˜„μž¬ μŠ€μΌ€μΌλ§λœ μƒνƒœμž…λ‹ˆλ‹€. μ›λž˜ μŠ€μΌ€μΌλ‘œ λ³€ν™˜μ‹œμΌœμ£ΌκΈ° μœ„ν•΄μ„œ μ•„λž˜ 산식을 ν™œμš©ν•˜κ² μŠ΅λ‹ˆλ‹€. MinMax μŠ€μΌ€μΌλ§μ„ μ μš©ν•  λ•Œ μ‚¬μš©ν•œ 산식을 μ‘μš©ν•˜μ—¬ μ›λž˜ κ°’μœΌλ‘œ λ³€ν™˜ν•˜λŠ” μ‚°μ‹μž…λ‹ˆλ‹€.

\(x = x_{scaled} * (x_{max} - x_{min}) + x_{min}\)

이번 λ°μ΄ν„°μ—μ„œ \(x_{min}\)은 0μ΄μ˜€μŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ―€λ‘œ μ›λž˜ μŠ€μΌ€μΌλ‘œ λ³΅κ΅¬ν•˜κΈ° μœ„ν•΄μ„  \(x_{max}\)만 κ³±ν•΄μ£Όλ©΄ λ©λ‹ˆλ‹€.

plt.plot(daily_cases.index[-len(y_test):], np.array(y_test) * MAX, label='True')
plt.plot(daily_cases.index[-len(preds):], np.array(preds) * MAX, label='Pred')
plt.xticks(rotation=45)
plt.legend()
<matplotlib.legend.Legend at 0x7f76dc9ac748>
../../_images/Ch4-LSTM_29_1.png

νŒŒλž€μƒ‰ κ·Έλž˜ν”„λŠ” μ‹œν—˜ λ°μ΄ν„°μ˜ μ‹€μ œκ°’μ„ λ‚˜νƒ€λ‚΄λ©° 주황색 κ·Έλž˜ν”„λŠ” μ˜ˆμΈ‘κ°’μ„ λ‚˜νƒ€λƒ…λ‹ˆλ‹€. ν™•μ§„μžκ°€ μƒμŠΉν•˜λŠ” μΆ”μ„ΈλŠ” λͺ¨λΈμ΄ μ˜ˆμΈ‘ν•˜κ³  μžˆμ§€λ§Œ ν™•μ§„μžκ°€ κΈ‰κ²©νžˆ μ¦κ°€ν•˜λŠ” ꡬ간에 λŒ€ν•΄μ„œλŠ” 예츑이 잘 λ˜μ§€ μ•ŠμŒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜ˆμΈ‘κ°’μ˜ 평균 였차λ₯Ό κ΅¬ν•˜κΈ° μœ„ν•΄ MAE 값을 μ‚°μΆœν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

def MAE(true, pred):
    return np.mean(np.abs(true-pred))
MAE(np.array(y_test)*MAX, np.array(preds)*MAX)
247.3132225984521

μ‹œν—˜ 데이터에 λŒ€ν•œ μ˜ˆμΈ‘κ°’μ΄ ν‰κ· μ μœΌλ‘œ μ‹€μ œκ°’κ³Ό μ•½ 250λͺ…μ˜ 차이λ₯Ό μ§€λ‹ˆκ³  μžˆλ‹€λŠ” 것을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. κ³Όκ±° ν™•μ§„μž 수 뿐만 μ•„λ‹ˆλΌ 인ꡬ 이동 데이터, 인ꡬ 톡계 데이터 등을 ν™œμš©ν•œλ‹€λ©΄ 보닀 μ •κ΅ν•œ 예츑이 κ°€λŠ₯ν•  κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€.

4.5.2 Multi-Step 예츑¢

One-Step예츑 λͺ¨λΈμ„ 반볡적으둜 ν™œμš©ν•΄ Multi-Stepμ˜ˆμΈ‘μ„ μ§„ν–‰ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. μ‹œν—˜ λ°μ΄ν„°μ˜ 첫번째 μƒ˜ν”Œμ„ ν™œμš©ν•΄ λ‚˜μ˜¨ μ˜ˆμΈ‘κ°’μ„ μž…λ ₯ μ‹œν€€μŠ€μ— ν¬ν•¨μ‹œμΌœ λ‹€μŒ 값을 μ˜ˆμΈ‘ν•˜κ³ , 또 ν•΄λ‹Ή 값을 μž…λ ₯ μ‹œν€ΈμŠ€μ— ν¬ν•¨μ‹œμΌœ λ‹€μŒ 값을 μ˜ˆμΈ‘ν•˜λŠ” 과정을 λ°˜λ³΅ν•˜κ² μŠ΅λ‹ˆλ‹€.

with torch.no_grad():
    test_seq = X_test[:1] # 첫번째 ν…ŒμŠ€νŠΈ μ…‹, 3차원
    preds = []
    for _ in range(len(X_test)):
        model.reset_hidden_state()
        y_test_pred = model(test_seq)
        pred = torch.flatten(y_test_pred).item()
        preds.append(pred)
        new_seq = test_seq.numpy().flatten()
        new_seq = np.append(new_seq, [pred]) # μ‹œν€€μŠ€μ— μΆ”κ°€ 
        new_seq = new_seq[1:] # μΆ”κ°€λœ 값을 ν¬ν•¨ν•˜μ—¬ seq_lengthκ°€ 5κ°œκ°€ λ˜λ„λ‘ λ§žμΆ”κΈ° 
        test_seq = torch.as_tensor(new_seq).view(1, seq_length, 1).float()

μ•žμ„œ μ–ΈκΈ‰ν•œ κ²ƒμ²˜λŸΌ ν•΄λ‹Ή 방법은 예츑 기간이 κΈΈμ–΄μ§ˆμˆ˜λ‘ μ˜€μ°¨κ°€ λˆ„μ λ˜μ–΄ λͺ¨λΈμ˜ μ„±λŠ₯을 보μž₯ν•˜κΈ° μ–΄λ ΅μŠ΅λ‹ˆλ‹€. μ•„λž˜ κ·Έλž˜ν”„λ‘œ μ˜ˆμΈ‘κ°’μ„ μ‹€μ œκ°’κ³Ό 비ꡐ해 μ‹œκ°ν™” ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

plt.plot(daily_cases.index[-len(y_test):], np.array(y_test) * MAX, label='True')
plt.plot(daily_cases.index[-len(preds):], np.array(preds) * MAX, label='Pred')
plt.xticks(rotation=45)
plt.legend()
<matplotlib.legend.Legend at 0x7f76dc271278>
../../_images/Ch4-LSTM_38_1.png

μ§€κΈˆκΉŒμ§€ μ½”λ‘œλ‚˜ ν™•μ§„μž 데이터λ₯Ό ν™œμš©ν•΄ LSTM λͺ¨λΈ ꡬ좕 μ‹€μŠ΅μ„ μ§„ν–‰ν–ˆμŠ΅λ‹ˆλ‹€. λ‹€μŒ μž₯μ—μ„œλŠ” CNN-LSTM을 μ‹œκ³„μ—΄ 데이터에 μ μš©ν•΄λ³΄λŠ” μ‹€μŠ΅μ„ μ§„ν–‰ν•˜κ² μŠ΅λ‹ˆλ‹€.