LoginSignup
22

More than 3 years have passed since last update.

初心者の1D-CNN | LSTMとKerasで比較しました (時系列データ編)

Last updated at Posted at 2020-07-18

Motivation

LSTM等のReccurent系のネットワークは,時系列データを予測するのに向いているのですが,

1次元のCNN (1D-CNN)も,1*N ピクセルの画像を予測することなんてほとんどないので,用途のほとんどが時系列のデータになると思います.

なので,どっちが良いのか時系列データの簡単な例 Sin波 で簡単に調べてみようと思います.

Jupyter Notebook

ここに,コードはここにUPしてます.

データの作成

はじめに,Sin波を作成します.

def sin(x, T=100):
    return np.sin(2.0 * np.pi * x / T)

# sin波にノイズを付与する
def toy_problem(T=100, ampl=0.05):
    x = np.arange(0, 2 * T + 1)
    noise = ampl * np.random.uniform(low=-1.0, high=1.0, size=len(x))
    return sin(x) + noise
function = toy_problem(T=300)

Sin波をRNNに通すための形に整形

以下のように学習データとして,学習データ: 50ステップ + 予測ラベル: 1ステップ分を1サンプルとして生成します.  

注意 : 図は,学習データ: 25ステップになります.

rnn_label.png

def make_dataset(raw_data, n_prev=100, maxlen=25):
    data, target  = [], []

    for i in range(len(raw_data) - maxlen):
        data.append(raw_data[i:i+maxlen])
        target.append(raw_data[i+maxlen])

    reshaped_data = np.array(data).reshape(len(data), maxlen, 1)
    reshaped_target = np.array(target).reshape(len(target), 1)

    return reshaped_data, reshaped_target
data, label = make_dataset(f, maxlen=50)

モデルの作成

モデル定義

モデルの構造は結構適当に決めてます.

inputs = Input(shape=(50, 1))

x = Conv1D(30, 2, padding='same', activation='relu')(inputs)
x = MaxPool1D(pool_size=2, padding='same')(x)

x = Conv1D(10, 2, padding='same', activation='relu')(x)
x = MaxPool1D(pool_size=2, padding='same')(x)

x = Flatten()(x)
x = Dense(300, activation='relu')(x)
x = Dense(1, activation='tanh')(x)

model = Model(inputs, outputs=x)

optimizer = Adam(lr=1e-3)
model.compile(loss="mean_squared_error", optimizer=optimizer)

Early Stopping 設定

Validation Lossが,10epoch 下がらなかったら学習を止めます.

Early Stopping わからない人はこちら

early_stopping = callbacks.EarlyStopping(monitor='val_loss', mode='min', patience=10)

学習

学習します.

model.fit(data, label,
         batch_size=64, epochs=1000,
          validation_split=0.2, callbacks=[early_stopping]
         )

予測

学習データ

まず,学習データに対応できるかを予測します.

predicted = model.predict(data)

将来の予測

学習データの,末尾50ステップで,学習データの後を予測します.
本来は,予測に用いるデータ(=モデルに投入されるデータ)は正しいデータでないといけないのですが,
今回のやり方だと,予測値に誤差があるので,だんだんと予測に用いるデータが誤差を含み出すので,
感覚的に段々とSin波から予測がずれていくのかなと思っています.

  • future_test: 未来の1点を予測するための,直近50ステップの時系列データ
  • time_length : モデルに投入する時系列データの長さ
  • future_result: モデルの予測点を記録する配列
future_test = data[-1].T
time_length = future_test.shape[1]
future_result = np.empty((0))

将来 400点の予測をしてみる

for step in range(400):
    test_data = np.reshape(future_test, (1, time_length, 1))
    batch_predict = model.predict(test_data)

    future_test = np.delete(future_test, 0)
    future_test = np.append(future_test, batch_predict)

    future_result = np.append(future_result, batch_predict)

結果のグラフ

プロットするコード

fig = plt.figure(figsize=(16, 9), dpi=200)

sns.lineplot(
    color="#fe90af",
    data=function,
    label="Raw Data"
)

sns.lineplot(
    color="#61c0bf",
    x=np.arange(50, len(predicted)+50),
    y=predicted.reshape(-1),
    label="Predicted Training Data"
)

sns.lineplot(
    color="#81c00e",
    x=np.arange(0+len(function), len(function)+len(future_result)),
    y=future_result.reshape(-1),
    label="Predicted Future Data"
)

01_result_cnn.png

LSTMを比べてみる

LSTMで同じデータを予測した時がこちら.

LSTMは安定して,予測できますね...

LSTMでの実装方法はこちら

02_compare_lstm.png

モデルで長めの点を予測する

今は,50ステップの点から,次の1ステップを予測しましたが,

各将来の予測点同士に関連が出にくくなり,時間が経てば程,周期に関する特徴が薄れていっています.

1step.png

なので,次の10点を予測することで、予測するデータに折り返し等の時系列特徴を保持できないか調べてみます.

10step.png

10ステップごとに,モデルに投入するデータを更新するコード

long_test = multiple_data[-1].T
long_test_length = long_test.shape[1]
long_cnn_result  = np.empty((0))

for step in range(120):
    long_test_data = np.reshape(long_test, (1,  long_test_length, 1))
    batch_predict = multiple_cnn.predict(long_test_data)

    long_test = np.delete(long_test, list(range(OUTPUT_LENGTH)))
    long_test = np.append(long_test, batch_predict)

    long_cnn_result = np.append(long_cnn_result, batch_predict)

結果

LSTMでは,周期を維持しやすいが,振幅を維持しにくい傾向になり,

1D-CNNは,振幅は維持しやすいが,周期が徐々に長くなっている気がします.

03_long_wave.png

まとめ

今回は,1D-CNNでSin波を予測して,LSTMと比較するために,

Sin波を用意して,長期的な予測をしてみました.

1D-CNN LSTM
周期 徐々に長くなりがち 維持しやすい
振幅 値域に収まってる 発散・収束しやすい

※ あくまで,一例です.実際に,NumpyとTensorflowの乱数Seedを変更すると.結構めちゃめちゃな推論になったりします.

  • 上の図:1ステップを予測
  • 下の図:10ステップを予測

random.png

もう少し複雑なものでやったり,普通のRNNでやったりしようかなと思います.

参考

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
22