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()