深度学习之RNN,LSTM,GRU,CNN-GRU,ABLSTM(pytorch实现)

本文聚焦于循环神经网络(RNN)、门循环控制单元(GRU)和长短期记忆网络(LSTM)。介绍了各模型原理,给出PyTorch代码实现,分析RNN存在的梯度爆炸、计算效率低等问题。还提及CNN - GRU串行和并行网络,以及基于注意力机制的双向LSTM网络(ABLSTM)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本文介绍了,循环神经网络(RNN),长短期记忆网络(LSTM),门循环控制单元(GRU)。这里顺便说了一下GRU-CNN并行网络和ABLSTM(基于注意力机制的双向LSTM网络)

一、RNN循环神经网络

卷积神经网络(CNN)只是对一个图片,或者一个样本进行特征提取,完全没有考虑到时序信息,前几刻的信息和这一刻信息的关联性。RNN模型的提出就解决了这个问题。

1.RNN模型介绍

RNN模型最重要的贡献就是引入了隐藏层,隐状态存储的都是前一些时刻的相关特征信息,也是隐状态让模型有了记忆的能力。计算公式就类似于线性层。
在这里插入图片描述
t时刻的H是由t-1时刻的H(也就是记忆的之前的信息),加上Xt(当前时刻的特征)
在这里插入图片描述
在这里插入图片描述

2.RNN模型pytorch代码实现

一个简单的rnn模型用pytorch实现起来很简单,主要是用nn.RNN加一个nn.Linear方法就可以实现。
这里需要注意的是RNN的几个常用的参数:
self.rnn = nn.RNN(input_size=self.num_input,hidden_size=self.num_hiddens,num_layers=self.num_layers,bidirectional=False,batch_first=True)
首先我们一般输入训练数据的特征X一般的维度特征是3个维度(batch_size,num_steps,input_features)
input_size : 也就是指的input_features,特征的维度。
hidden_size :隐藏层的神经元h的个数
num_layers: 隐藏层的层数(默认每个隐藏层里的神经元个数全都是hidden_size)
nonlinearity:激活函数
dropout:是否应用dropout, 默认不使用,如若使用将其设置成一个0-1的数字即可
batch_first:我们一般的数据传入都是(batch_size,num_steps,input_features),也即是batch在第0维度,最前面,如果想这样直接扔进去进行RNN运算,batch_first需要设置为True,但是batch_first默认为False,需要我们利用permute函数,x=x.permute(1,0,2)把num_steps放在前面。
bidirectional:从前面的图片也可以看出,我们的RNN是单向,只能提取之前的信息,如果想提取双向的信息,这里需要把bidirectional设置为True。

说完了输入参数,在说一下输出。nn.RNN()的输出是一个元组(output, h_n),其中output是所有批次,每一个时间步的最后一层隐状态,h_n是所有批次最后一个时间步所有隐藏态。h_n是最后一个时间步的输出。当batch_first设置为True时,output的形状为(batch_size, seq_length, hidden_size),但是h_n的形状不会因为batch_first而改变为( num_layers * num_directions,batch_size, hidden_size)。

这两个信息,我们一般选择其一就可以提取需要的信息,经过线性层分类。

注意:代码中的倒数第二行out = self.fc(out.reshape(-1,out.shape[-1])) ,这里把out reshape 成(batch_size *num_steps,num_hiddens) ,这里的是每个以时间戳的特征x对应一个output,通过nn.Linear方法,输出最后变成(batch_size *num_steps,output_size)。
但是有时可能多个时间戳,才对应一个输出。(比如我最近在学习利用WIFI信号识别人体的动作,一个时间戳的信号肯定不可以对应一个输出,需要上百个时间戳才对应一个动作。)比如250个时间戳对应一个输出结果,我们只需要提取第250个时间戳里的最后一个对应的out,他包含之前全部时间戳的特征信息。
out= self.fc(out[:,-1,:]) #这里进行切片,对这个批次里的每个每组seq只提取最后一个时间步

class rnn_model(nn.Module):    #这里来具体的实现RNN类
    def __init__(self,voacb_size,num_hiddens,num_layers,**kwargs):  
        super(rnn_model,self).__init__(**kwargs)
        self.num_input = voacb_size
        self.num_output = voacb_size
        self.num_hiddens = num_hiddens
        self.num_layers = num_layers
        #这里注意batch_first 就是维度放在第一维的是batch_size ,这样的设置在forward里就不要变换维度了
        #但是需要注意batch_first = True 这个对隐藏态和细胞状态h和c的形状没有影响,其batch_size 都在第二维上
        self.rnn = nn.RNN(input_size=self.num_input,hidden_size=self.num_hiddens,num_layers=self.num_layers,bidirectional=False,batch_first=True)      #LSTM需要的参数,这里规定了输入的大小和规模
        self.fc = nn.Linear(self.num_hiddens,self.num_output)   #全连接层输出类别判断

    def forward(self,X):
        #需要注意下面的 out 是 h[-1]即为隐藏层的 整个序列的最后一层隐藏态
        out,_ = self.rnn(X) #这里模型会自动给h0初始化zero,这里返回了out,这里的out时每一个元素对应的隐藏态的最后一层,而占位符对应的是最后的隐藏态这里的形状是(batch_sizes,num_layers×hidden_size)
        #out,_ = self.lstm(X)
        #out的形状是 batch_size *num _steps *num_hiddens
        out = self.fc(out.reshape(-1,out.shape[-1]))    #括号里面的形状变成,(batch_size *num_steps,num_hiddens)
        return out

2.RNN模型一些问题

1.RNN模型容易发生梯度爆炸或者梯度消失,所有的数据公用Whx,这才进行反向传播计算梯度,会进行指数计算,导致梯度过大或者过小,导致模型训练效果不好。
为了应对这个问题,可以进行梯度裁剪。
梯度裁剪很容易理解
这里面的𝜃是给定的参数半径,下面的保证梯度的g不超过这𝜃,也就是把控制||g||<=𝜃
在这里插入图片描述
这个梯度裁剪用pytorch实现起来也很简单。
只需要每次l.backward()计算梯度后,使用nn.utils.clip_grad_norm_()方法,需要传入两个参数,一个是net的参数,一个是𝜃。

import torch
import torch.nn as nn
import torch.optim as optim

# 定义RNN模型
# 训练模型
num_epochs = 100
for epoch in range(num_epochs):
    # 前向传播
    outputs = model(x_train)
    loss = criterion(outputs
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值