PyTorch中的循环神经网络相关函数的使用及记录

Posted 机器学习算法与自然语言处理

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PyTorch中的循环神经网络相关函数的使用及记录相关的知识,希望对你有一定的参考价值。

引言

我们都知道循环神经网络是一种能够自适应的变长网络,能够对带有上下文的连续序列很好地进行编码。

在PyTorch下面的torch.nn下,具有我们可以直接调用对应的类进行操作。比如nn.LSTM和nn.GRU。

下面直接进入到使用介绍。

基本文档

首先我们先带大家看一下文档中对应参数的意思。由于常用LSTM,我们这里就使用LSTM文档为例来简单进行介绍(GRU中的参数也是相同的)。

  • input_size: 单个LSTM神经元的输入维度。

  • hidden_size: 单个LSTM神经元的隐含层输出维度。

  • num_layers: LSTM的层数,这里指的是叠起来的层数,而不是展开的层数,展开是自适应的。

  • bias: 计算过程中是否需要偏置。

  • batch_first: batch是否位于第一个维度,这里有一个很多人容易混淆的点,后面再进行说明。

  • dropout: 中每一层的输出的dropout概率,默认为0即不进行dropout。需要注意的一点是最后一层的输出是不会加上dropout概率的。也就是说,当你只用到一层LSTM的时候,这个参数是不起作用的。在0.4.0中如果你只有一层并且dropout概率不为0的时候会抛出警告。

  • bidirectional: 是否双向。当置为True的时候,输出会为将双向LSTM的输出进行拼接,输出的feature size会增加一倍。

关于循环神经网络

2.1 关于输入输出的三个维度

对于输入输出,我们首先需要注意的是传给网络的输出必须是三维的。
其中每个维度代表的意思我们习惯的方式是[batch_size, sequence_length, feature_size]。
具体来说,假如输入的是句子的话,每个维度的含义就是:

[ 一次投入到网络中的句子的条数,句子的长度,以及句子中每个单词对应的向量维度 ]


说到这里,你是不是发现 —— 噢,还真要三个维度才能表达这么多的信息。

2.2 关于batch first

batch_first是一个非常有趣的参数,他能够将输入的形式变为我们习惯的[batch_size, seq_len, feature_size]。

也就是说原本输入参数的形式是[seq_len, batch_size, feture_size]。可以视作原本一列为一句话,现在给改成了我们更习惯的一行为一句话。

更通俗一点来说,就是原本一行为一个句子,变成每一列为一个句子。其实设置了batch first,也不过是在内部也是使用了第1和第2维度的转置操作来变成初始形式。

2.3 结合nn.Embedding的使用

我们常常在自然语言处理的任务上使用到循环卷积,因而也常会使用到词向量。
对于词向量的获取我们常常使用nn.Embedding来进行获取。

实际上,nn.Embedding仅仅是实现了查表的功能。

假设给出下面一句话:
i love nlp


我们需要对他进行处理,首先应当序列化单词,将单词转换成数字的形式来进行表示和存储。 
我们将单词与数字一一对应,那么有:
i -> 0
love -> 1
nlp -> 2

也就是将字符变成了[0, 1, 2]这个列表。
这个时候,我们可以通过nn.Embedding来方便地将索引转换成词向量的形式。这里使用随机初始化的词向量,并且每个词向量的维度为50。

# 创建一个输入输入50维的GRU
gru = nn.GRU(input_size=50, hidden_size=50, batch_first=True)

# 创建一个字典大小为3,词向量维度为50维的Embedding。  
embed = nn.Embedding(3, 50)

# 创建一个二维LongTensor索引数据(作为索引数据的时候一般都使用LongTensor)
x = torch.LongTensor([[0, 1, 2]]) # x.size() --> torch.Size([1, 3])

# 将索引映射到向量
x_embed = embed(x) # x_embed.size() --> torch.Size([1, 3, 50])

这样,我们就完成了从索引转变成向量的过程。如果想了解关于nn.Embedding的更多使用,可以翻阅PyTorch的相关文档,这里因为不是重点,就不多讲了。

循环神经网络中的输入输出

3.1 单条数据时

循环神经网络的输入输出非常容易让人困惑,下面延续上面的例子继续。

# 将embedding形式的数据投入到循环神经网络中去。  
out, hidden = gru(x_embed)
out.size()
#  =======> torch.Size([1, 3, 50])
hidden.size()
#  =======> torch.Size([1, 1, 50])

通过观察,我们可以看到几个点:

  1. 循环神经网络会自适应序列长度。比如这里使用的是长度3的序列,那么输出的序列也是长度为3。

PyTorch中的循环神经网络相关函数的使用及记录
输入与输出维度相对应
  1. 输出的out包括了每一个输入位所对应的输出。因而输入输出的格式相同。

  2. 输出的隐含层向量只返回最后一层所对应的隐含层输出。这是设计的原因是很多时候只需要使用最后一层的隐含层输出。比如Sequence to Sequence模型中的encoder一般就只传递最后一层的隐层输出给decoder。

如果我们需要使用到中间层的隐含层怎么?其实隐含层输出和输出层输出的数值是一样的,唯一的区别就是他们的输出格式可能有区别。在后面会继续说明,这也是很多人容易犯疑惑的地方。

3.2 多条数据时

大多数情况,我们会以mini-batch的形式输入数据。这个时候我们再来看看输入输出。

# 使用两条句子  
x = torch.LongTensor([[0, 1, 2], [0, 1, 2]])
x_embed = embed(x)
x_embed.size()
# =======> torch.Size([2, 3, 50])

out, hidden = gru(x_embed)

out.size()
#  =======> torch.Size([2, 3, 50])
hidden.size()
#  =======> torch.Size([1, 2, 50])

我们看到,输出还是如我们所想,跟输入的形式是一样的。
但是这里的隐含层输出就有点奇怪了。似乎与预期的不是很符合?

这里有两条的句子作为输入,那么我们应当得到的是两个隐含层的输出,按道理来说形式应当是[2, 1, 50]。而这里却是[1, 2, 50]。问题出在哪里呢?

回忆前面的batch_first这个参数,他是为了能够让我们将数据按照习惯的形式投入。而缺省状态是False,这个时候第二个维度才代表数据的输入batch数,但实际操作的过程中会还是会将第一和第二维度进行转置。
文档中的hidden输出维度的含义为[num_layers * num_directions, batch, hidden_size]。

而我们这里面的隐藏状态因为是在内部进行传递的,所以他并没有接收batch_first的影响,还是维持了内部操作时候的形式。

因而,当我们需要为循环神经网络提供隐含层的输入的时候,需要考虑到隐含层的输入形式的batch是位于第二维度的。

如果要某个输出的隐含层传递给另外序列作为隐含输入的时候(比如一些Seq2Seq模型),我们也只需要大胆地丢过去即可。

# 使用两条句子  
x = torch.LongTensor([[0, 1, 2], [0, 1, 2]])
x_embed = embed(x)

out1, hidden = gru(x_embed)
out2, hidden = gru(x_embed, hidden)
小结

PyTorch里面提供的循环神经网络非常好用,我们只需要将正确形式的一个三维矩阵投入进行即可。需要注意的是不同输出的batch所在维度。很多时候容易让人困惑,尤其是多层加上双向网络的时候。但只要我们稍加注意,还是不成问题的。


知乎专栏:Pytorch学习



推荐阅读:



  

知乎:忆臻

以上是关于PyTorch中的循环神经网络相关函数的使用及记录的主要内容,如果未能解决你的问题,请参考以下文章

Pytorch_循环神经网络RNN

仿真基本功PyTorch中torch.Tensor变形的相关函数使用方法及说明

Pytorch Note37 PyTorch 中的循环神经网络模块

全网最详细使用PyTorch实现循环神经网络

DataWhale 动手学深度学习PyTorch版-task3+4+5:文本预处理;语言模型;循环神经网络基础

Pytorch系列:自然语言处理NLP