习题6-4 推导LSTM网络中参数的梯度, 并分析其避免梯度消失的效果
LSTM(长短期记忆)网络是一种常用的循环神经网络(RNN)变体,用于处理序列数据。为了推导LSTM网络中参数的梯度,我们需要考虑网络的前向传播和反向传播过程。
首先,让我们回顾LSTM的基本结构。一个标准的LSTM单元由三个门控单元组成:输入门(input gate)、遗忘门(forget gate)和输出门(output gate)。这些门控单元帮助网络决定如何处理输入数据并记住/遗忘相关信息。
LSTM的前向传播过程如下:
1.输入门的计算:
2.遗忘门的计算:
3.细胞状态更新:
4.输出门的计算:
5.隐藏状态更新:
这是LSTM的前向传播过程。接下来,我们可以使用反向传播算法计算参数的梯度。
首先,我们定义损失函数(如交叉熵损失函数)并计算相对于输出h_tht的梯度。然后,我们根据链式法则逐层传播梯度,计算每个参数的梯度。
对于隐藏状态h_tht的梯度,我们首先计算相对于损失函数的梯度,然后应用链式法则逐层传播梯度:
1.相对于输出门的梯度:
2.相对于细胞状态的梯度:
3.相对于输入门的梯度:
4.相对于遗忘门的梯度:
5.相对于候选细胞状态的梯度:
然后,我们可以计算参数的梯度:
1.输入门参数的梯度:
2.遗忘门参数的梯度:
3.输出门参数的梯度:
4.候选细胞状态参数的梯度:
以上是LSTM网络中参数的梯度推导过程。
LSTM网络的设计使其能够有效地缓解梯度消失问题。
习题6-3P 编程实现下图LSTM运行过程
1. 使用Numpy实现LSTM算子
import numpy as np
x = np.array([[1, 0, 0, 1],
[3, 1, 0, 1],
[2, 0, 0, 1],
[4, 1, 0, 1],
[2, 0, 0, 1],
[1, 0, 1, 1],
[3, -1, 0, 1],
[6, 1, 0, 1],
[1, 0, 1, 1]])
# x = np.array([
# [3, 1, 0, 1],
#
# [4, 1, 0, 1],
# [2, 0, 0, 1],
# [1, 0, 1, 1],
# [3, -1, 0, 1]])
inputGate_W = np.array([0, 100, 0, -10])
outputGate_W = np.array([0, 0, 100, -10])
forgetGate_W = np.array([0, 100, 0, 10])
c_W = np.array([1, 0, 0, 0])
def sigmoid(x):
y = 1 / (1 + np.exp(-x))
if y >= 0.5:
return 1
else:
return 0
temp = 0
y = []
c = []
for input in x:
c.append(temp)
temp_c = np.sum(np.multiply(input, c_W))
temp_input = sigmoid(np.sum(np.multiply(input, inputGate_W)))
temp_forget = sigmoid(np.sum(np.multiply(input, forgetGate_W)))
temp_output = sigmoid(np.sum(np.multiply(input, outputGate_W)))
temp = temp_c * temp_input + temp_forget * temp
y.append(temp_output * temp)
print("memory:",c)
print("y :",y)
结果:
2. 使用nn.LSTMCell实现
import torch
from torch import nn
import numpy as np
print('one layer lstm')
cell = nn.LSTMCell(input_size=100, hidden_size=20)
h = torch.zeros(3, 20)
c = torch.zeros(3, 20)
x = torch.randn(10, 3, 100)
for xt in x:
h, c = cell(xt, [h, c])
print('h.shape: ', h.shape)
print('c.shape: ', c.shape)
结果:
3. 使用nn.LSTM实现
import torch
import torch.nn as nn
input_size = 4
hidden_size = 1
num_layers = 1
xt = torch.tensor([[1, 0, 0, 1], [3, 1, 0, 1], [2, 0, 0, 1], [4, 1, 0, 1], [2, 0, 0, 1], [1, 0, 1, 1], [3, -1, 0, 1], [6, 1, 0, 1], [1, 0, 1, 1]], dtype=torch.float32)
lstm = nn.LSTM(input_size, hidden_size, num_layers,bias=False)
# 手动设置参数权重
lstm.weight_ih_l0.data = torch.Tensor([[0.,100.,0.,10.],[0.,100.,0.,-10.],[1.,0.,0.,0.],[0.,0.,100.,-10.]])
lstm.weight_hh_l0.data = torch.zeros([4,1])
hx = torch.zeros(num_layers, 1, hidden_size)
cx = torch.zeros(num_layers, 1, hidden_size)
output,_= lstm(xt.unsqueeze(1), (hx, cx))
k=0
for i in output.squeeze(1).detach().numpy():
print(k," output y is : ",round(i[0]))
k=k+1
结果:
结果是使用相同的参数和激活函数,三种方法得到的结果都是一样的。
参考: