当前位置: 首页 > java >正文

5月12日复盘-RNN

5月12日复盘

二、RNN 模型

1.先导

1.1 为什么需要循环神经网络 RNN

在这里插入图片描述

​ 上图是一幅全连接神经网络图,我们可以看到输入层-隐藏层-输出层,他们每一层之间是相互独立地,(框框里面代表同一层),每一次输入生成一个节点,同一层中每个节点之间又相互独立的话,那么我们每一次的输入其实跟前面的输入是没有关系地。这样在某一些任务中便不能很好的处理序列信息。

1.2 时序数据

在这里插入图片描述

对于不同类型的数据和任务,理解数据的顺序和前后关系的重要性是至关重要的。对于图像识别这样的任务来说,图片的前后顺序并不会影响识别结果,因为图片中的像素是独立且无序的。但是,当涉及到文本、股票、天气、语音等具有时间顺序或逻辑顺序的数据时,顺序的变化会对结果产生显著影响。

在这里插入图片描述

举例来说,对于一句话"我吃苹果",如果改变顺序为"苹果吃我",意思完全变了。这种语言的表达方式对顺序和词语的关联有着极高的敏感性。而对于股票和天气数据,先后顺序也具有重要意义,因为后续的数据可能受到之前数据的影响,如股票市场的走势或天气的变化趋势。

在文本理解方面,顺序也至关重要。例如,对于经典的古诗"床前明月光,疑是地上霜。举头望明月,低头思故乡。“,如果改变了诗句的顺序,那么整首诗的意境和情感也将随之改变,这就是所谓的"蝴蝶效应”。一个微小的改变可能会导致整体结果的巨大变化。

因此,在处理具有时间或逻辑顺序的数据时,我们必须考虑到顺序的重要性,并设计相应的模型来充分利用数据的前后关系,以获得更准确和有意义的结果。这也是循环神经网络(RNN)等模型在这些任务中被广泛使用的原因之一,因为它们能够捕捉到数据的顺序信息,并根据前面的输入来预测后续的输出。

2. RNN 原理

2.1 概述

循环神经网络(Recurrent Neural Network,RNN)是一种神经网络结构,专门用于处理序列数据。与传统的前馈神经网络不同,RNN 在内部具有反馈连接,允许信息在网络内部传递。这种结构使得 RNN 能够对序列数据的历史信息进行建模,并在一定程度上具有记忆能力。

在自然语言处理领域,RNN 被广泛应用于语言建模、机器翻译、情感分析等任务。通过捕捉单词之间的上下文信息,RNN 能够更好地理解语言的含义和结构。

同时,RNN 也在时间序列预测领域发挥着重要作用,比如股票价格预测、天气预测等。通过学习序列数据的模式和趋势,RNN 能够提供有用的预测信息,帮助人们做出决策。

然而,传统的 RNN 存在一些问题,例如难以处理长期依赖关系、梯度消失或梯度爆炸等。为了解决这些问题,出现了一些改进的 RNN 变种,如长短期记忆网络(LSTM)和门控循环单元(GRU)。这些变种结构能够更有效地捕捉长期依赖关系,并且在训练过程中更加稳定。

2.2 模型架构

循环神经网络(RNN)是深度学习中的一种架构,专门设计来处理序列数据,例如时间序列数据或自然语言文本。RNN的核心特征在于它能够在处理序列的每个元素时保留一个内部状态(记忆),这个内部状态能够捕捉到之前元素的信息。这种设计使得RNN特别适合处理那些当前输出依赖于之前信息的任务。

常见的RNN架构如下图两种:

在这里插入图片描述

左图可以理解为,先将每一层的网络简化,再将网络旋转90度得到的简化图

而右边两种类型,可以理解为,再将左图继续进行简化

在RNN的经典架构中,网络通过一个特殊的循环结构将信息从一个处理步骤传递到下一个。这个循环结构通常被称为“隐藏层状态”或简单地称为“隐藏状态”。隐藏状态是RNN的记忆部分,它能够捕获并存储关于已处理序列元素的信息。

当RNN处理一个序列时,它会在每个时间步接受一个输入,并更新其隐藏状态。这个更新过程依赖于当前的输入和之前的隐藏状态,从而使得网络能够“记住”并利用过去的信息。这个过程可以通过以下数学公式简化表达:
s t = f ( U ⋅ x t + W ⋅ s t − 1 ) s_t=f(\mathbf{U}·x_t+\mathbf{W}·s_{t-1}) st=f(Uxt+Wst1)

在这个公式中, S t {S}_{t} St表示在时间步t的隐藏状态, x t {x}_{t} xt是当前时间步的输入,UW分别是输入到隐藏状态和隐藏状态到隐藏状态的权重矩阵。函数f通常是一个非线性函数,如tanh或ReLU,用于引入非线性特性并帮助网络学习复杂的模式。

RNN的输出在每个时间步也可以计算出来,这依赖于当前的隐藏状态:

  • O t {O}_{t} Ot = g(V s t {s}_{t} st)

其中 O t {O}_{t} Ot是时间步t的输出,V是从隐藏状态到输出层的权重矩阵,g是另一个非线性函数,常用于输出层。

RNN也是传统的神经网络架构,但是他里面包含了一个“盒子”,这个盒子里记录了输入时网络的状态,在下一次输入时,必须要考虑“盒子”。随着不断的输入,盒子里也会更新,那么这个盒子就是“隐藏态”

在这里插入图片描述

假设为一个包含三个单词的句子,将模型展开,即为一个三层的网络结构

可以理解为, x t − 1 {x}_{t-1} xt1为第一个词, x t {x}_{t} xt为第二个词, x t + 1 {x}_{t+1} xt+1为第三个词

图中参数含义:

  • x t {x}_{t} xt表示第t步的输入。比如 x 1 {x}_{1} x1为第二个词的词向量( x 0 {x}_{0} x0为第一个词);

  • H t {H}_{t} Ht为隐藏层的第t步的状态,它是网络的记忆单元。

    • H t {H}_{t} Ht根据当前输入层的输出与上一时刻隐藏层的状态 H t − 1 {H}_{t-1} Ht1进行计算,如下所示。
      H t = f ( U ⋅ x t + W ⋅ H t − 1 ) H_t=f(\mathbf{U}·x_t+\mathbf{W}·H_{t-1}) Ht=f(Uxt+WHt1)

    • 其中,U是输入层的连接矩阵,W是上一时刻隐含层到下一时刻隐含层的权重矩阵,f(·)一般是非线性的激活函数,如tanh或ReLU。

  • O t {O}_{t} Ot是第t步的输出。输出层是全连接层,即它的每个节点和隐含层的每个节点都互相连接,V是输出层的连接矩阵,g(·)一是激活函数。

    • o t = g ( V ⋅ s t ) o_t=g(\mathbf{V}·s_t) ot=g(Vst)

    • 带入可以得到
      H t = f ( W i n X t + W s H t − 1 + b t ) = f ( W i n X t + W s f ( W i n X t − 1 + W s H t − 2 + b t − 1 ) + b t ) = f ( W i n X t + W s f ( W i n X t − 1 + W s f ( W i n X t − 2 + W s H t − 3 + b t − 2 ) + b t − 1 ) + b t ) = f ( W i n X t + W s f ( W i n X t − 1 + W s f ( W i n X t − 2 + W s ( . . . ) ) + b t − 2 ) + b t − 1 ) + b t ) \begin{align} H_t&=f(\mathbf{W}_{in}X_t+\mathbf{W}_{s}H_{t-1}+b_t)\\ &=f(\mathbf{W}_{in}X_t+\mathbf{W}_{s}f(\mathbf{W}_{in}X_{t-1}+\mathbf{W}_{s}H_{t-2}+b_{t-1})+b_{t})\\ &=f(\mathbf{W}_{in}X_t+\mathbf{W}_{s}f(\mathbf{W}_{in}X_{t-1}+\mathbf{W}_{s}f(\mathbf{W}_{in}X_{t-2}+\mathbf{W}_{s}H_{t-3}+b_{t-2})+b_{t-1})+b_t)\\ &=f(\mathbf{W}_{in}X_t+\mathbf{W}_{s}f(\mathbf{W}_{in}X_{t-1}+\mathbf{W}_{s}f(\mathbf{W}_{in}X_{t-2}+\mathbf{W}_{s}(...))+b_{t-2})+b_{t-1})+b_t) \end{align} Ht=f(WinXt+WsHt1+bt)=f(WinXt+Wsf(WinXt1+WsHt2+bt1)+bt)=f(WinXt+Wsf(WinXt1+Wsf(WinXt2+WsHt3+bt2)+bt1)+bt)=f(WinXt+Wsf(WinXt1+Wsf(WinXt2+Ws(...))+bt2)+bt1)+bt)

通过这种逐步处理序列并在每一步更新隐藏状态的方式,RNN能够在其内部维持一个随时间变化的“记忆”。这使得它能够对之前序列元素的信息做出响应,并据此影响后续的输出。这种特性对于诸如语言模型、文本生成、语音识别等许多序列处理任务至关重要。

2.3 RNN的内部结构

在这里插入图片描述

在这里插入图片描述

2.4 RNN模型输入输出关系对应模式

在这里插入图片描述

通过改变RNN的结构,即调整其输入和输出的数量和形式,可以让它适应各种不同的任务。以下是几种常见的RNN结构调整示例,以及它们各自适用的任务类型:

  1. 一对多(One-to-Many):这种结构的RNN接受单个输入并产生一系列输出。这种模式常用于“看图说话”的任务,即给定一张图片(单个输入),RNN生成一段描述该图片的文本(一系列输出)。在这种情况下,RNN的结构被调整为首先对输入图片进行编码,然后根据这个编码连续生成文本序列中的词语。
  2. 多对一(Many-to-One):与一对多相反,多对一的RNN结构接受一系列输入并产生单个输出。这种结构适用于如文本分类和情感分析等任务,其中模型需要阅读和理解整个文本(一系列输入),然后决定文本属于哪个类别(单个输出)。在图片生成的上下文中,这种结构可以通过分析一系列的特征或指令来生成单个图片输出。
  3. 多对多(Many-to-Many):这种结构的RNN既接受一系列输入,也产生一系列输出。这在需要输入和输出均为序列的任务中非常有用,例如机器翻译,其中模型需要读取一个语言的文本(一系列输入),然后生成另一种语言的对应文本(一系列输出)。另一个例子是小说生成,其中RNN可以基于给定的开头或主题(一系列输入),连续生成故事的后续内容(一系列输出)。

3. RNN 代码实现

重要参数含义

  1. Batch Size (批量大小):

    • Batch size指的是在一次前向传播或反向传播过程中同时处理的样本数量。
    • 例如,在文本处理中,如果一批数据包含100个句子,那么batch size就是100。
  2. Sequence Length (序列长度):

    • Sequence length是指输入数据中每个样本的连续时间步(或词、字符)的数量。
    • 例如,在一个句子级别的任务中,一个句子可能包含10个单词,那么序列长度就是10。
  3. Input Size (输入大小):

    • Input size是指每个时间步输入向量的特征维度。
    • 在处理文本时,如果每个词都被表示为一个固定维度的向量,那么input size就是这个词向量的维度。
    • 如在情感分析任务中,每个词可能被嵌入为一个100维的向量,那么input size就是100。
  4. Hidden Size (隐藏层大小):

    • Hidden size是指RNN单元内部隐藏状态(Hidden State)的维度。
    • 在每个时间步,RNN都会根据当前输入和上一时间步的隐藏状态来计算新的隐藏状态,新隐藏状态的维度就是hidden size。
    • 例如,如果我们设置hidden size为256,那么每个时间步产生的隐藏状态就是一个256维的向量。
    • 根据实验和模型复杂度的要求自由选择隐藏层大小,它并不是通过特定计算得出的数值。
    • 隐藏层大小的选择会影响到模型的学习能力和表示能力,同时也影响到模型的计算资源消耗。
    • 实践中,较小的隐藏层大小可能会限制模型的表达能力,而过大的隐藏层大小则可能导致过拟合、训练时间增加等问题。
    • 在决定隐藏层大小时,通常需要结合具体任务的特点、数据集规模、计算资源等因素进行合理选择,并通过交叉验证、网格搜索等方式进行超参数调优,以找到最优的隐藏层大小以及其他超参数组合。
  5. Output Size (输出大小):

    • Output size通常与特定任务相关。

    • 对于一般的RNN,每个时间步的输出大小与hidden size相同,即输出也是一个隐藏状态维度的向量。

    • 在分类任务中,最后一层可能通过一个全连接层映射到类别数目,这时最后一个时间步的输出大小可能是类别数目的维度。

    • 如果是多层或双向RNN,输出也可能经过额外的处理(如拼接、池化等),最终的输出大小会根据具体应用需求来确定。

    • 在最简单的单向单层循环神经网络(RNN)中,输出大小(output size)的计算通常比较直接:

      • 如果目的是为了获取每个时间步(time step)的隐藏状态表示,并且不进行额外的转换操作,那么每个时间步的输出大小(output size)就等于您设定的隐藏层大小(hidden size)。

      例如,如果设置的隐藏层大小(hidden size)是256,那么在每个时间步,RNN的输出也将是一个256维的向量。

      • 如果在RNN之后添加了其他层(如全连接层或分类层)来进行进一步的处理,比如进行分类任务,那么输出大小取决于这些后续层的设计。例如,如果您接下来是一个Softmax层用于做多分类,且类别数是10,则输出大小将会是10,表示每个样本的概率分布。

      • 如果是在做序列到序列(Sequence-to-Sequence)的任务,比如机器翻译,最后的时间步的隐藏状态通常会通过一个线性层映射到目标词汇表大小,这样输出大小就会是目标词汇表的大小。

具体的单层单向RNN示例来说明维度变换过程:

假设正在处理一个文本分类任务,每个单词已经被嵌入为一个100维的向量,我们的序列长度(sequence length)是50(即最长句子有50个单词),批量大小(batch size)是32(一次处理32个句子),我们设定的隐藏层大小(hidden size)是128。

  1. 输入维度(input size): 每个时间步(每个单词)的输入向量维度是100,所以整个输入张量的维度是 (batch size, sequence length, input size),即 (32, 50, 100)

  2. 隐藏层计算: RNN会对每个时间步的输入进行处理,并基于上一时间步的隐藏状态生成当前时间步的隐藏状态。隐藏状态的维度由我们设定,这里是128维,所以每个时间步的隐藏状态和输出的维度都是 (batch size, hidden size),即 (32, 128)

  3. 输出维度(output size): 因为这里我们假设没有在RNN后添加额外的层(例如分类层),所以每个时间步的输出大小就等于隐藏层大小,也就是128维。但是,由于输出是针对每一个时间步的,所以整个输出序列的维度为 (batch size, sequence length, hidden size),即 (32, 50, 128)

​ 如果后续需要进行分类,比如这是一个二分类问题,我们会把最后一个时间步的隐藏状态(128维)通过一个全连接层(Dense Layer)映射到类别数目的维度,如2维,此时输出大小将变为 (32, 2),表示32个样本的二维概率分布。

3.1 原生代码

import numpy as np
# 原始数据 一句话 3个单词 每个2个维度
x = np.random.rand(3,2)# RNN参数
input_size = 2
hidden_size = 3
# 词表大小是输出维度 6
output_size = 6# 初始化权重矩阵
# 输入权重矩阵
Wx = np.random.rand(input_size, hidden_size)
# 隐藏权重矩阵
Wh = np.random.rand(hidden_size, hidden_size)
# 输出权重矩阵
Wout = np.random.rand(hidden_size, output_size)# 偏置
bh = np.zeros((hidden_size,))
bout = np.zeros((output_size,))# 激活函数
def tanh(x):return np.tanh(x)# 初始化隐藏状态
H_prev = np.zeros((hidden_size,))# 前向传播
# 时间步1 第一个单词
x1 = x[0,:]
H1 = tanh(np.dot(x1,Wx) + np.dot(H_prev,Wh)+bh)
Y = np.dot(H1,Wout)+bout# 时间步2 第二个单词
x2 = x[1,:]
H2 = tanh(np.dot(x2,Wx) + np.dot(H1,Wh)+bh)
Y2 = np.dot(H2,Wout)+bout# 时间步3 第三个单词
x3 = x[2,:]
H3 = tanh(np.dot(x3,Wx) + np.dot(H2,Wh)+bh)
Y3 = np.dot(H3,Wout)+bout# 输出结果
print("时间步1的隐藏状态:",H1)
print("时间步2的隐藏状态:",H2)
print("时间步3的隐藏状态:",H3)
print("时间步1的输出结果:",Y)
print("时间步2的输出结果:",Y2)
print("时间步3的输出结果:",Y3)

np.dot 是 NumPy 库中用于计算两个数组的点积(dot product)的函数。它的行为取决于输入数组的维度:

  1. 对于二维数组np.dot 计算的是矩阵乘积。这意味着如果 A 是一个 m×n 矩阵,B 是一个 n×p 矩阵,那么 np.dot(A, B) 的结果将是一个 m×p 矩阵,其中每个元素由 A 中的行与 B 中的列之间的点积计算得出。

  2. 对于一维数组np.dot 计算的是向量的内积(inner product)。即,如果 a 和 b 是长度相同的两个一维数组,那么 np.dot(a, b) 就是 a 和 b 对应元素相乘之后的结果之和。

  3. A 是一维数组(shape 为 (n,)),B 是二维数组(shape 为 (n, m)),相当于:

    👉 将 A 视为一个行向量(1×n),与矩阵 B(n×m)相乘,得到一个 1×m 的结果(即 shape 为 (m,) 的一维数组)。

3.2 基于 RNNCell 代码实现

import torch
from torch import nn# 创建数据 词嵌入之后的维度
x = torch.randn(10,6,5)# 模型参数
hidden_size = 8# 利用rnncell创建RNN模型
class RNN(nn.Module):def __init__(self,batch_size,hidden_size,batch_flag=True):super(RNN, self).__init__()# nn.RNNCell 返回的是隐藏状态 参数 第一个参数是输入维度(词嵌入之后的维度) 第二个是隐藏状态维度self.rnncell = nn.RNNCell(input_size=5, hidden_size=hidden_size)# self.fc = nn.Linear(20, 10)self.batch_size = batch_sizeself.hidden_size = hidden_size# 标识符 类型是布尔类型self.batch_flag = batch_flag# 初始化隐藏状态def intialize_hidden(self):self.h_prev = torch.zeros(self.batch_size, self.hidden_size)return self.h_prevdef forward(self, x,  h_prev=None):"""注意传递进来的数据格式 一般经过词嵌入格式(B,S,D)但是在rnn中 需要数据格式 (S,B,D)词数开头:param x: 如果说self.batch_flag是ture就代表传入的数据格式是(B,S,D):return:"""if self.batch_flag:batch_size,  seq_len, dim = x.size(0), x.size(1), x.size(2)# 数据处理为(S,B,D)x = x.permute(1, 0, 2)else:batch_size,  seq_len, dim = x.size(0), x.size(1), x.size(2)hiddens = []if h_prev is None:init_hidden = self.intialize_hidden()else:init_hidden = h_prev# 循环遍历每个时间步for t in range(seq_len):# 输入数据x_t = x[t]# 在t时刻获取隐藏状态 封装的是H1 = tanh(np.dot(x1,Wx) + np.dot(H_prev,Wh)+bh)# 初始化隐藏状态可以省略 init_hiddenhidden_t = self.rnncell(x_t, init_hidden)hiddens.append(hidden_t)# 所有时间步堆叠成新的张量output = torch.stack(hiddens)# 处理数据if self.batch_flag:output = output.permute(1,0,2)return outputmodel = RNN(10,hidden_size)
output = model(x)
print(output.shape)

nn.RNNCell 本质上只返回隐藏状态,它没有单独的输出结果。一般在 RNN 中,隐藏状态既可以被视为输出,也可以通过一个线性层将隐藏状态转化为实际的输出。

3.3 基于 pytorch API 代码实现

官方代码细节:https://pytorch.org/docs/stable/_modules/torch/nn/modules/rnn.html#RNN

官方文档解释:https://pytorch.org/docs/stable/generated/torch.nn.RNN.html#rnn

import torch
from torch import nn# 初始数据
x = torch.randn(10, 6, 5)# 模型设置参数
hidden_size = 8
batch_size = 10
sen_len = 6
input_size = 5# 初始化隐藏状态 (1, batch_size, hidden_size)
h_prev = torch.zeros(batch_size, hidden_size)# RNN调用
rnn = nn.RNN(input_size, hidden_size, batch_first=True)# h_prev 可以不传
out, h_last = rnn(x,h_prev.unsqueeze(0))
print(out.shape)
print(h_last.shape)
  • input 是一个形状为 (batch_size, sequence_length, input_size) 的张量,表示一批包含 T 个时间步长的序列,每个时间步长的输入特征维度为 input_size
  • h_prev 是所有序列共享的初始隐含状态,形状为 (batch_size, hidden_size)
  • h_prev.unsqueeze(0)h_prev 的批量维度增加一层,因为PyTorch RNN期望隐含状态作为一个元组 (num_layers, batch_size, hidden_size),在这里我们只有一个隐藏层,所以增加了一维使得形状变为 (1, batch_size, hidden_size)
  • rnn(input, h_prev.unsqueeze(0)) 执行RNN的前向传播,得到的 rnn_output 是整个序列的输出结果,形状为 (batch_size, sequence_length, hidden_size),而 state_final 是最后一个时间步的隐含状态,形状为 (num_layers, batch_size, hidden_size)
  • 两个返回值 rnn_outputstate_final 代表着循环神经网络在当前时间步的输出和最终的隐藏状态。
    • rnn_output:代表当前时间步的 RNN 输出。对于很多序列模型而言,每个时间步都会有一个输出。这个输出可能会被用于下一时间步的计算,或者作为模型的最终输出。
    • state_final:代表 RNN 模型在最后一个时间步的隐藏状态。这个隐藏状态通常被认为是对整个序列的编码或总结,它可能会被用于某些任务的最终预测或输出。
3.3.1 单向、单层RNN
  1. 定义一个单层循环神经网络(RNN)实例:

    signle_rnn = nn.RNN(4, 3, 1, batch_first=True)
    

    这行代码创建了一个RNN层,其参数含义如下:

    • 4 表示输入序列的特征维度(feature size),即每个时间步的输入向量长度为4。
    • 3 表示隐藏状态(hidden state)的维度,即RNN单元内部记忆的向量长度为3。
    • 1 表示RNN层的数量,这里仅为单层。
    • batch_first=True 指定输入张量的第一个维度代表批次(batch),第二个维度代表时间步(sequence length),这对于处理批次数据时更容易理解。
  2. 创建输入数据张量:

    input = torch.randn(1, 2, 4)
    

    这行代码生成了一个随机张量作为RNN的输入,它的形状为 (batch_size, sequence_length, feature_size),具体到这里的值是:

    • 1 表示批大小(batch size),即本次输入的数据样本数量。
    • 2 表示序列长度(sequence length),即每个样本的输入序列包含两个时间步。
    • 4 是每个时间步输入向量的特征维度,与RNN层设置一致。
  3. 对输入数据进行前向传播:

    output, h_n = signle_rnn(input)
    

    这行代码将之前创建的随机输入数据送入RNN层进行前向计算。执行后得到两个输出:

    • output 是经过RNN处理后的输出序列,其形状通常为 (batch_size, sequence_length, num_directions * hidden_size)。在这个例子中,因为没有指定双向RNN,所以 num_directions=1。因此,output 的尺寸将是 (1, 2, 3),对应每个批次中的每个时间步输出一个维度为3的向量。
    • h_n 是最后一个时间步的隐藏状态(hidden state),它通常是最终时间步的隐藏状态或者是所有时间步隐藏状态的某种聚合(取决于RNN类型)。在这里,h_n 的形状是 (num_layers * num_directions, batch_size, hidden_size),但由于只有一层并且是无方向的RNN,所以形状会简化为 (1, 1, 3),即单一隐藏状态向量。这个隐藏状态可以用于下个时间步的预测或者作为整个序列的编码。
3.3.2 双向、单层RNN

双向单层RNN(Recurrent Neural Network)是一种特殊类型的循环神经网络,它能够在两个方向上处理序列数据,即正向和反向。这使得网络在预测当前输出时,能够同时考虑到输入序列中当前元素之前的信息和之后的信息。双向单层RNN由两个独立的单层RNN组成,一个负责处理正向序列(从开始到结束),另一个负责处理反向序列(从结束到开始)。

主要特点

  1. 双向处理: 最显著的特点是双向结构,使得模型能够同时学习到序列中某一点前后的上下文信息,这对于很多序列任务来说是非常有价值的,比如自然语言处理中的文本理解、语音识别等。
  2. 单层结构: “单层”指的是在每个方向上,网络结构只有一层RNN,即每个方向上只有一层循环单元(如LSTM单元或GRU单元)。虽然是单层的,但由于其双向特性,实际上每个时间点都有两个循环单元对信息进行处理。

  1. 定义一个双向循环神经网络(Bi-RNN)实例:

    bi_rnn = nn.RNN(4, 3, 1, batch_first=True, bidirectional=True)
    

    这行代码创建了一个具有双向连接的RNN层,参数含义如下:

    • 4 依然是输入序列的特征维度(每个时间步长的输入向量有4个元素)。
    • 3 表示的是单向隐藏状态(hidden state)的维度;由于设置了 bidirectional=True,实际上模型会同时维护正向和反向两个隐藏状态,因此总的隐藏状态维度将是 2 * 3
    • 1 表示RNN层的数量,这里也是单层。
    • batch_first=True 保持输入张量的批量维度在最前面。
    • bidirectional=True 指定该RNN为双向的,这意味着对于每个时间步,除了向前传递的信息外,还会考虑向后传递的信息,从而能够捕捉序列中前后依赖关系。
  2. 创建输入数据张量:

    input = torch.randn(1, 2, 4)
    

    这行代码生成了一个随机张量作为双向RNN的输入,其形状仍为 (batch_size, sequence_length, feature_size),即 (1, 2, 4)。这表示有一个样本(batch_size=1),序列长度为2,每个时间步有4个特征。

  3. 对输入数据进行前向传播:

    output, h_n = bi_rnn(input)
    

    将随机输入数据传入双向RNN进行前向计算。执行后获取的结果与单向RNN有所不同:

    • output 现在包含了正向和反向两个方向的输出,其形状为 (batch_size, sequence_length, num_directions * hidden_size),在本例中为 (1, 2, 2 * 3),即每个时间步有两个方向上的隐藏状态输出拼接而成的向量。

    • h_n 包含了最后时间步的正向和反向隐藏状态,形状为 (num_layers * num_directions, batch_size, hidden_size),在本例中实际为 (2, 1, 3),分别对应正向和反向隐藏状态各一个。每个隐藏状态向量都是相应方向上整个序列信息的汇总。

http://www.xdnf.cn/news/5704.html

相关文章:

  • python打卡day23@浙大疏锦行
  • 【数据结构】双链表
  • 关于读写锁的一些理解
  • C++的构造函数和析构函数
  • 六、快速启动框架:SpringBoot3实战
  • RDB和AOF的区别
  • KUKA机器人中断编程2—中断相关的指令
  • 传导发射中的模拟手
  • P1460 [USACO2.1] 健康的荷斯坦奶牛 Healthy Holsteins
  • 【人工智能-agent】--Dify中MCP工具存数据到MySQL
  • 数据库实验报告 系统E-R图设计 2
  • [Git]ssh下用Tortoisegit每次提交都要输密码
  • el-table滚动条,都悬浮在页面的底层显示
  • 区块链技术构建电子发票平台“税链”
  • 2025年5月9日
  • CSPM-3 与 CSPM-4:项目管理认证的进阶之路
  • 【AutoGen革命】多智能体协作系统的架构设计与工程实践
  • 什么是数据集市(Data Mart)?
  • 链表面试题7之相交链表
  • Git日志信息
  • MyTinySTL
  • 【idea】快捷键ctrl+shift+F(Find in files)不起作用
  • C++.Windows图形
  • 养生:开启健康生活的全新篇章
  • C++类和对象--中阶
  • js 画立方体软件开发日记2
  • QuickList
  • Docker编排工具详解:Docker Compose与Docker Swarm
  • 08.webgl_buffergeometry_attributes_none ,three官方示例+编辑器+AI快速学习
  • 电子工程领域常见的缩略语及其对应的中文和英文释义