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

RNN(循环神经网络)

介绍

循环神经网络(Recurrent Neural Network,RNN)是一种强大的神经网络结构,主要用于处理序列数据,例如时间序列、文本、语音等。它通过引入循环结构,能够利用序列中的历史信息来预测当前的输出,非常适合处理具有时间依赖性或顺序关系的数据。
总的来讲:RNN的核心是记忆每个时间点的输出都收到之前时间点的影响
从输出来考虑,RNN有1-1的,1-多的,多-1的,也有多-多的
在这里插入图片描述

基本原理

循环神经网络的核心在于其“循环”的结构。在传统的神经网络中,输入和输出是独立的,而在RNN中,网络的输出不仅取决于当前的输入,还依赖于之前时刻的输出(即隐藏状态)。这种结构使得RNN能够记住之前的信息,并将其用于当前的计算。
假设我们有一个序列 x=(x_1,x_2,…,x_T),RNN会逐个处理序列中的元素,并在每一步更新其隐藏状态 h_t。隐藏状态的更新公式通常为:
在这里插入图片描述
其中,f 是一个非线性函数,通常是一个激活函数(如tanh或ReLU)。最终,RNN可以根据隐藏状态生成输出 y_t,输出的计算公式为:
在这里插入图片描述

其中,g 是另一个非线性函数。

数学表达

循环神经网络(RNN)的数学表达式主要包括隐藏状态的更新和输出的计算。
以下是RNN的基本数学表达式:

1. 隐藏状态的更新

在RNN中,隐藏状态h_t是通过当前输入x_t和前一时刻的隐藏状态 h_t−1来更新的。具体的更新公式为:
在这里插入图片描述
其中:

  • h_t是第 t 时刻的隐藏状态
  • x_t是第 t 时刻的输入
  • W_hh是隐藏状态到隐藏状态的权重矩阵
  • W_xh是输入到隐藏状态的权重矩阵
  • b_h是隐藏状态的偏置项
  • tanh 是激活函数,通常使用双曲正切函数(tanh),也可以使用其他非线性激活函数(如ReLU)

2. 输出的计算

在某些情况下,RNN的输出y_t可以直接从隐藏状态 h_t计算得到。输出的计算公式为:
在这里插入图片描述
其中:

  • y_t是第 t 时刻的输出
  • W_hy是隐藏状态到输出的权重矩阵
  • b_y是输出的偏置项

完整的RNN过程就是先完成隐藏状态更新,再进行输出计算

3. 初始化

在开始处理序列之前,需要初始化隐藏状态h_0。通常初始化为零向量:
在这里插入图片描述

4. 训练过程

在训练RNN时,通常使用反向传播通过时间(Backpropagation Through Time,BPTT)来计算梯度并更新权重。BPTT将RNN展开为一个时间步长的序列,并对每个时间步长进行反向传播。

梯度消失和梯度爆炸问题

在实际应用中,RNN可能会遇到梯度消失或梯度爆炸的问题,这使得训练长序列变得困难:

梯度消失问题(Vanishing Gradient Problem)

梯度消失问题是指在训练过程中,随着网络层数的增加或时间步长的增加,梯度逐渐变得非常小,导致网络的权重更新非常缓慢,甚至无法更新。这使得网络难以学习到长距离的依赖关系。

原因

在RNN中,隐藏状态的更新公式为:
在这里插入图片描述
在反向传播过程中,梯度会通过链式法则逐层传递。对于RNN,梯度会沿着时间步长反向传播,即:
在这里插入图片描述
其中:
在这里插入图片描述
由于 tanh 函数的导数范围在 (0,1) 之间,当多个这样的导数相乘时,梯度会迅速衰减。例如,假设每个时间步长的梯度乘积为 0.9,那么经过10个时间步长后,梯度会变为 0.910≈0.3487,经过20个时间步长后,梯度会变为 0.920≈0.1216。这种衰减使得网络难以学习到长距离的依赖关系。

梯度爆炸问题(Exploding Gradient Problem)

梯度爆炸问题是指在训练过程中,随着网络层数的增加或时间步长的增加,梯度逐渐变得非常大,导致网络的权重更新过大,甚至导致数值不稳定。这使得网络的训练变得非常困难,甚至无法收敛。

原因

与梯度消失问题相反,梯度爆炸问题通常发生在权重矩阵的范数较大时。在RNN中,如果权重矩阵 W_hh的范数较大,那么在反向传播过程中,梯度会迅速增大。例如,假设每个时间步长的梯度乘积为 1.1,那么经过10个时间步长后,梯度会变为 1.110
≈2.5937,经过20个时间步长后,梯度会变为 1.120≈6.7275。这种快速增大会导致权重更新过大,甚至导致数值不稳定。

梯度爆炸问题的解决方案

梯度裁剪(Gradient Clipping):

在反向传播过程中,对梯度进行裁剪,使其不超过某个阈值,防止梯度过大。

权重正则化:

使用权重正则化(如L2正则化)来限制权重的范数,防止权重过大。

改进的RNN架构:

使用LSTM或GRU等改进的RNN架构,这些架构通过引入门控机制来控制信息的流动,防止梯度爆炸。

RNN创建

from torch import nn
rnn_cell = nn.RNNCell(input_size=3, hidden_size=2)
print(rnn_cell)

用如上方法,我们可以很简单就创建一个隐藏向量为2维,输入向量为3维的RNN
现在,我们来用 PyTorch 实现了一个最简单、最朴素的 RNN 前向计算,用来演示 nn.RNN 的输入和输出长什么样。

导入所需模块

import torch
import torch.nn as nn

建网络

rnn = nn.RNN(5, 6, 2)

输入特征维度 input_size = 5:
每个时间步喂给 RNN 的向量长度是 5。
隐藏层维度 hidden_size = 6:
每个 RNN 单元内部有 6 个隐藏神经元,所以隐藏状态 h_t 是 6 维向量。
层数 num_layers = 2:
纵向堆了 2 层 RNN(第一层输出作为第二层输入)。

随机生成输入

input1 = torch.randn(1, 3, 5)

形状 (seq_len, batch, input_size) = (1, 3, 5)
序列长度 seq_len = 1:每个样本只有 1 个时间步。
批大小 batch = 3:每个时间步的行向量长度为 3。
特征维度 input_size = 5:每个时间步的列向量长度为 5(和网络配置一致)。

初始隐藏状态

h0 = torch.randn(2, 3, 6)

形状 (num_layers, batch, hidden_size) = (2, 3, 6)
2 层 RNN → 第一层、第二层各需要一个 (batch, hidden_size) 的隐藏状态。
这里直接随机初始化,实际训练时通常用零向量或学习到的初始状态。

前向计算

output, hn = rnn(input1, h0)

output:形状 (seq_len, batch, hidden_size) = (1, 3, 6)
保存每一层在最后一个时间步的输出。因为 seq_len = 1,所以只有 1 个时间步,也就是这 1 步的输出。
hn:形状 (num_layers, batch, hidden_size) = (2, 3, 6)
保存最后一层在每个时间步后的隐藏状态。因为是单层序列,所以这里直接就是最后一个时间步的隐藏状态。

网络结构

把它画成“两层、单向、无 bias、tanh 激活”的 RNN,就像下面这张“垂直切片”图:

时间步 t(只有 1 个)      输出
┌------------┐
│  x_t(5)    │ ─┐
└------------┘  │  ┌----------------Layer 0----------------┐│  │   (6 个 tanh 单元)                    ││  │   ┌-----┐  ┌-----┐        ┌-----┐   ││  │   │     │  │     │  ...   │     │   ││  │   │ tanh│  │ tanh│        │ tanh│   │└─>│   └-----┘  └-----┘        └-----┘   ││   ↑ 6 维 h_t^{(0)}                   ││   │                                   ││   │                                   ││   │                                   ││   │  ┌----------------Layer 1---------------┐│   └─>(6 个 tanh 单元)                   ││      │   ┌-----┐  ┌-----┐        ┌-----┐  ││      │   │     │  │     │  ...   │     │  ││      │   │ tanh│  │ tanh│        │ tanh│  │└----->│   └-----┘  └-----┘        └-----┘  ││   ↑ 6 维 h_t^{(1)}                  ││   │                                   ││   └--------------output_t(6)        │└----------------------------------------

打印结果

print(input1)         #形状(135print(output)         # 形状 (1, 3, 6)
print(output.shape)   # torch.Size([1, 3, 6])
print(hn)             # 形状 (2, 3, 6)
print(hn.shape)       # torch.Size([2, 3, 6])

output:

tensor([[[ 0.1578, -0.6580,  1.4173,  0.9465, -0.3168],[ 0.9392,  0.6594,  0.2955,  2.3961,  0.9437],[-0.3017,  0.6763,  0.2471, -1.1089,  0.3852]]])
tensor([[[ 0.1992,  0.4487,  0.7773,  0.4494,  0.3454, -0.4028],[-0.6498, -0.2264,  0.7769,  0.2682,  0.3949,  0.7619],[-0.4186, -0.9363, -0.8965, -0.0036, -0.8698, -0.3272]]],grad_fn=<StackBackward0>)
torch.Size([1, 3, 6])
tensor([[[-0.8587, -0.0270,  0.3406,  0.6439, -0.2807, -0.5318],[-0.1035, -0.4623, -0.5111,  0.0668, -0.4180, -0.1708],[-0.2161,  0.5315,  0.7317,  0.3937, -0.7685, -0.6344]],[[ 0.1992,  0.4487,  0.7773,  0.4494,  0.3454, -0.4028],[-0.6498, -0.2264,  0.7769,  0.2682,  0.3949,  0.7619],[-0.4186, -0.9363, -0.8965, -0.0036, -0.8698, -0.3272]]],grad_fn=<StackBackward0>)
torch.Size([2, 3, 6])
http://www.lryc.cn/news/587676.html

相关文章:

  • 【git fetch submodule报错】Errors during submodule fetch 如何解决?
  • VUE export import
  • 【算法深练】BFS:“由近及远”的遍历艺术,广度优先算法题型全解析
  • 人工智能如何重构能源系统以应对气候变化?
  • 从数据洞察到设计创新:UI前端如何利用数字孪生提升产品交互体验?
  • Pythonic:Python 语言习惯和哲学的代码风格
  • vue中使用西瓜播放器xgplayer (封装)+xgplayer-hls 播放.m3u8格式视频
  • Vue+axios
  • Rust语言实战:LeetCode算法精解
  • 从“炼丹”到“流水线”——如何用Prompt Engineering把LLM微调成本打下来?
  • 内容管理系统指南:企业内容运营的核心引擎
  • Retinex视网膜算法(SSR、MSR、MSRCR)
  • JVM监控及诊断工具-命令行篇
  • AI香烟检测实战:YOLO11模型训练全过程解析
  • 【第一章编辑器开发基础第一节绘制编辑器元素_7折叠面板控件(7/7)】
  • python学智能算法(十八)|SVM基础概念-向量点积
  • 【第一章编辑器开发基础第二节编辑器布局_3GUI元素和布局大小(3/4)】
  • python学智能算法(十七)|SVM基础概念-向量的值和方向
  • CISSP通过回顾
  • Unity中HumanBodyBones骨骼对照
  • [Nagios Core] 通知系统 | 事件代理 | NEB模块,事件,回调
  • 上下文管理器 和 contextlib 模块
  • Cocos Creator 高斯模糊效果实现解析
  • 2025高防CDN硬核防御指南:AI+量子加密如何终结DDoS/CC攻击?
  • VyOS起步指南:用Docker快速搭建网络实验环境
  • MCP终极篇!MCP Web Chat项目实战分享
  • android tabLayout 切换fragment fragment生命周期
  • VScode设计平台demo&前端开发中的常见问题
  • CentOS系统哪些版本?分别适用于那些业务或网站类型?
  • VMware 虚拟机装 Linux Centos 7.9 保姆级教程(附资源包)