

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Modificare uno script PyTorch di addestramento
<a name="model-parallel-customize-training-script-pt"></a>

In questa sezione, imparerai come modificare gli script di PyTorch addestramento per configurare la libreria di parallelismo dei SageMaker modelli per il partizionamento automatico e il partizionamento manuale.

**Nota**  
Per scoprire quali PyTorch versioni sono supportate dalla libreria, consulta. [Framework supportati e Regioni AWS](distributed-model-parallel-support.md)

**Suggerimento**  
Per alcuni esempi di end-to-end quaderni che dimostrano come utilizzare uno script di PyTorch addestramento con la libreria di parallelismo dei SageMaker modelli, vedete. [Esempi della libreria di parallelismo dei modelli Amazon SageMaker AI v1](distributed-model-parallel-examples.md)

Si prega di notare che il partizionamento automatico è abilitato come impostazione predefinita. Se non diversamente specificato, gli script seguenti utilizzano il partizionamento automatico. 

**Topics**
+ [Divisione automatica con PyTorch](#model-parallel-customize-training-script-pt-16)
+ [Divisione manuale con PyTorch](#model-parallel-customize-training-script-pt-16-hvd)
+ [Considerazioni](#model-parallel-pt-considerations)
+ [Funzionalità del framework non supportate](#model-parallel-pt-unsupported-features)

## Divisione automatica con PyTorch
<a name="model-parallel-customize-training-script-pt-16"></a>

Le seguenti modifiche allo script di addestramento sono necessarie per eseguire uno script di PyTorch addestramento con la libreria SageMaker di parallelismo dei modelli:

1. Importa e inizializza la libreria con [https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_common_api.html#smp.init](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_common_api.html#smp.init).

1. Esegui il wrapping del modello con [https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_pytorch.html#smp.DistributedModel](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_pytorch.html#smp.DistributedModel). Tieni presente che tutti i tensori restituiti dal metodo `forward` dell'oggetto `nn.Module` sottostante verranno trasmessi su dispositivi paralleli al modello, con un sovraccarico di comunicazione, quindi non dovrebbe essere restituito alcun tensore che non è necessario al di fuori del metodo di chiamata (come le attivazioni intermedie).
**Nota**  
Per la FP16 formazione, è necessario utilizzare il gestore di contesto [smdistributed.modelparallel.torch.model\$1creation () per avvolgere il modello.](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/latest/smd_model_parallel_pytorch.html) Per ulteriori informazioni, consulta [FP16 Formazione con Model Parallelism](model-parallel-extended-features-pytorch-fp16.md).

1. Esegui il wrapping dell'ottimizzatore con [https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_pytorch.html#smp.DistributedOptimizer](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_pytorch.html#smp.DistributedOptimizer).
**Nota**  
Per FP16 la formazione, è necessario impostare la scalabilità statica o dinamica delle perdite. Per ulteriori informazioni, consulta [FP16 Formazione con Model Parallelism](model-parallel-extended-features-pytorch-fp16.md).

1. Utilizza l'oggetto `DistributedModel` restituito anziché un modello utente.

1. Inserisci la logica forward e backward in una funzione a fasi e decorala con [https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_common_api.html#smp.init](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_common_api.html#smp.init).

1. Limita ciascun processo al proprio dispositivo tramite `torch.cuda.set_device(smp.local_rank())`.

1. Sposta i tensori di input sulla GPU utilizzando l'API `.to()` prima della chiamata `smp.step` (vedi esempio sotto riportato).

1. Sostituisci `torch.Tensor.backward` e `torch.autograd.backward` con `DistributedModel.backward`.

1. Esegui la post-elaborazione sugli output tra i microbatch utilizzando metodi [https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_common_api.html#StepOutput](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_common_api.html#StepOutput) come `reduce_mean`.

1. Se è presente una fase di valutazione, inserisci in modo analogo la logica forward all'interno di una funzione decorata `smp.step` e post-elabora gli output utilizzando l'[API `StepOutput`](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_common_api.html#StepOutput).

1. Imposta `drop_last=True` in `DataLoader`. In alternativa, salta manualmente un batch nel ciclo di addestramento se la dimensione del batch non è suddivisibile per il numero di microbatch.

[Per ulteriori informazioni sull'API della libreria SageMaker di parallelismo dei modelli, consulta la documentazione dell'API.](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smd_model_parallel.html) 

```
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchnet.dataset import SplitDataset
from torchvision import datasets

import smdistributed.modelparallel.torch as smp

class GroupedNet(nn.Module):
    def __init__(self):
        super(GroupedNet, self).__init__()
        # define layers

    def forward(self, x):
        # define forward pass and return model outputs


# smdistributed: Define smp.step. Return any tensors needed outside.
@smp.step
def train_step(model, data, target):
    output = model(data)
    loss = F.nll_loss(output, target, reduction="mean")
    model.backward(loss)
    return output, loss


def train(model, device, train_loader, optimizer):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        # smdistributed: Move input tensors to the GPU ID used by the current process,
        # based on the set_device call.
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        # Return value, loss_mb is a StepOutput object
        _, loss_mb = train_step(model, data, target)

        # smdistributed: Average the loss across microbatches.
        loss = loss_mb.reduce_mean()

        optimizer.step()

# smdistributed: initialize the backend
smp.init()

# smdistributed: Set the device to the GPU ID used by the current process.
# Input tensors should be transferred to this device.
torch.cuda.set_device(smp.local_rank())
device = torch.device("cuda")

# smdistributed: Download only on a single process per instance.
# When this is not present, the file is corrupted by multiple processes trying
# to download and extract at the same time
dataset = datasets.MNIST("../data", train=True, download=False)

# smdistributed: Shard the dataset based on data-parallel ranks
if smp.dp_size() > 1:
    partitions_dict = {f"{i}": 1 / smp.dp_size() for i in range(smp.dp_size())}
    dataset = SplitDataset(dataset, partitions=partitions_dict)
    dataset.select(f"{smp.dp_rank()}")

# smdistributed: Set drop_last=True to ensure that batch size is always divisible
# by the number of microbatches
train_loader = torch.utils.data.DataLoader(dataset, batch_size=64, drop_last=True)

model = GroupedNet()
optimizer = optim.Adadelta(model.parameters(), lr=4.0)

# smdistributed: Use the DistributedModel container to provide the model
# to be partitioned across different ranks. For the rest of the script,
# the returned DistributedModel object should be used in place of
# the model provided for DistributedModel class instantiation.
model = smp.DistributedModel(model)
optimizer = smp.DistributedOptimizer(optimizer)

train(model, device, train_loader, optimizer)
```

## Divisione manuale con PyTorch
<a name="model-parallel-customize-training-script-pt-16-hvd"></a>

Usa i gestori di contesto [https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_pytorch.html#smp.DistributedOptimizer](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_pytorch.html#smp.DistributedOptimizer) per posizionare i moduli in dispositivi specifici. Qualsiasi modulo non inserito in alcun contesto `smp.partition` viene inserito in `default_partition`. Deve essere indicata `default_partition` se `auto_partition` è impostata su `False`. I moduli creati in un contesto `smp.partition` specifico vengono posizionati nella partizione corrispondente.

[Per ulteriori informazioni sull'API della libreria SageMaker di parallelismo dei modelli, consulta la documentazione dell'API.](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smd_model_parallel.html) 

```
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchnet.dataset import SplitDataset
from torchvision import datasets

import smdistributed.modelparallel.torch as smp

class GroupedNet(nn.Module):
    def __init__(self):
        super(GroupedNet, self).__init__()
        with smp.partition(0):
            # define child modules on device 0
        with smp.partition(1):
            # define child modules on device 1

    def forward(self, x):
        # define forward pass and return model outputs


# smdistributed: Define smp.step. Return any tensors needed outside.
@smp.step
def train_step(model, data, target):
    output = model(data)
    loss = F.nll_loss(output, target, reduction="mean")
    model.backward(loss)
    return output, loss


def train(model, device, train_loader, optimizer):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        # smdistributed: Move input tensors to the GPU ID used by the current process,
        # based on the set_device call.
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        # Return value, loss_mb is a StepOutput object
        _, loss_mb = train_step(model, data, target)

        # smdistributed: Average the loss across microbatches.
        loss = loss_mb.reduce_mean()

        optimizer.step()

# smdistributed: initialize the backend
smp.init()

# smdistributed: Set the device to the GPU ID used by the current process.
# Input tensors should be transferred to this device.
torch.cuda.set_device(smp.local_rank())
device = torch.device("cuda")

# smdistributed: Download only on a single process per instance.
# When this is not present, the file is corrupted by multiple processes trying
# to download and extract at the same time
dataset = datasets.MNIST("../data", train=True, download=False)

# smdistributed: Shard the dataset based on data-parallel ranks
if smp.dp_size() > 1:
    partitions_dict = {f"{i}": 1 / smp.dp_size() for i in range(smp.dp_size())}
    dataset = SplitDataset(dataset, partitions=partitions_dict)
    dataset.select(f"{smp.dp_rank()}")

# smdistributed: Set drop_last=True to ensure that batch size is always divisible
# by the number of microbatches
train_loader = torch.utils.data.DataLoader(dataset, batch_size=64, drop_last=True)

model = GroupedNet()
optimizer = optim.Adadelta(model.parameters(), lr=4.0)

# smdistributed: Use the DistributedModel container to provide the model
# to be partitioned across different ranks. For the rest of the script,
# the returned DistributedModel object should be used in place of
# the model provided for DistributedModel class instantiation.
model = smp.DistributedModel(model)
optimizer = smp.DistributedOptimizer(optimizer)

train(model, device, train_loader, optimizer)
```

## Considerazioni
<a name="model-parallel-pt-considerations"></a>

Quando configurate uno script di PyTorch addestramento utilizzando SageMaker la libreria di parallelismo dei modelli, dovete tenere presente quanto segue:
+ Se utilizzi una tecnica di ottimizzazione che si basa su norme riguardanti i gradienti globali, ad esempio una norma sui gradienti di tutto il modello, come alcune varianti dell'ottimizzatore LAMB o il ritaglio del gradiente globale, è necessario raccogliere tutte le norme delle partizioni del modello per verificarne la correttezza. A tale scopo è possibile utilizzare i tipi di dati di base per la comunicazione della libreria.
+ Tutti gli argomenti `torch.Tensor` relativi ai metodi forward di `nn.Modules` nel modello devono essere utilizzati nel calcolo dell'output del modulo. In altre parole, la libreria non supporta il caso in cui esista un argomento `torch.Tensor` per un modulo da cui l'output del modulo non dipende.
+ L'argomento della chiamata `smp.DistributedModel.backward()` deve dipendere da tutti gli output del modello. In altre parole, non può esserci un output della chiamata `smp.DistributedModel.forward` che non venga utilizzato nel calcolo del tensore utilizzato per la chiamata `smp.DistributedModel.backward`.
+ Se nel codice sono presenti chiamate `torch.cuda.synchronize()`, potrebbe essere necessario chiamare `torch.cuda.set_device(smp.local_rank())` immediatamente prima della chiamata di sincronizzazione. Altrimenti potrebbero essere creati contesti CUDA non necessari nel dispositivo 0, che consumerebbe inutilmente memoria.
+ Poiché la libreria colloca `nn.Modules` su dispositivi diversi, i moduli nel modello non devono dipendere da alcuno stato globale modificato all'interno `smp.step`. È consentito qualsiasi stato che rimanga fisso durante l'addestramento o che venga modificato all'esterno di `smp.step` in modo visibile a tutti i processi.
+ Non è necessario spostare il modello nella GPU (ad esempio, utilizzando `model.to(device)`) quando si utilizza la libreria. Se provi a spostare il modello nella GPU prima che il modello sia partizionato (prima della prima chiamata `smp.step`), la chiamata di spostamento viene ignorata. La libreria sposta automaticamente la parte del modello assegnato a una classificazione nella sua GPU. Una volta iniziato l'addestramento con la libreria, non spostare il modello nella CPU e usalo, poiché non avrà i parametri corretti per i moduli non assegnati alla partizione tenuta dal processo. Se desideri riqualificare un modello o utilizzarlo per l'inferenza senza la libreria dopo che è stato addestrato utilizzando la libreria di parallelismo dei modelli, il modo consigliato è salvare il modello completo utilizzando la nostra API di checkpointing e caricarlo di nuovo su un normale Module. PyTorch 
+ Se disponi di un elenco di moduli tale per cui l'output di un modulo ne alimenta un altro, la sostituzione di tale elenco con `nn.Sequential` può migliorare significativamente le prestazioni.
+ L'aggiornamento del peso (`optimizer.step()`) deve avvenire all'esterno di `smp.step` perché è allora che l'intero passaggio indietro è terminato e i gradienti sono pronti. Quando si utilizza un modello ibrido con parallelismo di modelli e dati, a questo punto è garantito anche il completamento dei gradienti AllReduce .
+ Quando usi la libreria in combinazione con il parallelismo dei dati, assicurati che il numero di batch su tutti i ranghi paralleli dei dati sia lo stesso in modo da AllReduce non bloccare l'attesa di un rank che non partecipa al passaggio.
+ Se avvii un processo di addestramento utilizzando un tipo di istanza ml.p4d (ad esempio ml.p4d.24xlarge), è necessario impostare la variabile del caricatore di dati `num_workers=0`. Ad esempio, puoi definire il tuo `DataLoader` come di seguito indicato:

  ```
  dataloader = torch.utils.data.DataLoader(
              data,
              batch_size=batch_size,
              num_workers=0,
              pin_memory=True,
              drop_last=True,
              shuffle=shuffle,
          )
  ```
+ Gli input per `smp.step` devono essere gli input del modello generati da `DataLoader`. Ciò è necessario perché `smp.step` suddivide internamente i tensori di input lungo la dimensione del batch e li inserisce in pipeline. Di conseguenza, passare `DataLoader` alla funzione `smp.step` per generare gli input del modello all'interno non funziona. 

  Ad esempio, se definisci un `DataLoader` come di seguito indicato:

  ```
  train_loader = torch.utils.data.DataLoader(dataset, batch_size=64, drop_last=True)
  ```

  Dovresti accedere agli input del modello generati da `train_loader` e passarli a una funzione decorata `smp.step`. Non passare `train_loader` direttamente alla funzione `smp.step`.

  ```
  def train(model, device, train_loader, optimizer):
      model.train()
      for batch_idx, (data, target) in enumerate(train_loader):
          ...
          _, loss_mb = train_step(model, data, target)
          ...
  
  @smp.step
  def train_step(model, data, target):
      ...
      return output, loss
  ```
+ I tensori di input per `smp.step` devono essere spostati sul dispositivo attuale utilizzando l'API `.to()`, che deve avvenire dopo la chiamata `torch.cuda.set_device(local_rank())`.

  Ad esempio, puoi definire la funzione `train` nel modo seguente. Questa funzione aggiunge `data` e `target` al dispositivo attuale utilizzando l'API `.to()` prima di utilizzare i tensori di input per chiamare `train_step`.

  ```
  def train(model, device, train_loader, optimizer):
      model.train()
      for batch_idx, (data, target) in enumerate(train_loader):
          # smdistributed: Move input tensors to the GPU ID used by the current process,
          # based on the set_device call.
          data, target = data.to(device), target.to(device)
          optimizer.zero_grad()
          # Return value, loss_mb is a StepOutput object
          _, loss_mb = train_step(model, data, target)
  
          # smdistributed: Average the loss across microbatches.
          loss = loss_mb.reduce_mean()
  
          optimizer.step()
  ```

  I tensori di input per questa funzione decorata `smp.set` sono stati spostati nel dispositivo attuale nella funzione `train` sopra riportata. *Non* è necessario spostare il modello nel dispositivo corrente. La libreria sposta automaticamente la parte del modello assegnato a una classificazione nella sua GPU.

  ```
  @smp.step
  def train_step(model, data, target):
      output = model(data)
      loss = F.nll_loss(output, target, reduction="mean")
      model.backward(loss)
      return output, loss
  ```

## Funzionalità del framework non supportate
<a name="model-parallel-pt-unsupported-features"></a>

Le seguenti PyTorch funzionalità non sono supportate dalla SageMaker libreria di parallelismo dei modelli:
+ Se si utilizza il parallelismo dei dati con il [PyTorch DDP](https://pytorch.org/tutorials/intermediate/ddp_tutorial.html) nativo, il modulo [https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html](https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html)wrapper non è supportato dalla libreria. La libreria gestisce internamente l'integrazione con PyTorch DDP, inclusi i parametri broadcast e gradient. AllReduce Quando si utilizza la libreria, i buffer dei moduli vengono trasmessi una sola volta all'inizio dell'addestramento. Se il proprio modello ha buffer di moduli che devono essere sincronizzati tra gruppi paralleli di dati in ogni fase, è possibile farlo tramite l'API `torch.distributed`, utilizzando il gruppo di processi che può essere ottenuto tramite `smp.get_dp_process_group()`.
+ Per l'addestramento di precisione misto, il modulo `apex.amp` non è supportato. Per utilizzare la libreria con precisione mista automatica si consiglia di utilizzare `torch.cuda.amp`, utilizzando tuttavia `smp.amp.GradScaler` anziché l'implementazione in torch.
+ `torch.jit.ScriptModules` e `ScriptFunctions` non sono supportati da `smp.DistributedModel`.
+ `apex`: `FusedLayerNorm`, `FusedAdam`, `FusedLAMB` e `FusedNovoGrad` da `apex` sono supportati. È possibile utilizzare le relative implementazioni della libreria tramite e invece. `smp.optimizers` `smp.nn` APIs 