『Python数値計算ノート』ではアフィリエイトプログラムを利用して商品を紹介しています。

減衰振動曲線の学習

減衰振動曲線の学習

Output_layer_cクラスを少し改造して Output_layer クラスを設計し、他の必要なコードもまとめて以下のリストに載せておきます。

# In[1]

# モジュールをインポート
import numpy as np
import matplotlib.pyplot as plt

# 2乗和誤差RSS
def rss(y, t):
    f = 0.5 * np.sum((y - t)**2) / y.shape[0]
    return f

# 平均交差エントロピー誤差関数
def cross_entropy(y, t):
    ce = - np.sum(t * np.log(y + 1e-8)) / y.shape[0]
    return ce

# 恒等関数
def identity(x):
    return x

# シグモイド関数
def sigmoid(x):
    f = 1 / (1 + np.exp(-x))
    return f

# ソフトマックス関数
def softmax(x):
    f = np.exp(x) / np.sum(np.exp(x), axis = 1, keepdims = True)
    return f

# ReLUクラス
class ReLU:
    def __init__(self, x):
        self.y = np.where(x <= 0, 0, x)
        self.dy = np.where(x <= 0, 0, 1)

# Tanhクラス
class Tanh:
    def __init__(self, x):
        self.y = np.tanh(x)
        self.dy = 1 / np.cosh(x)**2

# Sigmoidクラス
class Sigmoid:
    def __init__(self, x):
        self.y = 1 / (1 + np.exp(-x))
        self.dy = (1 - self.y) * self.y

# 中間層クラス
class Middle_layer:
    def __init__(self, n_upper, n, func):
        self.w = np.random.normal(0, 0.05, (n, n_upper + 1))
        self.w2 = np.delete(self.w, -1, 1)
        self.func = func

    def forward(self, y_in):
        dummy = np.ones((y_in.shape[0], 1))
        self.y_in = np.append(y_in, dummy, axis = 1)
        self.u = np.dot(self.y_in, self.w.T)
        self.y_out = self.func(self.u).y

    def backward(self, y_b):
        self.delta = self.func(self.u).dy * y_b
        self.y_back = np.dot(self.delta, self.w2)
        self.w -= alpha * np.dot(self.delta.T, self.y_in)
        self.w2 = np.delete(self.w, -1, 1)

# 出力層クラス
class Output_layer:
    def __init__(self, n_upper, n, c = False):
        self.w = np.random.normal(0, 0.05, (n, n_upper + 1))
        self.w2 = np.delete(self.w, -1, 1)
        self.c = c

    def activate(self, y_in, t):
        self.t = t
        dummy = np.ones((y_in.shape[0], 1))
        y_in = np.append(y_in, dummy, axis = 1)
        u = np.dot(y_in, self.w.T)
        self.y_out = u
        if self.c == True:
            self.y_out = softmax(u)
        self.delta = self.y_out - self.t
        self.y_back = np.dot(self.delta, self.w2)
        self.w -= alpha * np.dot(self.delta.T, y_in)
        self.w2 = np.delete(self.w, -1, 1)

    def forward(self, y_in):
        dummy = np.ones((y_in.shape[0], 1))
        y_in = np.append(y_in, dummy, axis = 1)
        u = np.dot(y_in, self.w.T)
        self.y_out = u
        if self.c == True:
            self.y_out = softmax(u)

Output_layerクラスはデフォルトで回帰用の出力層を生成するようになっています。オプション引数 c を True に設定することで、分類用の出力層を生成することができます。
 
今回はニューラルネットワークに 減衰振動曲線
 \[y=\exp\left(-\frac{x}{4}\right)\sin 2x\]
を学習させます。学習用データセットは以下のコードで作成します。

# In[2]

# 学習用データセット
x = np.linspace(0, 8, 129)
t = np.exp(-x/4) * np.sin(2*x)

# データの次元を1つ増やす
x2d = x[:, np.newaxis]
t2d = t[:, np.newaxis]

以下のコードを実行すると、この曲線のグラフが描かれます。

# In[3]

# 正解値のグラフ
fig, ax = plt.subplots(1, 1, figsize = (5, 5))
ax.set_xlabel("x", fontsize = 15)
ax.set_ylabel("t", fontsize = 15)
ax.set_xlim([0, 8])
ax.set_ylim([-1, 1])
ax.grid()
ax.plot(x, t, color = "blue")

ニューラルネットワークによる回帰
8 個のユニットで構成される中間層を 2 層重ねてニューラルネットワークを構築し、入力した x が曲線の上に乗るようになるまでオンライン学習させます。簡単そうに思えますが、かなりのパワーを要求されるタスクなので、エポックは 8000 に設定しておきます。環境によっては学習を終えるまで数分かかる場合もあります。

# In[4]

# 乱数を初期化
np.random.seed(10)

# 学習率を設定
alpha = 0.25

# epochを設定
epoch = 8000

# 経過表示間隔を設定
interval = 1000

# 学習過程記録用変数を設定
y_r = []
epoch_r = []

# データの個数
nd = len(x)

# インデックス配列
idx = np.arange(nd)

# 中間層と出力層を作成
mid_1 = Middle_layer(1, 8, Sigmoid)
mid_2 = Middle_layer(8, 8, Sigmoid)
out = Output_layer(8, 1)

# エポック数だけ学習を繰り返す
for j in range(epoch):
    
    # インデックス配列をシャッフル
    np.random.shuffle(idx)

    # オンライン学習
    for k in idx:
        mid_1.forward(x2d[k : k + 1])
        mid_2.forward(mid_1.y_out)
        out.activate(mid_2.y_out, t2d[k : k + 1])
        mid_2.backward(out.y_back)
        mid_1.backward(mid_2.y_back)

    # 学習過程の記録
    if j % interval == 0:
        mid_1.forward(x2d)
        mid_2.forward(mid_1.y_out)
        out.forward(mid_2.y_out)
        y_r.append(out.y_out)
        epoch_r.append(j)

In[5] を実行すると、1000 epoch ごとに出力値が記録されるので、そのデータを使って学習過程を観察してみましょう。

# In[5]

# FigureとAxesを用意
fig, axs = plt.subplots(3, 2, sharey = "all", figsize = (8, 15))

# サブプロット間の縦のスペースを調整
fig.subplots_adjust(hspace = 0.3)

# カウント変数
ct = 0

# 学習過程を表示
for ax in axs.ravel():
    ct += 1
    ax.set_title("epoch : {}".format(epoch_r[ct]), size = 15)
    ax.set_xlabel("x", fontsize = 15)
    if ct % 2 == 1:
        ax.set_ylabel("t", fontsize = 15)
    ax.set_xlim([0, 8])
    ax.set_ylim([-1, 1])
    ax.grid()
    ax.plot(x, t, color = "gray")
    ax.scatter(x2d, y_r[ct], marker = "+", color = "red")

plt.savefig("python_epoch.png", bbox_inches = "tight")

減衰振動曲線の学習過程のエポックごとの記録

【おすすめ記事】≫ ソフトマックス関数

【おすすめ書籍】詳解ディープラーニング 第2版 ~TensorFlow/Keras・PyTorchによる時系列データ処理~

詳解ディープラーニング 第2版 ~TensorFlow/Keras・PyTorchによる時系列データ処理~ (Compass Booksシリーズ)

新品価格
¥3,740から
(2020/8/21 23:39時点)


ディープラーニングとニューラルネットワークについて、予備知識なしで基礎から学べる本です。理論だけでなく、TensorFlow と Keras を用いた実装法についても解説しています。扱う手法は、単純パーセプトロンから多層パーセプトロン、ディープニューラルネットワーク、リカレントニューラルネットワークなど多肢にわたります。

コメント