RNN 模型学习与实践

1.先导

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

循环神经网络(RNN)是一种专门设计用于处理序列数据的神经网络。在许多实际应用中,数据是以序列的形式出现的,例如文本、语音、时间序列数据等。这些数据中的元素不仅自身携带信息,而且与序列中的其他元素之间存在依赖关系。传统的前馈神经网络(如全连接层)无法直接处理这种依赖性,因为它们假设输入是独立同分布的。这就是为什么我们需要RNN的原因:

  1. 处理序列数据:RNN可以处理具有自然顺序的数据,比如一句话中的单词序列或一段音乐中的音符序列。在这些情况下,当前项的信息不仅取决于它自己,还可能依赖于前面的一项或多项。

  2. 记忆效应:RNN通过将先前的信息传递到后续步骤来利用序列中的上下文信息。这是通过在网络中引入反馈连接来实现的,允许信息在时间上"记住"并影响未来的输出。

  3. 可变长度输入/输出:RNN可以处理不定长的输入序列,并且也能产生不定长的输出序列。这对于机器翻译任务特别有用,其中输入句子的长度和输出句子的长度通常不同。

  4. 参数共享:在RNN中,相同的权重被应用于序列中的每个位置。这意味着模型学习到的特征可以应用于整个序列,而不仅仅局限于某个特定位置。这减少了需要学习的参数数量,并有助于避免过拟合。

2. RNN 原理

2.1 概述

循环神经网络(RNN)是一种专门用于处理序列数据的神经网络结构,能够捕捉序列中的时间依赖性。与传统的前馈神经网络不同,RNN 具有内部反馈连接,能够将之前的信息存储并传递到后续步骤。这使得 RNN 在自然语言处理(如语言建模、机器翻译和情感分析)和时间序列预测(如股票和天气预测)等领域表现出色。

2.2 模型架构

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

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

循环神经网络(RNN)通过一个特殊的循环结构将信息从一个时间步骤传递到下一个,通常称为"隐藏状态",它是RNN的记忆部分,能捕获已处理序列元素的信息。

在处理序列时,RNN在每个时间步接收输入并更新隐藏状态,更新过程依赖于当前输入和之前的隐藏状态,记住并利用过去的信息。其更新过程可以用数学公式表示:

其中,​是当前隐藏状态,​是当前输入,UW 是权重矩阵,f是非线性函数(如tanh或ReLU)。

输出在每个时间步也可以通过当前的隐藏状态计算得出:

其中,​Ot是输出,V 是隐藏状态到输出层的权重矩阵,g是另一个非线性函数。

RNN通过"隐藏状态"记录和更新输入时的网络状态,从而能够处理和记住序列信息。

2.3 RNN的内部结构

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

3. RNN 代码实现

Batch Size (批量大小)

Sequence Length (序列长度)

Input Size (输入大小)

Hidden Size (隐藏层大小)

Output Size (输出大小)

复制代码
import numpy as np
​
#假设输入数据有3个时间步,每个时间步有2个特征
x=np.random.randn(3,2)
print(x)
​
#定义RNN参数
input_size=2
hidden_size=3
output_size=1
​
#初始化权重和偏置
W_xh=np.random.randn(hidden_size,input_size)#输入到隐藏
W_hh=np.random.randn(hidden_size,hidden_size)#隐藏到隐藏
W_hy=np.random.randn(output_size,hidden_size)#隐藏到输出
​
bh=np.zeros((hidden_size,))#隐藏层偏置
by=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(W_xh,x1) + H_prev)#时间布1的隐藏状态,没有上个时刻的隐藏状态,
o1=np.dot(W_hy,H1)+by
​
​
#时间布2
x2=x[1,:]
H2=tanh(np.dot(W_xh,x2)+np.dot(W_hh,H1)+bh) #时间步2的隐藏状态,需要上个时间步的隐藏状态
o2=np.dot(W_hy,H2)+by #时间步2的输出
​
#时间布3
x3=x[2,:]
H3=tanh(np.dot(W_xh,x3)+np.dot(W_hh,H2)+bh)#时间步2的隐藏状态,需要上个时间步的隐藏状态
o3=np.dot(W_hy,H3)+by
​
#输出结果
​
print(H1)
print(o1)
print(H2)   
print(o2)
print(H3)
print(o3)
    

基于 RNNCell 代码实现

复制代码
import torch
import torch.nn as nn
​
# 创建数据
x_input = torch.randn(10,6,5)  # 假设 batch_size=2, seq_len=3, input_size=4
​
class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, batch_first=True):
        super(RNN, self).__init__()
        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):
        return torch.zeros(batch_size, self.hidden_size)
​
    def forward(self, x, init_hidden=None):
        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)
            
        else:
            batch_size, seq_len, 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)
​
        # 如果batch_first=True,重新排列维度,#jiang
        if self.batch_first:
            hiddens = hiddens.permute(1, 0, 2)
            
        print(hiddens)
        return hiddens  # 返回隐藏列表
​
# 实例化模型并进行前向传播
model = RNN(input_size=5, hidden_size=8, batch_first=True)
output = model(x_input)
print(output.shape)

基于 pytorch API 代码实现

复制代码
import torch
import torch.nn as nn
​
#设置超参数
batch_size ,seq_len, input_size=10,6,5 #Input_size词向量大小
hidden_size=3 #隐藏层大小
​
#数据输入
x=torch.randn(batch_size, seq_len, input_size)
#print(x.shape)
​
#初始化隐藏状态,全零向量
h_prev=torch.zeros(batch_size,hidden_size)
# print(h_prev.shape)
# print(h_prev)
​
#创建一个RNN实例
#input_size:输入向量维度 hidden_size:隐藏层维度 batch_first:是否使用batch_size作为第一维
rnn=nn.RNN(input_size,hidden_size,batch_first=True)
#输入第一份参数是数据 第二份参数是初始隐藏状态
#output:每个时间步输出的隐藏状态 state_final:最后一个时间步的隐藏状态
output,state_final=rnn(x,h_prev.unsqueeze(0))
print(output)
print(output.shape)
print(state_final)
print(state_final.shape)
  1. 输入张量 (input):

    • 形状为 (batch_size, sequence_length, input_size)

    • 表示一批(batch_size)序列,每个序列包含 sequence_length 个时间步,每个时间步的特征维度为 input_size

  2. 初始隐含状态 (h_prev):

    • 形状为 (batch_size, hidden_size)

    • RNN在开始处理序列时的初始状态,通常可以初始化为零或其他值。

  3. 隐含状态维度增加:

    • 使用 h_prev.unsqueeze(0) 增加一层批量维度,结果形状变为 (1, batch_size, hidden_size)

    • 这符合PyTorch RNN的输入要求,因为RNN期望隐含状态为 (num_layers, batch_size, hidden_size) 的形状。在这里只有一个层,因此 num_layers=1

  4. 前向传播:

    • rnn(input, h_prev.unsqueeze(0)):执行RNN的前向传播。

    • 返回的 rnn_output 的形状为 (batch_size, sequence_length, hidden_size),表示每个时间步的输出。

    • state_final 的形状为 (num_layers, batch_size, hidden_size),表示最后一个时间步的隐含状态。

输出解释

  • rnn_output:

    • 包含每个时间步的输出,对于很多序列模型而言,每个时间步都会产生一个输出。

    • 可以用于后续的时间步计算,或作为整个序列的最终输出。

  • state_final:

    • 代表RNN在处理完整个序列后的最终隐含状态。

    • 通常用于对整个序列的总结,可能用于任务的最终预测或输出。

单向、单层RNN,双向、单层RNN

主要特点

  1. 双向处理: 最显著的特点是双向结构,使得模型能够同时学习到序列中某一点前后的上下文信息,这对于很多序列任务来说是非常有价值的,比如自然语言处理中的文本理解、语音识别等。

  2. 单层结构: "单层"指的是在每个方向上,网络结构只有一层RNN,即每个方向上只有一层循环单元(如LSTM单元或GRU单元)。虽然是单层的,但由于其双向特性,实际上每个时间点都有两个循环单元对信息进行处理。

复制代码
import torch
import torch.nn as nn
​
inputs=torch.randn(5,3,4) #5:批次 3:词数 4:每个词的向量维度
​
bi_rnn=nn.RNN(4,6,1,batch_first=True,bidirectional=True)#4:每个词的向量维 6:隐藏层大小
​
output,h_n=bi_rnn(inputs)
​
print(output)
print(output.shape)
print(h_n)
print(h_n.shape)
相关推荐
抓哇能手6 分钟前
数据库系统概论
数据库·人工智能·sql·mysql·计算机
火云洞红孩儿12 分钟前
基于AI IDE 打造快速化的游戏LUA脚本的生成系统
c++·人工智能·inscode·游戏引擎·lua·游戏开发·脚本系统
风清扬雨40 分钟前
【计算机视觉】超简单!傅里叶变换的经典案例
人工智能·计算机视觉
HuggingFace1 小时前
自动评估基准 | 设计你的自动评估任务
人工智能·自动评估
GISer_Jing1 小时前
神经网络初学总结(一)
人工智能·深度学习·神经网络
szxinmai主板定制专家1 小时前
【国产NI替代】基于A7 FPGA+AI的16振动(16bits)终端PCIE数据采集板卡
人工智能·fpga开发
数据分析能量站2 小时前
神经网络-AlexNet
人工智能·深度学习·神经网络
Ven%2 小时前
如何修改pip全局缓存位置和全局安装包存放路径
人工智能·python·深度学习·缓存·自然语言处理·pip
szxinmai主板定制专家2 小时前
【NI国产替代】基于国产FPGA+全志T3的全国产16振动+2转速(24bits)高精度终端采集板卡
人工智能·fpga开发
YangJZ_ByteMaster2 小时前
EndtoEnd Object Detection with Transformers
人工智能·深度学习·目标检测·计算机视觉