Bonky
Neither beliver nor reject anything, because any other person has rejected of believed it. Heaven has given you a mind for judging truth and error, Use it.
By Thomas Jefferson

AllenNLP 中的延迟 (Laziness)

默认情况下,DatasetReader 将数据集中的每个实例作为列表返回。有几种原因可能导致您不希望这样子:

  • 您的数据集太大而无法放入内存
  • 您希望数据集中的每个迭代都进行某种采样
  • 您想立即开始对数据进行训练,而不是等待整个数据集被处理和建立索引。 (在这种情况下,您首先需要使用 make-vocab 命令来创建词汇表,这样训练就可以立即开始。)

在这些情况下,您将希望您的 DatasetReader 是“延迟”的。也就是说,根据需要而不是一次创建并产生(yield up) 实例。

本教程将向您展示如何创建允许这种延迟的 DatasetReader,以及在训练模型时如何处理这种延迟。

如果您对这些细节不感兴趣,我们用一个非常简短的“方法”来告诉您要执行的步骤。但实际上您应该对细节感兴趣。

您可以在 DatasetReader 构造函数中指定延迟

如果查看基础 DatasetReader 类的构造函数,则可以看到它带有一个参数:

这意味着,如果希望您的 DatasetReader 子类允许延迟,则其构造函数需要将延迟值传递给 DatasetReader 构造函数,并且其 from_params 方法需要允许此类参数。 AllenNLP 随附的所有数据集读取器都是以这种方式构建的。

例如,查看 SnliReader。它的构造函数采用一个延迟参数,并将其传递给超类构造函数:

您要处理延迟数据集的任何DatasetReader 就照着上面写就好了。

DatasetReader.read 中的延迟

DatasetReader 的主要公共接口是其 read 方法,该方法在基类(我已经精简了它的本质)

在这两种情况下,它都会调用 private_read 方法,您必须实现该方法,该方法本身会返回 Iterable [Instance]。下面包含更多内容。

如果使用 lazy = False 实例化了数据集读取器,则只需确保返回的实例在列表中并返回该列表即可。该列表已加载到内存中,并且(由于是列表)可以在初始调用 .read() 之后重复进行迭代。

更为有趣的情况是使用 lazy = True 实例化数据集读取器时。在那种情况下,我们返回一个_LazyInstances,该 LazyInstances 使用调用 _read()lambda 函数初始化。 _LazyInstances 只是一个简单的包装器,用于产生延迟的可迭代对象(再次,我精简了它的本质):

这意味着结果是一个可迭代的,每次对其进行迭代时都会调用提供的函数。使用 lambda 传递时,每次迭代时都会调用 self._read(file_path)

换句话说,如果您实现自己的数据集读取器并执行以下操作:

然后,每个实例的实例中的每个纪元都会导致对 MyDatasetReader._read()的新调用,并且您的实例将从磁盘中读取10次。

YourDatasetReader._read() 中的延迟

当实现 DatasetReader 子类时,必须使用逻辑覆盖private方法,以生成数据集中的所有实例。

您的实现可以返回一个列表,但是您应该考虑将 _read 实现为一次生成一个实例的生成器。否则,您的数据集阅读器将无法以延迟方式使用。 (实际上,如果read()实现返回一个列表,并且尝试在数据集读取器中设置lazy = True,则会收到错误)

这意味着您不应该做类似下面这样的事情

但应该这么做

lazy=FalseDatasetReader.read() 实例化为实例为一个列表,然后再返回。

experiment.json 中的延迟

如果您如上所述实现了数据集读取器,则延迟只需要在数据集读取器配置中添加 "lazy": true

DataIterator 中的延迟

AllenNLP使用DataIterator 来迭代读取数据集,使用可配置的批处理,打乱等方式。包括的数据集读取器均使用延迟数据集,但在这种情况下,您可能需要指定其他选项。特别是,我们将看一下BasicIterator

如果使用默认参数并在延迟数据集上调用此迭代器,它将一次延迟读取32个实例,将这些实例打包为批处理,然后逐个生成。如果在调用迭代器时指定 shuffle = True,它将对每个批处理中的 32 个实例进行打乱,但始终以相同的顺序提供批处理。

如果对内存中可以容纳多少个实例有所了解,可以指定 max_instances_in_memory 参数。例如,如果将其设置为 3200,则迭代器将一次将 3200 个实例加载到内存中,可能会对其进行打乱,然后创建100个大小为32的批次,并在加载下一个 3200 个实例之前产生它们。

如果您使用的是 BucketIterator,这尤其有用,它可以通过例如句子长度。如果您不指定max_instances_in_memory,则只需按句子长度将每批 32 个排序,这并不是特别有用。

这里的另一个选项是 instances_per_epoch。通过 默认情况下,每个时期都是一次遍历数据集。但是,如果您有数百万个实例,则可能希望每个时期都由数据集的一部分组成,在这种情况下,您可以在此处指定值。

Share

You may also like...

发表评论

电子邮件地址不会被公开。 必填项已用*标注