【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(WhhHt−1+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 模型,包括对输入、隐藏状态的初始化、和前向传播的实现。以下是经过改进并注释齐全的完整代码:

python 复制代码
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
python 复制代码
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
python 复制代码
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 的 官方文档

相关推荐
西岸行者11 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意11 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码11 天前
嵌入式学习路线
学习
毛小茛11 天前
计算机系统概论——校验码
学习
babe小鑫11 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms11 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下11 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。11 天前
2026.2.25监控学习
学习
im_AMBER11 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J11 天前
从“Hello World“ 开始 C++
c语言·c++·学习