LoginSignup
6
8

More than 3 years have passed since last update.

再帰型ニューラルネットワークの勉強:周期関数を再現できるか

Last updated at Posted at 2020-01-30

深層学習(ディープラーニング)の中で、時系列データなどの解析に使われる再帰型ニューラルネットワーク(Recurrent Neural Network)の勉強のため、周期関数をどの程度再現できるか試してみました。

3種類の周期関数の定義

ここでは、以下の f g h 3種類の周期関数を定義しました。

import numpy as np

def f(t, freq=25):
    return np.sin(2. * np.pi * t / freq)

def g(t, freq=25, amp=10, threshold = 10):
    return 1/(1 + np.exp(10 * np.sin(2 * np.pi * t / freq) + 10))

def h(t, freqs=[11, 23, 31, 41, 53, 61, 71, 83, 97]):
    value = np.zeros_like(t)
    for freq in freqs:
        value += f(t, freq)
    return value

関数f

関数fは、三角関数 $sin$ です。ここでは、周期を $25$ にしました。

%matplotlib inline
import matplotlib.pyplot as plt

total_time_length = 1000
times = np.linspace(0, total_time_length, total_time_length + 1)

plt.figure(figsize=(15, 6))
plt.plot(f(times))
plt.xticks(np.linspace(0, 1000, 11))
plt.grid()

output_2_0.png

関数g

関数gは、三角関数 $sin$ をシグモイド関数で変換して、変な形にしました。ここでも、周期を $25$ にしました。

%matplotlib inline
import matplotlib.pyplot as plt

total_time_length = 1000
times = np.linspace(0, total_time_length, total_time_length + 1)

plt.figure(figsize=(15, 6))
plt.plot(g(times))
plt.xticks(np.linspace(0, 1000, 11))
plt.grid()

output_3_0.png

関数h

関数hは、いくつかの素数を周期とする三角関数 $sin$ の和にしました。

%matplotlib inline
import matplotlib.pyplot as plt

total_time_length = 1000
times = np.linspace(0, total_time_length, total_time_length + 1)

plt.figure(figsize=(15, 6))
plt.plot(h(times))
plt.xticks(np.linspace(0, 1000, 11))
plt.grid()

output_4_0.png

これらの関数を、再帰型ニューラルネットワークで再現できるでしょうか?

フーリエ変換

再帰型ニューラルネットワークを使う前に、上の3つの関数をフーリエ変換で周波数に変換し、その逆数をとって「周期」を確認してみましょう。

plt.figure(figsize=(6,6))

sp = np.fft.fft(f(times))
freq = np.fft.fftfreq(times.shape[-1])

plt.subplot(311)
plt.plot(1/freq, abs(sp.real) + abs(sp.imag), label="f")
plt.plot(1/freq, abs(sp.real))
plt.plot(1/freq, abs(sp.imag), alpha=0.5)
plt.legend()
plt.xlim([0, 150])
plt.xticks(np.linspace(0, 150, 16))
plt.grid()

sp = np.fft.fft(g(times))
freq = np.fft.fftfreq(times.shape[-1])

plt.subplot(312)
plt.plot(1/freq, abs(sp.real) + abs(sp.imag), label="g")
plt.plot(1/freq, abs(sp.real))
plt.plot(1/freq, abs(sp.imag), alpha=0.5)
plt.legend()
plt.xlim([0, 150])
plt.xticks(np.linspace(0, 150, 16))
plt.grid()

sp = np.fft.fft(h(times))
freq = np.fft.fftfreq(times.shape[-1])

plt.subplot(313)
plt.plot(1/freq, abs(sp.real) + abs(sp.imag), label="h")
plt.plot(1/freq, abs(sp.real))
plt.plot(1/freq, abs(sp.imag), alpha=0.5)
plt.legend()
plt.xlim([0, 150])
plt.xticks(np.linspace(0, 150, 16))
plt.grid()

output_6_1.png

関数fは、周期 $25$ の sin 波のみから成ります。関数hは、指定した素数の周期の組み合わせから成っていることが分かります。一方、関数gは、指定した周期 $25$ 以外にも様々な成分が含まれていることが分かります。

時系列データセットの作成

それでは、再帰型ニューラルネットワークに学習させるための時系列データセットを作成しましょう。再帰型ニューラルネットワークでは、時刻 $t$ 〜 $t+w$ までの出力 $X_{t, t+w}$ を入力すると、次の時刻の出力 $X_{t+w+1}$ を予測するモデルの作成を目指します。

import numpy as np
from sklearn.model_selection import train_test_split

func = f # ここを書き換えれば関数を変更できる
#func = g
#func = h

total_time_length = 10000 # 取り扱う全ての時間幅
pred_length = 1000 # 予測する時間幅
learning_time_length = 100 # 学習に用いる時間幅

time_series_T = np.linspace(0, total_time_length, total_time_length + 1) # 予測する時間Tを刻む(グラフの横軸にあたる)
time_series_X = func(time_series_T) # 関数の出力X(グラフの縦軸にあたる)

X_learn = [] # 時刻 t 〜 t+learning_time_length までの X を格納
Y_learn = [] # 時刻 t+learning_time_length+1 の X を格納
for i in range(total_time_length - learning_time_length):
    X_learn.append(time_series_X[i:i+learning_time_length].reshape(1, learning_time_length).T)
    Y_learn.append([time_series_X[i+learning_time_length]])

# トレーニングデータ・検証データに分割
# 時系列データのときは shuffle=False にしなければいけない
X_train, X_val, Y_train, Y_val = \
train_test_split(X_learn, Y_learn, test_size=0.2, shuffle=False)

# scikit-learn 用のデータ型に変換
X_train2sklearn = [list(x.reshape(1, len(x))[0]) for x in X_train]
Y_train2sklearn = [y[0] for y in Y_train]

深層学習手法

MLP (Multi-Layer Perceptron)

比較対象として、深層学習の最も単純なモデルである多層パーセプトロン(Multi-Layer Perceptron, MLP)を使います。多層パーセプトロンは scikit-learn が手軽かつ高速なので、それを使いました。

%%time
from sklearn.neural_network import MLPRegressor
regressor = MLPRegressor(hidden_layer_sizes=(100, 100, 100), 
                         early_stopping=True, max_iter=10000) 
regressor.fit(X_train2sklearn, Y_train2sklearn) 
CPU times: user 2.84 s, sys: 1.41 s, total: 4.25 s
Wall time: 2.22 s

学習曲線は次のようにして描きました。

plt.plot(regressor.loss_curve_)
%matplotlib inline
import matplotlib.pyplot as plt
plt.subplot(211)
plt.plot(regressor.loss_curve_, label='train_loss')
plt.legend()
plt.grid()
plt.subplot(212)
plt.plot(regressor.loss_curve_, label='train_loss')
plt.yscale('log')
plt.legend()
plt.grid()

output_11_0.png

学習済みのモデルに、最初の数時刻(pred_length の長さだけの時刻)を入力として与え、次の時刻の出力を予測させます。出力された予測値を入力に追加して、その次の時刻の出力を予測させます。それを延々と繰り返します。

%%time
pred_length = 1000
X_pred_length = np.linspace(0, pred_length , pred_length + 1)
Y_observed = func(X_pred_length)
Y_pred = Y_observed[:learning_time_length+1]

for i in range(pred_length):
    X_ = [Y_pred[i:i+learning_time_length]]
    Y_ = regressor.predict(X_)
    Y_pred = np.append(Y_pred, Y_)
CPU times: user 383 ms, sys: 279 ms, total: 662 ms
Wall time: 351 ms

そうして得られた予測値の曲線と、実際の曲線とを図示して比較します。

plt.figure(figsize=(36, 6))
times = np.linspace(0, Y_pred.shape[0] - 1, Y_pred.shape[0])
plt.plot(func(times), label="time series")
plt.plot(Y_pred, alpha=0.5, label="predicted")
plt.xticks(np.linspace(0, 1000, 11))
plt.xlim([0, 1000])
plt.grid()
plt.legend()

output_13_1.png

図が小さくて見にくい場合は、図をクリックすると拡大できると思います。時刻 $100$ までは、学習用データをそのまま使っていますので一致していますが、時刻 $100$ 以降、実際の値(関数fの値)と予測値がだんだんズレていってるのが分かります。

フーリエ変換した結果を比べてみましょう。

plt.figure(figsize=(6,4))

sp = np.fft.fft(func(times))
freq = np.fft.fftfreq(times.shape[-1])

plt.subplot(211)
plt.plot(1/freq, abs(sp.real) + abs(sp.imag), label="observed")
plt.plot(1/freq, abs(sp.real))
plt.plot(1/freq, abs(sp.imag), alpha=0.5)
plt.legend()
plt.xlim([0, 150])
plt.xticks(np.linspace(0, 150, 16))
plt.grid()

sp = np.fft.fft(Y_pred)
freq = np.fft.fftfreq(times.shape[-1])

plt.subplot(212)
plt.plot(1/freq, abs(sp.real) + abs(sp.imag), label="predicted")
plt.plot(1/freq, abs(sp.real))
plt.plot(1/freq, abs(sp.imag), alpha=0.5)
plt.legend()
plt.xlim([0, 150])
plt.xticks(np.linspace(0, 150, 16))
plt.grid()

output_14_1.png

予測された周期は、実際の周期と近いですが少しだけズレているように見えます。

再帰型ニューラルネットワーク

それでは、いよいよ再帰型ニューラルネットワークを構築しましょう。ここでは深層学習用ライブラリである PyTorch を用います。

まず、デバイスの設定を行います。次のようにして、GPUが使える環境ならGPUを、そうでないならCPUを使うようにします。

import torch
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

EarlyStopping(早期終了)のための関数を用意します。早期終了とは、学習がこれ以上進まないと判断した時に、学習を打ち切る処理です。

深層学習では予測誤差のことを損失(loss)と呼びます。過適合(overfitting, 過学習ともいう)を防ぐため、ここではトレーニングデータのlossではなく、トレーニングに用いなかった検証用データのlossがこれ以上下がらないと判断した時に学習を打ち切ります。その判断としてpatienceという概念を導入します。patience=20なら、最近 $20$ 回のlossの最小値と、それ以前のlossの最小値を比較し、前者の方が大きければ「これ以上良くならない」と判断し学習を打ち切ります。

def EarlyStopping(log, patience=20):
    if len(log) <= patience:
        return False
    min1 = log[:len(log)-patience].min()
    min2 = log[len(log)-patience:].min()
    if min1 <= min2:
        return True
    else:
        return False

RNN(Recurrent Neural Network)

ちょっと紛らわしいですが、RNN(Recurrent Neural Network, 再帰型ニューラルネットワーク)には「広義のRNN」と「狭義のRNN」があります。狭義のRNNは、PyTorchで次のように実装できます。

import torch
class RNN(torch.nn.Module):
    def __init__(self, hidden_dim):
        super().__init__()
        self.l1 = torch.nn.RNN(1, hidden_dim,
                         nonlinearity='tanh',
                         batch_first=True)
        self.l2 = torch.nn.Linear(hidden_dim, 1)
        torch.nn.init.xavier_normal_(self.l1.weight_ih_l0)
        torch.nn.init.orthogonal_(self.l1.weight_hh_l0)

    def forward(self, x):
        h, _ = self.l1(x)
        y = self.l2(h[:, -1])
        return y

LSTM (Long Short-Term Memory)

LSTM (Long Short-Term Memory, 長・短期記憶)は、長いのか短いのかどっちだヨ!!と突っ込みたくなるネーミングですが、「広義のRNN」の一種です。「狭義のRNN」よりも長期記憶が優れていると言われています。PyTorchで次のように実装できます。

import torch
class LSTM(torch.nn.Module):
    def __init__(self, hidden_dim):
        super().__init__()
        self.l1 = torch.nn.LSTM(1, hidden_dim, batch_first=True)
        self.l2 = torch.nn.Linear(hidden_dim, 1)
        torch.nn.init.xavier_normal_(self.l1.weight_ih_l0)
        torch.nn.init.orthogonal_(self.l1.weight_hh_l0)

    def forward(self, x):
        h, _ = self.l1(x)
        y = self.l2(h[:, -1])
        return y

GRU (Gated Recurrent Unit)

GRUは、ロシア連邦軍参謀本部情報総局(Glavnoye Razvedyvatelnoye Upravleniye)ではなくて、ゲート付き回帰型ユニット(Gated Recurrent Unit)です。LSTMと同等以上の性能を持ちながら計算時間が少ないと言われています。

import torch
class GRU(torch.nn.Module):
    def __init__(self, hidden_dim):
        super().__init__()
        self.l1 = torch.nn.GRU(1, hidden_dim, batch_first=True)
        self.l2 = torch.nn.Linear(hidden_dim, 1)
        torch.nn.init.xavier_normal_(self.l1.weight_ih_l0)
        torch.nn.init.orthogonal_(self.l1.weight_hh_l0)

    def forward(self, x):
        h, _ = self.l1(x)
        y = self.l2(h[:, -1])
        return y

RNNによる学習実行

次のようにして学習しました。以下のコードはRNN用のコードですが、1箇所書き換えるだけでLSTMやGRUに変更できます。

%%time
from sklearn.utils import shuffle
model = RNN(50).to(device) # ここを書き換えればネットワークを変更できる
#model = LSTM(50).to(device)
#model = GRU(50).to(device)
criterion = torch.nn.MSELoss(reduction='mean')
optimizer = torch.optim.Adam(model.parameters(), lr=0.001, 
                            betas=(0.9, 0.999), amsgrad=True)

epochs = 1000
batch_size = 100
n_batches_train = len(X_train) // batch_size - 1
n_batches_test = len(X_val) // batch_size - 1
hist = {'train_loss':[], 'val_loss':[]}

for epoch in range(epochs):
    train_loss = 0.
    val_loss = 0.
    X_, Y_ = shuffle(X_train, Y_train)

    for batch in range(n_batches_train):
        start = batch * batch_size
        end = start + batch_size
        X = torch.Tensor(X_[start:end])
        Y = torch.Tensor(Y_[start:end])
        model.train()
        Y_pred = model(X)
        loss = criterion(Y, Y_pred)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_loss += loss.item()

    for batch in range(n_batches_test):
        start = batch * batch_size
        end = start + batch_size
        X = torch.Tensor(X_val[start:end])
        Y = torch.Tensor(Y_val[start:end])
        model.eval()
        Y_pred = model(X)
        loss = criterion(Y, Y_pred)
        val_loss += loss.item()

    train_loss /= n_batches_train
    val_loss /= n_batches_test
    hist['train_loss'].append(train_loss)
    hist['val_loss'].append(val_loss)
    print("Epoch:", epoch + 1, "Train loss:", train_loss, "Val loss:", val_loss)

    if EarlyStopping(np.array(hist['val_loss'])):
        print("Early stopping at epoch", epoch + 1)
        break
Epoch: 1 Train loss: 0.024917872337913975 Val loss: 6.828008190495893e-05
Epoch: 2 Train loss: 3.570798083110742e-05 Val loss: 3.464157634880394e-05
Epoch: 3 Train loss: 2.720728588638639e-05 Val loss: 1.954806430148892e-05
...
Epoch: 580 Train loss: 5.4337909078255436e-08 Val loss: 4.0113718569045886e-08
Epoch: 581 Train loss: 6.47745306281422e-08 Val loss: 5.6099906942108646e-08
Epoch: 582 Train loss: 5.797503896896836e-08 Val loss: 1.620698952820021e-07
Early stopping at epoch 582
CPU times: user 26min 9s, sys: 21.6 s, total: 26min 31s
Wall time: 26min 39s

学習が終わりました。学習曲線を出力します。

%matplotlib inline
import matplotlib.pyplot as plt
plt.subplot(211)
plt.plot(hist['train_loss'], label='train_loss')
plt.plot(hist['val_loss'], label='val_loss')
plt.legend()
plt.grid()
plt.subplot(212)
plt.plot(hist['train_loss'], label='train_loss')
plt.plot(hist['val_loss'], label='val_loss')
plt.yscale('log')
plt.legend()
plt.grid()

output_27_0.png

学習済みのモデルに、最初の数時刻(pred_length の長さだけの時刻)を入力として与え、次の時刻の出力を予測させます。出力された予測値を入力に追加して、その次の時刻の出力を予測させます。それを延々と繰り返します。

%%time
total_time_length = 10000
pred_length = 1000
learning_time_length = 100

X_pred_length = np.linspace(0, pred_length , pred_length + 1)
Y_observed = func(X_pred_length)
Y_pred = Y_observed[:learning_time_length+1]

for i in range(pred_length):
    X_ = Y_pred[i:i+learning_time_length+1].reshape(1, learning_time_length + 1, 1)
    Y_ = model(torch.Tensor(X_)).detach().numpy()
    Y_pred = np.append(Y_pred, Y_)
CPU times: user 2.54 s, sys: 5.97 ms, total: 2.55 s
Wall time: 2.55 s

そうして得られた予測値の曲線と、実際の曲線とを図示して比較します。

plt.figure(figsize=(36, 6))
times = np.linspace(0, Y_pred.shape[0] - 1, Y_pred.shape[0])
plt.plot(func(times), label="time series")
plt.plot(Y_pred, alpha=0.5, label="predicted")
plt.xticks(np.linspace(0, 1000, 11))
plt.xlim([0, 1000])
plt.grid()
plt.legend()

output_29_1.png

図が小さくて見にくい場合は、図をクリックすると拡大できると思います。時刻 $100$ までは、学習用データをそのまま使っていますので一致して当然ですが、時刻 $100$ 以降も、完全に一致しているの分かります。

フーリエ変換した結果を比べてみましょう。

plt.figure(figsize=(6,4))

sp = np.fft.fft(func(times))
freq = np.fft.fftfreq(times.shape[-1])

plt.subplot(211)
plt.plot(1/freq, abs(sp.real) + abs(sp.imag), label="observed")
plt.plot(1/freq, abs(sp.real))
plt.plot(1/freq, abs(sp.imag), alpha=0.5)
plt.legend()
plt.xlim([0, 150])
plt.xticks(np.linspace(0, 150, 16))
plt.grid()

sp = np.fft.fft(Y_pred)
freq = np.fft.fftfreq(times.shape[-1])

plt.subplot(212)
plt.plot(1/freq, abs(sp.real) + abs(sp.imag), label="predicted")
plt.plot(1/freq, abs(sp.real))
plt.plot(1/freq, abs(sp.imag), alpha=0.5)
plt.legend()
plt.xlim([0, 150])
plt.xticks(np.linspace(0, 150, 16))
plt.grid()

output_30_1.png

周期が完全に一致しているようです。

LSTMによる学習実行

上に述べたように、LSTMの学習は、上のコードの model = RNN(50).to(device)model = LSTM(50).to(device) に書き換えるだけです。

Epoch: 1 Train loss: 0.24947839844315192 Val loss: 0.0037629783619195223
Epoch: 2 Train loss: 0.0010665786028720248 Val loss: 0.0004544752591755241
Epoch: 3 Train loss: 0.000281030429528656 Val loss: 0.00014765093510504812
...
Epoch: 397 Train loss: 1.9865108783006072e-08 Val loss: 1.99065262052045e-08
Epoch: 398 Train loss: 1.840841412067617e-08 Val loss: 1.814414751777349e-08
Epoch: 399 Train loss: 1.7767042196444784e-08 Val loss: 1.9604467382805524e-08
Early stopping at epoch 399
CPU times: user 48min 40s, sys: 51.2 s, total: 49min 31s
Wall time: 49min 41s

output_33_0.png

CPU times: user 7.67 s, sys: 14 ms, total: 7.68 s
Wall time: 7.69 s

output_35_1.png

output_36_1.png

こちらも完璧な予測ができたようです。

GRUによる学習実行

上に述べたように、GRUの学習は、上のコードの model = RNN(50).to(device)model = GRU(50).to(device) に書き換えるだけです。

Epoch: 1 Train loss: 0.2067998453276232 Val loss: 0.0007729934877716005
Epoch: 2 Train loss: 0.0005770771786979495 Val loss: 0.00023205751494970173
Epoch: 3 Train loss: 0.00018625847849015816 Val loss: 0.00014329736586660147
...
Epoch: 315 Train loss: 5.816128262764026e-09 Val loss: 5.750611098420677e-09
Epoch: 316 Train loss: 5.757192062114896e-09 Val loss: 5.7092033323158375e-09
Epoch: 317 Train loss: 5.780735246610847e-09 Val loss: 5.6715170337895415e-09
Early stopping at epoch 317
CPU times: user 34min 51s, sys: 42.1 s, total: 35min 33s
Wall time: 35min 40s

output_39_0.png

CPU times: user 8.81 s, sys: 7.04 ms, total: 8.81 s
Wall time: 8.82 s

output_41_1.png

output_42_1.png

こちらも完璧な予測ができたようです。

関数の形や周期を変える実験

さて、動かし方はだいぶ分かった気がするので、最後に、関数の形や周期を変える実験をしてみましょう。結果は以下の通り。

関数 f の周期を長くする

MLP, 関数f

MLP, 関数f, 周期 = 25

学習曲線

output_11_0.png

予測曲線

output_13_1.png

フーリエ変換

output_14_1.png

MLP, 関数f, 周期 = 50

学習曲線

output_11_0.png

予測曲線

output_13_1.png

フーリエ変換

output_14_1.png

MLP, 関数f, 周期 = 100

学習曲線

output_11_0.png

予測曲線

output_13_1.png

フーリエ変換

output_14_1.png

MLP, 関数f まとめ

周期が大きくなっても、早期終了までに費やしたエポック数に大きな変化なし。カーブの形は大きく崩れないが、周期にズレが生じた。出力の高さ(振幅)は、周期が短い場合は保存されていたが、周期が長くなると短くなっていく傾向が見出された。

RNN, 関数f

RNN, 関数f, 周期 = 25

学習曲線

output_27_0.png

予測曲線

output_29_1.png

フーリエ変換

output_30_1.png

RNN, 関数f, 周期 = 50

学習曲線

output_27_0.png

予測曲線

output_29_1.png

フーリエ変換

output_30_1.png

RNN, 関数f, 周期 = 100

学習曲線

output_27_0.png

予測曲線

output_29_1.png

フーリエ変換

output_30_1.png

RNN, 関数f まとめ

早期終了までに費やしたエポック数は、周期が大きくなると少なくなる傾向があった。予測曲線は、短周期(25)または中周期(50)のときは良い予測ができたが、長周期(100)になると大きく形が崩れた。長周期の予測では、変なところに鋭いピークを観測した。

LSTM, 関数f

LSTM, 関数f, 周期 = 25

学習曲線

output_33_0.png

予測曲線

output_35_1.png

フーリエ変換

output_36_1.png

LSTM, 関数f, 周期 = 50

学習曲線

output_33_0.png

予測曲線

output_35_1.png

フーリエ変換

output_36_1.png

LSTM, 関数f, 周期 = 100

学習曲線

output_33_0.png

予測曲線

output_35_1.png

フーリエ変換

output_36_1.png

LSTM, 関数f まとめ

早期終了までに費やしたエポック数は、(はっきりしないが)周期が長くなると少なくなるかもしれない。短周期(25)中周期(50)長周期(100)全てで良好な予測ができた。

GRU, 関数f

GRU, 関数f, 周期 = 25

学習曲線

output_39_0.png

予測曲線

output_41_1.png

フーリエ変換

output_42_1.png

GRU, 関数f, 周期 = 50

学習曲線

output_39_0.png

予測曲線

output_41_1.png

フーリエ変換

output_42_1.png

GRU, 関数f, 周期 = 100

学習曲線

output_39_0.png

予測曲線

output_41_1.png

フーリエ変換

output_42_1.png

GRU, 関数f

GRUはLSTMより速いと聞いていたが、早期終了までに費やしたエポック数はむしろ長くなった(1000エポックに達してしまった)。短周期(25)中周期(50)長周期(100)全てで良好な予測ができた。

関数 g の周期を長くする

MLP, 関数g

MLP, 関数g, 周期 = 25

学習曲線

output_11_0.png

予測曲線

output_13_1.png

フーリエ変換

output_14_1.png

MLP, 関数g, 周期 = 50

学習曲線

output_11_0.png

予測曲線

output_13_1.png

フーリエ変換

output_14_1.png

MLP, 関数g, 周期 = 100

学習曲線

output_11_0.png

予測曲線

output_13_1.png

フーリエ変換

output_14_1.png

MLP, 関数g まとめ

早期終了までに費やしたエポック数は、周期が変化してもほぼ変化なし。おおよその形は保存されているが、足元がゴニョゴニョしているのと、ピークの高さが低くなるのと、周期がズレてしまうという傾向がある。フーリエ変換の図を見てみると、(ズレてはいるが)多くのピーク(周期)を拾えているらしい。

RNN, 関数g

RNN, 関数g, 周期 = 25

学習曲線

output_27_0.png

予測曲線

output_29_1.png

フーリエ変換

output_30_1.png

RNN, 関数g, 周期 = 50

学習曲線

output_27_0.png

予測曲線

output_29_1.png

フーリエ変換

output_30_1.png

RNN, 関数g, 周期 = 100

学習曲線

output_27_0.png

予測曲線

output_29_1.png

フーリエ変換

output_30_1.png

RNN, 関数g まとめ

短周期(25)のものは良好な予測。中周期(50)のものは、主なピークを拾っているけどズレてしまっている。長周期(100)では、RNNは早々に予測を諦めてしまったっぽい。たくさんある周期の中で、比較的短周期のものだけ拾ってるっぽい。

LSTM, 関数g

LSTM, 関数g, 周期 = 25

学習曲線

output_33_0.png

予測曲線

output_35_1.png

フーリエ変換

output_36_1.png

LSTM, 関数g, 周期 = 50

学習曲線

output_33_0.png

予測曲線

output_35_1.png

フーリエ変換

output_36_1.png

LSTM, 関数g, 周期 = 100

学習曲線

output_33_0.png

予測曲線

output_35_1.png

フーリエ変換

output_36_1.png

LSTM, 関数g まとめ

意外にも、全ての周期でうまく行かなかった。短周期はRNNではうまくいったが、LSTMではうまくいかず、ありもしないピークを拾っていた。中周期の結果が一番マシだが、それでも周期がズレていた。長周期はもう予測を諦めたーって声が聞こえそう。

GRU, 関数g

GRU, 関数g, 周期 = 25

学習曲線

output_39_0.png

予測曲線

output_41_1.png

フーリエ変換

output_42_1.png

GRU, 関数g, 周期 = 50

学習曲線

output_39_0.png

予測曲線

output_41_1.png

フーリエ変換

output_42_1.png

GRU, 関数g, 周期 = 100

学習曲線

output_39_0.png

予測曲線

output_41_1.png

フーリエ変換

output_42_1.png

GRU, 関数g まとめ

これも意外。LSTMはRNNより悪かったが、GRUはもっと悪かった。短周期のは、いくぶんかマシだが、中周期でもう諦めてしまった感じ。

多様な周期を持つ関数 h

当初、もっと多様な周期をミックスしてやろうと思っていましたが、上の結果を見て、こりゃもうちょっと少なめのミックスにしたほうがいいなと思いました。

MLP, 関数h

学習曲線

output_11_0.png

予測曲線

output_13_1.png

フーリエ変換

output_14_1.png

周期を拾ってくるのは、比較的うまく行ってるっぽい。しかし、振幅がうまく拾えていないので、結果、誤差の大きな予測になってしまっている。

RNN, 関数h

学習曲線

output_27_0.png

予測曲線

output_29_1.png

フーリエ変換

output_30_1.png

周期はおおむね拾ってるけど、なんかズレている。振幅はなぜか大きくなってしまっている。

LSTM, 関数h

学習曲線

output_33_0.png

予測曲線

output_35_1.png

フーリエ変換

output_36_1.png

いくつかの周期は拾えているけど、そこにない周期もけっこう拾ってしまっている。そういう意味ではRNNのほうが少しマシ?

GRU, 関数h

学習曲線

output_39_0.png

予測曲線

output_41_1.png

フーリエ変換

output_42_1.png

LSTMよりもさらにひどくなった感じ。

まとめ

以上、まとめです。周期関数をいくつか扱ってみた限りでは、

  • 短周期のsin波は、再帰型ニューラルネットワークで再現可能。
  • 長周期のsin波は、RNNでは無理。LSTMとGRUでは再現可能。
  • 形が崩れた波は、短周期ならRNNで再現可能。LSTMとGRUではどちらも難しい。
  • 能力は RNN < LSTM < GRU かと思っていたが、意外と逆(RNN > LSTM > GRU)になるケースも多いみたい。

まだまだ勉強不足なので、おかしな部分があるかもしれませんが、お気付きの点がございましたらご指摘いただきますと幸いです...(ヽ´ω`)

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8