飛行機の乗客数予測

LSTMの回帰問題への適用

LSTMは主に分類や回帰問題に用いられ,このチュートリアルではLSTMを回帰問題に適用します.
このページではLSTMを飛行機の乗客数の予測に応用します.
以下のURLからダウンロードが可能となります. http://datamarket.com/data/list/?q=provider:tsdl
[Box & Jenkins (1976)].
今回は3ヶ月分の乗客数から次の月の乗客数を予測するタスクを行います.
具体的には以下のようにデータを加工していきます.
  • 入力:予測に使う月の数のデータ
    • 今回の場合:3ヶ月
  • 出力:予測したい月
    • 今回の場合:次の1ヶ月

Required Libraries

  • matplotlib 2.0.2
  • numpy 1.12.1
  • scikit-learn 0.18.2
  • pandas 0.20.3
In [1]:
%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error

import renom as rm
from renom.optimizer import Adam
from renom.cuda import set_cuda_active
# if you would like to use GPU, set True, otherwise you should be set to False
set_cuda_active(False)

データの作成

1次元配列からなる時系列データを作成します.

In [2]:
def create_dataset(ds, look_back=1):
    X, y = [], []
    for i in range(len(ds)-look_back):
        X.append(ds[i : i+look_back])
        y.append(ds[i+look_back])
    X = np.reshape(np.array(X), [-1, look_back, 1])
    y = np.reshape(np.array(y), [-1, 1])
    return X, y

データの分割

データセットを訓練データとテストデータに分けます.

In [3]:
def split_data(X, y, test_size=0.1):
    pos = int(round(len(X) * (1-test_size)))
    X_train, y_train = X[:pos], y[:pos]
    X_test, y_test = X[pos:], y[pos:]
    return X_train, y_train, X_test, y_test

CSVファイルからの読み込み

データを読み込んでいくつかの調整をデータに加えます.学習を安定させるためにデータを正規化します.

In [4]:
df = pd.read_csv("./international-airline-passengers.csv",usecols=[1],header=None,skiprows=1,skipfooter=3,engine="python")
ds = df.values.astype("float32")
data = []
for i in range(ds.shape[0]-1):
    data.append(ds[i+1]-ds[i])
data = np.array(data)
plt.figure(figsize=(8,8))
plt.plot(data)
plt.show()
plt.clf()
v_min = np.min(np.abs(data))
v_max = np.max(np.abs(data))
data -= v_min
data /= v_max - v_min

look_back = 3
X, y = create_dataset(data, look_back)
print("X:{},y:{}".format(X.shape, y.shape))
X_train, y_train, X_test, y_test = split_data(X, y, 0.33)
print("X_train:{},y_train:{},X_test:{},y:test{}".format(X_train.shape, y_train.shape, X_test.shape, y_test.shape))
../../../_images/notebooks_time_series_lstm-regression_notebook_8_0.png
X:(140, 3, 1),y:(140, 1)
X_train:(94, 3, 1),y_train:(94, 1),X_test:(46, 3, 1),y:test(46, 1)
<matplotlib.figure.Figure at 0x7fe78bca5b00>

モデルの定義

In [5]:
sequential = rm.Sequential([
    rm.Lstm(50),
    rm.Lstm(20),
    rm.Dense(1)
])

学習ループ

初めにトレーニング用のバッチを作成します. `` T``は予測に必要な期間です.2ヶ月間のデータを用いて次の1ヶ月の予測をする場合,一度2ヶ月の学習が終わった段階で伝搬誤差を打ち切ります.そうしなければ次の2ヶ月を学習するときに影響を与えてしまします.そのために `` sequential.truncate``を書く必要があります. `` l.grad().update(optimizer)``で微分計算を行い,重みをアップデートします.

for t in range(T):
    z = sequential(X_test[:,t,:])
    l_test = rm.mse(zm response_batch)
sequential.truncate()
test_loss += l_test.as_ndarray()

上記の部分は各エポックの学習過程の確認のためのテストロスの計算です.

In [6]:
batch_size = 15
epoch = 800
N = len(X_train)
T = X_train.shape[1]

learning_curve = []
test_learning_curve = []
optimizer = Adam(lr=0.001)
for i in range(epoch):
    loss = 0
    test_loss = 0
    perm = np.random.permutation(N)
    for j in range(N//batch_size):
        train_batch = X_train[perm[j*batch_size : (j+1)*batch_size]]
        response_batch = y_train[perm[j*batch_size : (j+1)*batch_size]]
        l = 0
        with sequential.train():
            for t in range(T):
                z = sequential(train_batch[:, t, :])
                l = rm.mse(z, response_batch)
            sequential.truncate()
        l.grad().update(optimizer)
        loss += l.as_ndarray()
    l_test = 0
    for t in range(T):
        z = sequential(X_test[:, t, :])
        l_test = rm.mse(z, y_test)
    sequential.truncate()
    test_loss += l_test.as_ndarray()
    if i % 100 == 0:
        print("epoch:{:04d} loss:{:.5f} test_loss:{:.5f}".format(i, loss, test_loss))
    learning_curve.append(loss)
    test_learning_curve.append(test_loss)
epoch:0000 loss:0.16352 test_loss:0.11648
epoch:0100 loss:0.11747 test_loss:0.10974
epoch:0200 loss:0.08164 test_loss:0.20705
epoch:0300 loss:0.07782 test_loss:0.13734
epoch:0400 loss:0.06581 test_loss:0.08912
epoch:0500 loss:0.04596 test_loss:0.11666
epoch:0600 loss:0.03076 test_loss:0.12018
epoch:0700 loss:0.03054 test_loss:0.10680

モデルの評価と学習曲線の表示

for t in range(T):
    train_predict = sequential(X_train[:,t,:])
sequential.truncate()

この部分は訓練データに対して1ヶ月先の予測を行います.(訓練データを学習しているので,当然よくあてはまる曲線が得られる.

for t in range(T):
    test_predict = sequential(X_test[:,t,:])
sequential.truncate()

この部分はテストデータに対する1ヶ月先の予測を行います.

In [7]:
for t in range(T):
    test_predict = sequential(X_test[:, t, :])
sequential.truncate()

y_test_raw = y_test * (v_max - v_min) + v_min
test_predict = test_predict * (v_max - v_min) + v_min

print("Root mean squared error:{}".format(np.sqrt(mean_squared_error(y_test_raw, test_predict))))

plt.figure(figsize=(8,8))
plt.title("predictions")
plt.grid(True)
plt.plot(y_test_raw, marker=".", label ="original")
plt.plot(test_predict, marker=".", label="predict")

plt.legend()
plt.show("airline.png")
Root mean squared error:45.258628845214844
../../../_images/notebooks_time_series_lstm-regression_notebook_14_1.png

二乗平均平方根は元のデータと予測されたデータの間の平均的な損失を表現します.今回の場合は私達の作成したモデルは平均的に毎月の予測においておよそ45人程度の損失となっています.

最後の図は元のデータと予測されたデータの異なりを表しています.RMSEと合わせて見ることとモデルの評価の一つとすることができます.