

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

# Étape 1 : Modifiez votre propre script d'entraînement à l'aide SageMaker de la bibliothèque parallèle de modèles distribués
<a name="model-parallel-customize-training-script"></a>

Utilisez cette section pour apprendre à personnaliser votre script de formation afin d'utiliser les fonctionnalités principales de la bibliothèque de parallélisme de modèles Amazon SageMaker AI. Pour utiliser les fonctions et paramètres d'API spécifiques à la bibliothèque, nous vous recommandons d'utiliser cette documentation en plus de la [bibliothèque SageMaker model parallel APIs](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smd_model_parallel.html) dans la documentation du *SDK SageMaker Python*.

Les exemples de script d'entraînement fournis dans ces sections sont simplifiés et conçus pour mettre en évidence les modifications nécessaires à l'utilisation de la bibliothèque. Pour des end-to-end exemples de blocs-notes exécutables qui montrent comment utiliser un script TensorFlow ou un script d' PyTorch apprentissage avec la bibliothèque de parallélisme des SageMaker modèles, voir. [Exemples de bibliothèque de parallélisme de modèles Amazon SageMaker AI v2](distributed-model-parallel-v2-examples.md)

**Topics**
+ [Divisez le modèle de votre script d'entraînement à l'aide de la bibliothèque de parallélisme des SageMaker modèles](#model-parallel-model-splitting-using-smp-lib)
+ [Modifier un script TensorFlow d'entraînement](model-parallel-customize-training-script-tf.md)
+ [Modifier un script PyTorch d'entraînement](model-parallel-customize-training-script-pt.md)

## Divisez le modèle de votre script d'entraînement à l'aide de la bibliothèque de parallélisme des SageMaker modèles
<a name="model-parallel-model-splitting-using-smp-lib"></a>

Il existe deux manières de modifier votre script d'entraînement pour configurer le fractionnement des modèles : le fractionnement automatique ou le fractionnement manuel.

### Fractionnement automatisé du modèle
<a name="model-parallel-automated-model-splitting"></a>

Lorsque vous utilisez SageMaker la bibliothèque de parallélisme des modèles, vous pouvez tirer parti du *fractionnement automatique des modèles*, également appelé partitionnement *automatique* des modèles. La bibliothèque utilise un algorithme de partitionnement qui équilibre la mémoire, réduit la communication entre les périphériques et optimise la performance. Vous pouvez configurer l'algorithme de partitionnement automatique de sorte à optimiser la vitesse ou la mémoire. 

Vous pouvez également utiliser la division manuelle du modèle. Nous vous recommandons la division automatisée du modèle, sauf si vous connaissez très bien l'architecture du modèle et que vous savez déjà comment partitionner efficacement votre modèle.

#### Comment ça marche
<a name="model-parallel-automated-model-splitting-how-it-works"></a>

Le partitionnement automatique intervient dès la première étape d'entraînement, lors du tout premier appel de la fonction décorée `smp.step`. Durant cet appel, la bibliothèque commence par créer une version du modèle sur la RAM du CPU (pour éviter les limitations de mémoire GPU), puis elle analyse le graphe du modèle et décide du partitionnement. À partir de cette décision, chaque partition de modèle est chargée sur un GPU, et ce n'est qu'alors que la première étape est exécutée. Ces étapes d'analyse et de partitionnement peuvent contribuer à allonger la première étape de l'entraînement. 

Dans les deux frameworks, la bibliothèque gère la communication entre les appareils via son propre backend, optimisé pour AWS l'infrastructure.

La conception de la partition automatique s'adapte aux caractéristiques du cadre, et la bibliothèque effectue le partitionnement au niveau de granularité le plus naturel dans chaque cadre. Par exemple, dans TensorFlow, chaque opération spécifique peut être affectée à un appareil différent, tandis que dans PyTorch, l'attribution est effectuée au niveau du module, où chaque module comprend plusieurs opérations. La section qui suit examine les spécificités de conception dans chaque cadre.

##### Découpage automatique des modèles avec PyTorch
<a name="model-parallel-auto-model-split-pt"></a>

Durant la première étape d'entraînement, la bibliothèque de parallélisme de modèles exécute en interne une étape de traçage destinée à créer le graphe du modèle et à déterminer les formes du tenseur et des paramètres. Après cette étape de traçage, la bibliothèque crée un arbre, qui se compose des objets `nn.Module` imbriqués dans le modèle, ainsi que de données supplémentaires collectées à partir du traçage, comme la quantité de `nn.Parameters` stockés et le temps d'exécution de chaque `nn.Module`. 

Ensuite, la bibliothèque traverse cet arbre depuis la racine et exécute un algorithme de partitionnement qui affecte chaque `nn.Module` à un périphérique, ce qui équilibre la charge de calcul (mesurée par le temps d'exécution du module) et l'utilisation de la mémoire (mesurée par la taille totale des `nn.Parameter` stockés et les activations). Si plusieurs `nn.Modules` partagent le même `nn.Parameter`, ces modules sont alors placés sur le même périphérique afin de ne pas conserver plusieurs versions du même paramètre. Une fois la décision de partitionnement prise, les modules et les poids affectés sont chargés sur leurs périphériques.

Pour obtenir des instructions sur la façon d'enregistrer le `smp.step` décorateur dans votre script d' PyTorch entraînement, reportez-vous [Fractionnement automatique avec PyTorch](model-parallel-customize-training-script-pt.md#model-parallel-customize-training-script-pt-16) à.

##### Découpage automatique des modèles avec TensorFlow
<a name="model-parallel-auto-model-split-tf"></a>

La bibliothèque de parallélisme de modèles analyse les tailles des variables entraînables et la structure du graphe, et utilise en interne un algorithme de partitionnement des graphes. Cet algorithme affecte un périphérique pour chaque opération afin de réduire le volume de communication nécessaire entre les périphériques, sous réserve des deux contraintes suivantes : 
+ Équilibrage du nombre de variables stockées dans chaque périphérique
+ Équilibrage du nombre d'opérations exécutées dans chaque périphérique

Si vous spécifiez `speed` pour `optimize` (dans les paramètres de parallélisme de modèles dans le kit SDK Python), la bibliothèque essaie d'équilibrer le nombre d'opérations et d'objets `tf.Variable` dans chaque périphérique. Sinon, elle essaie d'équilibrer la taille totale de `tf.Variables`.

Une fois la décision de partitionnement prise, la bibliothèque crée une représentation sérialisée du sous-graphe que chaque périphérique doit exécuter et l'importe sur chaque périphérique. Lors du partitionnement, la bibliothèque place les opérations qui consomment la même `tf.Variable` et les opérations qui font partie de la même couche Keras sur le même périphérique. Il respecte également les contraintes de colocation imposées par TensorFlow. Cela signifie, par exemple, que si deux couches Keras partagent une `tf.Variable`, toutes les opérations qui font partie de ces couches sont placées sur un seul périphérique.

Pour obtenir des instructions sur la façon d'enregistrer le `smp.step` décorateur dans votre script d' PyTorch entraînement, reportez-vous [Fractionnement automatique avec TensorFlow](model-parallel-customize-training-script-tf.md#model-parallel-customize-training-script-tf-23) à.

##### Comparaison du fractionnement automatisé du modèle entre les frameworks
<a name="model-parallel-auto-model-split-comparison"></a>

Dans TensorFlow, l'unité fondamentale de calcul est a`tf.Operation`, et TensorFlow représente le modèle sous la forme d'un graphe acyclique dirigé (DAG) de `tf.Operation` s. Par conséquent, la bibliothèque de parallélisme du modèle partitionne ce DAG de telle sorte que chaque nœud soit attribué à un périphérique. Ce qui est intéressant ici est que les objets `tf.Operation` sont suffisamment riches en attributs personnalisables et qu'ils sont universels, c'est-à-dire que chaque modèle comprendra obligatoirement un graphe de ces objets. 

PyTorch d'autre part, n'a pas une notion de fonctionnement équivalente suffisamment riche et universelle. L'unité de calcul la plus proche présentant ces caractéristiques est un`nn.Module`, qui se trouve à un niveau de granularité beaucoup plus élevé, et c'est pourquoi la bibliothèque effectue le partitionnement à ce niveau dans. PyTorch PyTorch

### Division manuelle du modèle
<a name="model-parallel-manual-model-splitting"></a>

Si vous voulez spécifier manuellement le partitionnement de votre modèle entre les dispositifs, utilisez le gestionnaire de contexte `smp.partition`. Pour obtenir des instructions sur le partitionnement manuel du gestionnaire de contexte, consultez les pages suivantes.
+ [Découpage manuel avec TensorFlow](model-parallel-customize-training-script-tf.md#model-parallel-customize-training-script-tf-manual)
+ [Découpage manuel avec PyTorch](model-parallel-customize-training-script-pt.md#model-parallel-customize-training-script-pt-16-hvd)

Pour utiliser cette option après avoir apporté des modifications, à l'étape 2, vous devez `auto_partition` définir `False` et définir a `default_partition` dans la classe d'estimateur du SDK SageMaker Python. Toute opération non explicitement placée sur une partition à l'aide du gestionnaire de contexte de `smp.partition` est exécutée sur la `default_partition`. Dans ce cas, la logique de division automatisée est contournée et chaque opération est placée de la façon dont vous le spécifiez. En s'appuyant sur la structure de graphe ainsi obtenue, la bibliothèque de parallélisme de modèles crée automatiquement un calendrier d'exécution de pipeline.

# Modifier un script TensorFlow d'entraînement
<a name="model-parallel-customize-training-script-tf"></a>

Dans cette section, vous apprendrez à modifier les scripts d' TensorFlow apprentissage afin de configurer la bibliothèque de parallélisme des SageMaker modèles pour le partitionnement automatique et le partitionnement manuel. Cette sélection d'exemples inclut également un exemple intégré à Horovod pour le modèle hybride et le parallélisme des données.

**Note**  
Pour connaître les TensorFlow versions prises en charge par la bibliothèque, consultez[Frameworks pris en charge et Régions AWS](distributed-model-parallel-support.md).

Les modifications que vous devez apporter à votre script d'entraînement pour utiliser la bibliothèque sont répertoriées dans [Fractionnement automatique avec TensorFlow](#model-parallel-customize-training-script-tf-23).

Pour savoir comment modifier votre script d'entraînement pour utiliser un modèle hybride et le parallélisme de données avec Horovod, consultez [Division automatisée avec Horovod TensorFlow et Horovod pour le parallélisme des modèles hybrides et des données](#model-parallel-customize-training-script-tf-2.3).

Si vous optez pour le partitionnement manuel, consultez également [Découpage manuel avec TensorFlow](#model-parallel-customize-training-script-tf-manual). 

Les rubriques suivantes présentent des exemples de scripts de formation que vous pouvez utiliser pour configurer la bibliothèque SageMaker de parallélisme des modèles pour le partitionnement automatique et les modèles de partitionnement manuel. TensorFlow 

**Note**  
Le partitionnement automatique est activé par défaut. Sauf indication contraire, les exemples de scripts utilisent le partitionnement automatique.

**Topics**
+ [Fractionnement automatique avec TensorFlow](#model-parallel-customize-training-script-tf-23)
+ [Division automatisée avec Horovod TensorFlow et Horovod pour le parallélisme des modèles hybrides et des données](#model-parallel-customize-training-script-tf-2.3)
+ [Découpage manuel avec TensorFlow](#model-parallel-customize-training-script-tf-manual)
+ [Fonctionnalités de framework non prises en charge](#model-parallel-tf-unsupported-features)

## Fractionnement automatique avec TensorFlow
<a name="model-parallel-customize-training-script-tf-23"></a>

Les modifications de script d'entraînement suivantes sont nécessaires pour exécuter un TensorFlow modèle avec SageMaker la bibliothèque de parallélisme des modèles :

1. Importez et initialisez la bibliothèque avec [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. Définissez un modèle Keras en héritant de [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) au lieu de la classe de modèles Keras. Renvoyez les sorties du modèle à partir de la méthode d'appel de l'objet `smp.DistributedModel`. N'oubliez pas que tous les tenseurs renvoyés par la méthode d'appel seront diffusés sur des périphériques avec parallélisme des modèles. Comme cela induira un surdébit de communication, évitez de renvoyer les tenseurs qui ne sont pas nécessaires en dehors de la méthode d'appel (activations intermédiaires, par exemple).

1. Définissez `drop_remainder=True` dans la méthode `tf.Dataset.batch()`. Cela vise à garantir que la taille du lot est toujours divisible par le nombre de micro-lots.

1. Ensemencez les opérations aléatoires dans le pipeline de données en utilisant`smp.dp_rank()`, par exemple, `shuffle(ds, seed=smp.dp_rank())` pour garantir la cohérence des échantillons de données GPUs contenant différentes partitions de modèles.

1. Mettez la logique en avant et en arrière dans une fonction étape et décorez-la avec `smp.step`.

1. Effectuez un post-traitement sur les sorties des différents micro-lots à l'aide de méthodes [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) telles que `reduce_mean`. La fonction [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) doit avoir une valeur de retour qui dépend de la sortie de `smp.DistributedModel`.

1. De façon similaire, s'il y a une étape d'évaluation, placez la logique en avant dans une fonction décorée `smp.step` et post-traitez les sorties en utilisant 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).

Pour en savoir plus sur l'API SageMaker de la bibliothèque de parallélisme des modèles, consultez la documentation de l'[API](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smd_model_parallel.html). 

Le script Python suivant est un exemple de script d'entraînement après application des modifications.

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

Si vous avez fini de préparer votre scénario d'entraînement, passez à [Étape 2 : Lancer un job de formation à l'aide du SDK SageMaker Python](model-parallel-sm-sdk.md). Si vous souhaitez exécuter une tâche d'entraînement parallèle modèle et données hybride, passez à la section suivante.

## Division automatisée avec Horovod TensorFlow et Horovod pour le parallélisme des modèles hybrides et des données
<a name="model-parallel-customize-training-script-tf-2.3"></a>

Vous pouvez utiliser la bibliothèque de parallélisme de SageMaker modèles avec Horovod pour le parallélisme de modèles hybrides et de données. Pour en savoir plus sur la façon dont la bibliothèque divise un modèle pour le parallélisme hybride, reportez-vous à [Parallélisme du pipeline (disponible pour PyTorch et) TensorFlow](model-parallel-intro.md#model-parallel-intro-pp).

Dans cette étape, nous nous concentrons sur la manière de modifier votre script d'entraînement afin d'adapter la bibliothèque de parallélisme du SageMaker modèle.

Pour configurer correctement votre script d'entraînement afin qu'il prenne en compte la configuration du parallélisme hybride que vous définirez dans [Étape 2 : Lancer un job de formation à l'aide du SDK SageMaker Python](model-parallel-sm-sdk.md), utilisez les fonctions d'aide de la bibliothèque, `smp.dp_rank()` et `smp.mp_rank()`, qui détectent automatiquement le rang parallèle des données et le rang parallèle du modèle, respectivement. 

Pour trouver toutes les primitives MPI prises en charge par la bibliothèque, consultez les [bases du MPI dans la documentation](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/v1.2.0/smd_model_parallel_common_api.html#mpi-basics) du SDK SageMaker Python. 

Les modifications à apporter au script sont les suivantes :
+ Ajouter `hvd.allreduce`
+ Diffuser des variables après le premier lot, comme l'exige Horovod
+ Seeding, shuffling, sharding, opérations de and/or sharding dans le pipeline de données avec. `smp.dp_rank()`

**Note**  
Lorsque vous utilisez Horovod, vous ne devez pas faire appel directement à `hvd.init` dans votre script d'entraînement. Au lieu de cela, vous devrez le `"horovod"` définir `True` dans les `modelparallel` paramètres du SDK SageMaker Python dans[Étape 2 : Lancer un job de formation à l'aide du SDK SageMaker Python](model-parallel-sm-sdk.md). Cela permet à la bibliothèque d'initialiser Horovod en interne en se basant sur les affectations de périphériques des partitions du modèle. Le fait d'appeler directement `hvd.init()` dans votre script d'entraînement peut poser des problèmes.

**Note**  
L'utilisation de l'API `hvd.DistributedOptimizer` directement dans votre script d'entraînement peut entraîner une baisse des performances et de la vitesse d'entraînement, car l'API place implicitement l'opération `AllReduce` à l'intérieur de `smp.step`. Nous vous recommandons d'utiliser la bibliothèque de parallélisme de modèles avec Horovod en appelant directement `hvd.allreduce` après l'appel à `accumulate()` ou à `reduce_mean()` sur les gradients retournés par `smp.step`, comme le montre l'exemple suivant.

Pour en savoir plus sur l'API SageMaker de la bibliothèque de parallélisme des modèles, consultez la documentation de l'[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))
```

## Découpage manuel avec TensorFlow
<a name="model-parallel-customize-training-script-tf-manual"></a>

Utilisez les gestionnaires de contexte `smp.partition` pour placer les opérations dans une partition spécifique. Toute opération non placée dans un contexte `smp.partition` est placée dans le `default_partition`. Pour en savoir plus sur l'API SageMaker de la bibliothèque de parallélisme des modèles, consultez la documentation de l'[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()
```

## Fonctionnalités de framework non prises en charge
<a name="model-parallel-tf-unsupported-features"></a>

Les TensorFlow fonctionnalités suivantes ne sont pas prises en charge par la bibliothèque :
+ `tf.GradientTape()` n'est pas prise en charge pour le moment. À la place, vous pouvez utiliser `Optimizer.get_gradients()` ou `Optimizer.compute_gradients()` pour calculer les gradients.
+ L'API `tf.train.Checkpoint.restore()` n'est pas prise en charge pour le moment. Pour le pointage, utilisez `smp.CheckpointManager`, qui fournit la même API et la même fonctionnalité. Les restaurations de point de contrôle avec `smp.CheckpointManager` doivent intervenir après la première étape.

# Modifier un script PyTorch d'entraînement
<a name="model-parallel-customize-training-script-pt"></a>

Dans cette section, vous apprendrez à modifier les scripts d' PyTorch apprentissage afin de configurer la bibliothèque de parallélisme des SageMaker modèles pour le partitionnement automatique et le partitionnement manuel.

**Note**  
Pour connaître les PyTorch versions prises en charge par la bibliothèque, consultez[Frameworks pris en charge et Régions AWS](distributed-model-parallel-support.md).

**Astuce**  
Pour obtenir des exemples de end-to-end blocs-notes illustrant l'utilisation d'un script de PyTorch formation avec la bibliothèque de parallélisme des SageMaker modèles, reportez-vous à. [Exemples de bibliothèque de parallélisme de modèles Amazon SageMaker AI v1](distributed-model-parallel-examples.md)

Vous noterez que le partitionnement automatique est activé par défaut. Sauf indication contraire, les scripts suivants utilisent le partitionnement automatique. 

**Topics**
+ [Fractionnement automatique avec PyTorch](#model-parallel-customize-training-script-pt-16)
+ [Découpage manuel avec PyTorch](#model-parallel-customize-training-script-pt-16-hvd)
+ [Considérations](#model-parallel-pt-considerations)
+ [Fonctionnalités de framework non prises en charge](#model-parallel-pt-unsupported-features)

## Fractionnement automatique avec PyTorch
<a name="model-parallel-customize-training-script-pt-16"></a>

Les modifications de script d'entraînement suivantes sont nécessaires pour exécuter un script d' PyTorch entraînement avec SageMaker la bibliothèque de parallélisme des modèles :

1. Importez et initialisez la bibliothèque avec [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. Enveloppez le modèle avec [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). N'oubliez pas que tous les tenseurs renvoyés par la méthode `forward` de l'objet `nn.Module` sous-jacent seront diffusés sur des périphériques avec parallélisme des modèles. Comme cela induira un surdébit de communication, évitez de renvoyer les tenseurs qui ne sont pas nécessaires en dehors de la méthode d'appel (activations intermédiaires, par exemple).
**Note**  
Pour la FP16 formation, vous devez utiliser le gestionnaire de contexte [smdistributed.modelparallel.torch.model\$1creation () pour encapsuler](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smp_versions/latest/smd_model_parallel_pytorch.html) le modèle. Pour de plus amples informations, veuillez consulter [FP16 Entraînement avec le parallélisme des modèles](model-parallel-extended-features-pytorch-fp16.md).

1. Enveloppez l'optimiseur avec [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).
**Note**  
Pour l' FP16 entraînement, vous devez configurer une échelle statique ou dynamique des pertes. Pour de plus amples informations, veuillez consulter [FP16 Entraînement avec le parallélisme des modèles](model-parallel-extended-features-pytorch-fp16.md).

1. Utilisez l'objet `DistributedModel` renvoyé au lieu d'un modèle utilisateur.

1. Mettez la logique en avant et en arrière dans une fonction étape et décorez-la avec [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. Restreignez chaque processus à son propre périphérique via `torch.cuda.set_device(smp.local_rank())`.

1. Déplacez les tenseurs d'entrée vers le GPU à l'aide de l'API `.to()` avant l'appel `smp.step` (voir l'exemple ci-dessous).

1. Remplacez `torch.Tensor.backward` et `torch.autograd.backward` par `DistributedModel.backward`.

1. Effectuez un post-traitement sur les sorties des différents micro-lots à l'aide de méthodes [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) telles que `reduce_mean`.

1. De façon similaire, s'il y a une étape d'évaluation, placez la logique en avant dans une fonction décorée `smp.step` et post-traitez les sorties en utilisant 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. Définissez `drop_last=True` dans `DataLoader`. Vous pouvez également ignorer manuellement un lot dans la boucle d'entraînement si la taille du lot n'est pas divisible par le nombre de micro-lots.

Pour en savoir plus sur l'API SageMaker de la bibliothèque de parallélisme des modèles, consultez la documentation de l'[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)
```

## Découpage manuel avec PyTorch
<a name="model-parallel-customize-training-script-pt-16-hvd"></a>

Utilisez les gestionnaires de contexte [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) pour placer les modules dans des périphériques spécifiques. Tout module non placé dans un contexte `smp.partition` est placé dans le `default_partition`. La `default_partition` doit être fournie si `auto_partition` est défini sur `False`. Les modules qui sont créés dans un contexte `smp.partition` spécifique sont placés sur la partition correspondante.

Pour en savoir plus sur l'API SageMaker de la bibliothèque de parallélisme des modèles, consultez la documentation de l'[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)
```

## Considérations
<a name="model-parallel-pt-considerations"></a>

Lorsque vous configurez un script d' PyTorch entraînement à l'aide SageMaker de la bibliothèque de parallélisme des modèles, vous devez tenir compte des points suivants :
+ Si vous utilisez une technique d'optimisation reposant sur des normes de gradient globales, par exemple une norme de gradient du modèle tout entier, comme certaines variantes de l'optimiseur LAMB ou de l'écrêtage de gradient global, vous devez rassembler toutes les normes entre toutes les partitions de modèle pour vérifier l'exactitude. Pour ce faire, vous pouvez utiliser les types de données de base de communication de la bibliothèque.
+ Tous les arguments `torch.Tensor` aux méthodes de transmission des modules `nn.Modules` dans votre modèle doivent être utilisés dans le calcul de la sortie du module. En d'autres termes, la bibliothèque ne prend pas en charge le cas où il existe un argument `torch.Tensor` à un module dont la sortie du module ne dépend pas.
+ L'argument à l'appel `smp.DistributedModel.backward()` doit dépendre de toutes les sorties du modèle. En d'autres termes, il ne peut pas y avoir de sortie de l'appel `smp.DistributedModel.forward` qui ne soit pas utilisée dans le calcul du tenseur qui est intégré à l'appel `smp.DistributedModel.backward`.
+ S'il y a des appels `torch.cuda.synchronize()` dans votre code, vous devrez peut-être appeler `torch.cuda.set_device(smp.local_rank())` immédiatement avant l'appel de synchronisation. Sinon, des contextes CUDA inutiles pourraient être créés dans le périphérique 0, ce qui consommerait de la mémoire inutilement.
+ Comme la bibliothèque place `nn.Modules` sur différents périphériques, les modules du modèle ne doivent pas dépendre d'un état global modifié dans `smp.step`. Tout état qui reste fixe durant tout l'entraînement, ou qui est modifié en dehors de `smp.step` d'une manière visible par tous les processus, est autorisé.
+ Lorsque vous utilisez la bibliothèque, vous n'avez pas besoin de déplacer le modèle vers le GPU (par exemple, en utilisant `model.to(device)`). Si vous essayez de déplacer le modèle vers le GPU avant la partition du modèle (avant le premier appel `smp.step`), l'appel de déplacement est ignoré. La bibliothèque déplace automatiquement la partie du modèle affectée à un rang, vers son GPU. Une fois que l'entraînement avec la bibliothèque démarre, ne déplacez pas le modèle vers le CPU et ne l'utilisez pas, car il ne contiendra pas des paramètres corrects pour les modules non affectés à la partition maintenue par le processus. Si vous souhaitez réentraîner un modèle ou l'utiliser à des fins d'inférence sans la bibliothèque après l'avoir entraîné à l'aide de la bibliothèque de parallélisme des modèles, la méthode recommandée est d'enregistrer le modèle complet à l'aide de notre API de point de contrôle et de le charger à nouveau dans un module normal. PyTorch 
+ Si vous avez une liste de modules telle que la sortie de l'un en alimente un autre, vous pouvez améliorer la performance de façon significative en remplaçant cette liste par `nn.Sequential`.
+ La mise à jour du poids (`optimizer.step()`) doit se produire en dehors de `smp.step` car c'est à ce moment que la transmission vers l'arrière est entièrement terminée et que les gradients sont prêts. Lors de l'utilisation d'un modèle hybride avec parallélisme des modèles et des données, à ce stade, AllReduce la fin des dégradés est également garantie.
+ Lorsque vous utilisez la bibliothèque en combinaison avec le parallélisme des données, assurez-vous que le nombre de lots sur tous les classements data parallel est le même afin de AllReduce ne pas attendre un rang qui ne participe pas à l'étape.
+ Si vous lancez une tâche d'entraînement à l'aide d'un type d'instance ml.p4d (tel que ml.p4d.24xlarge), vous devez définir la variable `num_workers=0` du chargeur de données. Par exemple, vous pouvez définir votre `DataLoader` de la façon suivante :

  ```
  dataloader = torch.utils.data.DataLoader(
              data,
              batch_size=batch_size,
              num_workers=0,
              pin_memory=True,
              drop_last=True,
              shuffle=shuffle,
          )
  ```
+ Les entrées de `smp.step` doivent être les entrées de modèle générées par le `DataLoader`. En effet, `smp.step` divise en interne les tenseurs d'entrée sur toute la dimension du lot et les exécute en pipeline. Transmettre le `DataLoader` lui-même à la fonction `smp.step` pour générer les entrées de modèle à l'intérieur ne fonctionne donc pas. 

  Par exemple, si vous définissez un `DataLoader` de la façon suivante :

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

  Vous devez accéder aux entrées de modèle générées par le `train_loader` et les transmettre à une fonction décorée `smp.step`. Ne faites pas transmettre le `train_loader` directement à `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
  ```
+ Les tenseurs d'entrée à `smp.step` doivent être déplacés vers le périphérique actuel à l'aide de l'API `.to()`, et cela après l'appel `torch.cuda.set_device(local_rank())`.

  Par exemple, vous pouvez définir la fonction `train` de la façon suivante. Cette fonction ajoute `data` et `target` sur le périphérique actuel à l'aide de l'API `.to()` avant d'utiliser ces tenseurs d'entrée pour appeler `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()
  ```

  Dans la fonction `train` ci-dessus, les tenseurs d'entrée de cette fonction décorée `smp.set` ont été déplacés vers le périphérique actuel. Le modèle *ne doit pas* être déplacé vers le périphérique actuel. La bibliothèque déplace automatiquement la partie du modèle affectée à un rang, vers son 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
  ```

## Fonctionnalités de framework non prises en charge
<a name="model-parallel-pt-unsupported-features"></a>

Les PyTorch fonctionnalités suivantes ne sont pas prises en charge par SageMaker la bibliothèque de parallélisme des modèles :
+ Si vous utilisez le parallélisme des données avec le [PyTorch DDP](https://pytorch.org/tutorials/intermediate/ddp_tutorial.html) natif, le module [https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html](https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html)wrapper n'est pas pris en charge par la bibliothèque. La bibliothèque gère en interne l'intégration au PyTorch DDP, y compris la diffusion des paramètres et le gradient AllReduce. Lors de l'utilisation de la bibliothèque, les tampons de module ne sont diffusés qu'une seule fois au début de l'entraînement. Si votre modèle possède des tampons de module qui doivent être synchronisés entre des groupes de données parallèles à chaque étape, vous pouvez le faire à l'aide de l'API `torch.distributed`, en utilisant le groupe de processus qui peut être obtenu via `smp.get_dp_process_group()`.
+ Pour l'entraînement de précision mixte, le module `apex.amp` n'est pas pris en charge. Nous vous recommandons d'utiliser la bibliothèque avec une précision mixte automatique en utilisant `torch.cuda.amp`, à la seule exception d'utiliser `smp.amp.GradScaler` au lieu de la mise en œuvre dans Torch.
+ `torch.jit.ScriptModules` ou `ScriptFunctions` ne sont pas pris en charge par `smp.DistributedModel`.
+ `apex` : `FusedLayerNorm`, `FusedAdam`, `FusedLAMB` et `FusedNovoGrad` de `apex` ne sont pas pris en charge. Vous pouvez utiliser les implémentations de ces bibliothèques par le biais `smp.optimizers` et à la `smp.nn` APIs place.