2017年9月13日水曜日

ニューラルネットワークで正弦波にフィット

ニューラルネットワークを使って、正弦波のデータ\(y=\sin(fx)\)にノイズをのせた、\(y=\sin(fx)+n\) (\(n\)がノイズ)からサンプルしたデータにフィッティングをして、\(y=\sin(fx)\)を学習できるか試してみました。\(f\)は周波数の大小を表します。

モデルは、直線フィッティングのときと同じです。

学習データを[-10,10)の範囲で一様乱数で10000サンプルとって、100エポック学習しました。 学習できたモデルの出力は\(f\)の値ごとに下図のようになりました。横軸がx、縦軸がyです。 薄い水色の線は、ノイズを加える前の学習データの生成に使った正弦波です。紺色の線が、学習したニューラルネットワークが生成した曲線です。

\(f=0.5\)

\(f=1\)

\(f=2\)

\(f=4\)

\(f=8\)

\(f=16\)

\(f=0.5\)や\(f=1\)はともかく、それ以降はフィットできていません。特に\(f=4\)以降は完全に崩れています。\(f=16\)では、そもそも周波数が高すぎて、図では元の正弦波がつぶれてしまっています。きちんとフィットさせるためにはニューラルネットワークをもう少し工夫する必要があることが分かりました。

\(f=4\)のときの学習データに使ったデータの生成と同じ条件で、ただし乱数違いで生成したデータをプロットしたものが下図です。横軸の範囲が異なることに注意してください。

\(f=4\) sampled with noise
まだ正弦波を見て取れるのですが、今回使ったニューラルネットワークでは難しいようです。

同様に、\(f=16\)のときの学習データに使ったデータの生成と同じ条件で、ただし乱数違いで生成したデータをプロットしたものが下図です。一応、正弦波の名残は残っていますが、元が正弦波とは言いがたいです。

\(f=16\) sampled with noise

今回の実験のモデルの学習時に使ったコードは以下です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# This script trains a neural network model using MNIST.
import keras, os, math
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation

TRAIN_EPOCH = 100

def approx_model():
    model = Sequential()
    model.add(Dense(32, input_shape=(1,)))
    model.add(Activation('tanh'))
    model.add(Dense(32))
    model.add(Activation('tanh'))
    model.add(Dense(1))
    print(model.summary())
    return model

def sine_dist(freq):
    xd = np.random.rand(10000,)*20-10
    yd = [math.sin(x*freq)+np.random.normal(0, 1) for x in xd]
    return xd, yd

if __name__ == '__main__':
    if not os.path.exists("models"):
        os.mkdir("models")

    # Make a model
    model = approx_model()
    opt = keras.optimizers.Adamax(lr=0.002, beta_1=0.9, beta_2=0.999,
                                  epsilon=1e-08, decay=1e-4)
    model.compile(optimizer=opt, loss='mean_squared_error')
    model.save_weights("initial-weights.hdf5")

    for freq in [0.5, 1, 2, 4, 8, 16]:
        # Make data for training and validation
        x_train, y_train = sine_dist(freq)
        x_val, y_val = sine_dist(freq)
        
        # Train and save a model
        model.load_weights("initial-weights.hdf5")
        model.fit(x_train, y_train, validation_data=(x_val, y_val),
                  epochs=TRAIN_EPOCH, batch_size=32)
        model.save("models/sine-{0}.hdf5".format(freq))

グラフの描画に使ったコードは以下です(\(f=1\)の場合)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
%matplotlib inline
import keras, math
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(-30, 30, 0.001)
model = keras.models.load_model('models/sine-1.hdf5')
y = model.predict(x)
yc = np.array([math.sin(xi*1) for xi in x])
plt.grid()
plt.plot(x, yc, color='lightblue')
plt.plot(x, y, color='midnightblue')
plt.show()

0 件のコメント :