Skip to main content

SeqIO:基于任务的数据集、序列模型的预处理和评估。

项目描述

SeqIO:基于任务的数据集、序列模型的预处理和评估。

SeqIO是一个用于处理要输入下游序列模型的序列数据的库。它用于tf.data.Dataset 创建可扩展的数据管道,但需要最少的 TensorFlow。特别是,通过一行代码,返回的数据集可以转换为 numpy 迭代器,因此它与 JAXPyTorch等其他框架完全兼容。

目前,SeqIO 假设数据集是一个序列,即每个特征都是一维数组。自然支持文本或音频等模式。只要图像表示为序列(例如 Image GPT),就支持图像。为了支持更高维度的数据,我们将在未来释放这个约束。

SeqIO 是对用于 训练 T5 模型 的t5.data 库的重构(与Mesh Tensorflow Transformer 实现结合使用)探索使用统一文本到文本转换器的迁移学习的限制中介绍的 T5 模型。

如果您过去使用t5.data过并想知道 SeqIO 有何不同,请阅读本节

使用教程

在高层次上,我们通过以下步骤使用 SeqIO。

  1. 定义 a Task(和可选的 a Mixture)。

  2. FeatureConverter基于模型架构定义(或使用现有的)a 。

  3. 使用顶层函数seqio.get_dataset获取 tf.data.Dataset实例。

我们将详细研究这些步骤中的每一个。

定义一个Task

SeqIO 中最重要的类是Task. 它是一个抽象,它结合了:

  • 原始数据源
  • 一个或多个预处理步骤
  • 用于对模型的每个预处理特征进行标记/去标记的词汇表
  • 一个后处理器,用于将去标记的模型输出转换为评估格式
  • 用于评估的一个或多个指标

通常Task与一个共同的基准一致。在本教程中,我们使用 WMT 19 英德机器翻译任务。最后,我们的Task会是这样的:

seqio.TaskRegistry.add(
    "wmt19_ende",
    seqio.TfdsDataSource(tfds_name="wmt19_translate/de-en:1.0.0"),
    preprocessors=[
        functools.partial(
            translate, source_language='en', target_language='de'),
        seqio.preprocessors.tokenize, seqio.preprocessors.append_eos
    ],
    output_features={
        'inputs':
            seqio.Feature(
                seqio.SentencePieceVocabulary('/path/to/inputs/vocab'),
                add_eos=False,
                dtype=tf.int32),
        'targets':
            seqio.Feature(
                seqio.SentencePieceVocabulary('/path/to/targets/vocab'),
                add_eos=True,
                dtype=tf.int32),
    },
    metric_fns=[bleu])

我们通常在Task定义全局注册表时将其添加到全局注册表中(如上所示),以使其更易于与模型配置和标志一起使用。因此,它必须有一个唯一的字符串名称("wmt19_ende"在这种情况下)。但是请注意,seqio.Task如果需要,您也可以直接实例化 a 而无需将其添加到注册表中。

我们现在将分解任务定义的每个部分。

数据源

数据源是管道中的第一步,它提供了一种将多种格式的原始数据加载为tf.data.Dataset. 所有数据源都是DataSource基类的子类,并在 dataset_providers中定义,

现有的实现包括:

  • TfdsDataSource用于从TensorFlow Datasets加载示例。
  • TextLineDataSource用于从文本文件(例如 tsv)加载示例。
  • TFExampleDataSourcetf.train.Example用于从文件(例如TFRecord文件)加载原型。
  • FunctionDataSource用于提供返回 a 的自定义函数tf.data.Dataset

在我们的示例中,我们使用TfdsDataSource. 我们在 TFDS ( "wmt19_translate") 中指定 WMT 数据集的名称、排除开放域设置上下文的语言对的特定配置 ( "de-en") 以及版本号 ( "1.0.0")。

输出功能

output_features字段需要一个将字符串特征名称映射到seqio.Feature对象的字典。这定义了Task预期在其输出示例中产生的内容。输出示例可能包含其他字段,但它们必须包含指定格式的这些字段,否则将引发异常。

每个Feature包括:

  • Avocabulary必须是子类seqio.Vocabulary,以指定如何对特征进行标记化和去标记化。seqio.PassThroughVocabulary如果不需要标记化,您可以使用。
  • add_eos,它指定特征是否应以词汇表的 EOS 标记结尾。
  • 输出dtype必须是 a tf.dtypes.DType

注意:指定这些选项Feature本身并不能确保应用正确的转换——您还必须包括必要的预处理器。

T5 中使用的任务都会产生“输入”和“目标”特征以供文本到文本模型使用。对于只有解码器的语言模型,只需要一个特征(例如,“目标”)。尽管如此,SeqIO 足够灵活,可以生成任意输出特征,这些输出特征将被FeatureConverter管道中的后期转换为模型特征。

预处理器

预处理器是将一个tf.data.Dataset转换为新的tf.data.Dataset. 通常这涉及map在给定的数据集上执行。提供给 的预处理器Task将按顺序执行。

作为一个例子,让我们看一下translate上面“wmt19_ende”示例中先前未定义的内容。

def translate(dataset: tf.data.Dataset,
              source_language: str,
              target_language: str) -> tf.data.Dataset:
  def _translate(ex: Mapping[str, tf.Tensor]) -> Mapping[str, tf.Tensor]:
    """Convert a translation example to a text2text pair.

    For example, say the dataset returns examples of this format:
      {'de': 'Das ist gut.', 'en': 'That is good.'}
    If source_language = 'de', target_language = 'en', then the outputs will have
    the format:
      {'inputs': 'translate de to en: Das ist gut.',
      'targets': 'That is good.'}

    Args:
      x: an example to process.
      source_language: source language code (e.g. 'en') to translate from.
      target_language: target language code (e.g. 'de') to translate to.

    Returns:
      A preprocessed example with the format listed above.
    """
    src_str = f'translate {source_language}'
    tgt_str = f' to {target_language}: '
    return {
        'inputs': tf.strings.join([src_str, tgt_str, ex[source_language]]),
        'targets': ex[target_language],
    }

  return dataset.map(_translate,
                     num_parallel_calls=tf.data.experimental.AUTOTUNE)

TFDS 数据集提供了每个示例具有以下形式的数据集:{'de': 'Das ist gut.', 'en': 'That is good.'}. 我们使用适当的提示将其转换为“输入”和“目标”,以告知模型任务。

几个重要的注意事项:

  1. 实例化 aTask时,预处理器函数可以具有以下参数:datasetoutput_featuressequence_length. 第一个(位置)数据集参数始终是必需的。如果提供了一个名为的参数output_features,则输出特征映射将被传递给预处理器。如果sequence_length提供,则将传递从特征名称到其最大最终序列长度(由调用者提供)的映射——预处理后过长的任何序列都将被自动截断。如果预处理器函数确实有其他参数,则它们必须具有默认值或functools.partialtranslate实例化Task.

  2. 映射函数tf.Tensor使用 TensorFlow 操作对 s 进行操作并返回。这比听起来更灵活:

*  Automatic [AutoGraph](https://www.tensorflow.org/guide/function#autograph_transformations) conversion allow you to write python control flow in your transformations.
* [tf.experimental.numpy](https://www.tensorflow.org/guide/tf_numpy) provides a numpy interface.
* [`tf.py_function`](https://www.tensorflow.org/api_docs/python/tf/py_function) allows you to wrap arbitrary Python code. Note: `tf.data` pipelines using this function can only be run in the python process where they were defined, and performance is limited by the python GIL.

有关更多详细信息,请参阅tf.data.Dataset 文档

  1. 调用map时,务必始终设置num_parallel_calls=tf.data.experimental.AUTOTUNE以避免造成瓶颈。装饰器seqio.map_over_dataset有助于执行此操作,如下所示。
@seqio.map_over_dataset
def translate(ex: Mapping[str, tf.Tensor],
              source_language: str,
              target_language: str) -> Mapping[str, tf.Tensor]:
  """Convert a translation dataset to a text2text pair.

  For example, say the dataset returns examples of this format:
    {'de': 'Das ist gut.', 'en': 'That is good.'}
  If source_language = 'de', target_language = 'en', then the outputs will have
  the format:
    {'inputs': 'translate German to English: Das ist gut.',
    'targets': 'That is good.'}

  Args:
    x: an example to process.
    source_language: source language code (e.g. 'en') to translate from.
    target_language: target language code (e.g. 'de') to translate to.

  Returns:
    A preprocessed example with the format listed above.
  """
  src_str = f'translate {source_language}'
  tgt_str = f' to {target_language}: '
  return {
      'inputs': tf.strings.join([src_str, tgt_str, ex[source_language]]),
      'targets': ex[target_language],
  }

请注意,translate将单个示例作为输入。然后将 其装饰为一个接收实例seqio.map_over_dataset的函数 。tf.data.Dataset

  1. 如果需要确定性管道,随机操作必须是无状态的。要为这些操作获取(可选的确定性)种子,请使用seqio.map_over_dataset(num_seeds=n)装饰器。例如:
def random_chunk(
  dataset: tf.data.Dataset,
  sequence_length: Mapping[str, int]
) -> tf.data.Dataset:
"""Takes a random chunk out of each feature the size of `sequence_length`."""

  @seqio.map_over_dataset(num_seeds=1)
  def take_chunk(
      ex: Mapping[str, tf.Tensor],
      seed
  ) -> Mapping[str, tf.Tensor]:
    new_ex = {}
    for k, v in ex.items():
      if k in sequence_length:
        length = sequence_length[k]
        start_idx = tf.random.stateless_uniform(
           (), seed, 0, tf.size(v) - (length + 1))
        new_ex[k] = v[start_idx:start_idx+length]
      else:
        new_ex[k] = v
    return new_ex

return take_chunk(dataset)

如果num_seeds > 1,则将调用 arg 并将seeds包含一系列种子。

在我们的“wmt_19_ende”任务中,我们还使用了预定义的预处理器seqio.preprocessors.tokenizeseqio.preprocessors.append_eos. 前者使用 eachFeature.vocabulary对其进行标记,如果为 True ,则后者附加Feature.vocabulary.eos_id到特征。Feaure.add_eos有关它们的实现和其他有用的预处理器,请参见preprocessors.py

后处理器

在评估期间,首先使用输出特征词汇对模型输出进行去标记化。在将这些预测传递给度量函数之前,可以通过 Python 后处理函数以及完整的输入示例来运行它们。同样,原始目标在传递给指标之前会通过此函数运行。由于模型输出和目标都使用了后处理函数,因此它被传递一个is_target布尔值,以防行为应该不同。它还通过了完全预处理的示例,包括从output_features.

对于“wmt19_ende”,我们不需要任何后处理器。有关示例后处理器,请参阅高级后处理Task中的“trivia_qa_open”任务。

指标

指标是(由Evaluator传递的)后处理模型输出(或分数)和目标的完全物化列表并返回从字符串名称到MetricValue包含其值的对象的映射的函数。这些是最常见的浮点标量,但也可能是文本、图像、音频、直方图等(完整列表请参见metrics.py)。

度量函数的第一个参数必须始终被调用targets。如果调用度量函数的第二个参数predictions,它将传递解码和去标记化的模型预测。如果调用scores它,它将传递每个示例的对数似然分数列表。

如果提供了多个度量函数,它们都将被使用并且它们返回的映射被合并。

预测指标

使用后处理目标和模型输出(预测)计算预测指标。args 必须命名为targetsand predictions

让我们看看用于“wmt19_ende”任务的度量函数。翻译任务的标准度量是 BLEU,我们使用sacrebleu实现。

def bleu(targets: Sequence[str], predictions: Sequence[str]):
  """Computes BLEU score.

  Args:
    targets: list of strings or list of list of strings if multiple references
      are present.
    predictions: list of strings

  Returns:
    bleu_score across all targets and predictions
  """
  if isinstance(targets[0], list):
    targets = [[x for x in target] for target in targets]
  else:
    # Need to wrap targets in another list for corpus_bleu.
    targets = [targets]

  bleu_score = sacrebleu.corpus_bleu(predictions, targets,
                                     smooth_method="exp",
                                     smooth_value=0.0,
                                     force=False,
                                     lowercase=False,
                                     tokenize="intl",
                                     use_effective_order=False)
  return {"bleu": bleu_score.score}
分数指标

分数指标是根据模型使用后处理目标及其对数似然分数计算的。args 必须命名为targetsand scores

def perplexity(targets: Sequence[str], scores: Sequence[int]):
  return {
    "perplexity": seqio.metrics.Scalar(np.exp(np.mean(scores)))
  }

定义一个Mixture

一旦你有多个Tasks 添加到 中TaskRegistry,你可以定义Mixtures 将根据某个指定的速率组合来自它们的示例。然后将按比例从每个任务中抽取示例。

例如,多语言 T5使用Mixture每种语言 Task的 a 和尾语言在混合中加权。

有 3 种方法可以指定任务及其费率:

  1. 提供一个速率以及每个任务的名称(采样前对速率进行标准化):
seqio.MixtureRegistry.add(
  "mix1",
  [("task1", 1), ("task2", 7)]
)
  1. 为部分或所有任务提供恒定的默认率,仅在提供名称时使用。下面的示例将产生与前一个相同的混合速率。
seqio.MixtureRegistry.add(
  "mix1",
  [("task1", 0.5), "task2"],
  default_rate=3.5
)
  1. 提供一个在运行时为每个任务生成速率的函数。下面的示例使用提供的seqio.mixing_rate_num_examples,它使用示例数(在离线缓存期间计算)作为每个任务的速率。
seqio.MixtureRegistry.add(
  "mix2",
  ["task1", "task2"],
  default_rate=seqio.mixing_rate_num_examples
)

你也可以Mixture在你的Mixture! 例如,以下任务将包含 1/24(来自“mix1”)+ 1/3“task1”、7/24(来自“mix1”)的“task2”和 1/3“task3”。

seqio.MixtureRegistry.add(
  "mix3",
  ["mix1", "task1", "task3"],
  default_rate=1
)

获取预处理数据集

现在您的Task(and/or Mixture) 已定义,它的主要功能是使用它来生成数据集。

您可能首先需要使用seqio.get_mixture_or_task(mixture_or_task_name)从注册表访问您的数据集提供程序。

之后,您可以调用get_dataset构建tf.data.Dataset. 例如:

dataset = seqio.get_mixture_or_task("mix1").get_dataset(
    sequence_length={"inputs": 256, "targets": 128},
    split="train",
    shuffle=True,
    num_epochs=1,
    shard_info=seqio.ShardInfo(index=0, num_shards=10),
    use_cached=False,
    seed=42
)

# Print the first 5 examples.
for _, ex in zip(range(5), dataset.as_numpy_iterator()):
  print(ex)

关于一些论点的一些注释:

  • sequence_length:从特征名称到最大长度的可选映射。将通过参数传递给预处理器。如果不是,则最终示例特征如果超过指定长度将被截断。请注意,如果任何预处理器使用该参数并且不处理这种情况,则可能需要设置此值。sequence_lengthNonesequence_lengthNone
  • num_epochs:重复源数据集的次数。预处理将重新应用新种子,以启用来自随机步骤的新样本。请注意,如果CacheDatasetPlaceholder包含(见下文)预处理仅在该步骤之后重新应用。
  • shard_info:用于加载数据集确定性子集的可选分片规范。如果分片数均分原始数据源中的分片数,则加载效率最高。
  • use_cached:指定是从预先缓存的任务加载以提高性能还是即时进行预处理。有关如何缓存任务的详细信息,请参阅以下部分True,必须先完成,然后才能将其设置为.
  • seed:用于确定性洗牌和(无状态)随机操作的可选种子。这些操作仍然是伪随机的,但可以用相同的种子重现。None如果不需要确定性,则设置为。

(可选)离线缓存

为了提高加载时的性能并避免对常用任务进行冗余计算,您可以Task在训练之前使用全部或部分预处理来预先缓存您的数据。

这样做的第一步是将 a 添加seqio.CacheDatasetPlaceholder(required=False)为预处理管道中的步骤之一。占位符之前的所有步骤都将离线缓存,之后的所有步骤将在加载时动态执行。required=True如果你想get_dataset失败,你可以设置,除非use_cached=True

注意事项:

  • num_epochs > 1您希望在何时或以不同方式重新运行的任何随机操作seed 都应在占位符之后,因为只会缓存一个样本。
  • 任何使用sequence_length参数的预处理步骤都必须seqio.CacheDatasetPlaceholder预处理器之后,因为这仅在运行时才知道,否则将引发异常。如果您希望缓存特定的序列长度,可以使用seqio.experimental.add_fully_cached_task.

注册后,您Task可以运行以执行离线预处理,并通过标志cache_tasks_main为其提供包含您的任务定义的模块。--module_import对于非常大的数据集,建议您在Google Cloud DataFlow等分布式框架上运行此Apache Beam脚本。

最后,您已准备好加载包含它的Task(或)的缓存版本。Mixture您需要将路径添加到您传递给--output_cache_dirvia的目录seqio.add_global_cache_dirs(["/my/cache/dir"])。现在,当您调用 时task_or_mixture.get_dataset(..., use_cached=True),数据将从缓存目录而不是原始数据源加载。

特征转换器

的作用Task是为数据集对象提供尽可能少的特定于模型的特征(例如,通用“输入”和“目标”),而特征转换器将与模型无关的特征转换为特定于模型的特征(例如,“encoder_input_tokens”) . 我们将前者称为“任务特征”,将后者称为“模型特征”。

让我们使用机器翻译(英语到德语)作为运行示例。

原始数据由句子对组成,例如

"That is good\tDas ist gut."

注册到Task(例如, wmt_t2t_ende_v003)的任务从数据源读取这些句子对并应用一系列 预处理器。内部表示之一看起来像

{"inputs": "translate English to German: That is good.",
 "targets": "Das ist gut."}

的最终输出Task是并行句子的标记化版本。在以下玩具示例中(令牌 ID 与上述字符串示例不对应),数据集由 2 个示例组成。

dataset = [{"inputs": [7, 8, 5], "targets": [3, 9]},
           {"inputs": [8, 4, 9, 3], "targets": [4]}]

格式位于tf.data.Dataset(即,每个示例都是带有“输入”和“目标”字段的字典。

然后FeatureConverter将此作为输入并转换为特定于模型的特征。此外,特征转换器执行填充和可选打包(对于支持它的模型实现)以提高效率。例如,假设我们使用带有编码器和解码器的标准 Transformer 架构。特征转换器的输出为

converted_dataset = [{
    "encoder_input_tokens": [7, 8, 5, 1, 8, 4, 9, 3, 1, 0],
     "encoder_segment_ids": [1, 1, 1, 1, 2, 2, 2, 2, 2, 0],
       "encoder_positions": [0, 1, 2, 3, 0, 1, 2, 3, 4, 0],
   "decoder_target_tokens": [3, 9, 1, 4, 1, 0, 0],
    "decoder_input_tokens": [0, 3, 9, 0, 4, 0, 0],
    "decoder_loss_weights": [1, 1, 1, 1, 1, 0, 0],
       "decoder_positions": [0, 1, 2, 0, 1, 0, 0],
     "decoder_segment_ids": [1, 1, 1, 2, 2, 0, 0],
}]

在这种情况下,两个任务示例被打包成一个。*_segment_id*_position是用于表示打包令牌在原始序列中的成员资格和位置的字段。附加了 EOS id(即 1)。此外,每个字段都被填充到指定的长度。

我们将在 Encoder-decoder architecture: seqio.EncDecFeatureConvertersection 中查看此示例的详细信息。

开箱即用的功能转换器

我们为三种常见架构提供特征转换器:编码器-解码器、仅解码器和仅编码器。在这里,我们描述了用户如何使用这些开箱即用的特征转换器作为 SeqIO 库的一部分。

在 SeqIO 库中,每个架构都有一个类来定义如何将任务特征转换为模型特征。feature_converter由于这些特征转换器已经实现,通过提供类作为函数的参数来使用它们很简单 seqio.get_dataset。以下部分将展示seqio.get_dataset.

编码器-解码器架构:seqio.EncDecFeatureConverter

这是原始 Transformer 论文的架构。对于英语到德语的翻译任务,以下函数调用检索 tf.data.Dataset具有模型特征的对象。

dataset: tf.data.Dataset = seqio.get_dataset(
    mixture_or_task_name="wmt_t2t_ende_v003",
    task_feature_lengths={"inputs": 32, "targets": 32},
    dataset_split="train",
    shuffle=True,
    feature_converter=seqio.EncDecFeatureConverter(pack=True)
)

生成的数据集对象有以下 7 个字段

功能名称 解释
encoder_input_tokens 向编码器输入令牌。
encoder_positions 打包前在序列中的位置索引。
encoder_segment_ids 在打包之前对成员进行排序。具有相同正整数的两个位置表示它们在打包之前属于同一序列。
decoder_input_tokens 向解码器输入令牌。
decoder_target_tokens 解码器的输出令牌。
decoder_loss_weights 每个位置的权重可以用作掩码。
decoder_positions 打包前在序列中的位置索引。
decoder_segment_ids encoder_segment_ids解码器相同。
仅解码器架构

该架构由一个自回归堆栈组成,我们将其称为“解码器”。

解码器自回归产生输出序列。因此,如果任务数据集只有“目标”特征,即自我监督,它可以用作标准语言模型。如果任务数据集还具有“输入”字段,例如监督机器翻译,则仍可以通过连接输入和目标字段来使用解码器。参见Raffel 等人。(2020),第 3.2.1 节,以更详细地了解该主题。

我们支持这两种用例,将前者称为标准语言模型 ,将后者称为前缀语言模型。下面分别描述这些模型中的每一个。

请注意,我们不提供特殊功能来表示应如何使用数据集。例如,基于 Transformer 的全自回归解码器具有全因果自注意层。由于有很多方法可以为这种注意力层实现掩码模式,更重要的是,SeqIO 不限于基于注意力的模型,我们将其留给模型实现来应用掩码模式。有一个例外,我们在下面的前缀 LM 部分中对此进行了介绍。

一种常见的使用模式是使用从左到右的语言建模目标(无监督)预训练解码器模型,seqio.LMFeatureConverter然后使用 微调(监督)seqio.PrefixLMFeatureConverter

标准 LM

对于标准语言模型,任务数据集只有“目标”字段。因此,序列长度规范只需要指定目标。

dataset: tf.data.Dataset = seqio.get_dataset(
    mixture_or_task_name="standard_lm",
    task_feature_lengths={"targets": 32},
    dataset_split="train",
    shuffle=True,
    feature_converter=seqio.LMFeatureConverter(pack=True)
)

请注意,“standard_lm”不是代码库中的注册任务。这是从左到右的语言建模任务,即在给定某些语言语料库(例如, C4)上的先前标记的情况下预测下一个标记。

输出数据集具有以下模型特征。

功能名称 解释
decoder_target_tokens 解码器的输出令牌
decoder_input_tokens 解码器的输入令牌
decoder_loss_weights 二进制掩码以指示应在何处进行损失
decoder_positions 打包前在序列中的位置索引
decoder_segment_ids 在打包之前对成员进行排序。具有相同正整数的两个位置表示它们在打包之前属于同一序列。

decoder_target_tokensdecoder_input_tokens标准教师强制自回归训练的转换版本。

前缀 LM:seqio.PrefixLMFeatureConverter

如果输入数据集具有“输入”和“目标”的概念,我们可以将它们连接起来,以便我们仍然可以使用单个堆栈解码器。因此,输出只包含“目标”,就像标准 LM 案例一样。

我们将相同的玩具示例用于英德翻译任务作为运行示例:

{"inputs": "translate English to German: That is good.",
 "targets": "Das ist gut."}

要由仅解码器堆栈使用,seqio.PrefixLMFeatureConverter 将它们连接起来形成新的“目标”。考虑 2 层解码器架构,其激活如下所示


That  is  good <EOS> Das ist gut <EOS>
 |    |    |    |    |   |    |   |
 u1   u2   u3   u4   u5  u6   u7  u8
 |    |    |    |    |   |    |   |
 v1   v2   v3   v4   v5  v6   v7  v8
 |    |    |    |    |   |    |   |
<BOS> That is  good <EOS> Das ist gut

让我们将第一层在第ith 位置的激活表示为vi。类似地,让表示在第 th 位置ui的第二层的激活。i

对于 Transformer 解码器等基于注意力的序列模型,自注意力层用于对序列的上下文表示进行编码。在给定的层上,每个位置的表示都是根据令牌在前一层中的位置之前的表示来计算的。

参考玩具示例,当u2使用全因掩蔽进行计算时,我们不使用v3. 这导致u2单词“is”的表示没有考虑单词“good”,这是不必要的限制。

对于前缀 LM,此问题通过仅对输入部分具有完全可见的屏蔽模式来解决。例如,在计算u2v1v2、和时v3,它们都是可见的并被考虑在内。对于数据集“目标”中的标记,我们使用因果掩蔽。例如,在计算 时,考虑所有for但不考虑。v4v5Tasku6vii <= 6v7

为什么 `v5` 包含在输入注意模式中 在同一个翻译示例中,我们注意到在计算 `u2` 时,与输入 \<EOS\> 令牌的位置(即,`v5`)对应的激活是可见的。这不算作“作弊”,因为模型看不到下一个单词“Das”。这可以为构建“好”表示提供额外的上下文。在这种情况下,`u4` 的上下文是“good”是句子中的最后一个词。

seqio.PrefixLMFeatureConverter提供了decoder_causal_attention 对这些信息进行编码的功能。对于上面的例子,我们有

decoder_causal_attention = [1, 1, 1, 1, 1, 0, 0, 0]

表明非因果注意力可以应用于前五个位置。请注意,此功能似乎微不足道,但对于打包数据集,输入和目标边界更加细微。

前缀 LM 的最后一个考虑因素是,因为我们连接了“输入”和“目标”,所以将哪些标记用于损失计算是一个建模决策。例如,我们可以只为“目标”标记惩罚模型,或者我们可以选择惩罚为“输入”标记构建表示。这由构造函数的loss_on_targets_only参数(默认为True) 控制seqio.PrefixLMFeatureConverter。在上面的例子中,我们会得到

decoder_loss_weights = [0, 0, 0, 0, 1, 1, 1, 1]

这表明最后 4 个位置用于损失计算。

要获得具有前缀 LM 特征的数据集,我们可以使用

数据集 tf . 数据数据集 =  seqio get_dataset ( 
    mixture_or_task_name = "wmt_t2t_ende_v003"