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

【NLP】循环神经网络--RNN学习.day3

一.初步认识RNN

循环神经网络(Recurrent Neural Network, RNN)是一种用于处理序列数据的深度学习模型。与传统的静态神经网络相比,RNN 可以有效处理输入数据的时间序列特性。这使得 RNN 在处理自然语言处理(NLP)、时间序列预测、音频处理等任务时非常有效。以下是对 RNN 的详细解释。

1. 基本结构

1.1. 结构图示:

在传统的神经网络中,信息是单向流动的,而 RNN 具有一个循环结构,允许信息在时间步骤之间进行传递。下面是 RNN 的基本结构:

  x1 → [ H1 ] 
         ↑
  x2 → [ H2 ]
         ↑
  x3 → [ H3 ]
          ...
  • 输入 ( x t ) (x_t) (xt): 输入序列的第 (t) 个元素,如文本中的每个单词或时间序列数据中的每个点。
  • 隐藏状态 ( H t ) (H_t) (Ht): RNN 的隐藏状态,存储之前时间步的信息用于当前时间步的计算。

常见的RNN架构如下图两种:
在这里插入图片描述

1.2. 计算步骤:

对于 RNN,每个时间步的隐藏状态计算如下:

H t = tanh ( W h h H t − 1 + W h x x t + b ) H_t = \text{tanh}(W_{hh}H_{t-1} + W_{hx}x_t + b) Ht=tanh(WhhHt1+Whxxt+b)

  • ( W h h ) : 隐藏层到隐藏层的权重矩阵。 (W_{hh}): 隐藏层到隐藏层的权重矩阵。 (Whh):隐藏层到隐藏层的权重矩阵。
  • ( W h x ) : 输入层到隐藏层的权重矩阵。 (W_{hx}): 输入层到隐藏层的权重矩阵。 (Whx):输入层到隐藏层的权重矩阵。
  • ( b ) : 偏置向量。 (b): 偏置向量。 (b):偏置向量。

2. RNN 的前向传播

在前向传播中,RNN 根据输入序列逐步计算隐藏状态和输出。可以表示为:

  1. 初始隐藏状态 ( H 0 ) (H_0) (H0)通常为零。
  2. 对于每个时间步骤 ( t ) (t) (t):
    • 计算新的隐藏状态 ( H t ) (H_t) (Ht)
    • 可选地计算输出 ( y t ) (y_t) (yt)
      y t = W h y H t + b y y_t = W_{hy}H_t + b_y yt=WhyHt+by
    • 其中 ( W h y ) (W_{hy}) (Why) 是隐藏状态到输出的权重矩阵, ( b y ) (b_y) (by) 是输出的偏置。

3. RNN 的应用

RNN 和其变种广泛应用于以下领域:

在这里插入图片描述

  • 自然语言处理:

    • 语言模型
    • 机器翻译
    • 文本生成
  • 时间序列预测:

    • 股票价格预测
    • 气象数据分析
  • 语音和音频处理:

    • 语音识别
    • 音频生成

**

4.代码实现

**
下面的代码实现提供了一个使用 PyTorch 构建的基本 RNN 模型,包括对输入、隐藏状态的初始化、和前向传播的实现。以下是经过改进并注释齐全的完整代码:

import torch
import torch.nn as nn

# 创建一个随机输入张量:10个批次,6个时间步,5维特征(词向量大小)
x = torch.randn(10, 6, 5)  # batch_size=10, seq_len=6, input_size=5

# 定义 RNN 类
class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, batch_first=True): 
        super(RNN, self).__init__()

        # 使用 RNNCell 创建 RNN 单元
        self.rnn_cell = nn.RNNCell(input_size, hidden_size)
        self.hidden_size = hidden_size
        self.batch_first = batch_first  # 判断输入张量的第一个维度是否为批次大小

    def _initialize_hidden(self, batch_size):
        # 初始化隐藏层状态,形状为 (batch_size, hidden_size)
        return torch.zeros(batch_size, self.hidden_size)

    def forward(self, x, init_hidden=None):
        # 如果 batch_first 为 True,那么 x 的形状为 (batch_size, seq_len, input_size)
        if self.batch_first:
            batch_size, seq_len, input_size = x.size()
            # RNN 中需要数据维度是 (seq_len, batch_size, input_size)
            x = x.permute(1, 0, 2)  # 转换为 (seq_len, batch_size, input_size)
        else:
            seq_len, batch_size, input_size = x.size()

        hiddens = []  # 存储每个时间步的隐藏状态

        if init_hidden is None:
            init_hidden = self._initialize_hidden(batch_size)  # 初始化隐藏层
            init_hidden = init_hidden.to(x.device)  # 将初始化隐藏层状态移动到输入张量相同的设备上
        
        hidden_t = init_hidden  # 当前隐藏状态

        # 循环每个时间步
        for t in range(seq_len):
            # 在 t 个时间步更新隐藏层状态
            hidden_t = self.rnn_cell(x[t], hidden_t)
            # 将每个时间步的隐藏状态添加到列表中
            hiddens.append(hidden_t)

        # 将所有时间步的隐藏状态维度叠成一个新张量
        hiddens = torch.stack(hiddens)  # 形状为 (seq_len, batch_size, hidden_size)

        if self.batch_first:
            hiddens = hiddens.permute(1, 0, 2)  # 如果 batch_first 为 True,转换为 (batch_size, seq_len, hidden_size)

        return hiddens  # 返回所有时间步的隐藏状态


# 实例化 RNN 模型
model = RNN(input_size=5, hidden_size=8, batch_first=True)
outputs = model(x)  # 前向传播
print(outputs.shape)  # 打印输出形状

代码解析

  1. 输入张量:

    • 创建一个随机输入张量 x,其形状为 (10, 6, 5),即有 10 个批次,每个批次有 6 个时间步,每个时间步的输入为 5 维特征。
  2. RNN 类:

    • __init__: 定义 RNN 的构造函数,初始化 RNNCell 和隐藏状态的维数。
    • _initialize_hidden: 创建一个零张量作为初始化的隐藏状态。
    • forward: 定义前向传播,提取输入张量的形状并转换为适合 RNN 输入的格式。使用循环遍历每个时间步,更新隐藏状态,并将其保存到列表中。
  3. 模型实例化和前向传播:

    • 创建 RNN 类的实例 model,并用随机的输入 x 进行前向传播,最终打印输出的形状。

输出形状的解释

  • 输出的形状为 (batch_size, seq_len, hidden_size),即 (10, 6, 8),表示每个批次有 6 个时间步的隐藏状态,每个隐藏状态的维度是 8。

注意事项

  • 使用 RNNCell 可以进行较低级的操作,灵活度较高,如果需要处理复杂的 RNN 结构或多层 RNN,可以考虑使用 nn.RNNnn.LSTMnn.GRU

二.单向 RNN 和双向 RNN

  • 单向 RNN 和双向 RNN 是递归神经网络(RNN)的两种主要变体。它们的主要区别在于信息的流动方向,以及如何利用输入序列的信息。下面我们将详细解释这两种类型的 RNN,并提供相应的代码示例。

单向 RNN

  • 单向 RNN 是最基本的 RNN 形式。它从序列的开始(时间步 0)处理到序列的结束(时间步 T),在每个时间步 t,它仅依赖于时间步 t 及之前的输入。这种类型的 RNN 适合处理单向的序列数据,例如时间序列预测。

双向 RNN

双向 RNN 在模型的设计中同时引入两个 RNN,一个从前向后处理输入序列,另一个从后向前处理。每个 RNN 的输出可以联合使用,以捕捉前后文信息。这使得双向 RNN 能够在第二个时间步的输出中利用前面的数据,同时也能够利用后面的数据。这种结构在语言建模、语音识别等任务中非常有用。

代码示例

下面是使用 PyTorch 实现单向和双向 RNN 的代码示例。

单向 RNN
import torch
import torch.nn as nn

# 定义单向 RNN 类
class SimpleRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleRNN, self).__init__()
        self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)  # 单向 RNN
        self.fc = nn.Linear(hidden_size, output_size)  # 输出层

    def forward(self, x):
        # RNN 返回的输出和隐藏状态
        out, _ = self.rnn(x)  # out 的形状为 (batch_size, seq_len, hidden_size)
        out = self.fc(out[:, -1, :])  # 取序列最后一个时间步的输出
        return out

# 示例输入
input_size = 5
hidden_size = 8
output_size = 3
batch_size = 10
seq_len = 6

x = torch.randn(batch_size, seq_len, input_size)  # 输入张量

# 实例化单向 RNN 模型
model = SimpleRNN(input_size, hidden_size, output_size)
output = model(x)  # 前向传播
print("单向 RNN 输出形状:", output.shape)  # 输出形状应该是 (batch_size, output_size)
双向 RNN
import torch
import torch.nn as nn

# 定义双向 RNN 类
class BiDirectionalRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(BiDirectionalRNN, self).__init__()
        self.rnn = nn.RNN(input_size, hidden_size, batch_first=True, bidirectional=True)  # 双向 RNN
        self.fc = nn.Linear(hidden_size * 2, output_size)  # 输出层,hidden_size * 2 是因为是双向的

    def forward(self, x):
        out, _ = self.rnn(x)  # out 的形状为 (batch_size, seq_len, hidden_size * 2)
        out = self.fc(out[:, -1, :])  # 取序列最后一个时间步的输出
        return out

# 示例输入
input_size = 5
hidden_size = 8
output_size = 3
batch_size = 10
seq_len = 6

x = torch.randn(batch_size, seq_len, input_size)  # 输入张量

# 实例化双向 RNN 模型
model = BiDirectionalRNN(input_size, hidden_size, output_size)
output = model(x)  # 前向传播
print("双向 RNN 输出形状:", output.shape)  # 输出形状应该是 (batch_size, output_size)

代码解析

  1. 单向 RNN 示例:

    • SimpleRNN 类定义了一个单向 RNN,使用 nn.RNN 创建 RNN 层。在 forward 方法中,RNN 的输出 out 取值序列中最后一个时间步的输出,并通过线性层 fc 转换到所需的输出维度。
  2. 双向 RNN 示例:

    • BiDirectionalRNN 类与单向 RNN 相似,但通过设定 bidirectional=True 创建了一个双向 RNN。因为 RNN 是双向的,所以输出的隐藏状态维度将是 hidden_size * 2。在 forward 方法中,同样取最后一个时间步的输出进行处理。

输出形状

  • 单向 RNN 的输出形状是 (batch_size, output_size)。双向 RNN 的输出形状也是 (batch_size, output_size),但使用的隐藏层大小是原来的两倍。

PyTorch 的 官方文档。


http://www.kler.cn/a/320725.html

相关文章:

  • 【分布式】万字图文解析——深入七大分布式事务解决方案
  • 二、神经网络基础与搭建
  • 【.NET 8 实战--孢子记账--从单体到微服务】--简易权限--完善TODO标记的代码
  • 使用WebSocket技术实现Web应用中的实时数据更新
  • Go 语言切片初始化与性能优化:使用 cap 参数的重要性
  • 红外遥控信号解码
  • Rust编程的if选择语句
  • HTML中的表单(超详细)
  • pycirclize python包画circos环形图
  • .net 未能加载文件或程序集“System.Diagnostics.DiagnosticSource, Version=6.0.0.1 解决方案
  • OpenCV运动分析和目标跟踪(4)创建汉宁窗函数createHanningWindow()的使用
  • C++速通LeetCode中等第20题-随机链表的复制(三步简单图解)
  • 优化算法(四)—蚁群算法(附MATLAB程序)
  • spark的stage划分的原理
  • 如何完成一个每天自定义的主题,然后提出该主题的100个问题,然后自动完成。使用playwright
  • Chroma 向量数据入门
  • zico2打靶记录
  • 结合人工智能,大数据,物联网等主流技术实现业务流程的闭环整合的名厨亮灶开源了
  • Ubuntu上安装Git:简单步骤指南
  • vue3:路由守卫(全局守卫、路由独享守卫、组件内守卫)
  • XML:DOM4j解析XML
  • Swoole 高性能高并发 PHP 协程框架
  • 【手机马达共振导致后主摄马达声音异常】
  • 深入理解华为仓颉语言的数值类型
  • IP地址的打卡路径是什么?
  • 滚雪球学SpringCloud[10.2讲]:微服务项目的性能优化与调优