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

RNN模型学习

RNN模型

循环神经网络(Recurrent Neural Network, RNN)是一种用于处理序列数据的神经网络。**RNN具有内部状态(或称为记忆),这允许它在处理序列中的每个元素时考虑之前的信息。**这种特性使得RNN非常适合于自然语言处理、语音识别、时间序列预测等任务,因为这些任务都涉及到对序列信息的理解。

时间序列

时间序列是指按照时间顺序排列的一系列数据点,这些数据点通常是在连续相等的时间间隔内收集的。时间序列分析是统计学的一个分支,专注于通过观察过去的数据来理解和预测未来的发展趋势。它被广泛应用于经济学、金融学、气象学、医学等多个领域。

传统RNN存在的问题:难以处理长期依赖关系、梯度消失或梯度爆炸等。

RNN的变种:

  • 长短期记忆网络(LSTM)
  • 门控循环单元(GRU)

RNN模型框架

在这里插入图片描述

在RNN的经典架构中,网络通过一个特殊的循环结构将信息从一个处理步骤传递到下一个。这个循环结构通常被称为“隐藏层状态”或简单地称为“隐藏状态”。隐藏状态是RNN的记忆部分,它能够捕获并存储关于已处理序列元素的信息。

在这里插入图片描述

RNN的基本工作原理是通过一个循环结构来维持一种“记忆”,这个结构可以将信息从序列的一个步骤传递到下一个步骤,相当于递归的操作。具体来说,在每一个时间步t,RNN接收当前的输入xt和前一时间步的状态ht-1,并基于这两个信息计算出新的输出yt和更新后的状态ht。这一过程可以用以下公式简单表示:
h t = f ( W h x x t + W h h h t − 1 + b h ) y t = g ( W h y h t + b y ) h_t=f(W_{hx}x_t+W_{hh}h_{t−1}+b_h) \\y_t=g(W_{hy}h_t+b_y) ht=f(Whxxt+Whhht1+bh)yt=g(Whyht+by)
f 和 g 分别是激活函数,比如tanh或ReLU;Whx, Whh, Why 是权重矩阵;bh, by,是偏置向量。

RNN的内部结构

在这里插入图片描述

采用tanh激活函数

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

在这里插入图片描述

在循环神经网络(RNN)中,根据输入和输出序列的长度关系,可以将模型分为三种主要架构:一对多、多对一以及多对多。这些不同的架构适用于不同类型的任务。

一对多 (One-to-Many)

这种结构通常用于生成任务,比如基于一个初始状态或条件生成一系列输出。例如:

  • 音乐生成:给定一个起始音符,生成一段旋律。
  • 文本生成:给定一个单词或短语作为种子,生成一段连续的文字。

在这个架构中,只有一个输入提供给RNN,然后它会生成多个时间步上的输出。这可以通过让RNN进入自反馈模式来实现,即每个时间步的输出被用作下一个时间步的输入。

多对一 (Many-to-One)

这种结构适用于需要从整个序列中提取信息并做出单一决策的情况。常见的应用场景包括:

  • 情感分析:基于整篇文章的内容判断其情绪倾向。
  • 图像描述:给定一张图片(可以视为像素序列),生成一句话来描述图片内容。
  • 序列分类:识别一段信号是否属于某个类别。

在这种情况下,RNN接收一系列输入,并在最后一个时间步产生单个输出。这个输出通常是整个序列特征的一个总结或摘要。

多对多 (Many-to-Many)

这是最灵活的一种架构,允许处理任意长度的输入序列并生成相应长度的输出序列。多对多RNN有几种变体:

  • 同步序列到序列

    :输入和输出序列具有相同的长度。这种情况常见于:

    • 语音识别:将音频信号转换为文字。
    • 机器翻译:将一种语言的句子翻译成另一种语言的句子。
  • 非同步序列到序列

    :输入和输出序列长度不同。例如:

    • 视频标注:给定一个视频流,生成较短的标签或描述。
    • 摘要生成:从较长的文章中生成较短的摘要。

在同步的多对多情况下,每个时间步都有对应的输入和输出。而在非同步的情况下,可能需要使用编码器-解码器架构,其中编码器将输入序列编码为固定大小的向量,然后解码器基于这个向量生成输出序列。

RNN代码实现

RNN原理

import numpy as np# 假设输入数据有3个时间步,每个时间步两个特征
x = np.random.rand(3, 2)
# print(x)# 定义RNN的参数
input_size = 2
hidden_size = 3
output_size = 1# 初始化权重和偏置
w_xh = np.random.rand(input_size, hidden_size)  # 输入到隐藏
w_hh = np.random.rand(hidden_size, hidden_size)  # 隐藏到隐藏
w_hy = np.random.rand(hidden_size, output_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]
# 时间步1的隐藏状态, 没有上个时刻的隐藏状态,所以直接使用初始值
# H1 = tanh(np.dot(x1, w_xh) + np.dot(H0, H_prev) + bh)
# 因为没有上个时刻,所以没有H0,np.dot(H0, H_prev)就等于全0, bh初始也为0
H1 = tanh(np.dot(x1, w_xh) + H_prev)
o1 = np.dot(H1, w_hy) + by  # 计算输出# 时间步2
x2 = x[1]
H2 = tanh(np.dot(x2, w_xh) + np.dot(H1, w_hh) + bh)
o2 = np.dot(H2, w_hy) + by# 时间步3
x3 = x[2]
H3 = tanh(np.dot(x3, w_xh) + np.dot(H2, w_hh) + bh)
o3 = np.dot(H3, w_hy) + byprint("H1:", H1)
print("o1:", o1)
print("H2:", H2)
print("o2:", o2)
print("H3:", H3)
print("o3:", o3)

RNNCell的API调用

import torch
import torch.nn as nn# 创建数据
x = torch.randn(10, 6, 5)  # 批次,词数,每个词的向量维度# 定义一个rnn类
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_firstdef _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需要的数据维数是(batch_size, seq_len, input_size)x = x.permute(1, 0, 2)else:seq_len, batch_size, input_size = x.size()hiddens = []  # 用于储存每一个时间步的隐藏状态信息# 提供初始化为全0的隐藏状态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个时间步更新隐藏状态# x[t]取出来的值是(batch_size, 词向量)# 比如取出来的是三句话的第一个词的向量,每一个词向量是(4,)的话,取出来就是(3,4)的形状hidden_t = self.rnn_cell(x[t], hidden_t)# 保存该时间步的隐藏状态到列表当中hiddens.append(hidden_t)# 将所有时间步的隐藏状态堆叠成为一个新的张量,增加一个维度hiddens = torch.stack(hiddens)# 如果batch_first为True,需要重新排维度,将维度转回去(seq_len, batch_size, input_size)转回(batch_size, seq_len, hidden_size)if self.batch_first:hiddens = hiddens.permute(1, 0, 2)print(hiddens)return hiddens  # 返回隐藏状态列表model = RNN(5, 8, batch_first=True)
output = model(x)
print(output.shape)

RNN的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)# 我的版本的RNN不用传入初始化的隐藏状态
# 返回值:output,每个时间步输出的隐藏状态, state_final,最后一个时间步的隐藏状态
# output, state_final = rnn(x, h_prev.unsqueeze(0))
output, state_final = rnn(x)print(output)
print(output.shape)
print(state_final)
print(state_final.shape)

irst=True)

我的版本的RNN不用传入初始化的隐藏状态

返回值:output,每个时间步输出的隐藏状态, state_final,最后一个时间步的隐藏状态

output, state_final = rnn(x, h_prev.unsqueeze(0))

output, state_final = rnn(x)

print(output)
print(output.shape)
print(state_final)
print(state_final.shape)


相关文章:

  • C++ 排序算法
  • 自适应查询优化(Adaptive Query Optimization, AQO)技术简介
  • react crash course 2024(5) useState钩子
  • DPDK 简易应用开发之路 2:UDP数据包发送及实现
  • 记录打鼾软件
  • 2024最新版 Tuxera NTFS for Mac 2023绿色版图文安装教程
  • 基于单片机的智能温控风扇系统的设计
  • llamafactory0.9.0微调qwen2.5
  • 深度学习驱动智能超材料设计与应用
  • 云服务器连接不上是什么原因引起的?
  • spark 大表与大表join时的Shuffle机制和过程
  • 【视频讲解】非参数重采样bootstrap逻辑回归Logistic应用及模型差异Python实现
  • 【STM32】 TCP/IP通信协议(1)
  • Ubuntu24.04中安装Electron
  • 【好书推荐】掌握金仓数据库,从这些必读书籍开始! 亲爱的小伙伴们,今天我要给大家安利几本学习金仓
  • 【翻译】babel对TC39装饰器草案的实现
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • Apache的基本使用
  • canvas 五子棋游戏
  • CAP 一致性协议及应用解析
  • Docker入门(二) - Dockerfile
  • Js基础知识(一) - 变量
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • VuePress 静态网站生成
  • Webpack 4 学习01(基础配置)
  • windows-nginx-https-本地配置
  • 动态规划入门(以爬楼梯为例)
  • 规范化安全开发 KOA 手脚架
  • 理清楚Vue的结构
  • 码农张的Bug人生 - 见面之礼
  • 异步
  • 因为阿里,他们成了“杭漂”
  • 用Visual Studio开发以太坊智能合约
  • NLPIR智能语义技术让大数据挖掘更简单
  • #android不同版本废弃api,新api。
  • (1)Hilt的基本概念和使用
  • (11)MATLAB PCA+SVM 人脸识别
  • (35)远程识别(又称无人机识别)(二)
  • (Oracle)SQL优化基础(三):看懂执行计划顺序
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (二)c52学习之旅-简单了解单片机
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (黑马点评)二、短信登录功能实现
  • (每日一问)基础知识:堆与栈的区别
  • (三分钟)速览传统边缘检测算子
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (四)进入MySQL 【事务】
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • (转)C语言家族扩展收藏 (转)C语言家族扩展
  • .NET/MSBuild 中的发布路径在哪里呢?如何在扩展编译的时候修改发布路径中的文件呢?
  • .netcore 获取appsettings
  • .NET编程C#线程之旅:十种开启线程的方式以及各自使用场景和优缺点
  • .net和jar包windows服务部署
  • .NET之C#编程:懒汉模式的终结,单例模式的正确打开方式
  • .pub是什么文件_Rust 模块和文件 - 「译」