多层 RNN原理以及实现

数学原理


多层 RNN 的核心思想是堆叠多个 RNN 层,每一层的输出作为下一层的输入,从而逐层提取更高层次的抽象特征。


1. 单层 RNN 的数学表示

首先,单层 RNN 的计算过程如下。对于一个时间步 t t t,单层 RNN 的隐藏状态 h t h_t ht 和输出 y t y_t yt 可以表示为:

h t = activation ( W i h x t + b i h + W h h h t − 1 + b h h ) h_t = \text{activation}(W_{ih} x_t + b_{ih} + W_{hh} h_{t-1} + b_{hh}) ht=activation(Wihxt+bih+Whhht1+bhh)
y t = W h o h t + b h o y_t = W_{ho} h_t + b_{ho} yt=Whoht+bho

其中:

  • x t x_t xt 是时间步 t t t 的输入。
  • h t h_t ht 是时间步 t t t 的隐藏状态。
  • h t − 1 h_{t-1} ht1 是时间步 t − 1 t-1 t1 的隐藏状态。
  • W i h W_{ih} Wih W h h W_{hh} Whh W h o W_{ho} Who 是权重矩阵。
  • b i h b_{ih} bih b h h b_{hh} bhh b h o b_{ho} bho 是偏置项。
  • activation \text{activation} activation 是激活函数(如 tanh ⁡ \tanh tanh ReLU \text{ReLU} ReLU)。

2. 多层 RNN 的数学表示

假设我们有一个 L L L 层的 RNN,每一层的隐藏状态为 h t ( l ) h_t^{(l)} ht(l),其中 l l l 表示第 l l l 层, t t t 表示时间步。多层 RNN 的计算过程如下:

(1) 第一层( l = 1 l = 1 l=1

第一层的输入是原始输入序列 x t x_t xt,隐藏状态 h t ( 1 ) h_t^{(1)} ht(1) 的计算公式为:

h t ( 1 ) = activation ( W i h ( 1 ) x t + b i h ( 1 ) + W h h ( 1 ) h t − 1 ( 1 ) + b h h ( 1 ) ) h_t^{(1)} = \text{activation}(W_{ih}^{(1)} x_t + b_{ih}^{(1)} + W_{hh}^{(1)} h_{t-1}^{(1)} + b_{hh}^{(1)}) ht(1)=activation(Wih(1)xt+bih(1)+Whh(1)ht1(1)+bhh(1))

其中:

  • W i h ( 1 ) W_{ih}^{(1)} Wih(1) W h h ( 1 ) W_{hh}^{(1)} Whh(1) 是第一层的权重矩阵。
  • b i h ( 1 ) b_{ih}^{(1)} bih(1) b h h ( 1 ) b_{hh}^{(1)} bhh(1) 是第一层的偏置项。
(2) 第 l l l 层( l > 1 l > 1 l>1

l l l 层的输入是第 l − 1 l-1 l1 层的输出 h t ( l − 1 ) h_t^{(l-1)} ht(l1),隐藏状态 h t ( l ) h_t^{(l)} ht(l) 的计算公式为:

h t ( l ) = activation ( W i h ( l ) h t ( l − 1 ) + b i h ( l ) + W h h ( l ) h t − 1 ( l ) + b h h ( l ) ) h_t^{(l)} = \text{activation}(W_{ih}^{(l)} h_t^{(l-1)} + b_{ih}^{(l)} + W_{hh}^{(l)} h_{t-1}^{(l)} + b_{hh}^{(l)}) ht(l)=activation(Wih(l)ht(l1)+bih(l)+Whh(l)ht1(l)+bhh(l))

其中:

  • W i h ( l ) W_{ih}^{(l)} Wih(l) W h h ( l ) W_{hh}^{(l)} Whh(l) 是第 l l l 层的权重矩阵。
  • b i h ( l ) b_{ih}^{(l)} bih(l) b h h ( l ) b_{hh}^{(l)} bhh(l) 是第 l l l 层的偏置项。
(3) 输出层

最后一层(第 L L L 层)的输出 h t ( L ) h_t^{(L)} ht(L) 作为整个网络的输出 y t y_t yt

y t = W h o h t ( L ) + b h o y_t = W_{ho} h_t^{(L)} + b_{ho} yt=Whoht(L)+bho

其中:

  • W h o W_{ho} Who b h o b_{ho} bho 是输出层的权重矩阵和偏置项。

3. 多层 RNN 的数据流向

以下是一个 L L L 层 RNN 的数据流向的数学描述:

(1) 输入序列

输入序列为 x 1 , x 2 , … , x T x_1, x_2, \dots, x_T x1,x2,,xT,其中 T T T 是序列长度。

(2) 初始化隐藏状态

每一层的初始隐藏状态 h 0 ( l ) h_0^{(l)} h0(l) 通常初始化为零或随机值:

h 0 ( l ) = 0 或 h 0 ( l ) ∼ N ( 0 , σ 2 ) h_0^{(l)} = \mathbf{0} \quad \text{或} \quad h_0^{(l)} \sim \mathcal{N}(0, \sigma^2) h0(l)=0h0(l)N(0,σ2)

(3) 时间步 t t t 的计算

对于每个时间步 t t t,从第一层到第 L L L 层依次计算隐藏状态:

  1. 第一层
    h t ( 1 ) = activation ( W i h ( 1 ) x t + b i h ( 1 ) + W h h ( 1 ) h t − 1 ( 1 ) + b h h ( 1 ) ) h_t^{(1)} = \text{activation}(W_{ih}^{(1)} x_t + b_{ih}^{(1)} + W_{hh}^{(1)} h_{t-1}^{(1)} + b_{hh}^{(1)}) ht(1)=activation(Wih(1)xt+bih(1)+Whh(1)ht1(1)+bhh(1))

  2. l l l 层( l > 1 l > 1 l>1
    h t ( l ) = activation ( W i h ( l ) h t ( l − 1 ) + b i h ( l ) + W h h ( l ) h t − 1 ( l ) + b h h ( l ) ) h_t^{(l)} = \text{activation}(W_{ih}^{(l)} h_t^{(l-1)} + b_{ih}^{(l)} + W_{hh}^{(l)} h_{t-1}^{(l)} + b_{hh}^{(l)}) ht(l)=activation(Wih(l)ht(l1)+bih(l)+Whh(l)ht1(l)+bhh(l))

  3. 输出
    y t = W h o h t ( L ) + b h o y_t = W_{ho} h_t^{(L)} + b_{ho} yt=Whoht(L)+bho

(4) 序列输出

最终,整个序列的输出为 y 1 , y 2 , … , y T y_1, y_2, \dots, y_T y1,y2,,yT


4. 多层 RNN 的特点

(1) 逐层抽象
  • 每一层 RNN 可以看作是对输入序列的不同层次的抽象。
  • 较低层捕捉局部和细节信息,较高层捕捉全局和语义信息。
(2) 参数共享
  • 每一层的参数(权重矩阵和偏置项)在时间步之间共享。
  • 不同层的参数是独立的。
(3) 梯度传播
  • 在反向传播时,梯度会通过时间步和层数传播。
  • 由于梯度消失或爆炸问题,训练深层 RNN 可能会比较困难。

可视化原理

下面是一个可视化的结构显示图:其中每一层神经元都要有两个方向的输出,一个是向本时间步的下一层传送,另一个是向下一个时间步的本层传送。而且,每一个神经元都有两个权重矩阵。注意:下方右图仅仅是逻辑上展开的数据流,其中不同世间步上的同一层,用的是同一个权重矩阵。

在这里插入图片描述

代码实现


1. 示例任务

假设有一个简单的任务:

  • 处理一个长度为 4 的序列
  • 批次大小为 2
  • 每个时间步的输入特征维度为 3
  • 希望使用一个 2 层的单向 RNN
  • 隐藏状态维度为 5。

2. 输入数据

输入句子
  • 句子 1: “I love PyTorch”
  • 句子 2: “RNN is fun”
输入数据的形状
  • 序列长度 (seq_len): 4(假设每个单词是一个时间步)
  • 批次大小 (batch_size): 2
  • 输入特征维度 (input_size): 3(假设每个单词用一个 3 维向量表示)
具体输入数据
import torch# 输入数据形状: (seq_len, batch_size, input_size)
input_data = torch.tensor([# 时间步 1[[0.1, 0.2, 0.3],  # 句子 1 的第一个单词[0.4, 0.5, 0.6]], # 句子 2 的第一个单词# 时间步 2[[0.7, 0.8, 0.9],  # 句子 1 的第二个单词[1.0, 1.1, 1.2]], # 句子 2 的第二个单词# 时间步 3[[1.3, 1.4, 1.5],  # 句子 1 的第三个单词[1.6, 1.7, 1.8]], # 句子 2 的第三个单词# 时间步 4[[1.9, 2.0, 2.1],  # 句子 1 的第四个单词[2.2, 2.3, 2.4]]  # 句子 2 的第四个单词
])
print("Input shape:", input_data.shape)  # 输出: torch.Size([4, 2, 3])

3. 初始隐藏状态

初始隐藏状态的形状
  • RNN 层数 (num_layers): 2
  • 方向数 (num_directions): 1(单向 RNN)
  • 批次大小 (batch_size): 2
  • 隐藏状态维度 (hidden_size): 5
具体初始隐藏状态
# 初始隐藏状态形状: (num_layers * num_directions, batch_size, hidden_size)
h0 = torch.zeros(2, 2, 5)  # 2层RNN,批次大小为2,隐藏状态维度为5
print("h0 shape:", h0.shape)  # 输出: torch.Size([2, 2, 5])

4. 定义 RNN 模型

import torch.nn as nn# 定义 RNN
rnn = nn.RNN(input_size=3,  # 输入特征维度hidden_size=5, # 隐藏状态维度num_layers=2,  # RNN 层数batch_first=False  # 输入形状为 (seq_len, batch_size, input_size)
)

5. 前向传播

计算输出
# 前向传播
output, hn = rnn(input_data, h0)print("Output shape:", output.shape)  # 输出: torch.Size([4, 2, 5])
print("hn shape:", hn.shape)          # 输出: torch.Size([2, 2, 5])
输出解析
  1. output:

    • 形状为 (seq_len, batch_size, hidden_size),即 (4, 2, 5)
    • 包含了每个时间步的隐藏状态。
    • 例如,output[0] 是第一个时间步的隐藏状态,output[-1] 是最后一个时间步的隐藏状态。
  2. hn:

    • 形状为 (num_layers, batch_size, hidden_size),即 (2, 2, 5)
    • 包含了最后一个时间步的隐藏状态。
    • 例如,hn[0] 是第一层的最终隐藏状态,hn[1] 是第二层的最终隐藏状态。

6. 具体输出值

output 的值
print("Output (所有时间步的隐藏状态):")
print(output)

输出示例:

tensor([[[ 0.1234,  0.5678,  0.9101,  0.1121,  0.3141],[ 0.4151,  0.6171,  0.8191,  0.0212,  0.2232]],[[ 0.4252,  0.6272,  0.8292,  0.0313,  0.2333],[ 0.4353,  0.6373,  0.8393,  0.0414,  0.2434]],[[ 0.4454,  0.6474,  0.8494,  0.0515,  0.2535],[ 0.4555,  0.6575,  0.8595,  0.0616,  0.2636]],[[ 0.4656,  0.6676,  0.8696,  0.0717,  0.2737],[ 0.4757,  0.6777,  0.8797,  0.0818,  0.2838]]],grad_fn=<StackBackward>)
hn 的值
print("hn (最后一个时间步的隐藏状态):")
print(hn)

输出示例:

tensor([[[ 0.4656,  0.6676,  0.8696,  0.0717,  0.2737],[ 0.4757,  0.6777,  0.8797,  0.0818,  0.2838]],[[ 0.4858,  0.6878,  0.8898,  0.0919,  0.2939],[ 0.4959,  0.6979,  0.8999,  0.1020,  0.3040]]],grad_fn=<StackBackward>)

batch_first=True时

以下是一个具体的例子,展示当 batch_first=True 时,PyTorch 中 torch.nn.RNN 的输入、输出以及参数的作用。


任务

假设有一个简单的任务:

  • 处理一个长度为 4 的序列
  • 批次大小为 2
  • 每个时间步的输入特征维度为 3
  • 希望使用一个 2 层的单向 RNN
  • 隐藏状态维度为 5
  • 并且设置 batch_first=True

2. 输入数据

输入句子
  • 句子 1: “I love PyTorch”
  • 句子 2: “RNN is fun”
输入数据的形状
  • 批次大小 (batch_size): 2
  • 序列长度 (seq_len): 4(假设每个单词是一个时间步)
  • 输入特征维度 (input_size): 3(假设每个单词用一个 3 维向量表示)
具体输入数据
import torch# 输入数据形状: (batch_size, seq_len, input_size)
input_data = torch.tensor([# 句子 1[[0.1, 0.2, 0.3],  # 第一个单词[0.7, 0.8, 0.9],  # 第二个单词[1.3, 1.4, 1.5],  # 第三个单词[1.9, 2.0, 2.1]], # 第四个单词# 句子 2[[0.4, 0.5, 0.6],  # 第一个单词[1.0, 1.1, 1.2],  # 第二个单词[1.6, 1.7, 1.8],  # 第三个单词[2.2, 2.3, 2.4]]  # 第四个单词
])
print("Input shape:", input_data.shape)  # 输出: torch.Size([2, 4, 3])

3. 初始隐藏状态

初始隐藏状态的形状
  • RNN 层数 (num_layers): 2
  • 方向数 (num_directions): 1(单向 RNN)
  • 批次大小 (batch_size): 2
  • 隐藏状态维度 (hidden_size): 5
具体初始隐藏状态
# 初始隐藏状态形状: (num_layers * num_directions, batch_size, hidden_size)
h0 = torch.zeros(2, 2, 5)  # 2层RNN,批次大小为2,隐藏状态维度为5
print("h0 shape:", h0.shape)  # 输出: torch.Size([2, 2, 5])

4. 定义 RNN 模型

import torch.nn as nn# 定义 RNN
rnn = nn.RNN(input_size=3,  # 输入特征维度hidden_size=5, # 隐藏状态维度num_layers=2,  # RNN 层数batch_first=True  # 输入形状为 (batch_size, seq_len, input_size)
)

5. 前向传播

计算输出
# 前向传播
output, hn = rnn(input_data, h0)print("Output shape:", output.shape)  # 输出: torch.Size([2, 4, 5])
print("hn shape:", hn.shape)          # 输出: torch.Size([2, 2, 5])
输出解析
  1. output:

    • 形状为 (batch_size, seq_len, hidden_size),即 (2, 4, 5)
    • 包含了每个时间步的隐藏状态。
    • 例如,output[0] 是第一个句子的所有时间步的隐藏状态,output[1] 是第二个句子的所有时间步的隐藏状态。
  2. hn:

    • 形状为 (num_layers, batch_size, hidden_size),即 (2, 2, 5)
    • 包含了最后一个时间步的隐藏状态。
    • 例如,hn[0] 是第一层的最终隐藏状态,hn[1] 是第二层的最终隐藏状态。

6. 具体输出值

output 的值
print("Output (所有时间步的隐藏状态):")
print(output)

输出示例:

tensor([[[ 0.1234,  0.5678,  0.9101,  0.1121,  0.3141],[ 0.4252,  0.6272,  0.8292,  0.0313,  0.2333],[ 0.4454,  0.6474,  0.8494,  0.0515,  0.2535],[ 0.4656,  0.6676,  0.8696,  0.0717,  0.2737]],[[ 0.4151,  0.6171,  0.8191,  0.0212,  0.2232],[ 0.4353,  0.6373,  0.8393,  0.0414,  0.2434],[ 0.4555,  0.6575,  0.8595,  0.0616,  0.2636],[ 0.4757,  0.6777,  0.8797,  0.0818,  0.2838]]],grad_fn=<TransposeBackward0>)
hn 的值
print("hn (最后一个时间步的隐藏状态):")
print(hn)

输出示例:

tensor([[[ 0.4656,  0.6676,  0.8696,  0.0717,  0.2737],[ 0.4757,  0.6777,  0.8797,  0.0818,  0.2838]],[[ 0.4858,  0.6878,  0.8898,  0.0919,  0.2939],[ 0.4959,  0.6979,  0.8999,  0.1020,  0.3040]]],grad_fn=<StackBackward>)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/66952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RNA 测序技术概览(RNA-seq)

前言 转录组测序&#xff08;RNA-seq&#xff09;是当下最流行的二代测序&#xff08;NGS&#xff09;方法之一&#xff0c;使科研工作者实现在转录水平上定量、定性的研究&#xff0c;它的出现已经革命性地改变了人们研究基因表达调控的方式。然而&#xff0c;转录组测序&…

C语言练习(16)

猴子吃桃问题。猴子第一天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不过瘾&#xff0c;又多吃了一个。第二天早上又将剩下的桃子吃掉一半&#xff0c;又多吃了一个。以后每天早上都吃了前一天剩下的一半加一个。到第10天早上想再吃时&#xff0c;见只剩一个桃子了…

【机器学习】自定义数据集使用框架的线性回归方法对其进行拟合

一、使用框架的线性回归方法 1. 基础原理 在自求导线性回归中&#xff0c;我们需要先自定义参数&#xff0c;并且需要通过数学公式来对w和b进行求导&#xff0c;然后在反向传播过程中通过梯度下降的方式来更新参数&#xff0c;从而降低损失值。 2. 实现步骤 ① 散点输入 有一…

pytest执行报错:found no collectors

今天在尝试使用pytest运行用例的时候出现报错&#xff1a;found no collectors&#xff1b;从两个方向进行排查&#xff0c;一是看文件名和函数名是不是符合规范&#xff0c;命名要是"test_*"格式&#xff1b;二是是否存在修改文件名的情况&#xff0c;如果修改过文件…

mysql-06.JDBC

目录 什么是JDBC: 为啥存在JDBC: JDBC工作原理&#xff1a; JDBC的优势&#xff1a; 下载mysql驱动包&#xff1a; 用java程序操作数据库 1.创建dataSource: 2.与服务端建立连接 3.构造sql语句 4.执行sql 5.关闭连接&#xff0c;释放资源 参考代码&#xff1a; 插…

机器学习-线性回归(对于f(x;w)=w^Tx+b理解)

一、&#x1d453;(&#x1d499;;&#x1d498;) &#x1d498;T&#x1d499;的推导 学习线性回归&#xff0c;我们那先要对于线性回归的表达公示&#xff0c;有所认识。 我们先假设空间是一组参数化的线性函数&#xff1a; 其中权重向量&#x1d498; ∈ R&#x1d437; …

R语言学习笔记之语言入门基础

一、R语言基础 快速熟悉R语言中的基本概念&#xff0c;先入个门。 1、运算符 运算符含义例子加1 1-减3 - 2*乘3 * 2/除9 / 3^(**)乘方2 ^ 3 2 ** 3%%取余5 %% 2%/%取整5 %/% 2 2、赋值符号 等号a 1三者等价&#xff1a;把1赋值给变量a左箭头<−a <- 1右箭头−&g…

计算机网络三张表(ARP表、MAC表、路由表)总结

参考&#xff1a; 网络三张表&#xff1a;ARP表, MAC表, 路由表&#xff0c;实现你的网络自由&#xff01;&#xff01;_mac表、arp表、路由表-CSDN博客 网络中的三张表&#xff1a;ARP表、MAC表、路由表 首先要明确一件事&#xff0c;如果一个主机要发送数据&#xff0c;那么必…

【Nomoto 船舶模型】

【Nomoto 船舶模型】 1. Nomoto 船舶模型简介2. 来源及发展历程3. 构建 一阶模型Nomoto 船舶模型3.1 C 实现3.2 Python 实现3.3 说明 5. 参数辨识方法5.1 基于最小二乘法的参数辨识5.2 数学推导5.3 Python 实现5.4 说明 4. 结论参考文献 1. Nomoto 船舶模型简介 Nomoto 模型是…

深入理解GPT底层原理--从n-gram到RNN到LSTM/GRU到Transformer/GPT的进化

从简单的RNN到复杂的LSTM/GRU,再到引入注意力机制,研究者们一直在努力解决序列建模的核心问题。每一步的进展都为下一步的突破奠定了基础,最终孕育出了革命性的Transformer架构和GPT大模型。 1. 从n-gram到循环神经网络(RNN)的诞生 1.1 N-gram 模型 在深度学习兴起之前,处理…

【JWT】jwt实现HS、RS、ES、ED签名与验签

JWT 实现 HS、RS、ES 和 ED 签名与验签 签名方式算法密钥类型签名要点验签要点HSHMAC-SHA256对称密钥- 使用 crypto/hmac 和对称密钥生成 HMAC 签名- 将 header.payload 作为数据输入- 使用同一密钥重新计算 HMAC 签名- 比较计算结果与接收到的签名是否一致RSRSA-SHA256公钥 …

地址栏信息location

获取信息 页面跳转 location.href当前地址栏信息 location.assign()设置跳转新的页面 location.replace() location.reload()刷新页面

程序员转型测试:解锁漏洞挖掘新旅程

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 程序…

08-ArcGIS For JavaScript-通过Mesh绘制几何体(Cylinder,Circle,Box,Pyramid)

目录 概述代码实现1、Mesh.createBox2、createPyramid3、Mesh.createSphere4、Mesh.createCylinder 完整代码 概述 对于三维场景而言&#xff0c;二位的点、线、面&#xff0c;三维的圆、立方体、圆柱等都是比较常见的三维对象&#xff0c;在ArcGIS For JavaScript中我们知道点…

直线拟合例子 ,岭回归拟合直线

目录 直线拟合,算出离群点 岭回归拟合直线&#xff1a; 直线拟合,算出离群点 import cv2 import numpy as np# 输入的点 points np.array([[51, 149],[122, 374],[225, 376],[340, 382],[463, 391],[535, 298],[596, 400],[689, 406],[821, 407] ], dtypenp.float32)# 使用…

SVN客户端使用手册

目录 一、简介 二、SVN的安装与卸载 1. 安装&#xff08;公司内部一般会提供安装包和汉化包&#xff0c;直接到公司内部网盘下载即可&#xff0c;如果找不到可以看下面的教程&#xff09; 2. 查看SVN版本 ​编辑 3. SVN卸载 三、SVN的基本操作 1. 检出 2. 清除认证数据 3. 提交…

衡量算法性能的量级标准:算法复杂度

今天开始数据结构的学习&#xff01;作为一大重点&#xff0c;拿出态度很重要&#xff0c;想要真实掌握&#xff0c;博客笔记自然少不了&#xff01;重点全部上色&#xff01;避免疏忽 下面我们从0基础开始学习今天的第一节&#xff01;不用担心看不懂&#xff0c;拒绝枯燥的理…

Spring Boot Starter介绍

前言 大概10来年以前&#xff0c;当时springboot刚刚出现并没有流行&#xff0c;当时的Java开发者们开发Web应用主要是使用spring整合springmvc或者struts、iBatis、hibernate等开发框架来进行开发。项目里一般有许多xml文件配置&#xff0c;其中配置了很多项目中需要用到的Be…

Java面试题2025-Spring

讲师&#xff1a;邓澎波 Spring面试专题 1.Spring应该很熟悉吧&#xff1f;来介绍下你的Spring的理解 1.1 Spring的发展历程 先介绍Spring是怎么来的&#xff0c;发展中有哪些核心的节点&#xff0c;当前的最新版本是什么等 通过上图可以比较清晰的看到Spring的各个时间版本对…

浅谈Redis

2007 年&#xff0c;一位程序员和朋友一起创建了一个网站。为了解决这个网站的负载问题&#xff0c;他自己定制了一个数据库。于2009 年开发&#xff0c;称之为Redis。这位意大利程序员是萨尔瓦托勒桑菲利波(Salvatore Sanfilippo)&#xff0c;他被称为Redis之父&#xff0c;更…