

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# SageMaker 在训练脚本中进行智能筛选
<a name="train-smart-sifting-apply-to-script"></a>

 SageMaker 智能筛选库 DLCs作为补充库打包在 [SageMaker AI 框架](train-smart-sifting-what-is-supported.md#train-smart-sifting-supported-frameworks)中。它针对对模型训练影响相对较小的训练样本提供了筛选逻辑，与使用完整数据样本进行模型训练相比，您的模型只需使用较少的训练样本就能达到所需的模型精度。

要了解如何在训练脚本中实施智能筛选工具，请根据您使用的框架选择以下其中之一。

**Topics**
+ [对脚本应用 SageMaker 智能筛选 PyTorch](train-smart-sifting-apply-to-pytorch-script.md)
+ [在 Hugging Face Transformers 脚本中应用 SageMaker 智能筛选](train-smart-sifting-apply-to-hugging-face-transformers-script.md)

# 对脚本应用 SageMaker 智能筛选 PyTorch
<a name="train-smart-sifting-apply-to-pytorch-script"></a>

这些说明演示了如何使用训练脚本启用 SageMaker 智能筛选。

1. 配置 SageMaker 智能筛选界面。

    SageMaker 智能筛选库实现了一种基于相对阈值损耗的采样技术，该技术有助于筛选出对降低损耗值影响较小的样本。 SageMaker 智能筛选算法使用正向传递计算每个输入数据样本的损失值，并根据先前数据的损失值计算其相对百分位数。

   以下两个参数是创建筛选配置对象时需要为 `RelativeProbabilisticSiftConfig` 类指定的参数。
   + 指定用于 `beta_value` 参数训练的数据比例。
   + 使用 `loss_history_length` 参数指定用于比较的样本数。

   以下代码示例演示了如何设置 `RelativeProbabilisticSiftConfig` 类的对象。

   ```
   from smart_sifting.sift_config.sift_configs import (
       RelativeProbabilisticSiftConfig
       LossConfig
       SiftingBaseConfig
   )
   
   sift_config=RelativeProbabilisticSiftConfig(
       beta_value=0.5,
       loss_history_length=500,
       loss_based_sift_config=LossConfig(
            sift_config=SiftingBaseConfig(sift_delay=0)
       )
   )
   ```

   有关`loss_based_sift_config`参数和相关类的更多信息，请参阅[SageMaker 智能筛选配置模块](train-smart-sifting-pysdk-reference.md#train-smart-sifting-pysdk-base-config-modules) SageMaker 智能筛选 Python SDK 参考部分中的。

   前面代码示例中的 `sift_config` 对象在第 4 步中用于设置 `SiftingDataloader` 类。

1. （可选）配置 SageMaker 智能筛选批量转换类。

   不同的训练使用场景需要不同的训练数据格式。鉴于数据格式多种多样， SageMaker 智能筛选算法需要确定如何对特定批次进行筛选。为了解决这个问题， SageMaker 智能筛选提供了一个批量转换模块，可以帮助将批次转换为可以高效筛选的标准化格式。

   1. SageMaker 智能筛选处理以下格式的训练数据的批量转换：Python 列表、字典、元组和张量。对于这些数据格式， SageMaker 智能筛选会自动处理批量数据格式转换，您可以跳过此步骤的其余部分。如果您跳过此步骤，在配置 `SiftingDataloader` 的第 4 步中，请将 `SiftingDataloader` 的 `batch_transforms` 参数保留为默认值 `None`。

   1. 如果您的数据集不是这些格式，则您应继续本步骤的其余部分，使用 `SiftingBatchTransform` 创建自定义批量转换。

      如果您的数据集不是 SageMaker 智能筛选支持的格式之一，则可能会遇到错误。此类数据格式错误可以通过在 `SiftingDataloader` 类中添加 `batch_format_index` 或 `batch_transforms` 参数来解决，您可以在第 4 步中进行设置。下面显示了由于数据格式不兼容而导致的错误示例以及解决方法。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/sagemaker/latest/dg/train-smart-sifting-apply-to-pytorch-script.html)

      要解决上述问题，您需要使用 `SiftingBatchTransform` 模块创建自定义批处理转换类。批次转换类应由一对转换和反向转换函数组成。函数对将您的数据格式转换为 SageMaker 智能筛选算法可以处理的格式。创建批次转换类后，此类会返回一个 `SiftingBatch` 对象，您将在第 4 步中把此对象传递给 `SiftingDataloader` 类。

      以下是 `SiftingBatchTransform` 模块中自定义批次转换类的示例。
      + 使用 SageMaker 智能筛选实现自定义列表批量转换的示例，适用于数据加载器块包含输入、掩码和标签的情况。

        ```
        from typing import Any
        
        import torch
        
        from smart_sifting.data_model.data_model_interface import SiftingBatchTransform
        from smart_sifting.data_model.list_batch import ListBatch
        
        class ListBatchTransform(SiftingBatchTransform):
            def transform(self, batch: Any):
                inputs = batch[0].tolist()
                labels = batch[-1].tolist()  # assume the last one is the list of labels
                return ListBatch(inputs, labels)
        
            def reverse_transform(self, list_batch: ListBatch):
                a_batch = [torch.tensor(list_batch.inputs), torch.tensor(list_batch.labels)]
                return a_batch
        ```
      + 使用 SageMaker 智能筛选实现自定义列表批量转换的示例，适用于不需要标签进行反向转换的情况。

        ```
        class ListBatchTransformNoLabels(SiftingBatchTransform):
            def transform(self, batch: Any):
                return ListBatch(batch[0].tolist())
        
            def reverse_transform(self, list_batch: ListBatch):
                a_batch = [torch.tensor(list_batch.inputs)]
                return a_batch
        ```
      + 在数据加载器块有输入、掩码和标签的情况下，使用 SageMaker 智能筛选的自定义张量批处理实现示例。

        ```
        from typing import Any
        
        from smart_sifting.data_model.data_model_interface import SiftingBatchTransform
        from smart_sifting.data_model.tensor_batch import TensorBatch
        
        class TensorBatchTransform(SiftingBatchTransform):
            def transform(self, batch: Any):
                a_tensor_batch = TensorBatch(
                    batch[0], batch[-1]
                )  # assume the last one is the list of labels
                return a_tensor_batch
        
            def reverse_transform(self, tensor_batch: TensorBatch):
                a_batch = [tensor_batch.inputs, tensor_batch.labels]
                return a_batch
        ```

      在您创建已执行 `SiftingBatchTransform` 批次转换类后，可在第 4 步中使用 `SiftingDataloader` 类进行设置。本指南的其余部分假设已创建了一个 `ListBatchTransform` 类。在第 4 步中，此类将传递给 `batch_transforms`。

1. 创建用于实现 SageMaker 智能筛选`Loss`接口的类。本教程假定此类名为 `SiftingImplementedLoss`。在设置此类时，我们建议您在模型训练循环中使用相同的损失函数。按照以下子步骤创建 SageMaker 智能筛选`Loss`实现的类。

   1. SageMaker 智能筛选计算每个训练数据样本的损失值，而不是计算批次的单个损失值。为确保 SageMaker 智能筛选使用相同的损失计算逻辑，请使用 SageMaker 智能筛选`Loss`模块创建 smart-sifting-implemented损失函数，该模块使用您的损失函数并计算每个训练样本的损失。
**提示**  
SageMaker 智能筛选算法在每个数据样本上运行，而不是在整个批次上运行，因此您应该添加一个初始化函数来设置 PyTorch 损失函数，而无需任何还原策略。  

      ```
      class SiftingImplementedLoss(Loss):  
          def __init__(self):
              self.loss = torch.nn.CrossEntropyLoss(reduction='none')
      ```
以下代码示例也说明了这一点。

   1. 定义一个接受`original_batch`（或者`transformed_batch`如果您在步骤 2 中设置了批量变换）和 PyTorch模型的损失函数。 SageMaker 智能筛选使用不减值的指定损失函数，对每个数据样本进行正向传递，以评估其损失值。

   以下代码是一个名为的 smart-sifting-implemented`Loss`接口的示例`SiftingImplementedLoss`。

   ```
   from typing import Any
   
   import torch
   import torch.nn as nn
   from torch import Tensor
   
   from smart_sifting.data_model.data_model_interface import SiftingBatch
   from smart_sifting.loss.abstract_sift_loss_module import Loss
   
   model=... # a PyTorch model based on torch.nn.Module
   
   class SiftingImplementedLoss(Loss):   
       # You should add the following initializaztion function 
       # to calculate loss per sample, not per batch.
       def __init__(self):
           self.loss_no_reduction = torch.nn.CrossEntropyLoss(reduction='none')
   
       def loss(
           self,
           model: torch.nn.Module,
           transformed_batch: SiftingBatch,
           original_batch: Any = None,
       ) -> torch.Tensor:
           device = next(model.parameters()).device
           batch = [t.to(device) for t in original_batch] # use this if you use original batch and skipped step 2
           # batch = [t.to(device) for t in transformed_batch] # use this if you transformed batches in step 2
   
           # compute loss
           outputs = model(batch)
           return self.loss_no_reduction(outputs.logits, batch[2])
   ```

   在训练循环进入实际前向传递之前，每次迭代获取批次数据的数据加载阶段都会进行筛选损失计算。然后将单个损失值与之前的损失值进行比较，并根据步骤 1 中设置的 `RelativeProbabilisticSiftConfig` 对象估算出其相对百分位数。

1. 按 SageMaker AI `SiftingDataloader` 类封装 PyTroch 数据加载器。

   最后，将您在前面步骤中配置的所有 SageMaker 智能筛选实现的类用于 SageMaker AI `SiftingDataloder` 配置类。这个类是的封装器。 PyTorch [https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader](https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader)通过封装 PyTorch`DataLoader`， SageMaker 智能筛选被注册为在 PyTorch 训练作业的每次迭代中作为数据加载的一部分运行。以下代码示例演示如何实现 SageMaker AI 数据筛选到. PyTorch `DataLoader` 

   ```
   from smart_sifting.dataloader.sift_dataloader import SiftingDataloader
   from torch.utils.data import DataLoader
   
   train_dataloader = DataLoader(...) # PyTorch data loader
   
   # Wrap the PyTorch data loader by SiftingDataloder
   train_dataloader = SiftingDataloader(
       sift_config=sift_config, # config object of RelativeProbabilisticSiftConfig
       orig_dataloader=train_dataloader,
       batch_transforms=ListBatchTransform(), # Optional, this is the custom class from step 2
       loss_impl=SiftingImplementedLoss(), # PyTorch loss function wrapped by the Sifting Loss interface
       model=model,
       log_batch_data=False
   )
   ```

# 在 Hugging Face Transformers 脚本中应用 SageMaker 智能筛选
<a name="train-smart-sifting-apply-to-hugging-face-transformers-script"></a>

有两种方法可以实现变形金刚`Trainer`类的 SageMaker 智能筛选。

**注意**  
如果您使用安装了 SageMaker 智能筛选软件包 DLCs 的 PyTorch for 之一，请注意您需要安装该`transformers`库。您可以通过[扩展 DLCs](prebuilt-containers-extend.md)或传递`requirements.txt`到 SageMaker AI Python SDK 中 PyTorch ([https://sagemaker.readthedocs.io/en/stable/frameworks/pytorch/sagemaker.pytorch.html](https://sagemaker.readthedocs.io/en/stable/frameworks/pytorch/sagemaker.pytorch.html)) 的训练作业启动器类来安装其他包。

## 设置简单
<a name="train-smart-sifting-apply-to-hugging-face-transformers-script-simple"></a>

在《变形金刚》`Trainer`类中实现 SageMaker 智能筛选的最简单方法是使用该`enable_sifting`函数。此函数接受现有 `Trainer` 对象，并使用 `SiftingDataloader` 包装现有 `DataLoader` 对象。您可以继续使用相同的训练对象。请参阅以下使用示例。

```
from smart_sifting.integrations.trainer import enable_sifting
from smart_sifting.loss.abstract_sift_loss_module import Loss
from smart_sifting.sift_config.sift_configs import (
    RelativeProbabilisticSiftConfig
    LossConfig
    SiftingBaseConfig
)

class SiftingImplementedLoss(Loss):
   def loss(self, model, transformed_batch, original_batch):
        loss_fct = MSELoss(reduction="none") # make sure to set reduction to "none"
        logits = model.bert(**original_batch)
        return loss_fct(logits, original_batch.get("labels"))

sift_config = RelativeProbabilisticSiftConfig(
    beta_value=0.5,
    loss_history_length=500,
    loss_based_sift_config=LossConfig(
         sift_config=SiftingBaseConfig(sift_delay=0)
    )
)

trainer = Trainer(...)
enable_sifting(trainer, sift_config, loss=SiftingImplementedLoss()) # updates the trainer with Sifting Loss and config
trainer.train()
```

`SiftingDataloader` 类是可迭代数据加载器。由于在筛选过程中进行了随机采样，因此事先并不知道所得数据集的确切大小。因此，Hugging Face `Trainer` 预计 [`max_steps` 训练参数](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.TrainingArguments.max_steps)。请注意，此参数会覆盖历时配置参数 `num_train_epochs`。如果您的原始数据加载器也是可迭代的，或者您的训练使用的是 `max_steps` 和单个历时，则 `SiftingDataloader` 与现有数据加载器执行相同操作。如果原始数据加载器不可迭代或未提供 `max_steps`，Hugging Face Trainer 可能会抛出类似下面的错误消息。

```
args.max_steps must be set to a positive value if dataloader does not have a length,
was -1
```

为了解决此问题，`enable_sifting` 函数提供了一个可选 `set_epochs` 参数。这将使用 `Trainer` 类的 [num\$1train\$1epochs argument](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.TrainingArguments.num_train_epochs(float,) 提供的历时数启用历时训练，并将 `max_steps` 设置为系统最大整数，允许训练继续进行，直到指定的历时结束。

## 自定义设置
<a name="train-smart-sifting-apply-to-hugging-face-transformers-script-custom-trainer"></a>

要自定义集成 SageMaker 智能筛选数据加载器，你可以使用自定义 Hugging Face 类。`Trainer`在 `Trainer` 的任何子类中，都可以重写 `get_train_dataloader()` 函数，以返回 `SiftingDataloader` 类的一个对象。对于已有自定义训练器的情况，这种方法可能干扰较少，但与简单设置选项相比，需要修改代码。以下是 SageMaker 智能筛选到自定义 Hugging Face 类的示例实现。`Trainer`

```
from smart_sifting.sift_config.sift_configs import (
    RelativeProbabilisticSiftConfig
    LossConfig
    SiftingBaseConfig
)
from smart_sifting.dataloader.sift_dataloader import SiftingDataloader
from smart_sifting.loss.abstract_sift_loss_module import Loss
from smart_sifting.data_model.data_model_interface import SiftingBatch, SiftingBatchTransform
from smart_sifting.data_model.list_batch import ListBatch

class SiftingListBatchTransform(SiftingBatchTransform):
    def transform(self, batch: Any):
        inputs = batch[0].tolist()
        labels = batch[-1].tolist()  # assume the last one is the list of labels
        return ListBatch(inputs, labels)

    def reverse_transform(self, list_batch: ListBatch):
        a_batch = [torch.tensor(list_batch.inputs), torch.tensor(list_batch.labels)]
        return a_batch

class SiftingImplementedLoss():
    # You should add the following initializaztion function 
    # to calculate loss per sample, not per batch.
    def __init__(self):
        self.celoss = torch.nn.CrossEntropyLoss(reduction='none')

    def loss(
        self,
        model: torch.nn.Module,
        transformed_batch: SiftingBatch,
        original_batch: Any = None,
    ) -> torch.Tensor:
        device = next(model.parameters()).device
        batch = [t.to(device) for t in original_batch]

        # compute loss
        outputs = model(batch)
        return self.celoss(outputs.logits, batch[2])

class SiftingImplementedTrainer(Trainer):
    def get_train_dataloader(self):
        dl = super().get_train_dataloader()

        sift_config = RelativeProbabilisticSiftConfig(
            beta_value=0.5,
            loss_history_length=500,
            loss_based_sift_config=LossConfig(
                sift_config=SiftingBaseConfig(sift_delay=0)
            )
        )

        return SiftingDataloader(
                sift_config=sift_config,
                orig_dataloader=dl,
                batch_transforms=SiftingListBatchTransform(),
                loss_impl=SiftingImplementedLoss(),
                model=self.model
        )
```

使用封装的 `Trainer` 类创建其对象，如下所示。

```
trainer = SiftingImplementedTrainer(
    model=model,
    args=training_args,
    train_dataset=small_train_dataset,
    eval_dataset=small_eval_dataset
)

trainer.train()
```