您的位置:首页 > 编程语言 > Python开发

LSTM-RNN 前向传播原理及 Python 实现

2018-03-05 15:03 941 查看

1.Forward propagation for the basic Recurrent Neural Network

1.1 - RNN cell



1. 计算隐藏状态 : a⟨t⟩=tanh(Waaa⟨t−1⟩+Waxx⟨t⟩+ba)a⟨t⟩=tanh⁡(Waaa⟨t−1⟩+Waxx⟨t⟩+ba).

2. 使用新的隐藏状态 a⟨t⟩a⟨t⟩, 计算预测值 ŷ ⟨t⟩=softmax(Wyaa⟨t⟩+by)y^⟨t⟩=softmax(Wyaa⟨t⟩+by).

3. 在缓存中保存 (a⟨t⟩,a⟨t−1⟩,x⟨t⟩,parameters)(a⟨t⟩,a⟨t−1⟩,x⟨t⟩,parameters)

4. 返回 a⟨t⟩a⟨t⟩ , y⟨t⟩y⟨t⟩ 以及缓存

def rnn_cell_forward(xt, a_prev, parameters):
"""
rnn_cell_forward 主要是用来计算每个 cell 的输出值

xt:每个时刻(t)时的输入数据
a_prev:输入的隐藏层数据
"""
# 首先取出各个参数
Wax, Waa, Wya = parameters["Wax"], parameters["Waa"], parameters["Wya"]
ba, by = parameters["ba"],parameters["by"]

# 计算下一个激活状态
a_next = np.tanh(np.dot(Wax, xt) + np.dot(Waa, a_prev) + ba)
# 计算当前时刻 t 下 cell 的输出 y
yt_pret = softmax(np.dot(Wya, a_next) + by)

# 存储在反向传播需要的值
cache = (a_next, a_prev, xt, parameters)

return a_next, yt_pred, cache


1.2 - RNN forward pass



在这里,我们要构建一个函数,输入是每个序列需要输入的数据集合,隐藏层的状态a0,以及每个点需要计算的参数矩阵。并且每一次将更新的 a 作为输入继续传递到下一个 cell 中

创建零向量 (aa) 用于存储所有从 RNN 计算出来的隐藏层状态

初始化下一个时间序列的隐藏层状态 a0

对每一个时间序列进行循环 tt :

通过
rnn_step_forward
更新隐藏层的状态 a_next 和 cache

保存隐藏层的状态在 aa (tthtth position) 中

在 y 中保存预测值

在 caches 中存储所有的 cache

返回 aa, yy and caches

def rnn_forward(x, a0, parameters):

caches = []

n_x, m, T_x = x.shape
n_y, n_a = parameters["Wya"].shape

a = np.zeros((n_a, m, T_x))
y = np.zeros((n_y, m, T_x))

# 初始化 a_next
a_next = a0

# 循环所有的时间序列
for t in range(T_x):
a_next, yt_pred, cache = rnn_cell_forward(x[:,:,t], a_next, parameters)
# 存储所有的 a_next
a[:, :, t] = a_next
# 存储所有的预测值
y[:, :, t] = yt_pred
caches.append(cache)

caches = (caches, x)

return a, y_pred, caches


2 - Long Short-Term Memory (LSTM) network



遗忘门:Forget gate

在 LSTM 中,有时候需要去标注单词是单数还是复数,这时候使用 LSTM 就可以达到想要的效果



Γ⟨t⟩f=σ(Wf[a⟨t−1⟩,x⟨t⟩]+bf)(1)(1)Γf⟨t⟩=σ(Wf[a⟨t−1⟩,x⟨t⟩]+bf)



WfWf 是控制遗忘门的参数。 Γ⟨t⟩fΓf⟨t⟩ 的值为[0, 1],遗忘门向量会与之前的 cell 状态 c⟨t−1⟩c⟨t−1⟩ 相乘。因此,如果 Γ⟨t⟩fΓf⟨t⟩ 的值为 0 (或者接近于 0),这就意味着 LSTM 会移除之前在 cell 中保存的信息(比如说单数状态),如果 Γ⟨t⟩fΓf⟨t⟩ 的值为 1,表示保留 cell 中的信息。

更新门:Update gate

如果在遗忘门中决定物体的性质不是单数(忘记单数的信息),那么需要更新门来更新物体的状态为复数。公式如下:

Γ⟨t⟩u=σ(Wu[a⟨t−1⟩,x{t}]+bu)(2)(2)Γu⟨t⟩=σ(Wu[a⟨t−1⟩,x{t}]+bu)

Γ⟨t⟩uΓu⟨t⟩ 的值为[0, 1],同样会与 c̃ ⟨t⟩c~⟨t⟩ 进行元素间的乘积,用于计算 c⟨t⟩c⟨t⟩.

更新 cell 状态

首先使用一个向量来保存之前 cell 的状态:

c̃ ⟨t⟩=tanh(Wc[a⟨t−1⟩,x⟨t⟩]+bc)(3)(3)c~⟨t⟩=tanh⁡(Wc[a⟨t−1⟩,x⟨t⟩]+bc)

新的 cell 的状态为:

c⟨t⟩=Γ⟨t⟩f∗c⟨t−1⟩+Γ⟨t⟩u∗c̃ ⟨t⟩(4)(4)c⟨t⟩=Γf⟨t⟩∗c⟨t−1⟩+Γu⟨t⟩∗c~⟨t⟩

输出门:Output gate

用于决定最后输出的值:

Γ⟨t⟩o=σ(Wo[a⟨t−1⟩,x⟨t⟩]+bo)(5)(5)Γo⟨t⟩=σ(Wo[a⟨t−1⟩,x⟨t⟩]+bo)



a⟨t⟩=Γ⟨t⟩o∗tanh(c⟨t⟩)(6)(6)a⟨t⟩=Γo⟨t⟩∗tanh⁡(c⟨t⟩)

2.1 - LSTM cell

def lstm_cell_forward(xt, a_prev, c_prev, parameters):
"""
m: 特征的数量
"""
# 取出各个参数
Wf, Wi, Wo = parameters["Wi"], parameters["Wi"], parameters["Wo"]
Wc, Wy = parameters["Wc"], parameters["Wy"]
bf, bi, bo = parameters["bf"], parameters["bi"], parameters["bo"]
bc, by = parameters["bc"], parameters["by"]

n_x, m = xt.shape
n_y, n_a = Wy.shape

concat = np.zeros((n_a + n_x), m)
concat[: n_a, :] = a_prev
concat[n_a :, :] = xt

ft = sigmoid(np.dot(Wf, concat) + bf)
it = sigmoid(np.dot(Wi, concat) + bi)

cct = np.tanh(np.dot(Wc, concat) + bc)
c_next = ft * c_prev + i * cct

ot = sigmoid(np.dot(Wo, concat) + bo)
a_next = ot * np.tanh(c_next)

y_pred = softmax(np.dot(Wy, a_next) + by)

cache = (a_next, c_next, a_prev, c_prev, ft, it, ot, cct, xt, parameters)

return a_next, c_next, yt_pred, cache


2.2 - Forward pass for LSTM



def lstm_forward(x, a0, parameters):

caches = []

n_x, m, T_x = x.shape
n_y, n_a = parameters["Wy"].shape

# 保存每一个时刻的各个变量
a = np.zeros((n_a, m, T_x))
c = np.zeros((n_a, m, T_x))
y = np.zeros((n_y, m, T_x))
# c 有啥用????
a_next = a0
c_next = 0

for t in range(T_x):
a_next, c_next, yt_pred, cache = lstm_cell_forward(x[:, :, t], a_next, c_next, parameters)
a[:, :, t] = a_next
c[:, :, t] = c_next
y[:, :, t] = yt_pred

caches.append(cache)

caches = (cache, x)

return a, y, c, caches
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: