目录

  • 一、简介
  • 二、前置知识
  • 三、解析
    • 3.1 所有参数
    • 3.2 输入参数
    • 3.3 输出参数
  • 四、通过例子来进一步理解 nn.RNN
  • 五、从零开始手写一个单隐层单向RNN
  • 最后

一、简介

torch.nn.RNN 用于构建循环层,其中的计算规则如下:

h t = tanh ⁡ ( W i h x t + b i h + W h h h t − 1 + b h h ) (1) \boldsymbol{h}_{t}=\tanh({\bf W}_{ih}\boldsymbol{x}_t+\boldsymbol{b}_{ih}+{\bf W}_{hh}\boldsymbol{h}_{t-1}+\boldsymbol{b}_{hh}) \tag{1} ht​=tanh(Wih​xt​+bih​+Whh​ht−1​+bhh​)(1)

其中 h t \boldsymbol{h}_{t} ht​ 是 t t t 时刻的隐层状态, x t \boldsymbol{x}_{t} xt​ 是 t t t 时刻的输入。下标 i i i 是 i n p u t input input 的简写,下标 h h h 是 h i d d e n hidden hidden 的简写。 W , b {\bf W},\boldsymbol{b} W,b 分别是权重和偏置。

二、前置知识

先回顾一下普通的神经网络,我们在训练它的过程中通常会投喂一小批量的数据。不妨设 batch_size = N \text{batch\_size}=N batch_size=N,则投喂的数据的形式为:

X = [ x 1 T ⋮ x N T ] N × d {\bf X}= \begin{bmatrix} \boldsymbol{x}_1^{\text T} \\ \vdots \\ \boldsymbol{x}_N^{\text T} \end{bmatrix}_{N\times d} X=⎣ ⎡​x1T​⋮xNT​​⎦ ⎤​N×d​

其中 x i = ( x i 1 , x i 2 , ⋯ , x i d ) T \boldsymbol{x}_i=(x_{i1},x_{i2},\cdots,x_{id})^{\text T} xi​=(xi1​,xi2​,⋯,xid​)T 为特征向量,维数为 d d d。

在处理序列问题中,我们会将词元转化成对应的特征向量。例如在处理一个英文句子时,我们通常会通过某种手段将每个单词转化为合适的特征向量。设序列(句子)长度为 L L L,于是在此情景下,一个句子可以表示为:

seq i = [ x i 1 T ⋮ x i L T ] L × d \text{seq}_i= \begin{bmatrix} \boldsymbol{x}_{i1}^{\text T} \\ \vdots \\ \boldsymbol{x}_{iL}^{\text T} \end{bmatrix}_{L\times d} seqi​=⎣ ⎡​xi1T​⋮xiLT​​⎦ ⎤​L×d​

其中的每个 x i j , j = 1 , ⋯ , L \boldsymbol{x}_{ij},\;j=1,\cdots, L xij​,j=1,⋯,L 都对应了句子 seq i \text{seq}_i seqi​ 中的一个单词。在上述约定下,我们在 t t t 时刻投喂给RNN的数据为:

X t = [ x 1 t T ⋮ x N t T ] N × d (2) {\bf X}_t= \begin{bmatrix} \boldsymbol{x}_{1t}^{\text T} \\ \vdots \\ \boldsymbol{x}_{Nt}^{\text T} \end{bmatrix}_{N\times d}\tag{2} Xt​=⎣ ⎡​x1tT​⋮xNtT​​⎦ ⎤​N×d​(2)

从而 ( 1 ) (1) (1) 式改写为

H t = tanh ⁡ ( X t W i h + b i h + H t − 1 W h h + b h h ) (3) {\bf H}_t=\tanh({\bf X}_t{\bf W}_{ih}+\boldsymbol{b}_{ih}+{\bf H}_{t-1}{\bf W}_{hh}+\boldsymbol{b}_{hh})\tag{3} Ht​=tanh(Xt​Wih​+bih​+Ht−1​Whh​+bhh​)(3)

其中 H t , H t − 1 {\bf H}_t,{\bf H}_{t-1} Ht​,Ht−1​ 的形状为 N × h N\times h N×h, W i h {\bf W}_{ih} Wih​ 的形状为 d × h d\times h d×h, W h h {\bf W}_{hh} Whh​ 的形状为 h × h h\times h h×h, b i h , b h h \boldsymbol{b}_{ih},\boldsymbol{b}_{hh} bih​,bhh​ 的形状为 1 × h 1\times h 1×h,求和时利用广播机制。

nn.RNN 中,我们是一次性将所有时刻的数据全部投喂进去,数据形式为:

X = [ seq 1 , seq 2 , ⋯ , seq N ] N × L × d or X = [ X 1 , X 2 , ⋯ , X L ] L × N × d {\bf X}=[\text{seq}_1,\text{seq}_2,\cdots,\text{seq}_N]_{N\times L\times d}\quad\text{or}\quad {\bf X}=[{\bf X}_1,{\bf X}_2,\cdots,{\bf X}_L]_{L\times N\times d} X=[seq1​,seq2​,⋯,seqN​]N×L×d​orX=[X1​,X2​,⋯,XL​]L×N×d​

其中左边代表 batch_first=True 的情形,右边代表 batch_first=False 的情形。

注意: 在一个 batch 中,所有 sequence 的长度要保持相同,即 L L L 需一致。

三、解析

3.1 所有参数

有了前置知识后,我们就能很方便的解释这些参数了。

  • input_size:即 d d d;
  • hidden_size:即 h h h;
  • num_layers:即RNN的层数。默认是 1 1 1 层。该参数大于 1 1 1 时,会形成 Stacked RNN,又称多层RNN或深度RNN;
  • nonlinearity:即非线性激活函数。可以选择 tanhrelu,默认是 tanh
  • bias:即偏置。默认启用,可以选择关闭;
  • batch_first:即是否选择让 batch_size 作为输入的形状中的第一个参数。当 batch_first=True 时,输入应具有 N × L × d N\times L\times d N×L×d 这样的形状,否则应具有 L × N × d L\times N\times d L×N×d 这样的形状。默认是 False
  • dropout:即是否启用 dropout。如要启用,则应设置 dropout 的概率,此时除最后一层外,RNN的每一层后面都会加上一个dropout层。默认是 0 0 0,即不启用;
  • bidirectional:即是否启用双向RNN,默认关闭。

3.2 输入参数


这里我们只考虑有 batch 的情况。

batch_first=True 时,输入 input 应具有形状 N × L × d N\times L\times d N×L×d,否则应具有形状 L × N × d L\times N\times d L×N×d。

h_0 为初始时刻的隐状态。当RNN为单向RNN时,h_0 的形状应为 num_layers × N × h \text{num\_layers}\times N\times h num_layers×N×h;当RNN为双向RNN时,h_0 的形状应为 ( 2 ⋅ num_layers ) × N × h (2\cdot \text{num\_layers})\times N\times h (2⋅num_layers)×N×h。如不提供该参数的值,则默认为全0张量。

3.3 输出参数

这里我们只考虑有 batch 的情况。

当RNN为单向RNN时:若 batch_first=True,输出 output 具有形状 N × L × h N\times L\times h N×L×h,否则具有形状 L × N × h L\times N\times h L×N×h。当 batch_first=False 时,output[t, :, :] 代表时刻 t t t 时,RNN最后一层(之所以用最后一层这个术语是因为有可能出现Stacked RNN情形)的输出 h t \boldsymbol{h}_t ht​。h_n 代表最终的隐状态,形状为 num_layers × N × h \text{num\_layers}\times N\times h num_layers×N×h。

当RNN为双向RNN时:若 batch_first=True,输出 output 具有形状 N × L × 2 h N\times L\times 2h N×L×2h,否则具有形状 L × N × 2 h L\times N\times 2h L×N×2h。h_n 的形状为 ( 2 ⋅ num_layers ) × N × h (2\cdot \text{num\_layers})\times N\times h (2⋅num_layers)×N×h。

事实上,对于单向RNN,有

output = [ H 1 , H 2 , ⋯ , H L ] L × N × h , h_n = [ H L ] 1 × N × h \text{output}=[{\bf H}_1,{\bf H}_2,\cdots,{\bf H}_L]_{L\times N\times h},\quad \text{h\_n}=[{\bf H}_L]_{1\times N\times h} output=[H1​,H2​,⋯,HL​]L×N×h​,h_n=[HL​]1×N×h​

四、通过例子来进一步理解 nn.RNN

以单隐层单向RNN为例(接下来的例子都默认 batch_first=False)。

假设有一个英文句子:He ate an apple.,忽略 . 并设置词元为单词(word)时,该序列的长度为 4 4 4。简便起见,我们假设每个词元都对应了一个 6 6 6 维的特征向量,则上述的序列可写成:

import torch
import torch.nn as nntorch.manual_seed(42)
seq = torch.randn(4, 6)  # 只是为了举例
print(seq)
# tensor([[ 1.9269,  1.4873,  0.9007, -2.1055,  0.6784, -1.2345],
#         [-0.0431, -1.6047,  0.3559, -0.6866, -0.4934,  0.2415],
#         [-1.1109,  0.0915, -2.3169, -0.2168, -0.3097, -0.3957],
#         [ 0.8034, -0.6216, -0.5920, -0.0631, -0.8286,  0.3309]])

将这个句子视为一个 batch,即(注意形状为 L × N × d L\times N\times d L×N×d):

inputs = seq.unsqueeze(1)
print(inputs)
# tensor([[[ 1.9269,  1.4873,  0.9007, -2.1055,  0.6784, -1.2345]],
#         [[-0.0431, -1.6047,  0.3559, -0.6866, -0.4934,  0.2415]],
#         [[-1.1109,  0.0915, -2.3169, -0.2168, -0.3097, -0.3957]],
#         [[ 0.8034, -0.6216, -0.5920, -0.0631, -0.8286,  0.3309]]])
print(inputs.shape)
# torch.Size([4, 1, 6])

有了 inputs,我们还需要初始化隐状态 h_0,不妨设 h = 3 h=3 h=3:

h_0 = torch.randn(1, 1, 3)
print(h_0)
# tensor([[[ 1.3525,  0.6863, -0.3278]]])

接下来创建RNN层,事实上只需要输入 input_sizehidden_size 即可:

rnn = nn.RNN(6, 3)

观察输出:

outputs, h_n = rnn(inputs, h_0)
print(outputs)
# tensor([[[-0.5428,  0.9207,  0.7060]],
#         [[-0.2245,  0.2461, -0.4578]],
#         [[ 0.5950, -0.3390, -0.4598]],
#         [[ 0.9281, -0.7660,  0.5954]]], grad_fn=<StackBackward0>)
print(h_n)
# tensor([[[ 0.9281, -0.7660,  0.5954]]], grad_fn=<StackBackward0>)

五、从零开始手写一个单隐层单向RNN

首先写好框架:

class RNN(nn.Module):def __init__(self, input_size, hidden_size):super().__init__()passdef forward(self, inputs, h_0):pass

我们的计算遵循 ( 3 ) (3) (3) 式,即:

H t = tanh ⁡ ( X t W i h + b i h + H t − 1 W h h + b h h ) {\bf H}_t=\tanh({\bf X}_t{\bf W}_{ih}+\boldsymbol{b}_{ih}+{\bf H}_{t-1}{\bf W}_{hh}+\boldsymbol{b}_{hh}) Ht​=tanh(Xt​Wih​+bih​+Ht−1​Whh​+bhh​)

class RNN(nn.Module):def __init__(self, input_size, hidden_size):super().__init__()self.W_ih = torch.randn(input_size, hidden_size)self.W_hh = torch.randn(hidden_size, hidden_size)self.b_ih = torch.randn(1, hidden_size)self.b_hh = torch.randn(1, hidden_size)def forward(self, inputs, h_0):L, N, d = inputs.shape  # 分别对应序列长度、批量大小和特征维度H = h_0[0]  # 因为h_0的形状为(1,N,h),我们需要使用(N,h)去计算outputs = torch.zeros(L, N, H.shape[1])for t in range(L):X_t = inputs[t]H = torch.tanh(X_t @ self.W_ih + self.b_ih + H @ self.W_hh + self.b_hh)outputs[t] = Hh_n = outputs[-1].unsqueeze(0)  # h_n实际上就是h_L,但此时的形状为(N,h)return outputs, h_n

为了检验我们的RNN是正确的,我们需要使用相同的输入来验证我们的输出是否与之前的一致。

torch.manual_seed(42)
seq = torch.randn(4, 6)
inputs = seq.unsqueeze(1)
h_0 = torch.randn(1, 1, 3)# 保持RNN内部参数:权重和偏置一致
rnn = nn.RNN(6, 3)
params = [param.data.T for param in rnn.parameters()]
my_rnn = RNN(6, 3)
my_rnn.W_ih = params[0]
my_rnn.W_hh = params[1]
my_rnn.b_ih[0] = params[2]
my_rnn.b_hh[0] = params[3]outputs, h_n = my_rnn(inputs, h_0)
print(outputs)
# tensor([[[-0.5428,  0.9207,  0.7060]],
#         [[-0.2245,  0.2461, -0.4578]],
#         [[ 0.5950, -0.3390, -0.4598]],
#         [[ 0.9281, -0.7660,  0.5954]]])
print(h_n)
# tensor([[[ 0.9281, -0.7660,  0.5954]]])

可以看出结果与之前的一致,这说明我们构造的RNN是正确的。

最后

博主才疏学浅,如有错误请在评论区指出,感谢!

PyTorch nn.RNN 参数全解析相关推荐

  1. 百度_谷歌_雅虎搜索引擎URL参数全解析

    一.百度搜索引擎URL参数全解析 在搜索引擎的RUL地址参数中,我们可以清晰的知道,该软件是否与搜索引擎进行了点击付费类的合作,我们以百度搜索的URL参数部分举例说明如下: http://www.ba ...

  2. pytorch nn.LSTM()参数详解

    输入数据格式: input(seq_len, batch, input_size) h0(num_layers * num_directions, batch, hidden_size) c0(num ...

  3. html 手机端的meta,H5手机端网站开发meta参数全解析

    先看看标准化H5移动端顶部meta 参数设置是如何: web_mobile 1.  指定的iphone中safari顶端的状态条的样式 ,仅限苹果端. 参数:默认值为default(白色),可以定为b ...

  4. Pytorch nn.init 参数初始化方法

  5. (pytorch-深度学习)使用pytorch框架nn.RNN实现循环神经网络

    使用pytorch框架nn.RNN实现循环神经网络 首先,读取周杰伦专辑歌词数据集. import time import math import numpy as np import torch f ...

  6. Vision Transformer(ViT)PyTorch代码全解析(附图解)

    Vision Transformer(ViT)PyTorch代码全解析 最近CV领域的Vision Transformer将在NLP领域的Transormer结果借鉴过来,屠杀了各大CV榜单.本文将根 ...

  7. Pytorch.nn.Linear 解析(数学角度)

    pytorch.nn.Linear 是一个类,下面是它的一些初始化参数 in_features : 输入样本的张量大小 out_features : 输出样本的张量大小 bias : 偏置 它主要是对 ...

  8. 机器学习算法源码全解析(三)-范数规则化之核范数与规则项参数选择

    前言 参见上一篇博文,我们聊到了L0,L1和L2范数,这篇我们絮叨絮叨下核范数和规则项参数选择.知识有限,以下都是我一些浅显的看法,如果理解存在错误,希望大家不吝指正.谢谢. 机器学习算法源码全解析( ...

  9. 深度学习总结:tensorflow和pytorch关于RNN的对比,tf.nn.dynamic_rnn,nn.LSTM

    tensorflow和pytorch关于RNN的对比: tf.nn.dynamic_rnn很难理解,他的意思只是用数据走一遍你搭建的RNN网络. 可以明显看出pytorch封装更高,更容易理解,动态图 ...

最新文章

  1. 中国污水处理行投资建议与运营价值分析报告2022-2027年
  2. 微软 python教程_最强福利——来自微软的Python学习教程(开发指南)
  3. 对使用CodeSmith模板生成NHibernate的代码的分析
  4. gradle插件 java_简单的Gradle Java插件自定义
  5. IE针对Ajax请求结果的缓存IE浏览器同一个请求第二次是缓存数据不刷新
  6. Ubuntu 16.zabbixserver服务报错: Assuming that agent dropped connection because of access permissions.
  7. shell 判断目录还是文件
  8. 归并排序JAVA代码详解
  9. 分享 野人老师-高级信息系统项目管理师-全部的课程资料
  10. Web前端开发的项目开发流程
  11. 加密货币如何改变着IT产业
  12. matlab数据读入,Matlab数据导入
  13. 快速调用企业工商数据
  14. 基本过滤工具之配置前缀列表
  15. Excel图表制作(二):动态图表制作
  16. Deltix宣布CryptoCortex与MPC钱包提供商Curv整合
  17. C语言程序设计期末考试试题(含答案)
  18. 数据结构_线段树 详解+模板
  19. Clone from GitHub with Git extension shows: Received HTTP code 407 from proxy after CONNECT(Git 代理)
  20. Linux简介和开发环境

热门文章

  1. 以Echo为例,从锂矿厂到数据矿厂的人工智能产业全景地图是这样的!
  2. 悠星网络基于阿里云分析型数据库PostgreSQL版的数据实践
  3. golang 实现类似in_array的功能
  4. Office设置不同章节的页眉页脚及下划线的清除与添加
  5. vue-cli创建vue项目详细步骤
  6. ContentResolver系统短信备份
  7. 上传文件提示413 Request Entity Too Large错误
  8. 华为云mysql怎么玩_7天搞定MySQL!华为云新推精品课程了解一下
  9. 在html页面中的js如何调试,你可能不知道的14个JavaScript调试技巧
  10. python数字转换成中文大写_python 人民币数字转大写中文