LSTM是一种特殊的RNN,其引入状态量来保留历史信息,同时引入门的概念来更新状态量。
RNN:
LSTM:
原理
RNN网络中历史信息在每个RNN单元,都经过tanh/ReLu,信息在逐渐流失;而LSTM,采用信息更新的方式,更容易将有用的信息传递下去,传得更远。也就是下图中C随序列传递的过程。
为了实现状态量C的旧状态删除、新状态更新、当前结果有用状态信息的提取,分别引入“遗忘门”、“输入门”、“输出门”三个结构。
门:使用前一个输出,结合当前输入,通过sigmod函数,得到输出值,在0~1之间,决定信息量各部分被遗忘/选择的程度。
遗忘门
其中ht−1表示的是上一个cell的输出,xt表示的是当前细胞的输入。σ表示sigmod函数。
输入门
输入门挑选信息来更新状态量C。
输出门
输出门挑选更新后的状态量C。
LSTM变体GRU
其中, rt表示重置门,zt表示更新门。重置门决定是否将之前的状态忘记。(作用相当于合并了 LSTM 中的遗忘门和传入门)当rt趋于0的时候,前一个时刻的状态信息ht−1会被忘掉,隐藏状态h^t会被重置为当前输入的信息。更新门决定是否要将隐藏状态更新为新的状态h^t(作用相当于 LSTM 中的输出门) 。
和 LSTM 比较一下:
1) GRU 少一个门,同时少了细胞状态Ct。
2) 在 LSTM 中,通过遗忘门和传入门控制信息的保留和传入;GRU 则通过重置门来控制是否要保留原来隐藏状态的信息,但是不再限制当前信息的传入。
3) 在 LSTM 中,虽然得到了新的细胞状态 Ct,但是还不能直接输出,而是需要经过一个过滤的处理;同样,在 GRU 中, 虽然我们也得到了新的隐藏状态h^t, 但是还不能直接输出,而是通过更新门来控制最后的输出:ht=(1−zt)∗ht−1+zt∗h^t
LSTM变体FC-LSTM
更新公式:
演示实验代码:
import torch from torch import nn import numpy as np class Rnn(nn.Module): def __init__(self, INPUT_SIZE): super(Rnn, self).__init__() self.rnn = nn.LSTM( input_size=INPUT_SIZE, hidden_size=32, num_layers=2, batch_first=True ) self.out = nn.Linear(32, 1) def forward(self, x, hc_state): # input(x): batch, seq_len, input_size = 1, 10, 2 # output(r_out): batch, seq_len, hidden_size * num_directions = 1, 10, 32*1 r_out, hc_state = self.rnn(x, hc_state) outs = [] for time in range(r_out.size(1)): outs.append(self.out(r_out[:, time, :])) return torch.stack(outs, dim=1), hc_state # 定义一些超参 TIME_STEP = 10 INPUT_SIZE = 2 LR = 0.02 # “看”数据 # plt.plot(steps, y_np, 'r-', label='target(cos)') # plt.plot(steps, x_np, 'b-', label='input(sin)') # plt.legend(loc='best') # plt.show() # 选择模型 model = Rnn(INPUT_SIZE) print(model) # 定义优化器和损失函数 loss_func = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=LR) h_state = torch.autograd.Variable(torch.zeros(2,1,32)) # h0/c0: num_layers * num_directions, batch, hidden_size = 2*1, 1, 32 c_state = torch.autograd.Variable(torch.zeros(2,1,32)) # 第一次的时候,暂存为0 for step in range(300): start, end = step * np.pi, (step+1)*np.pi steps = np.linspace(start, end, TIME_STEP, dtype=np.float32) x_np = np.sin(steps) y_np = np.cos(steps) x = torch.from_numpy(x_np[np.newaxis, :, np.newaxis]) y = torch.from_numpy(y_np[np.newaxis, :, np.newaxis]) # 为了演示,重复x将输入数据特征扩展为两维 prediction, (h_state, c_state) = model(torch.cat((x,x), 2), (h_state, c_state)) h_state = h_state.data c_state = c_state.data loss = loss_func(prediction, y) optimizer.zero_grad() loss.backward() optimizer.step() print("x:") print(x) print("y:") print(y) print("predict:") print(prediction)