Руководство по распределенному обучению с поддержкой GPU (пакет SDK версии 1)

ОБЛАСТЬ ПРИМЕНЕНИЯ:Пакет SDK для Python azureml версии 1

Узнайте больше об использовании кода распределенного обучения с поддержкой GPU в Машинном обучении Azure (ML). Эта статья не поможет вам изучить распределенное обучение. Она поможет вам запустить существующий код распределенного обучения в Машинном обучении Azure. В ней представлены советы и примеры для каждой из платформ:

  • Message Passing Interface (MPI):
    • Horovod
    • DeepSpeed
    • переменные среды из Open MPI.
  • PyTorch
    • инициализация группы процессов;
    • параметры запуска;
    • DistributedDataParallel (per-process-launcher);
    • использование torch.distributed.launch (per-node-launcher);
    • PyTorch Lightning;
    • библиотека Transformers в Hugging Face.
  • TensorFlow
    • переменные среды для TensorFlow (TF_CONFIG).
  • Ускорение обучения с поддержкой GPU с помощью InfiniBand.

Предварительные требования

Ознакомьтесь с основными принципами распределенного обучения с поддержкой GPU, например с параллельной обработкой данных, параллельной обработкой распределенных данных и параллельной обработкой моделей.

Совет

Если неизвестно, какой тип параллельной обработки нужно использовать, то более 90 % времени следует использовать параллельную обработку распределенных данных.

MPI

Машинное обучение Azure предлагает задание MPI для запуска заданного количества процессов на каждом узле. Этот подход можно использовать для выполнения распределенного обучения в режиме per-process-launcher или per-node-launcher в зависимости от того, задано ли для параметра process_count_per_node значение 1 (по умолчанию) для per-node-launcher или для него задано число устройств и GPU для per-process-launcher. Машинное обучение Azure создает полную команду запуска MPI (mpirun) в фоновом режиме. Вы не можете предоставить собственные полные команды head-node-launcher, такие как mpirun или DeepSpeed launcher.

Совет

В базовом образе Docker, используемом заданием MPI Машинного обучения Azure, должна быть установлена библиотека MPI. Открытый MPI включен во все базовые образы GPU Машинного обучения Azure. При использовании пользовательского образа Docker вы несете ответственность за то, чтобы он включал в себя библиотеку MPI. Рекомендуется использовать Open MPI, но вы можете также использовать другую реализацию MPI, например Intel MPI. Машинное обучение Azure также предоставляет курируемые среды для популярных платформ.

Чтобы выполнить распределенное обучение с помощью MPI, сделайте следующее.

  1. Используйте среду машинного обучения Azure с предпочтительной платформой глубокого обучения и MPI. Машинное обучение Azure предоставляет курируемую среду для популярных платформ.
  2. Определите MpiConfiguration с process_count_per_node и node_count. Значение process_count_per_node должно равняться количеству GPU на узел для режима per-process-launcher или равняться 1 (по умолчанию) для режима per-node-launcher, если пользовательский сценарий будет отвечать за запуск процессов на каждом узле.
  3. Передайте объект MpiConfiguration в параметр distributed_job_config конфигурации 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

Используйте конфигурацию задания MPI для распределенного обучения с помощью платформы глубокого обучения Horovod.

Убедитесь, что в вашем коде применены следующие советы.

  • Код обучения правильно инструментируется с помощью Horovod перед добавлением частей Машинного обучения Azure
  • Среда Машинного обучения Azure содержит Horovod и MPI. Курированные среды с поддержкой GPU PyTorch и TensorFlow предварительно настроены с помощью Horovod и соответствующих зависимостей.
  • Создайте MpiConfiguration с требуемым распределением.

Пример Horovod

DeepSpeed

Не используйте настраиваемое средство запуска DeepSpeed для выполнения распределенного обучения с помощью библиотеки DeepSpeed в Машинном обучении Azure. Вместо этого настройте задание MPI для запуска задания обучения с помощью MPI.

Убедитесь, что в вашем коде применены следующие советы.

  • Среда Машинного обучения Azure содержит DeepSpeed и его зависимости, Open MPI и mpi4py.
  • Создайте MpiConfiguration с требуемым распределением.

Пример DeepSpeed

Переменные среды из Open MPI

При выполнении заданий MPI с образами Open MPI для каждого запущенного процесса используются следующие переменные среды:

  1. OMPI_COMM_WORLD_RANK — ранг процесса;
  2. OMPI_COMM_WORLD_SIZE — размер пакета;
  3. AZ_BATCH_MASTER_NODE — основной адрес с портом, MASTER_ADDR:MASTER_PORT;
  4. OMPI_COMM_WORLD_LOCAL_RANK— локальный ранг процесса на узле;
  5. OMPI_COMM_WORLD_LOCAL_SIZE — количество процессов на узле.

Совет

Несмотря на имя, переменная среды не OMPI_COMM_WORLD_NODE_RANK соответствует NODE_RANK. Чтобы использовать режим per-node-launcher, задайте process_count_per_node=1 и используйте OMPI_COMM_WORLD_RANK в качестве NODE_RANK.

PyTorch

Машинное обучение Azure поддерживает выполнение распределенных заданий с помощью собственных возможностей распределенного обучения PyTorch (torch.distributed).

Совет

Для параллельной обработки данных в официальном руководстве по PyTorch предписывается использовать DistributedDataParallel (DDP) и DataParallel для распределенного обучения как с одним узлом, так и с несколькими узлами. PyTorch также рекомендует использовать DistributedDataParallel в пакете многопроцессной обработки. Поэтому документация и примеры для Машинного обучения Azure будут сосредоточены на обучении DistributedDataParallel.

Инициализация группы процессов

Основа любого распределенного обучения — это группа процессов, которые связаны и могут взаимодействовать друг с другом, используя внутренний сервер. Для PyTorch группа процессов создается путем вызова torch.distributed.init_process_group во всех распределенных процессах для формирования группы процессов.

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

Наиболее распространенные внутренние серверы подключений: mpi, nccl и gloo. Для обучения с поддержкой GPU рекомендуется использовать nccl, чтобы обеспечить оптимальную производительность, когда это возможно.

init_method указывает, как процессы могут обнаружить друг друга, как они инициализируются и проверяют группу процессов, используя внутренний сервер подключений. По умолчанию, если параметр init_method не указан, PyTorch будет использовать метод инициализации переменной среды (env://). init_method — это рекомендуемый метод инициализации, используемый в коде обучения для запуска распределенного PyTorch в Машинном обучении Azure. PyTorch будет искать следующие переменные среды для инициализации:

  • MASTER_ADDR — IP-адрес компьютера, на котором будет размещен процесс с рангом 0.
  • MASTER_PORT — свободный порт компьютера, на котором будет размещен процесс с рангом 0.
  • WORLD_SIZE — общее количество процессов. Оно должно быть равно общему количеству устройств (GPU), используемых для распределенного обучения.
  • RANK — ранг (глобальный) текущего процесса. Возможные значения: от 0 до (world_size — 1).

Дополнительные сведения об инициализации группы процессов см. в документации по PyTorch.

Помимо этого, многим приложениям также потребуются следующие переменные среды:

  • LOCAL_RANK — локальный (относительный) ранг процесса в пределах узла. Возможные значения: от 0 до (число процессов на узле — 1). Эта информация полезна, так как многие операции, такие как подготовка данных, должны выполняться один раз для каждого узла (обычно для local_rank = 0).
  • NODE_RANK — ранг узла для обучения с несколькими узлами. Возможные значения: от 0 до (общее число узлов — 1).

Параметры запуска PyTorch

Задание PyTorch Машинного обучения Azure поддерживает два типа вариантов запуска распределенного обучения:

  • Режим per-process-launcher. Система запустит все распределенные процессы со всеми соответствующими сведениями (например, переменными среды) в группе процессов.
  • Средство запуска для каждого узла. Вы предоставляете Машинному обучению Azure средство запуска служебной программы, которое будет выполняться на каждом узле. Эта служебная программа запуска будет обрабатывать запуск каждого процесса на заданном узле. Средство запуска локально на каждом узле настраивает RANK и LOCAL_RANK. Служебная программа torch.distributed.launch и PyTorch Lightning относятся к этой категории.

Между этими режимами запуска нет фундаментальных отличий. В основном выбор зависит от предпочтений пользователя или соглашений о платформах и библиотеках, созданных на основе обыкновенного PyTorch (такого как Lightning или Hugging Face).

В следующих разделах подробно описывается настройка заданий PyTorch Машинного обучения Azure для каждого из вариантов запуска.

DistributedDataParallel (per-process-launcher);

Не нужно использовать служебную программу запуска, например torch.distributed.launch. Чтобы отправить распределенное задание PyTorch, сделайте следующее.

  1. Укажите сценарий обучения и аргументы.
  2. Создайте PyTorchConfiguration, затем укажите process_count и node_count. process_count соответствует общему количеству процессов, которые вы хотите выполнить для задания. Обычно значение process_count равно # GPUs per node x # nodes. Если process_count параметр не указан, Машинное обучение Azure по умолчанию запустит по одному процессу на каждом узле.

Машинное обучение Azure задаст MASTER_ADDRпеременные среды , MASTER_PORT, WORLD_SIZEи NODE_RANK на каждом узле, а также задаст переменные уровня RANK процесса и LOCAL_RANK среды.

Чтобы использовать этот параметр для обучения с несколькими процессами на узле, используйте пакет SDK >= 1.22.0Для Python для Машинного обучения Azure. Параметр process_count был введен в версии 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)

Совет

Если ваш сценарий обучения передает такие сведения, как ранг или локальный ранг, в качестве аргументов сценария, вы можете ссылаться в аргументах на переменные среды:

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

Пример PyTorch для режима per-process-launcher

Использование torch.distributed.launch (per-node-launch)

PyTorch предоставляет в torch.distributed.launch служебную программу запуска, которую можно использовать для запуска нескольких процессов на каждом узле. Модуль torch.distributed.launch порождает несколько процессов обучения на каждом из узлов.

Ниже показано, как настроить задание PyTorch с помощью средства запуска каждого узла в Машинном обучении Azure. Задание достигает эквивалента выполнения следующей команды:

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. Укажите команду torch.distributed.launch в параметре command конструктора ScriptRunConfig. Машинное обучение Azure выполняет эту команду на каждом узле кластера обучения. Значение --nproc_per_node должно быть меньше или равно количеству GPU, доступных на каждом узле. MASTER_ADDR, MASTER_PORT и NODE_RANK задаются Машинным обучением Azure, поэтому в команде можно просто ссылаться на переменные среды. Машинное обучение Azure задает для MASTER_PORT 6105значение , но при необходимости можно передать другое значение аргументу --master_port команды torch.distributed.launch. (Служебная программа запуска выполнит сброс переменных среды.)
  2. Создайте PyTorchConfiguration и укажите 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)

Совет

Обучение на одном узле с несколькими GPU. Если вы используете служебную программу запуска для выполнения задания обучения PyTorch на одном узле с несколькими GPU, вам не нужно указывать параметр distributed_job_config для 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-launcher

PyTorch Lightning;

PyTorch Lightning — это упрощенная библиотека с открытым кодом, которая предоставляет высокоуровневый интерфейс для PyTorch. Lightning абстрагируется от многих низкоуровневых конфигураций распределенного обучения, необходимых для обыкновенного PyTorch. Lightning позволяет запускать сценарии обучения на одном GPU, на одном узле с несколькими GPU и на нескольких узлах с несколькими GPU. В фоновом режиме Lightning запускает несколько процессов, как и torch.distributed.launch.

Для обучения с одним узлом (включая один узел с несколькими GPU) можно запустить код в Машинном обучении Azure без указания distributed_job_config. Существует 2 варианта запуска эксперимента с использованием нескольких узлов с несколькими графическими процессорами:

  • Использование конфигурации PyTorch (рекомендуется): определите PyTorchConfiguration, укажите communication_backend="Nccl", node_count и process_count (обратите внимание, что это общее количество процессов, т. е num_nodes * process_count_per_node). В модуле Lightning Trainer укажите как num_nodes, так и gpus, чтобы они соответствовали PyTorchConfiguration. Например, num_nodes = node_count и gpus = process_count_per_node.

  • Использование конфигурации MPI:

    • Определите MpiConfiguration и укажите как node_count, так и process_count_per_node. В модуле Lightning Trainer укажите, что num_nodes и gpus должны быть соответственно такими же, как node_count и process_count_per_node из MpiConfiguration.

    • Для обучения с несколькими узлами с помощью MPI для Lightning требуется, чтобы следующие переменные среды были установлены на каждом узле кластера обучения:

      • MASTER_ADDR
      • MASTER_PORT
      • NODE_RANK
      • LOCAL_RANK

      Вручную задайте эти переменные среды, необходимые Lightning в основных сценариях обучения:

    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
      )
    

    Lightning обрабатывает размер пакета в соответствии с флагами инструктора --gpus и --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)
    

библиотека Transformers в Hugging Face.

Hugging Face предоставляет множество примеров использования собственной библиотеки Transformers с torch.distributed.launch для распределенного обучения. Чтобы выполнить эти примеры и собственные пользовательские сценарии обучения с помощью API Transformers Trainer, следуйте указаниям в разделе Использование torch.distributed.launch.

Пример кода конфигурации задания для точной настройки большой модели BERT в задаче классификации текста MNLI с использованием сценария run_glue.py на одном узле с 8 GPU:

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

Вы можете также использовать режим per-process-launcher, чтобы выполнить распределенное обучение без использования torch.distributed.launch. Следует помнить, что при использовании этого метода преобразователи TrainingArguments ожидают передачи локального ранга в качестве аргумента (--local_rank). torch.distributed.launch отвечает за это при --use_env=Falseиспользовании , но если вы используете для запуска процесса, необходимо явно передать локальный ранг в в качестве аргумента в скрипт --local_rank=$LOCAL_RANK обучения, так как Машинное обучение Azure задает LOCAL_RANK только переменную среды.

TensorFlow

Если вы используете собственный распределенный TensorFlow в коде обучения, например API TensorFlow 2.x tf.distribute.Strategy , вы можете запустить распределенное задание через Машинное обучение Azure с помощью TensorflowConfiguration.

Для этого укажите объект TensorflowConfiguration в параметре distributed_job_config конструктора ScriptRunConfig. Если вы используете tf.distribute.experimental.MultiWorkerMirroredStrategy, укажите worker_count в параметре TensorflowConfiguration в соответствии с количеством узлов для вашего задания обучения.

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)

Если сценарий обучения использует стратегию сервера параметров для распределенного обучения, например для устаревшего TensorFlow 1.x, вам также придется указать количество серверов параметров, которое будет использоваться в задании, например tf_config = TensorflowConfiguration(worker_count=2, parameter_server_count=1).

TF_CONFIG

В TensorFlow для обучения на нескольких компьютерах требуется переменная среды TF_CONFIG. Для заданий TensorFlow Машинное обучение Azure настроит и задаст переменную TF_CONFIG соответствующим образом для каждой рабочей роли перед выполнением сценария обучения.

При необходимости вы можете обращаться к TF_CONFIG из сценария обучения: os.environ['TF_CONFIG'].

Пример параметра TF_CONFIG, заданного на главном рабочем узле:

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

Пример TensorFlow

Ускорение распределенного обучения с поддержкой GPU с помощью InfiniBand

По мере увеличения количества виртуальных машин, обучающих модель, время, необходимое для обучения модели, должно уменьшаться. Уменьшение времени в идеальном случае должно быть пропорционально количеству обучающих виртуальных машин. Например, если обучение модели на одной виртуальной машине занимает 100 секунд, то при обучении той же модели на двух виртуальных машинах в идеале потребуется 50 секунд. Обучение модели на четырех виртуальных машинах займет 25 секунд и т. д.

InfiniBand может играть важную роль в обеспечении такого линейного масштабирования. InfiniBand обеспечивает связь на уровне GPU между узлами в кластере с низкой задержкой. Для работы с InfiniBand требуется специализированное оборудование. Некоторые серии виртуальных машин Azure, в частности серии NC, ND и H, теперь поддерживают виртуальные машины RDMA с поддержкой SR-IOV и InfiniBand. Эти виртуальные машины обмениваются данными по сети InfiniBand с низкой задержкой и высокой пропускной способностью, которая гораздо быстрее, чем сеть Ethernet. SR-IOV для InfiniBand обеспечивает производительность практически на уровне компьютера без операционной системы для любой библиотеки MPI (MPI используется многими платформами и инструментами для распределенного обучения, включая программное обеспечение NVIDIA NCCL). Эти номера SKU предназначены для удовлетворения потребностей ресурсоемких рабочих нагрузок машинного обучения с большим количеством вычислений и поддержкой GPU. Дополнительные сведения см. в разделе Accelerating Distributed Training in Azure Machine Learning with SR-IOV (Ускорение распределенного обучения в Машинном обучении Azure с помощью SR-IOV).

Как правило, номера SKU виртуальных машин, имена которых включают букву "r", имеют необходимое оборудование InfiniBand, а те номера SKU, имена которых не включают эту букву, не имеют такого оборудования. ("r" — это ссылка на сокращение "RDMA", которое означает "удаленный доступ к памяти".) Например, номер SKU виртуальной машины Standard_NC24rs_v3 поддерживает InfiniBand, а номер SKU Standard_NC24s_v3 — нет. За исключением возможностей InfiniBand характеристики этих двух номеров SKU в целом одинаковы — 24 ядра, 448 ГБ ОЗУ, 4 GPU с одним и тем же SKU и т. д. Дополнительные сведения об RDMA и о номерах SKU виртуальных машин, поддерживающих InfiniBand.

Предупреждение

Номер SKU более старого поколения виртуальных машин Standard_NC24r поддерживает RDMA, но не включает оборудование SR-IOV, необходимое для InfiniBand.

При создании кластера AmlCompute на основе одного из таких размеров с поддержкой RDMA и InfiniBand образ ОС будет содержать драйвер Mellanox OFED, необходимый для включения предварительно установленного и настроенного компонента InfiniBand.

Дальнейшие действия