본 예제는 모두를 위한 딥러닝 시즌2의 데이터(data-02-stock_daily.csv)와 모델을 제외한 소스 코드를 참고했습니다.
# Reference # 모두를 위한 딥러닝 시즌 2 - PyTorch # Lab-11-4 RNN timeseries
필요한 라이브러리를 임포트 합니다.
import torch import torch.optim as optim import numpy as np import matplotlib.pyplot as plt from sklearn.preprocessing import MinMaxScaler
모델에 사용할 파라메터를 셋팅해줍니다. seq_length는 입력 시퀀스 정보, data_dim은 입력 데이터의 차원, hidden_dim은 출력 데이터의 차원, output_dim은 최종 예측 데이터의 차원 입니다.
# hyper parameters seq_length = 7 data_dim = 5 hidden_dim = 30 output_dim = 1 learning_rate = 0.01 iterations = 501
해당 데이터는 주가 데이터로 개장 포인트, 가장 높은 포인트, 가장 낮은 포인트, 폐장시 포인트와 거래량으로 5개 변수 데이터를 가지고 있습니다. 본 예측은 학습 데이터를 7일로 분리하여 다음 폐장 포인트를 예측하는 모델입니다.
# load data xy = np.loadtxt("data-02-stock_daily.csv", delimiter=",") xy = xy[::-1] # reverse order
학습용 데이터와 테스트용 데이터를 분리하는 내용입니다. 학습 데이터와 검증 데이터는 7:3 비율로 분리합니다.
# split train-test set train_size = int(len(xy) * 0.7) train_set = xy[0:train_size] test_set = xy[train_size - seq_length:] train_set.shape, test_set.shape
입력한 데이터를 학습에 사용하기 위해서는 정규화 과정이 필요합니다.
정규화를 왜 해야 하는지에 대해서는 아래의 그래프를 참고하시기 바랍니다.
해당 데이터셋은 총 5개로 구성되어 있습니다. 그중 4개의 데이터는 단위가 비슷하기 때문에 그래프를 통해 보면 유사한 형태를 보이고 있습니다. 그러나 Volume 이라는 컬럼을 같이 표현하고자 한다면 입력 단위의 차이가 매우 크기 때문에 아래의 그림과 같이 나머지 데이터는 식별이 불가능하게 됩니다.

- 단위의 차이로 인해서 Volume 데이터를 시각화 하는데 한계가 있음

그러나 MinMaxScaler를 활용하여 정규화 하게 되면 모든 데이터를 0,1의 범위 안에 표현할 수 있기 때문에 모든 그래프를 한번에 그릴 수 있습니다. 그리고 이렇게 표현한 데이터는 다시 원래 단위의 형태로 복원 할 수 있습니다.
학습에서 MinMaxScaler를 사용하는 이유는 다차원 데이터값을 비교 분석하기 쉽게 만들어주고 자료의 오버플로우나 언더플로우를 방지해주고 최적과 과정에서 안정성 및 수렴 속도를 향상 시키기 위함입니다.

scaler = MinMaxScaler() scaler.fit(train_set) print(scaler.n_samples_seen_, scaler.data_min_, scaler.data_max_, scaler.feature_range) train_set = scaler.transform(train_set) scaler.fit(test_set) print(scaler.n_samples_seen_, scaler.data_min_, scaler.data_max_, scaler.feature_range) test_set = scaler.transform(test_set)
build_dataset 함수는 RNN 학습을 위해서 입력 텐서를 만들어 주는 부분입니다.
time_series[0:7,], time_series[7,[-1]] 형식으로 되어 있습니다.
# make dataset to input def build_dataset(time_series, seq_length): dataX = [] dataY = [] for i in range(0, len(time_series) - seq_length): _x = time_series[i:i + seq_length, :] _y = time_series[i + seq_length, [-1]] # Next close price #print(_x, "->", _y) dataX.append(_x) dataY.append(_y) return np.array(dataX), np.array(dataY)
# make train-test dataset to input trainX, trainY = build_dataset(train_set, seq_length) testX, testY = build_dataset(test_set, seq_length) print(trainX.shape, trainY.shape) # convert to tensor trainX_tensor = torch.FloatTensor(trainX) trainY_tensor = torch.FloatTensor(trainY) testX_tensor = torch.FloatTensor(testX) testY_tensor = torch.FloatTensor(testY)
이제 학습 데이터를 통해서 데이터를 학습하는 모델을 만듭니다. 본 예제에서는 BiLSTM 방식으로 4개의 층을 쌓아 올린 형태입니다.
위의 그림은 해당 데이터에 대한 입력 데이터 형태와 입력와 출력의 형태는 아래 그림과 같습니다. 일단 벡터는 (n,7,5) -> (n,7,30) 형태로 나옵니다. 그러나 BiLSTM 모델을 사용했기 때문에 마지막 output은 30*2의 형태가 됩니다.

class Net(torch.nn.Module): def __init__(self, input_dim, hidden_dim, output_dim, layers): super(Net, self).__init__() self.rnn = torch.nn.LSTM(input_dim, hidden_dim, num_layers=layers, batch_first=True, bidirectional=True) self.layers = torch.nn.Sequential( torch.nn.Linear(hidden_dim*2, 20), torch.nn.Linear(20, 10), torch.nn.Linear(10, output_dim) ) def forward(self, x): x, (hidden, cell) = self.rnn(x) x = self.layers(x[:, -1, ]) return x net = Net(data_dim, hidden_dim, output_dim, 4)
이제 학습을 수행합니다.
# loss & optimizer setting criterion = torch.nn.MSELoss() optimizer = optim.Adam(net.parameters(), lr=learning_rate) # start training for i in range(iterations): outputs = net(trainX_tensor) loss = criterion(outputs, trainY_tensor) optimizer.zero_grad() loss.backward() optimizer.step() if i%50 == 0: print(i, loss.item())
학습이 완료된 후 테스트를 수행합니다.
net.eval() predict_data = net(testX_tensor).data.numpy() plt.grid(True) plt.autoscale(axis='x', tight=True) plt.plot(testY) plt.plot(predict_data, color='red') plt.legend(['original', 'prediction']) plt.show()
학습된 결과와 원본 데이터를 통해 비교해보면 예측이 비교적 잘됐음을 알 수 있습니다.
