【Tensorflow2】1次元CNNとTensorflow2での実装 Conv1D

tensorflow

今回は1次元CNNについて紹介します。1次元CNNについて簡単に概要をお話した後に、Tensorflowとkerasを使って1次元CNNによってsin波の予測をしてみます。

ちなみに畳込みについてはある程度知っていることを想定しています。

1次元CNNとは

CNN(畳込みニューラルネットワーク)はよく画像などの二次元のデータに対してよく聴くと思いますが、1次元のデータ(時系列等)のデータに使うことができます。
簡単な例を見てみようと思います。まず以下のように入力データとフィルタがあるとします。

入力データをフィルタ

このフィルタを使って入力データを以下のように畳み込んでいきます。

1次元CNNの畳み込みの流れ


2次元で行っていたことを1次元にするだけですね。複数のデータを畳み込んでいるので、時系列等も考慮して学習させることもできるはずです。

Tensorflow2(keras)での1次元CNN(Conv1d)

tensorflowでは1次元CNNをtf.keras.layers.Conv1Dとして提供してくれています。
先程の例で上げた1次元CNNを作ってみます。

import tensorflow as tf
# 入力
# The inputs are 1-length vectors with 9 timesteps, and the batch size is 2.(この文言が一番わかりやすいから引用)
input_shape = (2, 9, 1)
x = tf.random.normal(input_shape)
# バッチサイズ以降のshapeを指定
conv1d_input_shape = input_shape[1:]
# Conv1Dを生成
y = tf.keras.layers.Conv1D(filters=1, kernel_size=3, activation='relu',input_shape=conv1d_input_shape)(x)
print(y.shape)
#=>(2, 7, 1)

コメントにもありますが、入力データは(バッチサイズ,時系列データの長さ,各データのベクトル)になります。先程の例ですと(2, 9 ,1)となります。

Conv1Dの引数ですが、filters, kernel_sizeに関しては以下のようになります。

出力の次元についてですが、(バッチサイズ, フィルタで畳み込んだ後の要素数、フィルター数)になります。
ちなみにフィルターで畳み込んだ後の要素数は以下のように計算できます。

$$
\begin{aligned}
W-2(H/2)
\end{aligned}
$$

Wが時系列データの長さ、Hはカーネルサイズになります。

Conv1dによるsin波の予測

RNNの記事でも使いましたが、1次元CNNによってsin波の予測をしてみます。

ソースコードはgithubにもあげてあります。
https://github.com/tocom242242/aifx_blog_codes/blob/master/nn_tf2/conv/conv1d_sin.ipynb

まず、いつものように必要なモジュールをimportします。

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

次にsin波のデータを作ります。

x = np.linspace(-3*np.pi,3*np.pi,1000)
y = np.sin(x)
plt.plot(x, y)

このデータを学習用に加工していきます。

input_len = 5 # 入力の長さ
X, Y = [], []
for i, _ in enumerate(x):
    if (i+input_len+1 >= len(x)):
        break
    X.append(y[i:i+input_len])
    Y.append(y[i+input_len+1])

input_lenが入力の長さです。
データとして、i番目からinput_lenまでを入力データ、i+input_len+1を出力データになるようにデータを加工します。

次に、学習と検証用にデータを分けます。
今回は単純に左から8割を学習用、残りの2割を評価用とします。
つまり、以下のような感じです。

コードでは以下のようになります。

split_index = int(len(X)*0.8)

train_x = X[:split_index]
train_y = Y[:split_index]
test_x = X[split_index:]
test_y = Y[split_index:]

データをnumpy等に変換していきます。

train_x = np.array(train_x)
train_x = train_x.reshape(train_x.shape[0], -1, 1)
test_x = np.array(test_x)
test_x = test_x.reshape(test_x.shape[0], -1, 1)

train_y = np.array(train_y)
test_y = np.array(test_y)

では、1次元CNNモデルを作ってみます。

input_shape = train_x.shape[1:]
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Conv1D(32, 3, activation='relu',input_shape=input_shape))
model.add(tf.keras.layers.Dense(16,activation='relu'))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(1))
model.compile(loss="mse", optimizer="adam")

学習前のモデルで予測してみます。

plt.plot(np.arange(len(test_y)), test_y, label="sin")
predict_y = model.predict(test_x)
predict_y = predict_y.reshape(predict_y.shape[:2])
plt.plot(np.arange(len(test_y)), predict_y, label="predict(before)")
plt.legend()

予想通りまったく予測できていません。
では、次にモデルを学習させて、学習中の誤差の推移をプロットします。

hist = model.fit(train_x, train_y, batch_size=32, epochs=100, verbose=1)
history = hist.history
plt.plot(hist.epoch, history["loss"], label="loss")

うまく誤差は小さくできているようです。

では、学習したモデルでもう一度予測してみます。

plt.plot(np.arange(len(test_y)), test_y, label="sin")
predict_y = model.predict(test_x)
predict_y = predict_y.reshape(predict_y.shape[:2])
plt.plot(np.arange(len(test_y)), predict_y, label="predict(after)")
plt.legend()

うまく予測できていそうですね。実際にはちゃんと数値的にも評価しないといけませんが、今回は省略します。

参考文献

https://www.tensorflow.org/api_docs/python/tf/keras/layers/Conv1D

タイトルとURLをコピーしました