RNNの順伝播と逆伝播について調べみた(ChatGPTから聞いたRNNのコードについて調べてみた)

はじめに

前回はRNNのモデルについて調べました。

今回からChatGPTに聞いたコードを勉強していき、RNNを実装できるようにしていきたいと思います。

RNNのコードについて勉強してみた

コード

import numpy as np
import matplotlib.pyplot as plt

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# RNNセルの定義
class SimpleRNNCell:
    def __init__(self, input_size, hidden_size):
        self.input_size = input_size
        self.hidden_size = hidden_size

        # 重み行列の初期化
        self.Wx = np.random.randn(hidden_size, input_size)
        self.Wh = np.random.randn(hidden_size, hidden_size)
        self.b = np.zeros((hidden_size, 1))

    def forward(self, x, h_prev):
        # 順伝播
        self.x = x
        self.h_prev = h_prev
        self.a = np.dot(self.Wx, x) + np.dot(self.Wh, h_prev) + self.b
        self.h = np.tanh(self.a)
        return self.h

    def backward(self, dh_next):
        # 逆伝播
        da = dh_next * (1 - np.tanh(self.a)**2)
        self.db = np.sum(da, axis=1, keepdims=True)
        self.dWh = np.dot(da, self.h_prev.T)
        self.dWx = np.dot(da, self.x.T)
        self.dx = np.dot(self.Wh.T, da)
        self.dh_prev = np.dot(self.Wx.T, da)
        return self.dx, self.dh_prev

    def update_parameters(self, learning_rate):
        # 重みの更新
        self.Wx -= learning_rate * self.dWx
        self.Wh -= learning_rate * self.dWh
        self.b -= learning_rate * self.db

# サンプルデータの生成
seq_length = 5
input_size = 10
hidden_size = input_size

fig = plt.figure()
# 入力データ (shape: (input_size, seq_length))
X = np.random.randn(input_size, seq_length)
print(X)
ax1 = fig.add_subplot(3, 1, 1)
ax1.plot(range(len(X)), X, marker="o")
ax1.set_ylabel("input")

# 目標出力 (仮の値)
y = np.random.randn(hidden_size, 1)
print(y)
ax2 = fig.add_subplot(3, 1, 2)
ax2.plot(range(len(y)), y, label="original")
ax2.set_ylabel("output")

# 初期隠れ状態 (shape: (hidden_size, 1))
h0 = np.zeros((hidden_size, 1))

# RNNセルの作成
rnn_cell = SimpleRNNCell(seq_length, hidden_size)

# 順伝播
h_t = h0
epochs = 100
loss_list = []
for epoch in range(epochs):
    for t in range(input_size):
        x_t = X[t, :].reshape(-1, 1)
        h_t = rnn_cell.forward(x_t, h_t)
    
    # 仮の損失関数 (例: 平均二乗誤差)
    loss = np.mean((h_t - y)**2)
    loss_list.append(loss)
    
    # 逆伝播
    dh_next = 2 * (h_t - y)  # 仮の勾配
    dx, dh_prev = rnn_cell.backward(dh_next)
    
    # 重みの更新
    learning_rate = 0.01
    rnn_cell.update_parameters(learning_rate)
    
    print(loss)

ax2.plot(range(len(y)), h_t, marker="o", label="predict")
ax2.legend(loc="upper left")

ax3 = fig.add_subplot(3, 1, 3)
ax3.plot(range(epochs)[1:], loss_list[1:], marker="o")
ax3.set_ylabel("loss")
ax3.set_yscale('log')


x_t = X[-1, :].reshape(-1, 1)
pred = rnn_cell.forward(x_t, h_t)[-1]

調べてみた

RNNの順伝播と逆伝播のイメージを作ってみた

今回はRNNの順伝播と逆伝播について調べていきたいと思います。

RNNを調べて、私が理解した範囲で図化しました。

各時間毎にこれまでのニューラルネットワークと変わりませんが、隠れ層が次の時間のニューラルネットワークのインプットにもなるのかなと思います。

隠れ層にも重みが与えられ、インプットデータと隠れ層の重み(Wx)と隠れ層同士の重み(Wh)を最適化していくことがRNNの目的になるのかなと思います。

該当コードを調べてみた

まずは該当箇所のコード

# 順伝播
h_t = h0
epochs = 100
loss_list = []
for epoch in range(epochs):
    for t in range(input_size):
        x_t = X[t, :].reshape(-1, 1)
        h_t = rnn_cell.forward(x_t, h_t)
    
    # 仮の損失関数 (例: 平均二乗誤差)
    loss = np.mean((h_t - y)**2)
    loss_list.append(loss)
    
    # 逆伝播
    dh_next = 2 * (h_t - y)  # 仮の勾配
    dx, dh_prev = rnn_cell.backward(dh_next)
    
    # 重みの更新
    learning_rate = 0.01
    rnn_cell.update_parameters(learning_rate)
    
    print(loss)

RNNに限らずニューラルネットワークの順伝播と逆伝播はこちらの記事こちらの記事で勉強しました。

RNNの特徴のコード

RNNの特徴としては入力層と隠れ層を何度も使いまわしているというところでしょうか

for t in range(input_size):
        x_t = X[t, :].reshape(-1, 1)
        h_t = rnn_cell.forward(x_t, h_t)

の部分です。

x_tは入力層ですが、X[t, :]のtがループで1行ずつずれていくことが分かります。つまり、時系列データの時間を少しずつずらしていくということになると思われます。

一方、h_tもx_tを入力して、隠れ層に自分自身h_tを使って隠れ層を更新しています。これを行うことで時系列毎で隠れ層を共有、更新を行っているのだと思います。

それ以外の順伝播と逆伝播はこれまでのニューラルネットワークと同じですね。

コメント

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