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)
相关推荐
飞哥数智坊几秒前
OpenClaw 中国行济南站圆满结束
人工智能
飞哥数智坊几秒前
openclaw 最近版本的崩溃与抢救
人工智能
起个名字总是说已存在1 分钟前
github开源AI Vibe Coding训练你的AI编程工具
人工智能·开源·github
饼干哥哥1 分钟前
OpenClaw真变态!我跑通了跨境电商的10个落地场景
人工智能
Mintopia2 分钟前
为什么同样写代码,有的人越写越轻松,有的人越写越乱
人工智能
hhzz4 分钟前
Openclaw案例之构建《全自动化、高适配、可定制”的AI绘画生产体系》
人工智能·ai作画·自动化·openclaw
飞哥数智坊4 分钟前
没有内测邀请码?我来帮你实测下 SOLO 网页端
人工智能·trae
G皮T6 分钟前
【OpenClaw】创建一个每日热点新闻 Skill
人工智能·ai·大模型·agent·skills·openclaw·龙虾
Agent产品评测局6 分钟前
医药行业合规自动化平台选型,核心要点详解:企业级智能体驱动的合规化演进与实测分析
运维·网络·人工智能·ai·chatgpt·自动化
net3m337 分钟前
可微分结构搜索, 可微分算子选择 —— 让程序“结构”也可学习 , 具体怎么实现结构的轮询穷举
人工智能·线性代数·矩阵