はじめに
前回は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セルの定義
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
行っていることとしては
となっています。各項目の基礎的な詳細は他の記事で説明していますので、ぜひご確認を。
ここではRNNに関わる部分について勉強していきたいと思います。
RNNのコードの特徴
ほとんどこれまで勉強したコードと変わらないのですが、これまでにない特徴があるようです。
それが、隠れ層自体にも重みがあることです。
何を言っているんだと思われる方が多いと思います。私もきちんと理解したといわれるとやや不安ですが、なんとなく分かりました。比較的分かりやすい説明はこちらでされています。
私が理解した範囲で図化しました。
各時間毎にこれまでのニューラルネットワークと変わりませんが、隠れ層が次の時間のニューラルネットワークのインプットにもなるのかなと思います。
隠れ層にも重みが与えられ、インプットデータと隠れ層の重み(Wx)と隠れ層同士の重み(Wh)を最適化していくことがRNNの目的になるのかなと思います。
さて、コードを見てみましょう。
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
これまでのニューラルネットワークとは異なる点は、Whが定義され、順伝播時に隠れ層もインプット(h_prev)に含まれていることです。
あとは、これを逆伝播することで、各重みを最適化しているようです。
とりあえず、これでRNNモデルが定義されるようです。
うまくできてますね~。改めてすごいですね。
コメント