【深層学習】pytorchでRNNを実装して時系列予測をする

Posted: , Category: pytorch , 時系列分析 , 深層学習

RNN(Recurrent Neural Network)は、深層学習のアルゴリズムの中でも、CNNと並ぶ代表的なアルゴリズムです。基本的に深層学習を学び始めると、CNNに続いてRNNを学ぶことになると思います。

RNN自体はかなり古いアルゴリズムで、勾配爆発など学習がうまく進まない問題などもあり、RNNを発展させたGRUやLSTMなどが2023年現在広く活用されていますが、RNNは再帰的な構造をもった、ニューラルネットワークであり、学習を進めることで、さらなる発展形のモデルを理解するのに役立ちます。

今回は、pytorchを用いてRNNを実装していきます

RNNの理論的な解説は別の記事で行いたいと思うので、今回は実装面に特化した解説を行っていきます。

pytorchでRNNを実装する

今回データセットは、Airpassengerの単変量データセットを利用します。AirPassengerの解説は別記事でしているので、省略します。

まず、今回のモデルで用いるライブラリ群とデータセットを用意するところまでやってみましょう。

ライブラリのimportとデータセットの準備

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

from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split

# pytorch library

import torch
from torch import nn,optim
from torch.utils.data import DataLoader, TensorDataset, Dataset
from torchvision import transforms
from torchinfo import summary

ライブラリをimportできました。実行している環境に不足しているライブラリ等があれば、まずimportしておきましょう。

続いてデータセットを準備します。

df = pd.read_csv("https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv", index_col='Month', parse_dates=True)
plt.figure(figsize=(10, 3), dpi=100)
plt.plot(df)

今回はこの時系列データの予測をRNNを用いて実行していきます。

まずデータを正規化(0~1の範囲にデータが収まるような加工)をしていきます。正規化には、sikit-learnに入っているMinMaxScaler関数を利用すると便利です。

df = df.values.reshape(-1, 1).astype("float32")
trans = MinMaxScaler(feature_range=(0, 1))
df_trans = trans.fit_transform(df)

plt.plot(df_trans) # 正規化できたかどうかを確認

うまく正規化することができていますね。それでは続いて訓練データと検証データを準備します。

# テスト用と訓練用で分割
df_train, df_test = train_test_split(df_trans, test_size=0.3, shuffle=False)

window_size = 10
# 入力データと正解ラベルを準備

n_train = len(df_train) - window_size - 1
train = np.zeros((n_train, window_size, 1))
train_labels = np.zeros((n_train, 1))

for i in range(n_train):
    train[i] = df_train[i:i+window_size].reshape(-1, 1)
    train_labels[i] = df_train[i+window_size]
    
train = torch.tensor(train, dtype=torch.float)
labels = torch.tensor(train_labels, dtype=torch.float)
dataset = torch.utils.data.TensorDataset(train, labels)
train_loader = DataLoader(dataset, batch_size=4, shuffle=True)

torchのDataLoaderの形式でデータセットの準備をすることができました。

RNNモデルの実装

続いて、RNNモデルの実装を行っていきます。

pytorchには、すでにRNNレイヤーが実装されているので、今回はそのAPIを利用する形式で、RNNモデルの実装を進めていきます。

class MyRNN(nn.Module):
    def __init__(self, input_size, output_size, hidden_dim, n_layers):
        super(MyRNN, self).__init__()

        self.input_size = input_size
        self.hidden_dim = hidden_dim
        self.n_layers = n_layers

        self.rnn = nn.RNN(input_size, hidden_dim, n_layers, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_size)

    def forward(self, x):
        y_rnn, h = self.rnn(x, None)
        y = self.fc(y_rnn[:, -1, :])

        return y

#RNNの設定
n_inputs  = 1
n_outputs = 1
n_hidden  = 64
n_layers  = 1

net = MyRNN(n_inputs, n_outputs, n_hidden, n_layers)

MyRNNクラスでRNNを実装しました。

ここで、ネットワーク構成を見ておきましょう。

# モデルの概要を確認。パラメータ数などがわかる。
summary(net)

RNNによる学習と結果の確認

それでは、RNNによる学習と結果の確認をしてみましょう。

まずは学習において必要なパラメータを設定します。

func_loss = nn.MSELoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)
loss_history = []
device = torch.device("cuda:0" if torch.cuda. is_available() else "cpu")
epochs = 200 

net.to(device)

続いて、学習と結果の確認をしてみます。

for i in range(epochs+1):
    net.train()
    tmp_loss = 0.0
    for j, (x, t) in enumerate(train_loader):
        x = x.to(device)
        optimizer.zero_grad()
        y = net(x) 
        y = y.to('cpu')
        loss = func_loss(y, t)
        loss.backward()
        optimizer.step() 
        tmp_loss += loss.item()
    tmp_loss /= j+1
    loss_history.append(tmp_loss)
    
    if i%100 == 0:
        print('Epoch:', i, 'Loss_Train:', tmp_loss)
        input_train = list(train[0].reshape(-1))
        predicted_train_plot = []
        net.eval()
        for k in range(n_train):
            x = torch.tensor(input_train[-window_size:])
            x = x.reshape(1, window_size, 1)
            x = x.to(device).float()
            y = net(x)
            y = y.to('cpu')

            if k <= n_train-2: 
                input_train.append(train[k+1][9].item())
            predicted_train_plot.append(y[0].item())

        plt.plot(range(len(df_train)), df_train, label='Correct')
        plt.plot(range(window_size, window_size+len(predicted_train_plot)), predicted_train_plot, label='Predicted')
        plt.legend()
        plt.show()

損失関数の収束具合はこのようになっています。

plt.plot(range(len(loss_history)), loss_history, label='train')
plt.legend()

plt.xlabel("epochs")
plt.ylabel("loss")
plt.show()

【広告】
統計学的にあなたの悩みを解決します。
仕事やプライベートでお悩みの方は、ベテラン占い師 蓮若菜にご相談ください。

機械学習と情報技術