如何使用双向RNN和pytorch填补空白? - python

我正在尝试使用双向RNN和pytorch填补空白。

输入如下:The dog is _____, but we are happy he is okay.

输出将如下所示:

1. hyper (Perplexity score here) 
2. sad (Perplexity score here) 
3. scared (Perplexity score here)

我在这里发现了这个主意:https://medium.com/@plusepsilon/the-bidirectional-language-model-1f3961d1fb27

import torch, torch.nn as nn
from torch.autograd import Variable

text = ['BOS', 'How', 'are', 'you', 'EOS']
seq_len = len(text)
batch_size = 1
embedding_size = 1
hidden_size = 1
output_size = 1

random_input = Variable(
    torch.FloatTensor(seq_len, batch_size, embedding_size).normal_(), requires_grad=False)

bi_rnn = torch.nn.RNN(
    input_size=embedding_size, hidden_size=hidden_size, num_layers=1, batch_first=False, bidirectional=True)

bi_output, bi_hidden = bi_rnn(random_input)

# stagger
forward_output, backward_output = bi_output[:-2, :, :hidden_size], bi_output[2:, :, hidden_size:]
staggered_output = torch.cat((forward_output, backward_output), dim=-1)

linear = nn.Linear(hidden_size * 2, output_size)

# only predict on words
labels = random_input[1:-1]

# for language models, use cross-entropy :)
loss = nn.MSELoss()
output = loss(linear(staggered_output), labels)

我正在尝试重新实现以上在博客文章底部找到的代码。我是pytorch和nlp的新手,无法理解代码的输入和输出是什么。

关于输入的问题:我猜输入是给出的几个单词。在这种情况下,为什么需要一个句子的开头和结尾?为什么我看不到输入像其他经典的NLP问题一样是训练模型的语料库?我想使用Enron电子邮件语料库来训练RNN。

关于输出的问题:我看到输出是张量。我的理解是张量是一个向量,因此在这种情况下可能是一个词向量。您如何使用张量输出单词本身?

参考方案

由于这个问题是开放式的,因此我将从最后一部分开始,朝标题中提出的主要问题的更一般的答案过渡。

快速说明:正如@Qusai Alothman的评论中所指出的那样,您应该找到关于该主题的更好的资源,当涉及必要的信息时,该资源很少。

附加说明:上一节中描述的过程的完整代码将占用太多空间以提供确切的答案,而更多的是博客文章。我将重点介绍在建立这样一个具有有用链接的网络时应该采取的步骤。

最后说明:如果下面有什么愚蠢的东西(或者您想以任何方式或形式扩展答案,请通过在下面发表评论来纠正我/添加信息)。

有关输入的问题

这里的输入是从随机正态分布生成的,与实际单词无关。它应该代表word embeddings,例如将单词表示为带有语义(这很重要!)含义的数字(有时还取决于上下文(请参见当前的最新技术方法之一,例如BERT))。

输入形状

在您的示例中,它提供为:

seq_len, batch_size, embedding_size

哪里

seq_len-表示单个句子的长度(因您
数据集),我们稍后再讨论。
batch_size-多少个句子
应该在forward通过的一步中处理(如果
PyTorch是类继承的前向方法
torch.nn.Module)
embedding_size-代表一个单词的向量(它
可能范围从通常使用100/300word2vec4096
因此,使用提到的BERT等最新方法
以上)

在这种情况下,它们全都是硬编码的,大小为1,对于新手并没有真正的用处,只是以这种方式概述了这个想法。

在这种情况下,为什么需要一个句子的开头和结尾?

如果我错了,请指正我,但如果您将输入分为句子,则不需要。如果您为模型提供了多个句子,并希望明确指出每个句子的开头和结尾,则可以使用它(用于依赖于前一个/后一个句子的模型,这里似乎不是这种情况)。这些通过特殊标记(在整个语料库中不存在的特殊标记)进行编码,因此神经网络“可以学习”它们表示句子的结尾和开头(此方法只需一个特殊标记即可)。

如果您要使用严肃的数据集,我建议您使用spaCy或nltk之类的库来拆分文本(第一个是使用IMO的荣幸),它们可以很好地完成此任务。

您的数据集可能已经被拆分为句子,在这种情况下,您已经准备好了。

为什么我看不到输入像其他经典的NLP问题一样是训练模型的语料库?

我不记得在语料库上按原样训练模型,例如使用字符串。通常,这些浮点数使用以下方法表示:

简单的方法,例如Bag Of
Words或
TF-IDF
更复杂的语言,提供有关单词的一些信息
关系(例如king在语义上与queen
而不是banana)。这些已经在上面链接了,有些
其他值得注意的可能是
GloVe或
ELMo和其他大量创意
方法。

有关输出的问题

应该将索引输出到embeddings中,该索引又对应于由向量表示的单词(上述更复杂的方法)。

此类嵌入的每一行代表一个唯一的词,并且其相应的列是它们的唯一表示(在PyTorch中,第一个索引可能会保留未知表示的词的索引[如果使用预训练的嵌入],您也可以删除这些词,或者将它们表示为句子/文档的平均值,还有其他一些可行的方法)。

示例中提供的损失

# for language models, use cross-entropy :)
loss = nn.MSELoss()

对于此任务,这没有任何意义,因为Mean Squared Error是一种回归指标,而不是分类指标。

我们要使用一个进行分类,因此softmax应该用于多类情况(我们应该输出跨越[0, N]的数字,其中N是我们语料库中唯一词的数量)。

PyTorch的CrossEntropyLoss已经获取了logit(没有激活的最后一层输出,如softmax),并为每个示例返回了损失值。我会建议这种方法,因为它在数值上是稳定的(我喜欢它是最小的方法)。

我正在尝试使用双向RNN和pytorch填补空白

这是一个漫长的过程,我将仅重点介绍为创建一个模型代表其帖子中概述的模型而执行的步骤。

数据集的基本准备

您可以使用上面提到的一个,也可以从scikit-learn中的20 newsgroups开头。

第一步应该大致如下:

从数据集中抓取元数据(如果有的话)(这些元数据可能是HTML标记,某些标头等)
使用预制库将文本分成句子(如上所述)

接下来,您想在每个句子中创建目标(例如,要填充的单词)。
每个单词都应替换为特殊标记(例如<target-token>)并移到目标位置。

例:

句子:Neural networks can do some stuff.

会给我们以下句子及其各自的目标:

句子:<target-token> networks can do some stuff.目标:Neural
句子:Neural <target-token> can do some stuff.目标:networks
句子:Neural networks <target-token> do some stuff.目标:can
句子:Neural networks can <target-token> some stuff.目标:do
句子:Neural networks can do <target-token> stuff.目标:some
句子:Neural networks can do some <target-token>.目标:some
句子:Neural networks can do some stuff <target-token>目标:.

您应该通过纠正错别字来调整这种方法以解决眼前的问题,如果有错别字,请标记化,词根化等,请尝试!

嵌入

每个句子中的每个单词都应该用一个整数代替,该整数又指向它的嵌入。

我建议您使用预先训练的。 spaCy提供了单词向量,但是我强烈推荐的另一种有趣的方法是在开源库flair中。

您可以自己训练,但是在无监督的情况下需要花费大量的时间和大量的数据,我认为这超出了此问题的范围。

数据批处理

一个应该使用PyTorch的torch.utils.data.Dataset和torch.utils.data.DataLoader。

在我的情况下,一个好主意是为collate_fn提供自定义DataLoader,它负责创建填充的数据批次(或已表示为torch.nn.utils.rnn.PackedSequence)。

重要提示:当前,您必须按长度(按字排列)对批处理进行排序,并使索引能够将批处理“取消排序”为原始形式,您应该记住在实现过程中。您可以将torch.sort用于该任务。在将来的PyTorch版本中,有机会可能不必这样做,请参见this issue。

哦,记住,在我们使用数据时,请使用DataLoader随机播放数据集。

模型

您应该通过从torch.nn.Module继承来创建适当的模型。我建议您创建一个更通用的模型,在该模型中可以提供PyTorch的单元(如GRU,LSTM或RNN),多层和双向的单元(如后所述)。

关于模型构建,遵循这些原则:

import torch


class Filler(torch.nn.Module):
    def __init__(self, cell, embedding_words_count: int):
        self.cell = cell
        # We want to output vector of N
        self.linear = torch.nn.Linear(self.cell.hidden_size, embedding_words_count)

    def forward(self, batch):
        # Assuming batch was properly prepared before passing into the network
        output, _ = self.cell(batch)
        # Batch shape[0] is the length of longest already padded sequence
        # Batch shape[1] is the length of batch, e.g. 32
        # Here we create a view, which allows us to concatenate bidirectional layers in general manner
        output = output.view(
            batch.shape[0],
            batch.shape[1],
            2 if self.cell.bidirectional else 1,
            self.cell.hidden_size,
        )

        # Here outputs of bidirectional RNNs are summed, you may concatenate it
        # It makes up for an easier implementation, and is another often used approach
        summed_bidirectional_output = output.sum(dim=2)
        # Linear layer needs batch first, we have to permute it.
        # You may also try with batch_first=True in self.cell and prepare your batch that way
        # In such case no need to permute dimensions
        linear_input = summed_bidirectional_output.permute(1, 0, 2)
        return self.linear(embedding_words_count)

如您所见,有关形状的信息可以以常规方式获得。这种方法将允许您使用所需的层数来创建一个模型,该层是否为双向(batch_first参数是有问题的,但是您也可以以一般的方式解决它,为更好的清晰度而将其省略),请参见下文:

model = Filler(
    torch.nn.GRU(
        # Size of your embeddings, for BERT it could be 4096, for spaCy's word2vec 300
        input_size=300,
        hidden_size=100,
        num_layers=3,
        batch_first=False,
        dropout=0.4,
        bidirectional=True,
    ),
    # How many unique words are there in your dataset
    embedding_words_count=10000,
)

您可以将torch.nn.Embedding传递到模型中(如果经过预训练并已经填充),则可以从numpy矩阵或大量其他方法创建它,这在很大程度上取决于代码的结构。不过,请使您的代码更通用,除非完全必要(通常​​不是),否则不要对形状进行硬编码。

请记住,这只是一个展示柜,您必须自己调整和修复它。
此实现返回logits,并且不使用softmax层。如果要计算困惑度,则可能必须添加它,以便在所有可能的向量上获得正确的概率分布。

顺便说一句:Here是有关RNN双向输出串联的一些信息。

模型训练

我强烈建议PyTorch ignite,因为它非常可定制,您可以使用它记录很多信息,进行验证和抽象混乱的部分,例如训练中的循环。

哦,将您的模型,训练和其他模型分成单独的模块,不要将所有内容都放入一个不可读的文件中。

最后的笔记

这是我如何解决此问题的概述,尽管您不应该以此为起点,但使用注意力网络而不是仅使用本示例中的最后一个输出层可能会带来更多乐趣。

并且,请查看PyTorch的1.0文档,不要盲目跟随您在网上看到的教程或博客文章,因为它们可能真的过时了,并且代码质量差异很大。例如,不赞成使用torch.autograd.Variable,如在链接中所见。

Python GPU资源利用 - python

我有一个Python脚本在某些深度学习模型上运行推理。有什么办法可以找出GPU资源的利用率水平?例如,使用着色器,float16乘法器等。我似乎在网上找不到太多有关这些GPU资源的文档。谢谢! 参考方案 您可以尝试在像Renderdoc这样的GPU分析器中运行pyxthon应用程序。它将分析您的跑步情况。您将能够获得有关已使用资源,已用缓冲区,不同渲染状态上…

Python:图像处理可产生皱纹纸效果 - python

也许很难描述我的问题。我正在寻找Python中的算法,以在带有某些文本的白色图像上创建皱纹纸效果。我的第一个尝试是在带有文字的图像上添加一些真实的皱纹纸图像(具有透明度)。看起来不错,但副作用是文本没有真正起皱。所以我正在寻找更好的解决方案,有什么想法吗?谢谢 参考方案 除了使用透明性之外,假设您有两张相同尺寸的图像,一张在皱纹纸上明亮,一张在白色背景上有深…

Python uuid4,如何限制唯一字符的长度 - python

在Python中,我正在使用uuid4()方法创建唯一的字符集。但是我找不到将其限制为10或8个字符的方法。有什么办法吗?uuid4()ffc69c1b-9d87-4c19-8dac-c09ca857e3fc谢谢。 参考方案 尝试:x = uuid4() str(x)[:8] 输出:"ffc69c1b" Is there a way to…

Python sqlite3数据库已锁定 - python

我在Windows上使用Python 3和sqlite3。我正在开发一个使用数据库存储联系人的小型应用程序。我注意到,如果应用程序被强制关闭(通过错误或通过任务管理器结束),则会收到sqlite3错误(sqlite3.OperationalError:数据库已锁定)。我想这是因为在应用程序关闭之前,我没有正确关闭数据库连接。我已经试过了: connectio…

python:ConfigParser对象,然后再阅读一次 - python

场景:我有一个配置文件,其中包含要执行的自动化测试的列表。这些测试是长期循环执行的。   配置文件的设计方式使ConfigParser可以读取它。由于有两个三个参数,因此我需要通过每个测试。现在,此配置文件由script(s1)调用,并且按照配置文件中的列表执行测试。Script(s1)第一次读取配置,并且在每次测试完成后都会执行。阅读两次的要求:由于可能会…