【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 的 官方文档

相关推荐
chao_6666663 分钟前
【深度】为GPT-5而生的「草莓」模型!从快思考—慢思考到Self-play RL的强化学习框架
人工智能·深度学习·学习·机器学习
丹丹的笑意27 分钟前
学习记录:js算法(四十七):相同的树
javascript·学习·算法
Ace'1 小时前
学习笔记&&每日一题
笔记·学习·算法
mez_Blog1 小时前
React学习笔记(3.0)
前端·笔记·学习·react.js·前端框架
啊波阿波波1 小时前
NLP--自然语言处理学习-day1
学习·自然语言处理·easyui
知来者逆1 小时前
EAGLE——探索混合编码器的多模态大型语言模型的设计空间
人工智能·深度学习·语言模型·自然语言处理·llm·大语言模型·多模态
秋秋秋叶2 小时前
【2】图像视频的加载和显示
opencv·学习
斯派的曼2 小时前
学习C++的第七天!
开发语言·c++·学习
苏格拉没有底1112 小时前
数据结构——顺序表、链表
c语言·开发语言·数据结构·笔记·学习·算法·链表
野生派蒙2 小时前
IDEA 关闭自动补全功能(最新版本)
java·开发语言·ide·后端·学习·intellij-idea