Guia de preparação de GPU distribuído (SDK v1)

APLICA-SE A:Python SDK azureml v1

Saiba mais sobre como utilizar código de preparação de GPU distribuído no Azure Machine Learning (ML). Este artigo não irá ensinar-lhe sobre a formação distribuída. Irá ajudá-lo a executar o código de preparação distribuído existente no Azure Machine Learning. Oferece sugestões e exemplos para seguir para cada arquitetura:

  • Interface de Passagem de Mensagens (MPI)
    • Horovod
    • DeepSpeed
    • Variáveis de ambiente do Open MPI
  • PyTorch
    • Inicialização do grupo de processos
    • Opções de iniciação
    • DistributedDataParallel (por processo de lançamento)
    • Utilizar torch.distributed.launch (por node-launch)
    • Relâmpago de PyTorch
    • Transformadores de Rostos Abraçados
  • TensorFlow
    • Variáveis de ambiente para TensorFlow (TF_CONFIG)
  • Acelerar a preparação da GPU com o InfiniBand

Pré-requisitos

Reveja estes conceitos básicos de preparação de GPU distribuída , como paralelismo de dados, paralelismo de dados distribuídos e paralelismo de modelos.

Dica

Se não souber que tipo de paralelismo deve utilizar, mais de 90% das vezes deve utilizar o Paralelismo de Dados Distribuídos.

MPI

O Azure Machine Learning oferece uma tarefa de MPI para iniciar um determinado número de processos em cada nó. Pode adotar esta abordagem para executar a preparação distribuída com o iniciador por processo ou por iniciador por nó, dependendo se process_count_per_node está definido como 1 (a predefinição) para o iniciador por nó ou igual ao número de dispositivos/GPUs para o iniciador por processo. O Azure Machine Learning constrói o comando completo de iniciação de MPI (mpirun) nos bastidores. Não pode fornecer os seus próprios comandos de iniciador de nós principais completos, como mpirun ou DeepSpeed launcher.

Dica

A imagem base do Docker utilizada por uma tarefa de MPI do Azure Machine Learning tem de ter uma biblioteca de MPI instalada. O Open MPI está incluído em todas as imagens base da GPU do Azure Machine Learning. Quando utiliza uma imagem personalizada do Docker, é responsável por se certificar de que a imagem inclui uma biblioteca de MPI. Recomenda-se abrir o MPI, mas também pode utilizar uma implementação de MPI diferente, como o Intel MPI. O Azure Machine Learning também fornece ambientes organizados para arquiteturas populares.

Para executar a preparação distribuída com o MPI, siga estes passos:

  1. Utilize um ambiente do Azure Machine Learning com o MPI e a arquitetura de aprendizagem profunda preferenciais. O Azure Machine Learning fornece um ambiente organizado para arquiteturas populares.
  2. Definir MpiConfiguration com process_count_per_node e node_count. process_count_per_node deve ser igual ao número de GPUs por nó para o lançamento por processo ou definido como 1 (a predefinição) para o lançamento por nó se o script de utilizador for responsável por iniciar os processos por nó.
  3. Passe o MpiConfiguration objeto para o distributed_job_config parâmetro de ScriptRunConfig.
from azureml.core import Workspace, ScriptRunConfig, Environment, Experiment
from azureml.core.runconfig import MpiConfiguration

curated_env_name = 'AzureML-PyTorch-1.6-GPU'
pytorch_env = Environment.get(workspace=ws, name=curated_env_name)
distr_config = MpiConfiguration(process_count_per_node=4, node_count=2)

run_config = ScriptRunConfig(
  source_directory= './src',
  script='train.py',
  compute_target=compute_target,
  environment=pytorch_env,
  distributed_job_config=distr_config,
)

# submit the run configuration to start the job
run = Experiment(ws, "experiment_name").submit(run_config)

Horovod

Utilize a configuração da tarefa de MPI quando utilizar o Horovod para formação distribuída com a arquitetura de aprendizagem profunda.

Certifique-se de que o código segue estas sugestões:

  • O código de preparação é instrumentado corretamente com o Horovod antes de adicionar as peças do Azure Machine Learning
  • O seu ambiente do Azure Machine Learning contém Horovod e MPI. Os ambientes de GPU organizados do PyTorch e do TensorFlow são pré-configurados com o Horovod e as respetivas dependências.
  • Crie uma MpiConfiguration com a distribuição pretendida.

Exemplo do Horovod

DeepSpeed

Não utilize o iniciador personalizado do DeepSpeed para executar a preparação distribuída com a biblioteca DeepSpeed no Azure Machine Learning. Em vez disso, configure uma tarefa de MPI para iniciar a tarefa de preparação com o MPI.

Certifique-se de que o código segue estas sugestões:

  • O seu ambiente do Azure Machine Learning contém DeepSpeed e as respetivas dependências, Open MPI e mpi4py.
  • Crie uma MpiConfiguration com a sua distribuição.

Exemplo de DeepSpeed

Variáveis de ambiente do Open MPI

Ao executar tarefas de MPI com imagens de MPI Abertas, são iniciadas as seguintes variáveis de ambiente para cada processo:

  1. OMPI_COMM_WORLD_RANK - a classificação do processo
  2. OMPI_COMM_WORLD_SIZE - o tamanho do mundo
  3. AZ_BATCH_MASTER_NODE - endereço primário com porta, MASTER_ADDR:MASTER_PORT
  4. OMPI_COMM_WORLD_LOCAL_RANK - a classificação local do processo no nó
  5. OMPI_COMM_WORLD_LOCAL_SIZE - número de processos no nó

Dica

Apesar do nome, a variável OMPI_COMM_WORLD_NODE_RANK de ambiente não corresponde ao NODE_RANK. Para utilizar o iniciador por nó, defina process_count_per_node=1 e utilize OMPI_COMM_WORLD_RANK como o NODE_RANK.

PyTorch

O Azure Machine Learning suporta a execução de trabalhos distribuídos com as capacidadestorch.distributed de preparação distribuída nativas () do PyTorch.

Dica

Para o paralelismo de dados, a orientação oficial do PyTorch é utilizar DistributedDataParallel (DDP) através de DataParallel para preparação distribuída de nó único e de vários nós. O PyTorch também recomenda a utilização de DistributedDataParallel através do pacote de multiprocessamento. Por conseguinte, a documentação e os exemplos do Azure Machine Learning irão focar-se na preparação DistributedDataParallel.

Inicialização do grupo de processos

A espinha dorsal de qualquer preparação distribuída baseia-se num grupo de processos que se conhecem e podem comunicar entre si através de um back-end. Para o PyTorch, o grupo de processos é criado ao chamar torch.distributed.init_process_group em todos os processos distribuídos para formar coletivamente um grupo de processos.

torch.distributed.init_process_group(backend='nccl', init_method='env://', ...)

Os back-ends de comunicação mais comuns utilizados são mpi, nccle gloo. Para a preparação nccl baseada em GPU, é recomendado para um melhor desempenho e deve ser utilizado sempre que possível.

init_method indica como cada processo pode detetar uns aos outros, como inicializam e verificam o grupo de processos com o back-end de comunicação. Por predefinição, se init_method não for especificado, o PyTorch utilizará o método de inicialização de variáveis de ambiente (env://). init_method é o método de inicialização recomendado para utilizar no código de preparação para executar o PyTorch distribuído no Azure Machine Learning. O PyTorch irá procurar as seguintes variáveis de ambiente para inicialização:

  • MASTER_ADDR - Endereço IP do computador que irá alojar o processo com a classificação 0.
  • MASTER_PORT - Uma porta gratuita no computador que irá alojar o processo com a classificação 0.
  • WORLD_SIZE - O número total de processos. Deve ser igual ao número total de dispositivos (GPU) utilizados para preparação distribuída.
  • RANK - A classificação (global) do processo atual. Os valores possíveis são 0 a (tamanho do mundo - 1).

Para obter mais informações sobre a inicialização do grupo de processos, veja a documentação do PyTorch.

Além destas, muitas aplicações também precisarão das seguintes variáveis de ambiente:

  • LOCAL_RANK - A classificação local (relativa) do processo no nó. Os valores possíveis são 0 a (# de processos no nó - 1). Estas informações são úteis porque muitas operações, como a preparação de dados, só devem ser executadas uma vez por nó --- normalmente em local_rank = 0.
  • NODE_RANK - A classificação do nó para a preparação de vários nós. Os valores possíveis são 0 a (número total de nós - 1).

Opções de lançamento do PyTorch

A tarefa Azure Machine Learning PyTorch suporta dois tipos de opções para iniciar a preparação distribuída:

  • Por iniciador de processos: o sistema iniciará automaticamente todos os processos distribuídos, com todas as informações relevantes (como variáveis de ambiente) para configurar o grupo de processos.
  • Iniciador por nó: fornece ao Azure Machine Learning o iniciador de utilitários que será executado em cada nó. O iniciador de utilitários processará o início de cada um dos processos num determinado nó. Localmente, em cada nó, RANK e LOCAL_RANK são configurados pelo iniciador. O utilitário torch.distributed.launch e o PyTorch Lightning pertencem a esta categoria.

Não existem diferenças fundamentais entre estas opções de lançamento. A escolha depende, em grande parte, da sua preferência ou das convenções das arquiteturas/bibliotecas criadas sobre o Vanilla PyTorch (como Relâmpago ou Rosto Abraçado).

As secções seguintes explicam mais detalhadamente como configurar tarefas do PyTorch do Azure Machine Learning para cada uma das opções de iniciação.

DistributedDataParallel (per-process-launch)

Não precisa de utilizar um utilitário de iniciador como torch.distributed.launch. Para executar uma tarefa do PyTorch distribuída:

  1. Especificar o script de preparação e os argumentos
  2. Crie um PyTorchConfiguration e especifique e process_countnode_count. Corresponde process_count ao número total de processos que pretende executar para a sua tarefa. process_count normalmente, deve ser igual # GPUs per node x # nodesa . Se process_count não for especificado, o Azure Machine Learning iniciará por predefinição um processo por nó.

O Azure Machine Learning irá definir as MASTER_ADDRvariáveis de ambiente , MASTER_PORT, WORLD_SIZEe NODE_RANK em cada nó e definir as variáveis de ambiente e LOCAL_RANK ao nível RANK do processo.

Para utilizar esta opção para a preparação de vários processos por nó, utilize o SDK >= 1.22.0Python do Azure Machine Learning. Process_count foi introduzida em 1.22.0.

from azureml.core import ScriptRunConfig, Environment, Experiment
from azureml.core.runconfig import PyTorchConfiguration

curated_env_name = 'AzureML-PyTorch-1.6-GPU'
pytorch_env = Environment.get(workspace=ws, name=curated_env_name)
distr_config = PyTorchConfiguration(process_count=8, node_count=2)

run_config = ScriptRunConfig(
  source_directory='./src',
  script='train.py',
  arguments=['--epochs', 50],
  compute_target=compute_target,
  environment=pytorch_env,
  distributed_job_config=distr_config,
)

run = Experiment(ws, 'experiment_name').submit(run_config)

Dica

Se o script de preparação passar informações como classificação local ou classificação como argumentos de script, pode referenciar as variáveis de ambiente nos argumentos:

arguments=['--epochs', 50, '--local_rank', $LOCAL_RANK]

Exemplo de Pytorch per-process-launch

Utilizar torch.distributed.launch (por nó-launch)

O PyTorch fornece um utilitário de iniciação em torch.distributed.launch que pode utilizar para iniciar vários processos por nó. O torch.distributed.launch módulo gera vários processos de preparação em cada um dos nós.

Os passos seguintes demonstram como configurar uma tarefa do PyTorch com um iniciador por nó no Azure Machine Learning. A tarefa obtém o equivalente à execução do seguinte comando:

python -m torch.distributed.launch --nproc_per_node <num processes per node> \
  --nnodes <num nodes> --node_rank $NODE_RANK --master_addr $MASTER_ADDR \
  --master_port $MASTER_PORT --use_env \
  <your training script> <your script arguments>
  1. Indique o torch.distributed.launch comando para o command parâmetro do ScriptRunConfig construtor. O Azure Machine Learning executa este comando em cada nó do cluster de preparação. --nproc_per_node deve ser menor ou igual ao número de GPUs disponíveis em cada nó. MASTER_ADDR, MASTER_PORT e NODE_RANK estão definidos pelo Azure Machine Learning, pelo que pode apenas referenciar as variáveis de ambiente no comando . O Azure Machine Learning define MASTER_PORT como 6105, mas pode transmitir um valor diferente para o --master_port argumento do comando torch.distributed.launch, se assim o desejar. (O utilitário de iniciação irá repor as variáveis de ambiente.)
  2. Crie um PyTorchConfiguration e especifique o node_count.
from azureml.core import ScriptRunConfig, Environment, Experiment
from azureml.core.runconfig import PyTorchConfiguration

curated_env_name = 'AzureML-PyTorch-1.6-GPU'
pytorch_env = Environment.get(workspace=ws, name=curated_env_name)
distr_config = PyTorchConfiguration(node_count=2)
launch_cmd = "python -m torch.distributed.launch --nproc_per_node 4 --nnodes 2 --node_rank $NODE_RANK --master_addr $MASTER_ADDR --master_port $MASTER_PORT --use_env train.py --epochs 50".split()

run_config = ScriptRunConfig(
  source_directory='./src',
  command=launch_cmd,
  compute_target=compute_target,
  environment=pytorch_env,
  distributed_job_config=distr_config,
)

run = Experiment(ws, 'experiment_name').submit(run_config)

Dica

Preparação multi-GPU de nó único: Se estiver a utilizar o utilitário de iniciação para executar a preparação PyTorch multi-GPU de nó único, não precisa de especificar o distributed_job_config parâmetro de ScriptRunConfig.

launch_cmd = "python -m torch.distributed.launch --nproc_per_node 4 --use_env train.py --epochs 50".split()

run_config = ScriptRunConfig(
 source_directory='./src',
 command=launch_cmd,
 compute_target=compute_target,
 environment=pytorch_env,
)

PyTorch per-node-launch example

Relâmpago PyTorch

O PyTorch Lightning é uma biblioteca open source leve que fornece uma interface de alto nível para o PyTorch. Os relâmpagos abstraem muitas das configurações de preparação distribuídas de nível inferior necessárias para o Vanilla PyTorch. O Lightning permite-lhe executar os scripts de preparação em gpu único, gpu multi-GPU de nó único e definições multi-GPU de vários nós. Nos bastidores, inicia vários processos para si de forma semelhante a torch.distributed.launch.

Para a preparação de nós únicos (incluindo multi-GPU de nó único), pode executar o código no Azure Machine Learning sem ter de especificar um distributed_job_config. Para executar uma experimentação com vários nós com várias GPUs, existem 2 opções:

  • Com a configuração do PyTorch (recomendado): defina PyTorchConfiguration e especifique communication_backend="Nccl", node_counte process_count (tenha em atenção que este é o número total de processos, ou seja, num_nodes * process_count_per_node). No módulo Lightning Trainer, especifique e num_nodesgpus seja consistente com PyTorchConfiguration. Por exemplo, num_nodes = node_count e gpus = process_count_per_node.

  • Utilizar a Configuração do MPI:

    • Defina MpiConfiguration e especifique e node_countprocess_count_per_node. No Lightning Trainer, especifique e num_nodesgpus seja, respetivamente, igual a node_count e process_count_per_node de MpiConfiguration.

    • Para a preparação de vários nós com MPI, o Lightning requer que as seguintes variáveis de ambiente sejam definidas em cada nó do cluster de preparação:

      • MASTER_ADDR
      • MASTER_PORT
      • NODE_RANK
      • LOCAL_RANK

      Defina manualmente estas variáveis de ambiente que o Lightning necessita nos scripts de preparação principais:

    import os
    from argparse import ArgumentParser
    
    def set_environment_variables_for_mpi(num_nodes, gpus_per_node, master_port=54965):
         if num_nodes > 1:
             os.environ["MASTER_ADDR"], os.environ["MASTER_PORT"] = os.environ["AZ_BATCH_MASTER_NODE"].split(":")
         else:
             os.environ["MASTER_ADDR"] = os.environ["AZ_BATCHAI_MPI_MASTER_NODE"]
             os.environ["MASTER_PORT"] = str(master_port)
    
         try:
             os.environ["NODE_RANK"] = str(int(os.environ.get("OMPI_COMM_WORLD_RANK")) // gpus_per_node)
             # additional variables
             os.environ["MASTER_ADDRESS"] = os.environ["MASTER_ADDR"]
             os.environ["LOCAL_RANK"] = os.environ["OMPI_COMM_WORLD_LOCAL_RANK"]
             os.environ["WORLD_SIZE"] = os.environ["OMPI_COMM_WORLD_SIZE"]
         except:
             # fails when used with pytorch configuration instead of mpi
             pass
    
    if __name__ == "__main__":
         parser = ArgumentParser()
         parser.add_argument("--num_nodes", type=int, required=True)
         parser.add_argument("--gpus_per_node", type=int, required=True)
         args = parser.parse_args()
         set_environment_variables_for_mpi(args.num_nodes, args.gpus_per_node)
    
         trainer = Trainer(
          num_nodes=args.num_nodes,
          gpus=args.gpus_per_node
      )
    

    O Lightning processa a computação do tamanho do mundo a partir dos sinalizadores --gpus trainer e --num_nodes.

    from azureml.core import ScriptRunConfig, Experiment
    from azureml.core.runconfig import MpiConfiguration
    
    nnodes = 2
    gpus_per_node = 4
    args = ['--max_epochs', 50, '--gpus_per_node', gpus_per_node, '--accelerator', 'ddp', '--num_nodes', nnodes]
    distr_config = MpiConfiguration(node_count=nnodes, process_count_per_node=gpus_per_node)
    
    run_config = ScriptRunConfig(
       source_directory='./src',
       script='train.py',
       arguments=args,
       compute_target=compute_target,
       environment=pytorch_env,
       distributed_job_config=distr_config,
    )
    
    run = Experiment(ws, 'experiment_name').submit(run_config)
    

Transformadores faciais abraçados

O Rosto Abraçado fornece muitos exemplos para utilizar a respetiva biblioteca Transformers com torch.distributed.launch para executar a preparação distribuída. Para executar estes exemplos e os seus próprios scripts de preparação personalizados com a API transformers Trainer, siga a secção Utilizar torch.distributed.launch .

Código de configuração da tarefa de exemplo para ajustar o modelo grande BERT na tarefa MNLI de classificação de textos com o run_glue.py script num nó com 8 GPUs:

from azureml.core import ScriptRunConfig
from azureml.core.runconfig import PyTorchConfiguration

distr_config = PyTorchConfiguration() # node_count defaults to 1
launch_cmd = "python -m torch.distributed.launch --nproc_per_node 8 text-classification/run_glue.py --model_name_or_path bert-large-uncased-whole-word-masking --task_name mnli --do_train --do_eval --max_seq_length 128 --per_device_train_batch_size 8 --learning_rate 2e-5 --num_train_epochs 3.0 --output_dir /tmp/mnli_output".split()

run_config = ScriptRunConfig(
  source_directory='./src',
  command=launch_cmd,
  compute_target=compute_target,
  environment=pytorch_env,
  distributed_job_config=distr_config,
)

Também pode utilizar a opção por início do processo para executar a preparação distribuída sem utilizar torch.distributed.launch. Uma coisa a ter em conta se utilizar este método é que os transformadores TrainingArguments esperam que a classificação local seja transmitida como um argumento (--local_rank). torch.distributed.launch trata deste problema quando --use_env=False, mas se estiver a utilizar o lançamento por processo, terá de transmitir explicitamente a classificação local como um argumento para o script --local_rank=$LOCAL_RANK de preparação, uma vez que o Azure Machine Learning define apenas a variável de LOCAL_RANK ambiente.

TensorFlow

Se estiver a utilizar o TensorFlow distribuído nativo no código de preparação, como a API do tf.distribute.Strategy TensorFlow 2.x, pode iniciar a tarefa distribuída através do Azure Machine Learning com o TensorflowConfiguration.

Para tal, especifique um TensorflowConfiguration objeto para o distributed_job_config parâmetro do ScriptRunConfig construtor. Se estiver a utilizar tf.distribute.experimental.MultiWorkerMirroredStrategy, especifique o worker_countTensorflowConfiguration no correspondente ao número de nós da sua tarefa de preparação.

from azureml.core import ScriptRunConfig, Environment, Experiment
from azureml.core.runconfig import TensorflowConfiguration

curated_env_name = 'AzureML-TensorFlow-2.3-GPU'
tf_env = Environment.get(workspace=ws, name=curated_env_name)
distr_config = TensorflowConfiguration(worker_count=2, parameter_server_count=0)

run_config = ScriptRunConfig(
  source_directory='./src',
  script='train.py',
  compute_target=compute_target,
  environment=tf_env,
  distributed_job_config=distr_config,
)

# submit the run configuration to start the job
run = Experiment(ws, "experiment_name").submit(run_config)

Se o script de preparação utilizar a estratégia de servidor de parâmetros para preparação distribuída, como para o TensorFlow 1.x legado, também terá de especificar o número de servidores de parâmetros a utilizar na tarefa, por exemplo, tf_config = TensorflowConfiguration(worker_count=2, parameter_server_count=1).

TF_CONFIG

No TensorFlow, a variável de ambiente TF_CONFIG é necessária para a preparação em vários computadores. Para tarefas do TensorFlow, o Azure Machine Learning irá configurar e definir a variável TF_CONFIG adequadamente para cada trabalho antes de executar o script de preparação.

Pode aceder a TF_CONFIG a partir do script de preparação se precisar de: os.environ['TF_CONFIG'].

Exemplo TF_CONFIG definido num nó de trabalho principal:

TF_CONFIG='{
    "cluster": {
        "worker": ["host0:2222", "host1:2222"]
    },
    "task": {"type": "worker", "index": 0},
    "environment": "cloud"
}'

Exemplo do TensorFlow

Acelerar a preparação de GPU distribuída com o InfiniBand

À medida que o número de VMs a preparar um modelo aumenta, o tempo necessário para preparar esse modelo deve diminuir. A diminuição no tempo, idealmente, deve ser linearmente proporcional ao número de VMs de preparação. Por exemplo, se a preparação de um modelo numa VM demorar 100 segundos, a preparação do mesmo modelo em duas VMs deverá, idealmente, demorar 50 segundos. A preparação do modelo em quatro VMs deve demorar 25 segundos, etc.

O InfiniBand pode ser um fator importante na obtenção deste dimensionamento linear. O InfiniBand permite comunicação GPU a GPU de baixa latência entre nós num cluster. O InfiniBand requer hardware especializado para funcionar. Algumas séries de VMs do Azure, especificamente a série NC, ND e H, têm agora VMs compatíveis com RDMA com suporte SR-IOV e InfiniBand. Estas VMs comunicam através da baixa latência e da rede InfiniBand de alta largura de banda, que é muito mais eficaz do que a conectividade baseada em Ethernet. A SR-IOV para InfiniBand permite um desempenho quase bare-metal para qualquer biblioteca de MPI (o MPI é utilizado por muitas arquiteturas e ferramentas de preparação distribuídas, incluindo o software NCCL da NVIDIA.) Estes SKUs destinam-se a satisfazer as necessidades de cargas de trabalho de machine learning computacionalmente intensivas e aceleradas por GPU. Para obter mais informações, veja Accelerating Distributed Training in Azure Machine Learning with SR-IOV (Acelerar a Preparação Distribuída no Azure Machine Learning com SR-IOV).

Normalmente, os SKUs de VM com um "r" no respetivo nome contêm o hardware InfiniBand necessário e os que não têm um "r" normalmente não contêm. ('r' é uma referência ao RDMA, que significa "acesso remoto direto à memória".) Por exemplo, o SKU Standard_NC24rs_v3 da VM está ativado para InfiniBand, mas o SKU Standard_NC24s_v3 não. Além das capacidades do InfiniBand, as especificações entre estes dois SKUs são praticamente as mesmas – ambas têm 24 núcleos, 448 GB de RAM, 4 GPUs do mesmo SKU, etc. Saiba mais sobre os SKUs de máquinas compatíveis com RDMA e InfiniBand.

Aviso

O SKU Standard_NC24r da máquina de geração mais antiga está ativado para RDMA, mas não contém hardware SR-IOV necessário para InfiniBand.

Se criar um AmlCompute cluster de um destes tamanhos compatíveis com RDMA, com InfiniBand ativado, a imagem do SO será apresentada com o controlador Mellanox OFED necessário para ativar o InfiniBand pré-instalado e pré-configurado.

Passos seguintes