

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# トレーニングスクリプト内の SageMaker スマートふるい
<a name="train-smart-sifting-apply-to-script"></a>

SageMaker スマートふるいライブラリは、補完ライブラリとして [SageMaker AI フレームワーク DLC](train-smart-sifting-what-is-supported.md#train-smart-sifting-supported-frameworks) にパッケージ化されています。モデルトレーニングへの影響が比較的少ないトレーニングサンプルに対するフィルタリングロジックを提供し、全データサンプルを使用したモデルトレーニングと比較して、より少ないトレーニングサンプルで、モデルが必要なモデル精度を達成できます。

スマートふるいツールをトレーニングスクリプトに実装する方法については、使用するフレームワークに基づいて、次のいずれかを選択します。

**Topics**
+ [PyTorch スクリプトに SageMaker スマートふるいを適用する](train-smart-sifting-apply-to-pytorch-script.md)
+ [Hugging Face Transformers スクリプトに SageMaker スマートふるいを適用する](train-smart-sifting-apply-to-hugging-face-transformers-script.md)

# PyTorch スクリプトに SageMaker スマートふるいを適用する
<a name="train-smart-sifting-apply-to-pytorch-script"></a>

以下の手順は、SageMaker スマートふるいをトレーニングスクリプトで有効にする方法を示しています。

1. SageMaker スマートふるいインターフェイスを設定します。

   SageMaker スマートふるいライブラリは、相対的しきい値の損失ベースのサンプリング手法を実装しています。これにより、損失値の低減への影響が少ないサンプルを除外できます。SageMaker スマートふるいアルゴリズムは、フォワードパスを使用してすべての入力データサンプルの損失値を計算し、前のデータの損失値に対する相対パーセンタイルを計算します。

   次の 2 つのパラメータを、ふるい分け設定オブジェクトを作成するために `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 smart sifting Python SDK reference」セクションの「[SageMaker スマートふるい設定モジュール](train-smart-sifting-pysdk-reference.md#train-smart-sifting-pysdk-base-config-modules)」を参照してください。

   前述のコード例の `sift_config` オブジェクトは、`SiftingDataloader` クラスのセットアップのステップ 4 で使用されます。

1. (オプション) SageMaker スマートふるいのバッチ変換クラスを設定します。

   トレーニングデータ形式は、トレーニングのユースケースごとに異なります。さまざまなデータ形式を考慮すると、SageMaker スマートふるいアルゴリズムは、特定のバッチでふるい分けを実行する方法を特定する必要があります。これに対処するために、SageMaker スマートふるいはバッチ変換モジュールを提供します。このモジュールを使用すると、効率的にふるい分けできる標準化された形式にバッチを変換できます。

   1. SageMaker スマートふるいは、Python リスト、ディクショナリ、タプル、テンソルの形式のトレーニングデータのバッチ変換を処理します。これらのデータ形式の場合、SageMaker スマートふるいはバッチデータ形式の変換を自動的に処理するため、このステップの残りの部分はスキップして構いません。このステップをスキップする場合は、`SiftingDataloader` の設定のステップ 4 で、`SiftingDataloader` のパラメータ `batch_transforms` を、デフォルト値である `None` のままにします。

   1. データセットがこれらの形式でない場合は、このステップの残りの部分に進んで、`SiftingBatchTransform` を使用してカスタムバッチ変換を作成する必要があります。

      データセットが SageMaker スマートふるいでサポートされている形式のいずれにもない場合、エラーが発生する可能性があります。このようなデータ形式のエラーは、`batch_format_index` または `batch_transforms` パラメータを `SiftingDataloader` クラスに追加することで解決できます。これは、ステップ 4 で設定します。以下は、互換性のないデータ形式によるエラーの例と、その解決策を示しています。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/train-smart-sifting-apply-to-pytorch-script.html)

      上記の問題を解決するには、`SiftingBatchTransform` モジュールを使用してカスタムバッチ変換クラスを作成する必要があります。バッチ変換クラスは、変換関数と逆変換関数のペアで構成される必要があります。関数ペアは、データ形式を、SageMaker スマートふるいアルゴリズムが処理できる形式に変換します。バッチ変換クラスを作成すると、クラスはステップ 4 で `SiftingDataloader` クラスに渡される `SiftingBatch` オブジェクトを返します。

      以下は、`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` モジュールを使用し、スマートふるいに実装された損失関数を作成します。
**ヒント**  
SageMaker スマートふるいアルゴリズムは、バッチ全体ではなく、データサンプルそれぞれで実行されるため、初期化関数を追加して、削減戦略なしで PyTorch 損失関数を設定する必要があります。  

      ```
      class SiftingImplementedLoss(Loss):  
          def __init__(self):
              self.loss = torch.nn.CrossEntropyLoss(reduction='none')
      ```
以下の例でも、これを示しています。

   1. `original_batch` (または、ステップ 2 でバッチ変換を設定している場合 `transformed_batch`) と PyTorch モデルを受け入れる損失関数を定義します。SageMaker スマートふるいは、指定された損失関数を削減なしで使用して、各データサンプルに対してフォワードパスを実行して損失値を評価します。

   次のコードは、`SiftingImplementedLoss` という名前のスマートふるい実装 `Loss` インターフェイスの例です。

   ```
   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. PyTroch データローダーを SageMaker AI `SiftingDataloader` クラスでラップします。

   最後に、前のステップで設定したすべての 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 トレーニングジョブのイテレーションごとにデータロードの一部として実行されるように登録されます。次のコード例は、PyTorch `DataLoader` への SageMaker AI データふるいの実装を示しています。

   ```
   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>

SageMaker スマートふるいを Transformers `Trainer` クラスに実装するには、2 つの方法があります。

**注記**  
SageMaker スマートふるいパッケージがインストールされている PyTorch 用の DLC のいずれかを使用する場合は、`transformers` ライブラリをインストールする必要があることに注意してください。[DLC を拡張する](prebuilt-containers-extend.md)か、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)) のトレーニングジョブランチャークラスに `requirements.txt` 渡すことで、追加のパッケージをインストールできます。

## シンプルセットアップ
<a name="train-smart-sifting-apply-to-hugging-face-transformers-script-simple"></a>

Transformers `Trainer` クラスに SageMaker スマートふるいを実装する最も簡単な方法は、`enable_sifting` 関数を使用することです。この関数は既存の `Trainer` オブジェクトを受け入れ、既存の `DataLoader` オブジェクトを `SiftingDataloader` でラップします。同じトレーニングオブジェクトを引き続き使用できます。次の使用例を参照してください。

```
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` と 1 つのエポックが使用されていた場合、`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 引数](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` クラスのオブジェクトを返すことができます。既存のカスタムトレーナーを使用する場合、このアプローチは侵入性は低いかもしれませんが、シンプルセットアップオプションよりもコードの変更が必要です。以下は、カスタム Hugging Face の `Trainer` クラスへの SageMaker スマートふるいの実装例です。

```
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()
```