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

这篇具有很好参考价值的文章主要介绍了PyTorch RNN的原理及其手写复现。。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

0、前言

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

在看代码之前有必要了解输入输出有哪些,以及他们的特性。
官方教程在:
https://pytorch.org/docs/stable/generated/torch.nn.RNN.html#torch.nn.RNN
PyTorch 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.多层单向或双向循环

PyTorch RNN的原理及其手写复现。
PyTorch RNN的原理及其手写复现。
既能看到过去又能看到未来。

优缺点

优点:

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

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

应用场景

  1. AI诗歌生成(one2many)
    PyTorch RNN的原理及其手写复现。

  2. 文本情感分类(many2one)PyTorch RNN的原理及其手写复现。

  3. 词法识别(many2many)
    PyTorch RNN的原理及其手写复现。

  4. 机器翻译(many2many)seq2seq有编码器和解码器中间有attention帮助解码器
    PyTorch RNN的原理及其手写复现。

  5. 语音识别/合成

  6. 语言模型

具体公式

PyTorch RNN的原理及其手写复现。
可以看作是对输入 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文章来源地址https://www.toymoban.com/news/detail-480951.html

到了这里,关于PyTorch RNN的原理及其手写复现。的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【模型+代码/保姆级教程】使用Pytorch实现手写汉字识别

    参考文章: 最初参考的两篇: 【Pytorch】基于CNN手写汉字的识别 「Pytorch」CNN实现手写汉字识别(数据集制作,网络搭建,训练验证测试全部代码) 模型: EfficientNetV2网络详解 数据集(不必从这里下载,可以看一下它的介绍): CASIA Online and Offline Chinese Handwriting Databases 鉴于

    2024年02月07日
    浏览(44)
  • 【代码复现】Windows10复现nerf-pytorch

    由于这段时间正在学习NeRF系列的论文,所以博主决定尝试复现一下原文中实现的效果。 这篇文章将会介绍在win10系统下对NeRF的复现过程。最初是想复现原文作者Ben Mildenhall的源码,不过一直在编译环节报错,参考了几位大佬的blog之后发现是因为原作者使用了tensorflow 1.15,本

    2024年02月08日
    浏览(34)
  • 【大数据&AI人工智能】HBase的核心数据结构和算法原理是什么?给出代码实例

    HBase是一个开源的非关系型分布式数据库,它参考了Google的BigTable模型,实现语言为 Java。它是Apache软件基金会的Hadoop项目的一部分,运行在HDFS文件系统之上,为 Hadoop 提供类BigTable 的服务。 HBase的核心数据结构和算法原理是什么?给出代码实例。HBase的核心数据结构和算法原

    2024年02月09日
    浏览(58)
  • DenseNet代码复现+超详细注释(PyTorch)

    关于DenseNet的原理和具体细节,可参见上篇解读:经典神经网络论文超详细解读(六)——DenseNet学习笔记(翻译+精读+代码复现) 接下来我们就来复现一下代码。 整个DenseNet模型主要包含三个核心细节结构,分别是 DenseLayer (整个模型最基础的原子单元,完成一次最基础的

    2023年04月23日
    浏览(53)
  • ResNeXt代码复现+超详细注释(PyTorch)

    ResNeXt就是一种典型的混合模型,由基础的Inception+ResNet组合而成,本质在gruops分组卷积,核心创新点就是用一种平行堆叠相同拓扑结构的blocks代替原来 ResNet 的三层卷积的block,在不明显增加参数量级的情况下提升了模型的准确率,同时由于拓扑结构相同,超参数也减少了,便

    2024年02月15日
    浏览(51)
  • ResNet代码复现+超详细注释(PyTorch)

    关于ResNet的原理和具体细节,可参见上篇解读:经典神经网络论文超详细解读(五)——ResNet(残差网络)学习笔记(翻译+精读+代码复现) 接下来我们就来复现一下代码。 源代码比较复杂,感兴趣的同学可以上官网学习:  https://github.com/pytorch/vision/tree/master/torchvision 本

    2024年02月11日
    浏览(43)
  • (完结篇)什么是语义分割?原理+手写代码实现?

    目录 Unet语义分割 1. 如何理解“语义”“分割”。 2. 语义分割原理(重点) 3. 语义分割意义 4. 语义分割应用场景 5. Unet的优势(医学领域) 6. 先行知识储备 7. 语义分割流程 8. 项目结构及介绍 9. 安装环境(python=3.8,pytorch) 10. 实现流程(重点) 11. 损失函数 12. 评估指标 13.

    2024年02月04日
    浏览(69)
  • 卷积神经网络学习—Resnet50(论文精读+pytorch代码复现)

    如果说在CNN领域一定要学习一个卷积神经网络,那一定非Resnet莫属了。 接下来我将按照:Resnet论文解读、Pytorch实现ResNet50模型两部分,进行讲解,博主也是初学者,不足之处欢迎大家批评指正。 预备知识 :卷积网络的深度越深,提取的特征越高级,性能越好,但传统的卷积

    2024年01月19日
    浏览(45)
  • 《图像分割Unet网络分析及其Pytorch版本代码实现》

      最近两个月在做学习图像分割方面的学习,踩了无数的坑,也学到了很多的东西,想了想还是趁着国庆节有时间来做个总结,以后有这方面需要可以来看看。   神经网络被大规模的应用到计算机视觉中的分类任务中,说到神经网络的分类任务这里不得不提到CNN(卷积神经网

    2024年02月05日
    浏览(45)
  • 复现PointNet++(语义分割网络):Windows + PyTorch + S3DIS语义分割 + 代码

    Windows 10 GPU RTX 3090 + CUDA 11.1 + cudnn 8.9.6 Python 3.9 Torch 1.9.1 + cu111 所用的原始代码:https://github.com/yanx27/Pointnet_Pointnet2_pytorch Stanford3dDataset_v1.2_Aligned_Version 分享给有需要的人,代码质量勿喷。 对源代码进行了简化和注释。 分割结果保存成txt,或者利用 laspy 生成点云。 别问为啥在

    2024年01月21日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包