

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à.

# Passaggio 1: modifica il tuo script di addestramento utilizzando SageMaker la libreria parallela di modelli distribuiti
<a name="model-parallel-customize-training-script"></a>

Utilizza questa sezione per scoprire come personalizzare lo script di formazione per utilizzare le funzionalità principali della libreria di parallelismo dei modelli Amazon SageMaker AI. *Per utilizzare le funzioni e i parametri API specifici della libreria, ti consigliamo di utilizzare questa documentazione insieme alla [libreria model SageMaker parallel APIs](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smd_model_parallel.html) nella documentazione di Python SageMaker SDK.*

Gli esempi di script di addestramento forniti in queste sezioni sono semplificati e progettati per evidenziare le modifiche necessarie da apportare per utilizzare la libreria. Per end-to-end esempi di notebook eseguibili che dimostrano come utilizzare uno script di PyTorch addestramento TensorFlow o di addestramento con la libreria Model Parallelism, consulta. SageMaker [Esempi di Amazon SageMaker AI Model Parallelism Library v2](distributed-model-parallel-v2-examples.md)

**Topics**
+ [Dividi il modello del tuo script di addestramento utilizzando la libreria di parallelismo dei modelli SageMaker](#model-parallel-model-splitting-using-smp-lib)
+ [Modificare uno script di addestramento TensorFlow](model-parallel-customize-training-script-tf.md)
+ [Modificare uno script PyTorch di addestramento](model-parallel-customize-training-script-pt.md)

## Dividi il modello del tuo script di addestramento utilizzando la libreria di parallelismo dei modelli SageMaker
<a name="model-parallel-model-splitting-using-smp-lib"></a>

Esistono due modi per modificare lo script di addestramento per configurare la suddivisione dei modelli: suddivisione automatica o suddivisione manuale.

### Suddivisione automatica dei modelli
<a name="model-parallel-automated-model-splitting"></a>

*Quando si utilizza SageMaker la libreria di parallelismo dei modelli, è possibile sfruttare la *suddivisione automatica dei modelli*, nota anche come partizionamento automatico dei modelli.* La libreria utilizza un algoritmo di partizionamento che bilancia la memoria, riduce al minimo la comunicazione tra i dispositivi e ottimizza le prestazioni. È possibile configurare l'algoritmo di partizionamento automatico per ottimizzare la velocità o la memoria. 

In alternativa, è possibile ricorrere alla suddivisione manuale dei modelli. Consigliamo la suddivisione automatica dei modelli, a meno che non si abbia molta dimestichezza con l'architettura dei modelli e si abbia una buona idea di come partizionare i modelli in modo efficiente.

#### Come funziona
<a name="model-parallel-automated-model-splitting-how-it-works"></a>

Il partizionamento automatico avviene durante la prima fase di addestramento, quando viene chiamata per la prima volta la funzione lavorata da `smp.step`. Durante questa chiamata, la libreria costruisce innanzitutto una versione del modello sulla RAM della CPU (per evitare limitazioni di memoria della GPU), quindi analizza il grafico del modello e prende una decisione sul partizionamento. In base a questa decisione, ogni partizione del modello viene caricata su una GPU e solo allora viene eseguita la prima fase. A causa di queste fasi di analisi e partizionamento, la prima fase di addestramento potrebbe richiedere più tempo. 

In entrambi i framework, la libreria gestisce la comunicazione tra i dispositivi tramite il proprio backend, ottimizzato per l'infrastruttura. AWS 

Il design della partizione automatica si adatta alle caratteristiche del framework e la libreria esegue il partizionamento al livello di granularità più naturale in ciascun framework. Ad esempio, in TensorFlow, ogni operazione specifica può essere assegnata a un dispositivo diverso, mentre in PyTorch, l'assegnazione viene eseguita a livello di modulo, dove ogni modulo è composto da più operazioni. La sezione seguente esamina le specifiche di design in ciascun framework.

##### Divisione automatica del modello con PyTorch
<a name="model-parallel-auto-model-split-pt"></a>

Durante la prima fase di addestramento, la libreria di parallelismo dei modelli esegue internamente una fase di tracciamento che ha lo scopo di realizzare il grafico dei modelli e determinare le forme del tensore e dei parametri. Dopo questa fase di tracciamento, la libreria realizza un albero, costituito dagli oggetti `nn.Module` annidati nel modello, oltre ai dati aggiuntivi raccolti dal tracciamento, come la quantità di dati archiviati `nn.Parameters` e il tempo di esecuzione per ciascuno `nn.Module`. 

Successivamente, la libreria attraversa questo albero dalla radice ed esegue un algoritmo di partizionamento che assegna ciascun `nn.Module` a un dispositivo, che bilancia il carico computazionale (misurato dal tempo di esecuzione del modulo) e l'uso della memoria (misurato dalla dimensione `nn.Parameter` totale memorizzata e dalle attivazioni). Se più `nn.Modules` condividono lo stesso `nn.Parameter`, questi moduli vengono posizionati sullo stesso dispositivo per evitare di mantenere più versioni dello stesso parametro. Una volta presa la decisione sul partizionamento, i moduli e i pesi assegnati vengono caricati sui rispettivi dispositivi.

Per istruzioni su come registrare il `smp.step` decoratore nel copione di PyTorch allenamento, consulta. [Divisione automatica con PyTorch](model-parallel-customize-training-script-pt.md#model-parallel-customize-training-script-pt-16)

##### Divisione automatica del modello con TensorFlow
<a name="model-parallel-auto-model-split-tf"></a>

La libreria di parallelismo dei modelli analizza le dimensioni delle variabili addestrabili e la struttura dei grafici e utilizza internamente un algoritmo di partizionamento dei grafici. Questo algoritmo prevede l'assegnazione di un dispositivo per ogni operazione, con l'obiettivo di ridurre al minimo la quantità di comunicazioni necessarie tra i dispositivi, nel rispetto di due vincoli: 
+ Bilanciamento del numero di variabili memorizzate in ogni dispositivo
+ Bilanciamento del numero di operazioni eseguite in ogni dispositivo

Se specifichi `speed` per `optimize` (nei parametri di parallelismo dei modelli in SDK per Python), la libreria tenta di bilanciare il numero di operazioni e gli oggetti `tf.Variable` in ogni dispositivo. Altrimenti, tenta di bilanciare la dimensione totale di `tf.Variables`.

Una volta presa la decisione sul partizionamento, la libreria crea una rappresentazione serializzata del grafico secondario che ogni dispositivo deve eseguire e la importa su ogni dispositivo. Durante il partizionamento, la libreria colloca le operazioni che utilizzano la stessa `tf.Variable` e quelle che fanno parte dello stesso livello Keras sullo stesso dispositivo. Rispetta inoltre i vincoli di colocation imposti da. TensorFlow Ciò significa che, ad esempio, se ci sono due livelli Keras che condividono una `tf.Variable`, tutte le operazioni che fanno parte di questi livelli vengono posizionate su un singolo dispositivo.

Per istruzioni su come registrare il `smp.step` decoratore nel copione di allenamento, consulta. PyTorch [Divisione automatica con TensorFlow](model-parallel-customize-training-script-tf.md#model-parallel-customize-training-script-tf-23)

##### Confronto della suddivisione automatica dei modelli tra framework
<a name="model-parallel-auto-model-split-comparison"></a>

In TensorFlow, l'unità di calcolo fondamentale è a`tf.Operation`, e TensorFlow rappresenta il modello come un grafo aciclico diretto (DAG) di `tf.Operation` s, e quindi la libreria di parallelismo del modello partiziona questo DAG in modo che ogni nodo vada su un dispositivo. È fondamentale che gli oggetti `tf.Operation` siano sufficientemente ricchi di attributi personalizzabili e siano universali, nel senso che è garantito che ogni modello sia costituito di un grafico di tali oggetti. 

PyTorch d'altra parte, non ha una nozione di funzionamento equivalente sufficientemente ricca e universale. L'unità di calcolo più simile PyTorch che presenta queste caratteristiche è an`nn.Module`, che ha un livello di granularità molto più elevato, ed è per questo che la libreria esegue il partizionamento a questo livello in. PyTorch

### Suddivisione manuale dei modelli
<a name="model-parallel-manual-model-splitting"></a>

Se desideri specificare manualmente come partizionare il modello tra i dispositivi, usa il gestore di contesto `smp.partition`. Per istruzioni sulla modalità di impostazione del gestore di contesto per il partizionamento manuale, consulta le pagine seguenti.
+ [Divisione manuale con TensorFlow](model-parallel-customize-training-script-tf.md#model-parallel-customize-training-script-tf-manual)
+ [Divisione manuale con PyTorch](model-parallel-customize-training-script-pt.md#model-parallel-customize-training-script-pt-16-hvd)

Per utilizzare questa opzione dopo aver apportato le modifiche, nel passaggio 2, dovrai impostare `auto_partition` e definire un `default_partition` nella classe di stima del framework dell'SDK Python SageMaker. `False` Qualsiasi operazione che non viene inserita in modo esplicito su una partizione tramite il gestore di contesto `smp.partition` è eseguita su `default_partition`. In questo caso, la logica di suddivisione automatica viene ignorata e ogni operazione viene posizionata in base alle specifiche dell'utente. In base alla struttura del grafico risultante, la libreria di parallelismo dei modelli crea automaticamente una pianificazione di esecuzione in pipeline.

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

In questa sezione, imparerai come modificare gli script di TensorFlow addestramento per configurare la libreria di parallelismo dei SageMaker modelli per il partizionamento automatico e il partizionamento manuale. Gli esempi scelti comprendono anche un esempio integrato con Horovod per il modello ibrido e il parallelismo dei dati.

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

Le modifiche necessarie da apportare allo script di addestramento per utilizzare la libreria sono elencate in [Divisione automatica con TensorFlow](#model-parallel-customize-training-script-tf-23).

Per maggiori informazioni su come modificare lo script di addestramento per utilizzare il modello ibrido e il parallelismo dei dati con Horovod, consulta [Suddivisione automatizzata con TensorFlow e Horovod per modelli ibridi e parallelismo dei dati](#model-parallel-customize-training-script-tf-2.3).

Se desideri utilizzare il partizionamento manuale, consulta anche [Divisione manuale con TensorFlow](#model-parallel-customize-training-script-tf-manual). 

I seguenti argomenti mostrano esempi di script di addestramento che è possibile utilizzare per configurare la libreria di parallelismo SageMaker dei modelli per il partizionamento automatico e i modelli di partizionamento manuale. TensorFlow 

**Nota**  
Il partizionamento automatico è abilitato come impostazione predefinita. Se non diversamente specificato, gli script di esempio utilizzano il partizionamento automatico.

**Topics**
+ [Divisione automatica con TensorFlow](#model-parallel-customize-training-script-tf-23)
+ [Suddivisione automatizzata con TensorFlow e Horovod per modelli ibridi e parallelismo dei dati](#model-parallel-customize-training-script-tf-2.3)
+ [Divisione manuale con TensorFlow](#model-parallel-customize-training-script-tf-manual)
+ [Funzionalità del framework non supportate](#model-parallel-tf-unsupported-features)

## Divisione automatica con TensorFlow
<a name="model-parallel-customize-training-script-tf-23"></a>

Le seguenti modifiche allo script di addestramento sono necessarie per eseguire un TensorFlow modello 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. Definisci un modello Keras ereditandolo da [https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_tensorflow.html](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_tensorflow.html) anziché dalla classe di modelli Keras. Restituisci gli output del modello dal metodo di chiamata dell'oggetto `smp.DistributedModel`. Tieni presente che tutti i tensori restituiti dal metodo di chiamata 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) .

1. Imposta `drop_remainder=True` nel metodo `tf.Dataset.batch()`. Ciò serve a garantire che la dimensione del batch sia sempre divisibile per il numero di microbatch.

1. Semina le operazioni casuali nella pipeline di dati utilizzando`smp.dp_rank()`, ad esempio, `shuffle(ds, seed=smp.dp_rank())` per garantire la coerenza dei campioni di dati su GPUs cui sono presenti diverse partizioni del modello.

1. Inserisci la logica forward e backward in una funzione a fasi e decorala con `smp.step`.

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`. La funzione [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) deve restituire un valore che dipende dall'output di `smp.DistributedModel`.

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).

[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) 

Il seguente script Python è un esempio di script di addestramento dopo l'introduzione delle modifiche.

```
import tensorflow as tf

# smdistributed: Import TF2.x API
import smdistributed.modelparallel.tensorflow as smp

# smdistributed: Initialize
smp.init()

# Download and load MNIST dataset.
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data(
    "MNIST-data-%d" % smp.rank()
)
x_train, x_test = x_train / 255.0, x_test / 255.0

# Add a channels dimension
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]

# smdistributed: If needed, seed the shuffle with smp.dp_rank(), and drop_remainder
# in batching to make sure batch size is always divisible by number of microbatches
train_ds = (
    tf.data.Dataset.from_tensor_slices((x_train, y_train))
    .shuffle(10000, seed=smp.dp_rank())
    .batch(256, drop_remainder=True)
)

# smdistributed: Define smp.DistributedModel the same way as Keras sub-classing API 
class MyModel(smp.DistributedModel):
    def __init__(self):
        super(MyModel, self).__init__()
        # define layers

    def call(self, x, training=None):
        # define forward pass and return the model output

model = MyModel()

loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
optimizer = tf.keras.optimizers.Adam()
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name="train_accuracy")

# smdistributed: Define smp.step. Return any tensors needed outside
@smp.step
def get_grads(images, labels):
    predictions = model(images, training=True)
    loss = loss_object(labels, predictions)

    grads = optimizer.get_gradients(loss, model.trainable_variables)
    return grads, loss, predictions


@tf.function
def train_step(images, labels):
    gradients, loss, predictions = get_grads(images, labels)

    # smdistributed: Accumulate the gradients across microbatches
    gradients = [g.accumulate() for g in gradients]
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))

    # smdistributed: Merge predictions and average losses across microbatches
    train_accuracy(labels, predictions.merge())
    return loss.reduce_mean()


for epoch in range(5):
    # Reset the metrics at the start of the next epoch
    train_accuracy.reset_states()
    for images, labels in train_ds:
        loss = train_step(images, labels)
    accuracy = train_accuracy.result()
```

Se hai finito di preparare lo script di addestramento, procedi con [Fase 2: Avviare un job di formazione utilizzando SageMaker Python SDK](model-parallel-sm-sdk.md). Se desideri eseguire un modello ibrido e un processo di addestramento per il parallelismo dei dati, continua con la sezione successiva.

## Suddivisione automatizzata con TensorFlow e Horovod per modelli ibridi e parallelismo dei dati
<a name="model-parallel-customize-training-script-tf-2.3"></a>

Puoi utilizzare la libreria di parallelismo dei SageMaker modelli con Horovod per il parallelismo ibrido di modelli e dati. Per saperne di più su come la libreria suddivide un modello per il parallelismo ibrido, consulta [PyTorch TensorFlowParallelismo delle pipeline (disponibile per e)](model-parallel-intro.md#model-parallel-intro-pp).

In questo passaggio, ci concentriamo su come modificare lo script di addestramento per adattare la libreria di parallelismo dei modelli. SageMaker

Per configurare correttamente il proprio script di addestramento per acquisire la configurazione di parallelismo ibrido che sarà impostata in [Fase 2: Avviare un job di formazione utilizzando SageMaker Python SDK](model-parallel-sm-sdk.md), usa le funzioni di supporto della libreria, `smp.dp_rank()` e `smp.mp_rank()`, che rilevano automaticamente rispettivamente la classificazione parallela dei dati e la classificazione parallela dei modelli. 

Per trovare tutte le primitive MPI supportate dalla libreria, consulta [MPI Basics](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_common_api.html#mpi-basics) nella documentazione di Python SDK. SageMaker 

Le modifiche richieste nello script sono:
+ Aggiunta di `hvd.allreduce`
+ Variabili di trasmissione dopo il primo batch, come richiesto da Horovod
+ Semina le operazioni di and/or shuffling sharding nella pipeline di dati con. `smp.dp_rank()`

**Nota**  
Quando usi Horovod, non devi chiamare `hvd.init` direttamente nel tuo script di addestramento. Dovrai invece `"horovod"` impostarlo `True` nei parametri dell'SDK `modelparallel` di SageMaker Python in. [Fase 2: Avviare un job di formazione utilizzando SageMaker Python SDK](model-parallel-sm-sdk.md) Ciò consente alla libreria di inizializzare internamente Horovod in base alle assegnazioni dei dispositivi delle partizioni del modello. La chiamata di `hvd.init()` direttamente nello script di addestramento può causare problemi.

**Nota**  
L'utilizzo dell'API `hvd.DistributedOptimizer` direttamente nello script di addestramento potrebbe comportare velocità e prestazioni di addestramento scadenti, poiché l'API inserisce implicitamente l'operazione `AllReduce` all'interno di `smp.step`. Ti consigliamo di utilizzare la libreria di parallelismo dei modelli con Horovod chiamando direttamente `hvd.allreduce` dopo aver chiamato `accumulate()` o `reduce_mean()` sui gradienti restituiti da `smp.step`, come verrà mostrato nell'esempio seguente.

[Per saperne di più 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 tensorflow as tf
import horovod.tensorflow as hvd

# smdistributed: Import TF2.x API 
import smdistributed.modelparallel.tensorflow as smp

# smdistributed: Initialize
smp.init()

# Download and load MNIST dataset.
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data(
    "MNIST-data-%d" % smp.rank()
)
x_train, x_test = x_train / 255.0, x_test / 255.0

# Add a channels dimension
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]

# smdistributed: Seed the shuffle with smp.dp_rank(), and drop_remainder
# in batching to make sure batch size is always divisible by number of microbatches
train_ds = (
    tf.data.Dataset.from_tensor_slices((x_train, y_train))
    .shuffle(10000, seed=smp.dp_rank())
    .batch(256, drop_remainder=True)
)

# smdistributed: Define smp.DistributedModel the same way as Keras sub-classing API 
class MyModel(smp.DistributedModel):
    def __init__(self):
        super(MyModel, self).__init__()
        # define layers

    def call(self, x, training=None):
        # define forward pass and return model outputs


model = MyModel()

loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
optimizer = tf.keras.optimizers.Adam()
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name="train_accuracy")

# smdistributed: Define smp.step. Return any tensors needed outside
@smp.step
def get_grads(images, labels):
    predictions = model(images, training=True)
    loss = loss_object(labels, predictions)

    grads = optimizer.get_gradients(loss, model.trainable_variables)
    return grads, loss, predictions


@tf.function
def train_step(images, labels, first_batch):
    gradients, loss, predictions = get_grads(images, labels)

    # smdistributed: Accumulate the gradients across microbatches
    # Horovod: AllReduce the accumulated gradients
    gradients = [hvd.allreduce(g.accumulate()) for g in gradients]
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))

    # Horovod: Broadcast the variables after first batch 
    if first_batch:
        hvd.broadcast_variables(model.variables, root_rank=0)
        hvd.broadcast_variables(optimizer.variables(), root_rank=0)

    # smdistributed: Merge predictions across microbatches
    train_accuracy(labels, predictions.merge())
    return loss.reduce_mean()


for epoch in range(5):
    # Reset the metrics at the start of the next epoch
    train_accuracy.reset_states()

    for batch, (images, labels) in enumerate(train_ds):
        loss = train_step(images, labels, tf.constant(batch == 0))
```

## Divisione manuale con TensorFlow
<a name="model-parallel-customize-training-script-tf-manual"></a>

Usa i gestori di contesto `smp.partition` per inserire le operazioni in una partizione specifica. Qualsiasi operazione non inserita in alcun contesto `smp.partition` viene inserita in `default_partition`. [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 tensorflow as tf

# smdistributed: Import TF2.x API.
import smdistributed.modelparallel.tensorflow as smp

# smdistributed: Initialize
smp.init()

# Download and load MNIST dataset.
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data(
    "MNIST-data-%d" % smp.rank()
)
x_train, x_test = x_train / 255.0, x_test / 255.0

# Add a channels dimension
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]

# smdistributed: If needed, seed the shuffle with smp.dp_rank(), and drop_remainder
# in batching to make sure batch size is always divisible by number of microbatches.
train_ds = (
    tf.data.Dataset.from_tensor_slices((x_train, y_train))
    .shuffle(10000, seed=smp.dp_rank())
    .batch(256, drop_remainder=True)
)

# smdistributed: Define smp.DistributedModel the same way as Keras sub-classing API.
class MyModel(smp.DistributedModel):
    def __init__(self):
         # define layers

    def call(self, x):
        with smp.partition(0):
            x = self.layer0(x)
        with smp.partition(1):
            return self.layer1(x)


model = MyModel()

loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
optimizer = tf.keras.optimizers.Adam()
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name="train_accuracy")

# smdistributed: Define smp.step. Return any tensors needed outside
@smp.step
def get_grads(images, labels):
    predictions = model(images, training=True)
    loss = loss_object(labels, predictions)

    grads = optimizer.get_gradients(loss, model.trainable_variables)
    return grads, loss, predictions


@tf.function
def train_step(images, labels):
    gradients, loss, predictions = get_grads(images, labels)

    # smdistributed: Accumulate the gradients across microbatches
    gradients = [g.accumulate() for g in gradients]
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))

    # smdistributed: Merge predictions and average losses across microbatches
    train_accuracy(labels, predictions.merge())
    return loss.reduce_mean()


for epoch in range(5):
    # Reset the metrics at the start of the next epoch
    train_accuracy.reset_states()
    for images, labels in train_ds:
        loss = train_step(images, labels)
    accuracy = train_accuracy.result()
```

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

Le seguenti TensorFlow funzionalità non sono supportate dalla libreria:
+ `tf.GradientTape()` non è attualmente supportato. Puoi invece usare `Optimizer.get_gradients()` o `Optimizer.compute_gradients()` per calcolare i gradienti.
+ L'API `tf.train.Checkpoint.restore()` non è attualmente supportata. Per il checkpoint, usa invece `smp.CheckpointManager`, che fornisce la stessa API e funzionalità. Tieni presente che il ripristino dei checkpoint con `smp.CheckpointManager` dovrebbe avvenire dopo la prima fase.

# 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 