对LSTM的简单理解

LSTM,长短期记忆(Long short-term memory是一种特殊的RNN。需要上下文的依赖信息,但不希望这些依赖信息过长(过大 计算图会越来越复杂),所以叫长的短时记忆。相比普通的RNN,LSTM在长的序列中有更好的表现。

在学习LSTM之前我们需要了解什么是RNN循环神经网络?

RNN

循环神经网络和传统的卷积神经网络不同,CNN只能单独的取处理一个个的输入,前一个输入和后一个输入是完全没有关系的。但是,有时候我们需要能处理连续的序列数据。即前面的输入和后面的输入是有关系的。比如常见的NLP自然语言处理(上下文是有语义联系的)、视频处理(需要把每一帧连接起来)等等。

RNN结构如下:

一个简单的循环神经网络由一个输入层、一个隐藏层和一个输出层组成:

在这里插入图片描述
将序列展开来 就是下面的结构:在这里插入图片描述

O t = g ( V ∗ S t ) O_t=g(V * S_t) Ot=g(VSt) 输出层是一个全连接层(输出的每个节点都和隐藏层的每个节点相连)
S t = f ( U ∗ X t + W ∗ S t − 1 ) S_t=f(U * X_t + W * S_{t-1}) St=f(UXt+WSt1) 隐藏层是一个循环层(隐藏层S会一直循环传播更新下去)
V是隐藏层到输出层的权重矩阵。
U是输入层到隐藏层的权重矩阵
o表示输出层的值;
gf都是激活函数;

隐藏层的值S不仅取决于当前输入x,还取决于上一时刻隐藏层的值 S t − 1 S_{t-1} St1W是上一时刻隐藏层的值作为当前输入的权重矩阵

循环神经网络的输出值O是受当前输入 x t x_t xt和隐藏层的值 S t S_t St 影响;而隐藏层又是受前面前面每个时刻输入值 x 1 x_1 x1, x 2 x_2 x2 x t − 1 x_{t-1} xt1影响的,所以网络输出结果受多个连续的输入值影响。

但是对于长序列,这样循环下去就会造成RNN在结构上很深,会产生梯度消失和梯度爆炸问题。LSTM是升级后的RNN,在梯度消失问题上表现好。

LSTM

LSTM主要是为了解决长序列训练过程中的梯度消失和梯度爆炸问题。
注意!!!这里的梯度消失/梯度爆炸和前面我们间的深层 CNN网络堆叠导致的梯度消失/梯度爆炸的含义不一样。

CNN 中不同的层,有不同的参数w,各是各的梯度;而 RNN 中同样的权重在各个时刻共享,最终的梯度 G= 各个时间段的梯度 g t g_t gt 的和。RNN 中总的梯度是不会消失的。只是随着循环的不断进行,梯度越传越弱,远距离的梯度消失,但近距离的梯度不会消失,所以梯度之和不会消失。
RNN 所谓梯度消失的真正含义是,梯度被近距离梯度主导,导致模型难以学到远距离的依赖关系。(可以看作随着时间序列的推进,模型会逐渐遗忘之前学到的依赖)所以不适合处理长序列。

所以在LSTM中再增加一个单元状态,即c我叫它长时记忆单元,让它来保存长期的状态,那么结构就变成:
在这里插入图片描述
在某时刻t算出来的梯度信息存储在c里后,然后让它把梯度一路带下去而无任何损耗,这样就能解决远距离依赖的问题了。
但是往长时记忆单元C添加每一个时刻的信息,这样效率频率肯定很低,因此我们让模型只记忆该记的信息。所以增加控制开关来选择对新信息选择记还是不记,以此来控制长时记忆单元c.

三个控制开关

LSTM通过设计门限结构解决长期依赖问题。三个开关分别用三个实现。门实际上就是一层全连接层,它的输入是一个向量,门上使用 s i g m o d sigmod sigmod激活函数输出是一个0-1之间的实数向量。这样就可以定义开关的打开程度,从而有了去除或添加信息的能力,让模型只记忆该记的信息。
g ( x ) = σ ( W x + b ) g(x)=\sigma(Wx+b) g(x)=σ(Wx+b)

W是门的权重向量,b是偏置项, σ \sigma σ是sigmoid函数
门的使用:用门的输出向量按元素乘 ∘ \circ 以要控制的那个向量。当门输出为0时,任何向量与之相乘都会得到0向量,不能通过;输出为1时,可以通过。sigmoid函数的值域是(0,1),所以门的状态都是半开半闭的。

三个门的输入都是当前时刻的输入 x t x_t xt和上一时刻的输出 h t − 1 h_{t-1} ht1。输出都是0-1的向量。

输入门(input gate)/更新门 i t = σ ( W i ⋅ [ h t − 1 , x t ] + b i ) i_t = \sigma(W_i \cdot [h_{t-1},x_t] + b_i) it=σ(Wi[ht1,xt]+bi)
它决定了当前时刻网络的输入有多少保存到单元状态,即实际要输入到神经元状态的信息

遗忘门(forget gate):
f t = σ ( W i ⋅ [ h t − 1 , x t ] + b f ) f_t = \sigma(W_i\cdot [h_{t-1},x_t] + b_f) ft=σ(Wi[ht1,xt]+bf)
决定了上一时刻的单元状态有多少保留到当前时刻,也就是哪些信息被舍去(遗忘)

由于遗忘门的控制,它可以记忆很久之前的信息,由于输入门的控制,又可以避免保存太多无关紧要的内容进入记忆,而LSTM最终的输出,是由输出门和单元状态共同决定。

输出门(output gate): 控制单元状态c有多少输出到LSTM的当前输出值。

o t = σ ( W o ⋅ [ h t − 1 , x t ] + b o ) o_t = \sigma(W_o\cdot [h_{t-1},x_t] + b_o) ot=σ(Wo[ht1,xt]+bo)
h t h_t ht= o t o_t ot ∗ t a n h ( c t ) * tanh(c_t) tanh(ct)
网络隐含层状态 c t c_t ct再通过一个 t a n h tanh tanh层,范围为(-1,1),对记忆单元中的信息产生候选输出,然后与输出们 o t o_t ot相乘。
最终输出

下面用LSTM模型简单实现mnist手写数据集分类

import torch
from torch import nn
import torchvision.datasets as dsets
import torchvision.transforms as transforms
import torch.optim
import warnings

# GPU or CPU

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 利用过滤器来忽略warnings
warnings.filterwarnings('ignore')


# 超参
EPOCH = 1
BATCH_SIZE = 64
TIME_STEP = 28  # image height
INPUT_SIZE = 28  # 输入的大小 mnist数据集是28*28 piex
LR = 0.01
DOWNLOAD_MNIST = False  # set to True if haven't download the data

# Mnist 数据集
train_data = dsets.MNIST(
    root='./mnist/',
    train=True,
    transform=transforms.ToTensor(),
    download=DOWNLOAD_MNIST,
)

# 批处理
train_loader = torch.utils.data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
test_data = dsets.MNIST(root='./mnist/', train=False, transform=transforms.ToTensor())
test_x = test_data.test_data.type(torch.FloatTensor)[:2000] / 255.  # shape (2000, 28, 28) value in range(0,1)
test_y = test_data.test_labels.numpy()[:2000]  # 转化成 numpy array


class Lstm(nn.Module):


    # 初始化 LSTM model in torch.nn模块中的lstm的参数

    def __init__(self, input_size=28, hidden_size=64, output_size=10, num_layers=1):
        super().__init__()

        self.lstm = nn.LSTM(
            input_size=input_size,
            hidden_size=hidden_size,   # 隐藏层节点数量
            num_layers=num_layers,     # recurrent layer的数量,默认等于1
            batch_first=True         # 官方不推荐把batch放在第一维,此时输入输出的各个维度含义为 (seq_length,batch,feature)
        )  #
        self.out = nn.Linear(64, output_size)


    def forward(self, _x):
        x, (h_n, h_c) = self.lstm(_x, None)  # _x is input, size (seq_len, batch, input_size)
        # print(x.shape)
        # print(x[:, -1, :])
        out = self.out(x[:, -1, :])
        return out


net = Lstm()

optimizer = torch.optim.Adam(net.parameters(), lr=LR)
loss_func = nn.CrossEntropyLoss()  # 交叉熵

# training and testing
for epoch in range(EPOCH):
    for step, (train_x, train_y) in enumerate(train_loader):  # gives batch data
        train_x = train_x.view(-1, 28, 28)  # reshape x to (batch, time_step, input_size)

        output = net(train_x)  # rnn output
        loss = loss_func(output, train_y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()  # 更新参数

        if step % 50 == 0:
            test_output = net(test_x)  # (samples, time_step, input_size)
            pred_y = torch.max(test_output, 1)[1].data.numpy()
            accuracy = float((pred_y == test_y).astype(int).sum()) / float(test_y.size)
            print('Epoch: ', epoch, '| train loss: %.4f' % loss.data.numpy(), '| test accuracy: %.2f' % accuracy)

# 打印预测结果
test_output = Lstm(test_x[:10].view(-1, 28, 28))
pred_y = torch.max(test_output, 1)[1].data.numpy()
print(pred_y, 'pred_label')
print(test_y[:10], 'real_label'))
 

打印精度如下:
在这里插入图片描述
Pytorch里面有关于LSTM的封装
torch.nn.LSTM(*args, **kargs)
参数:
input_size: 输入数据的特征数
hidden_size:隐藏层状态的特征数
num_layers: 循环层数
bias :是否使用偏置项,默认True
batch_first :如果为true,输入和输出的tensor应该为(batch,seq,feature)的形状
dropout: 非零 就在除输出层外的其他层添加Dropout层
bidirectional: True表示双向的LSTM,默认False

#torch.nn.LSTM(*args, **kargs)

net = nn.LSTM(10,20,2) #输入的特征数为10,隐含层20 2个循环层
input = Variable(torch.randn(5,3,10))  #输入
h0 = Variable(torch.randn(2,3,20)) #隐含层状态
c0 = Variable(torch.randn(2,3,20)) #记忆单元状态
output, ht = net(input, (h0,c0)) #输出是最后一层的输出特征的tensor ,还有隐含层状态

在这里插入图片描述

LSTM的变体

  • GRU :
    将遗忘,门和输入门整合成一个更新门;将单元状态 c t c_t ct和隐藏状态 h t h_t ht合并.
    GRU比LSTM少了一个门,使得其训练速度更快,更方便构建更复杂的网络

在这里插入图片描述

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值