PyTorch RNN的原理及其手写复现。

PyTorch RNN的原理及其手写复现。

  • 0、前言
  • 代码实现
    • 记忆单元(考虑过去的信息)分类包括:1.RNN 2.GRU 3.LSTM
    • 模型类别:1.单向循环(左到右) 2.双向循环(考虑未来信息) 3.多层单向或双向循环
    • 优缺点
    • 应用场景
    • 具体公式

0、前言

先给出代码的实现(包括官方API和手动实现)然后逐步介绍RNN的优缺点,应用场景等。

在看代码之前有必要了解输入输出有哪些,以及他们的特性。
官方教程在:
https://pytorch.org/docs/stable/generated/torch.nn.RNN.html#torch.nn.RNN
在这里插入图片描述
参数:(实例化时候可以传入的参数)

  • input_size - 输入 x 中预期特征的数量。
  • hidden_size - 隐藏状态h的特征数。
  • num_layers - 循环层数。例如,设置 num_layers=2 意味着将两个 RNN 堆叠在一起形成堆叠式 RNN,第二个 RNN 接收第一个 RNN 的输出并计算最终结果。默认值:1
  • nonlinearity - 使用的非线性。可以是tanhrelu。默认值:tanh
  • bias - 如果为 False,则该层不使用偏置权重 b_ih 和 b_hh。默认值:True
  • batch_first - 如果为 True,则输入和输出张量将作为 (batch, seq, feature) 而不是 (seq, batch, feature) 提供。请注意,这不适用于隐藏状态或细胞状态。有关详细信息,请参阅下面的输入/输出部分。默认值:False
  • dropout - 如果非零,则在除最后一层之外的每个 RNN 层的输出上引入一个 Dropout 层,dropout 概率等于 dropout。默认值:0
  • bidirectional - 如果为True,则成为双向 RNN。默认值:False

输入:(需要初始化的值)

  • input:对于非批处理输入,形状为 ( L , H i n ) (L, H_{in}) (L,Hin) 的张量, ( L , N , H i n ) (L, N, H_{in}) (L,N,Hin)batch_first=False ( N , L , H i n ) ( N, L, H_{in}) (N,L,Hin)batch_first=True 时包含输入序列的特征。输入也可以是打包的可变长度序列。有关详细信息,请参阅 torch.nn.utils.rnn.pack_padded_sequence() torch.nn.utils.rnn.pack_sequence()
  • h_0:形状张量 ( D ∗ num_layers , H o u t ) (D * \text{num\_layers}, H_{out}) (Dnum_layers,Hout) 对于非批处理输入或 ( D ∗ num_layers , N , H o u t ) (D * \text{num\_layers}, N, H_{out}) (Dnum_layers,N,Hout)包含输入序列批次的初始隐藏状态。如果未提供,则默认为零
    其中:
    N = b a t c h   s i z e L = s e q u e n c e   l e n g t h D = 2   i f   b i d i r e c t i o n a l = T r u e   o t h e r w i s e   1 H i n = i n p u t _ s i z e H o u t = h i d d e n _ s i z e N=batch\ size\\ L=sequence\ length\\ D=2\ if\ bidirectional=True\ otherwise\ 1\\ H_{in}=input\_size\\ H_{out}=hidden\_size N=batch sizeL=sequence lengthD=2 if bidirectional=True otherwise 1Hin=input_sizeHout=hidden_size
    输出:
  • output:形状张量 ( L , D ∗ H o u t ) (L, D * H_{out}) (L,DHout) 对于非批处理输入, ( L , N , D ∗ H o u t ) (L, N, D * H_{out} ) (L,N,DHout)batch_first=False ( N , L , D ∗ H o u t ) (N, L, D * H_{out}) (N,L,DHout)batch_first=True 对于每个 t,包含来自 RNN 最后一层的输出特征 (h_t)。如果torch.nn.utils.rnn.PackedSequence 已作为输入给出,输出也将是一个打包序列。
  • h_n:形状张量 ( D ∗ n u m _ l a y e r s , H o u t ) (D * num\_layers, H_{out}) (Dnum_layers,Hout) 对于非批处理输入或 ( D ∗ n u m _ l a y e r s , N , H o u t ) (D∗num\_layers,N,Hout) (Dnum_layers,N,Hout)包含批次中每个元素的最终隐藏状态。(其实output的最后一个元素就是h_n)

变量:

  • weight_ih_l[k] - 第 k 层的可学习输入隐藏权重,形状为 (hidden_size, input_size) for k = 0。否则,形状为 (hidden_size, num_directions * hidden_size)
  • weight_hh_l[k] - 第 k 层的可学习隐藏-隐藏权重,形状为 (hidden_size, hidden_size)
  • bias_ih_l[k] - 第 k 层的可学习输入隐藏偏置,形状为 (hidden_size)
  • bias_hh_l[k] - 第 k 层的可学习隐藏-隐藏偏差,形状为 (hidden_size)

注意:

  • 所有的权重和偏置都从 U ( − k , k ) \mathcal{U}(-\sqrt{k}, \sqrt{k}) U(k ,k ) 初始化,其中 k = 1 h i d d e n _ s i z e k = \frac{1 }{hidden\_size} k=hidden_size1
  • 对于双向 RNN,前向和后向分别是方向 0 和 1。 batch_first=False 时分割输出层的示例:output.view(seq_len, batch, num_directions, hidden_​​size)
  • batch_first 参数对于未批处理的输入会被忽略。

读代码实现以加深理解。

代码实现

import torch
import torch.nn as nn
# 单向单层rnn
single_rnn = nn.RNN(4,3,1,batch_first=True)#inputs hiddens numlayer
input = torch.randn(1, 2, 4)# bs sl inputs sl为一句话的符号长度sequence_length
output, h_n = single_rnn(input) # h_0默认为0
output # N L D*hiddens D为是否双向2和1

tensor([[[-0.7853, 0.0544, 0.7991],
[-0.9409, -0.6767, 0.3354]]], grad_fn=< TransposeBackward1>)

h_n # D*num_layer bs hiddens(是output的最后一行)

tensor([[[-0.9409, -0.6767, 0.3354]]], grad_fn=< StackBackward0>)

# 双向单层RNN
bidirection_rnn = nn.RNN(4, 3, 1, batch_first=True, bidirectional=True)
bi_output, bi_h_n = bidirection_rnn(input) # h_0默认为0
bi_output # 可见D为2时长度翻倍,前向和后向的一个拼接

tensor([[[-0.0232, 0.2236, 0.2157, 0.6127, -0.2099, -0.1464],
[-0.4751, 0.0314, -0.2041, 0.0172, 0.1942, -0.0315]]],
grad_fn=< TransposeBackward1>)

bi_h_n

tensor([[[-0.4751, 0.0314, -0.2041]],
[[ 0.6127, -0.2099, -0.1464]]], grad_fn=< StackBackward0>)

h_n.shape

torch.Size([1, 1, 3])

bi_h_n.shape # 可以看成是几行几列个元素,然后元素里面有几维

torch.Size([2, 1, 3])

output.shape

torch.Size([1, 2, 3])

bi_output.shape

torch.Size([1, 2, 6])

bs, T =2, 3 # 批大小 序列长度
input_size, hidden_size = 2, 3 # 输入特征大小,隐含层特征大小
input = torch.randn(bs, T, input_size) #随机初始化一个输入特征序列
h_prev = torch.zeros(bs, hidden_size) #初始隐含状态
rnn = nn.RNN(input_size, hidden_size, batch_first=True)
rnn_output, state_final = rnn(input, h_prev.unsqueeze(0))
print(rnn_output) # bs sqlen D*h_dim [2.3.3]
print(state_final) # D*numlayer bs h_dim [1,2,3]

tensor([[[ 0.6071, 0.1986, 0.0281],
[ 0.4046, -0.0669, -0.6962],
[ 0.7330, -0.2803, 0.8956]],
[[ 0.3249, 0.1065, -0.0519],
[ 0.3640, -0.0357, -0.3023],
[ 0.5715, -0.0152, 0.1839]]], grad_fn=< TransposeBackward1>)
tensor([[[ 0.7330, -0.2803, 0.8956],
[ 0.5715, -0.0152, 0.1839]]], grad_fn=< StackBackward0>)

# 单向RNN
def rnn_forward(input, weight_ih, bias_ih, weight_hh, bias_hh, h_prev):
  bs, T, input_size = input.shape
  h_dim = weight_ih.shape[0]# 第二个维度为input_size
  h_out = torch.zeros(bs, T, h_dim) #初始化一个输出(状态)矩阵

  for t in range(T):
    x = input[:,t,:].unsqueeze(2)# 获取当前时刻输入特征bs*input_size*1
    w_ih_batch = weight_ih.unsqueeze(0).tile(bs,1,1)# bs*h_dim*input_size
    w_hh_batch = weight_hh.unsqueeze(0).tile(bs,1,1)# bs+h_dim*h_dim

    w_times_x = torch.bmm(w_ih_batch, x).squeeze(-1) # bs* h_dim
    w_times_h = torch.bmm(w_hh_batch, h_prev.unsqueeze(2)).squeeze(-1) # bs*h_dim

    h_prev = torch.tanh(w_times_x + bias_ih + w_times_h + bias_hh)

    h_out[:,t,:] = h_prev
  return h_out, h_prev.unsqueeze(0)
for k,v in rnn.named_parameters():
  print(k, v)

weight_ih_l0 Parameter containing:
tensor([[-0.1932, -0.1968],
[-0.0271, 0.1088],
[-0.1119, -0.5345]], requires_grad=True)
weight_hh_l0 Parameter containing:
tensor([[ 0.0845, 0.3735, 0.3438],
[-0.4489, -0.3440, 0.1652],
[-0.2699, -0.4274, -0.5328]], requires_grad=True)
bias_ih_l0 Parameter containing:
tensor([ 0.2186, 0.0261, -0.1649], requires_grad=True)
bias_hh_l0 Parameter containing:
tensor([ 0.2488, 0.1776, -0.0539], requires_grad=True)

custom_rnn_output, custom_state_final = rnn_forward(input, rnn.weight_ih_l0, rnn.bias_ih_l0, \
                                                    rnn.weight_hh_l0, rnn.bias_hh_l0, h_prev)
print(custom_rnn_output)
print(custom_state_final)

tensor([[[ 0.6071, 0.1986, 0.0281],
[ 0.4046, -0.0669, -0.6962],
[ 0.7330, -0.2803, 0.8956]],
[[ 0.3249, 0.1065, -0.0519],
[ 0.3640, -0.0357, -0.3023],
[ 0.5715, -0.0152, 0.1839]]], grad_fn=< CopySlices>)
tensor([[[ 0.7330, -0.2803, 0.8956],
[ 0.5715, -0.0152, 0.1839]]], grad_fn=< UnsqueezeBackward0>)

# 手写bidirection双向RNN计算原理
def bidirection_rnn_forward(input, weight_ih, bias_ih, weight_hh, bias_hh, h_prev, \
                            weight_ih_reverse, bias_ih_reverse, weight_hh_reverse, bias_hh_reverse, h_prev_reverse):
  bs, T, input_size = input.shape
  h_dim = weight_ih.shape[0]# 第二个维度为input_size
  h_out = torch.zeros(bs, T, h_dim*2) #初始化一个输出(状态)矩阵,注意双向是两倍的特征大小
  forward_output = rnn_forward(input, weight_ih, bias_ih, weight_hh, bias_hh, h_prev)[0]# forward layer
  backward_output = rnn_forward(torch.flip(input, [1]), weight_ih_reverse, bias_ih_reverse, weight_hh_reverse, bias_hh_reverse, h_prev_reverse)[0]# backward layer

  h_out[:,:,:h_dim] = forward_output
  h_out[:,:,h_dim:] = torch.flip(backward_output,[1])

  h_n=torch.zeros(bs, 2, h_dim)
  h_n[:,0,:] = forward_output[:,-1,:]
  h_n[:,1,:] = backward_output[:,-1,:]
  h_n=h_n.transpose(0, 1)

  return h_out, h_n
  # return h_out, h_out[:,-1,:].reshape((bs, 2, h_dim)).transpose(0, 1)# 最后一行所有元素
# 验证双向正确性
bi_rnn  = nn.RNN(input_size, hidden_size, batch_first=True, bidirectional=True)
h_prev = torch.zeros(2, bs, hidden_size)
bi_rnn_output, bi_state_final = bi_rnn(input, h_prev)
# for k,v in bi_rnn.named_parameters():
#   print(k, v) # 8个参数,正向和反向
print(bi_rnn_output)
print(bi_state_final)

tensor([[[-0.3642, 0.4562, 0.4547, -0.6855, 0.4821, 0.4744],
[ 0.0578, -0.4896, -0.1514, 0.1265, 0.2736, -0.3262],
[-0.8295, 0.7790, 0.7138, 0.0208, 0.7951, 0.8989]],
[[-0.7812, -0.2871, -0.4005, 0.3212, 0.7452, 0.7127],
[-0.5989, -0.2411, -0.6252, 0.7171, 0.2230, -0.0753],
[-0.4590, 0.5220, 0.0923, -0.4908, 0.4354, 0.5577]]],
grad_fn=< TransposeBackward1>)
tensor([[[-0.8295, 0.7790, 0.7138],
[-0.4590, 0.5220, 0.0923]],
[[-0.6855, 0.4821, 0.4744],
[ 0.3212, 0.7452, 0.7127]]], grad_fn=< StackBackward0>)

custom_bi_rnn_output, custom_bi_state_final = bidirection_rnn_forward(input, bi_rnn.weight_ih_l0, \
                                                                      bi_rnn.bias_ih_l0, bi_rnn.weight_hh_l0, \
                                                                      bi_rnn.bias_hh_l0, h_prev[0], \
                                                                      bi_rnn.weight_ih_l0_reverse, \
                                                                      bi_rnn.bias_ih_l0_reverse, bi_rnn.weight_hh_l0_reverse, \
                                                                      bi_rnn.bias_hh_l0_reverse, h_prev[1])
print(custom_bi_rnn_output)
print(custom_bi_state_final)

tensor([[[-0.3642, 0.4562, 0.4547, -0.6855, 0.4821, 0.4744],
[ 0.0578, -0.4896, -0.1514, 0.1265, 0.2736, -0.3262],
[-0.8295, 0.7790, 0.7138, 0.0208, 0.7951, 0.8989]],
[[-0.7812, -0.2871, -0.4005, 0.3212, 0.7452, 0.7127],
[-0.5989, -0.2411, -0.6252, 0.7171, 0.2230, -0.0753],
[-0.4590, 0.5220, 0.0923, -0.4908, 0.4354, 0.5577]]],
grad_fn=< CopySlices>)
tensor([[[-0.8295, 0.7790, 0.7138],
[-0.4590, 0.5220, 0.0923]],
[[-0.6855, 0.4821, 0.4744],
[ 0.3212, 0.7452, 0.7127]]], grad_fn=< TransposeBackward0>)

可以看到结果是一样的!!!

RNN的手写算法在这个colab链接里面,包括调用api和自己写。
https://drive.google.com/file/d/1ph2dN92gnzYcAFxWrqEYY9vopznoldP4/view?usp=sharing

记忆单元(考虑过去的信息)分类包括:1.RNN 2.GRU 3.LSTM

模型类别:1.单向循环(左到右) 2.双向循环(考虑未来信息) 3.多层单向或双向循环

请添加图片描述
在这里插入图片描述
既能看到过去又能看到未来。

优缺点

优点:

  1. 可以处理变长序列(权重在每一时刻共享)
  2. 模型大小与序列长度无关
  3. 计算量与序列长度呈线性增长
  4. 考虑历史信息(隐含层)
  5. 便于流式输出
  6. 权重时不变

缺点:
1.串行计算比较慢
2.无法获取太长的历史信息 (Transformer可以)

应用场景

  1. AI诗歌生成(one2many)
    请添加图片描述

  2. 文本情感分类(many2one)请添加图片描述

  3. 词法识别(many2many)
    请添加图片描述

  4. 机器翻译(many2many)seq2seq有编码器和解码器中间有attention帮助解码器
    请添加图片描述

  5. 语音识别/合成

  6. 语言模型

具体公式

在这里插入图片描述
可以看作是对输入 x t x_t xt和前一个隐含层的状态 h t − 1 h_{t-1} ht1的一个线性层。权重加偏置,然后就是一个非线性层tanh或者ReLU。

主要参考自
https://www.bilibili.com/video/BV13i4y1R7jB/?spm_id_from=333.999.0.0&vd_source=5413f4289a5882463411525768a1ee27

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单位、家庭建筑物电气、电子设备防雷举措

前 言 在现实的学习、工作、生活中&#xff0c;有时会面对自然灾害、重特大事故、环境公害及人为破坏等突发事件&#xff0c;为了控制事故的发展&#xff0c;就不得不需要事先制定应急预案。那要怎么制定科学的应急预案呢﹖下面是小编为大家整理的单位、住宅建筑物、电子电气防…

如何搭建一个高效、可靠的积分商城系统?

互联网购物的普及&#xff0c;积分商城系统已经成为商家和消费者之间互动的一种常见方式。它不仅可以帮助商家增加品牌影响力&#xff0c;还可以提高顾客体验&#xff0c;从而增加销售额。下面就如何搭建一个高效、可靠的积分商城系统作一些简单介绍。 第一步&#xff1a;确定需…

DHTMLX Suite JS PRO 8.1.1 Crack

适用于现代 Web 应用程序的强大 JavaScript 小部件库 - DHTMLX 套件 用于创建现代用户界面的轻量级、快速且通用的 JavaScript/HTML5 UI 小部件库。 DHTMLX Suite 有助于推进 Web 开发和构建具有丰富功能的数据密集型应用程序。 DHTMLX Suite 是一个 UI 小部件库&#xff0c;用…

Spring Boot中使用Spring Batch处理批量任务

Spring Boot中使用Spring Batch处理批量任务 Spring Batch是Spring框架的一个模块&#xff0c;它提供了一组API和工具&#xff0c;用于处理批量任务。在本文中&#xff0c;我们将会介绍如何在Spring Boot中使用Spring Batch来处理批量任务。我们将会使用一个简单的示例来说明如…

Install Prometheus Monitoring On Kubernetes Cluster

目录 Node & Software & Docker Images Lists ​Prometheus introduction Download Kubernetes Prometheus Manifest Files Install Prometheus Monitoring Kubernetes Create a Namespace Create a Cluster Role And Binding It Create a Config Map Create…

二、CNNs网络架构-卷积分离网络架构

《A review of convolutional neural network architectures and their optimizations》论文指出AlexNet的优异性能证明了可以通过增加网络深度提高网络性能。随着网络层数的不断增加&#xff0c;不断增加的计算负担和不显著的性能提升使得更先进的网络架构成为另一个主要的研究…

搭建监控日志系统

在微服务或者集群架构中&#xff0c;一次请求的调用会跨多个服务&#xff08;web&#xff0c;mysql&#xff0c;feign等&#xff09;、多个模块&#xff08;用户模块&#xff0c;商品模块等&#xff09;、多个容器&#xff08;用户模块可能有多个实例&#xff09;&#xff0c;这…

Linux命令(21)之usermod

Linux命令之usermod 1.usermod介绍 usermod命令用来更改/etc/passwd或/etc/shadow文件下用户属性&#xff0c;包括但不限于shell类型、用户id&#xff0c;用户gid、家目录、锁定及解锁用户等等。 2.usermod用法 usermod [参数] [用户名] usermod常用参数 参数说明-u修改UID…

Restful接口开发与测试—接口测试

开发完接口&#xff0c;接下来我们需要对我们开发的接口进行测试。接口测试的方法比较多&#xff0c;使用接口工具或者Python来测试都可以&#xff0c;工具方面比如之前我们学习过的Postman或者Jmeter &#xff0c;Python脚本测试可以使用Requests unittest来测试。 测试思路…

四维轻云平台常见问题及解决方法

1、在地图中看不见加载的点云或倾斜摄影模型数据&#xff1f; 若点云或模型数据加载后&#xff0c;在地图中看不见&#xff0c;可能是地形的高度高于倾斜模型的高度&#xff0c;导致数据漂浮在空中或者在地形以下&#xff0c;可通过增加数据的移动值Y来调整点云或者模型数据的…

支付宝SDK接口调试- cpolar内网穿透工具实现公网地址调试(1)

文章目录 1.测试环境2.本地配置3. 内网穿透3.1 下载安装cpolar内网穿透3.2 创建隧道 4. 测试公网访问5. 配置固定二级子域名5.1 保留一个二级子域名5.2 配置二级子域名 6. 使用固定二级子域名进行访问 转发自cpolar内网穿透的文章&#xff1a;Java支付宝沙箱环境支付&#xff0…

16款ChatGPT工具,太炸裂了,收藏!

1.ChatGPT for google 一个浏览器插件&#xff0c;可搭配现有的搜索引擎来使用。 最大化搜索效率&#xff0c;对搜索体验的提升相当离谱&#xff1a; 安装完插件后&#xff0c;在搜索引擎搜索任何问题&#xff0c;都能获取两份答案。 左边是谷歌抓取的全网资源&#xff0c;右…

chatgpt赋能python:简介:什么是PythonShapiro?

简介&#xff1a;什么是Python Shapiro&#xff1f; Python Shapiro是一种用来进行正态性检验的工具&#xff0c;也就是说&#xff0c;它可以帮助我们检验一个给定的数据集是否符合正态分布的要求。它是从R语言中的Shapiro-Wilk测试方法改编而来的。 如何使用Python Shapiro&…

ELK 企业级日志分析系统

---------------------- ELK 概述 ---------------------------------------- 1、ELK 简介 ELK平台是一套完整的日志集中处理解决方案&#xff0c;将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用&#xff0c; 完成更强大的用户对日志的查询、排序、统计需求。 ●…

1123 Is It a Complete AVL Tree (PAT甲级)

这道题是看了柳婼的解法才搞定的。开始想着把height和parent放到结构体中去&#xff0c;很繁琐最后还搞不定…… #include <cstdio> #include <algorithm> #include <vector>struct node{int key;node* left nullptr;node* right nullptr; };int N, t, pi…

一文打通File类

目录 基本概述 常用构造器 构造方法 路径分隔符 常用方法 File类的获取功能 File类的重命名功能 File类的判断功能 File类的创建功能 File类的删除功能 在 Java 中&#xff0c;File 类是 java.io 包中唯一代表磁盘文件本身的对象&#xff0c;也就是说&#xff0c;如果…

2023/5/21总结

因为之前高中学过一点点的html。虽然不是很多&#xff0c;但是有一点点基础&#xff0c;看了一些关于html的知识点&#xff0c;算是复习了&#xff0c;如果后面忘记打算再去查。 html是超文本标记语言&#xff0c;通常由<></>构成&#xff0c;当然也有单标记&…

Cisco Secure Web Appliance Virtual 15.0 发布 - 适用于网络安全的思科高级威胁防护

Cisco Secure Web Appliance Virtual, AsyncOS for WSA 15.0.0 LD 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-secure-web-appliance-15/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Cisco Secure Web Appli…

游资92科比到底牛在哪里?

昨天一天时间把92科比之前的一个帖子全部看完&#xff0c;从科比对情绪周期的把握来看那简直总结的已经是标准答案了&#xff0c;那么为何92科比公布了答案&#xff0c;还是有很多人“痛苦”的做不到&#xff1f; 这个问题我觉得跟退学炒股是一样的&#xff0c;退学先解决了小…

关于Jetpack DataStore(Preferences)的八点疑问

前言 DataStore是Android上一种轻量级存储方案&#xff0c;依据官方教程很容易就写出简易的Demo。 本篇主要是分析关于DataStore(Preferences)使用过程中的一些问题&#xff0c;通过问题寻找本质&#xff0c;反过来能更好地指导我们合理使用DataStore。 本篇内容目录&#xff…