Todos os produtos
Search
Central de documentação

Platform For AI:Quick start: Distributed training with DLC

Última atualização: Jul 24, 2026

O Deep Learning Containers (DLC) permite criar rapidamente jobs de treinamento em nó único ou distribuído, sem provisionar máquinas ou configurar ambientes manualmente. Este tutorial demonstra o processo completo para treinamento com GPU única em nó único e com múltiplas GPUs em múltiplos nós, usando a tarefa de reconhecimento de dígitos manuscritos MNIST.

Nota

O reconhecimento de dígitos manuscritos MNIST é uma tarefa introdutória clássica em deep learning. O objetivo é construir um modelo de machine learning capaz de reconhecer 10 dígitos manuscritos (de 0 a 9).

1. Pré-requisitos

Use sua conta principal para ativar o PAI e crie um workspace. Acesse o PAI console, selecione uma região no canto superior esquerdo e siga as instruções para conceder permissões e ativar o service.

2. Faturamento

Os exemplos deste tutorial usam recursos públicos para criar jobs do DLC, cobrados na modalidade de pagamento conforme o uso. Para mais informações, consulte Billing for Deep Learning Containers (DLC).

3. Treinamento em nó único com GPU única

3,1 Criar um dataset

Um dataset armazena seu código de treinamento, dados e artefatos do modelo. Esta seção explica como usar um dataset baseado no Object Storage Service (OSS) da Alibaba Cloud.

  1. No painel de navegação à esquerda do PAI console, escolha Datasets > Custom Dataset > Create datasets.

  2. Configure os seguintes parâmetros principais e mantenha os valores padrão para os demais.

    • Name: Por exemplo, insira dataset_mnist.

    • Storage Type: Selecione OSS.

    • OSS Path: Clique no ícone de pasta image, selecione um bucket e crie um novo diretório, como dlc_mnist.

      Se você ainda não ativou o OSS ou não tem um bucket disponível na região atual, siga estas etapas para ativar o OSS e criar um bucket:

      (Opcional) Ativar o OSS e criar um bucket

      1. Ativar o OSS.

      2. Acesse o OSS console, clique em Create Bucket, insira um Bucket name e selecione a mesma Region do PAI. Mantenha os valores padrão para os outros parâmetros e clique em Create.

    Clique em Confirm para criar o dataset.

  3. Faça upload do código de treinamento e dos dados.

    1. Baixe o script de treinamento fornecido: mnist_train.py. Para simplificar o processo, o script baixa automaticamente os dados de treinamento para o diretório dataSet do seu dataset durante a execução.

      Para seus próprios projetos, faça upload prévio do código e dos dados de treinamento para um dataset do PAI.

      Exemplo de código de treinamento em nó único com GPU única: mnist_train.py

      import torch
      import torch.nn as nn
      import torch.nn.functional as F
      import torch.optim as optim
      from torch.utils.data import DataLoader
      from torchvision import datasets, transforms
      from torch.utils.tensorboard import SummaryWriter
      # Hyperparameters
      batch_size = 64  # Batch size for each training iteration
      learning_rate = 0.01  # Learning rate
      num_epochs = 20  # Number of training epochs
      # Check if a GPU is available
      device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
      # Data preprocessing
      transform = transforms.Compose([
          transforms.ToTensor(),
          transforms.Normalize((0.5,), (0.5,))
      ])
      train_dataset = datasets.MNIST(root='/mnt/data/dataSet', train=True, download=True, transform=transform)
      val_dataset = datasets.MNIST(root='/mnt/data/dataSet', train=False, download=False, transform=transform)
      train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
      val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
      # Define a simple neural network
      class SimpleCNN(nn.Module):
          def __init__(self):
              super(SimpleCNN, self).__init__()
              # First convolutional layer: 1 input channel (grayscale image), 10 output channels, 5x5 kernel
              self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
              # Second convolutional layer: 10 input channels, 20 output channels, 3x3 kernel
              self.conv2 = nn.Conv2d(10, 20, kernel_size=3)
              # Fully connected layer: Input is 20 * 5 * 5 (feature map size after convolution and pooling), output is 128
              self.fc1 = nn.Linear(20 * 5 * 5, 128)
              # Output layer: 128 -> 10 (for 10 digit classes)
              self.fc2 = nn.Linear(128, 10)
          def forward(self, x):
              # Input x shape: [batch, 1, 28, 28]
              x = F.max_pool2d(F.relu(self.conv1(x)), 2)  # [batch, 10, 12, 12]
              x = F.max_pool2d(F.relu(self.conv2(x)), 2)  # [batch, 20, 5, 5]
              x = x.view(-1, 20 * 5 * 5)  # Flatten to [batch, 500]
              x = F.relu(self.fc1(x))      # [batch, 128]
              x = self.fc2(x)              # [batch, 10]
              return x
      # Instantiate the model and move it to the GPU if available
      model = SimpleCNN().to(device)
      criterion = nn.CrossEntropyLoss()
      optimizer = optim.SGD(model.parameters(), lr=learning_rate)
      # Create a TensorBoard SummaryWriter to visualize the model training process
      writer = SummaryWriter('/mnt/data/output/runs/mnist_experiment')
      # Variable to save the model with the highest accuracy
      best_val_accuracy = 0.0
      # Train the model and log loss and accuracy
      for epoch in range(num_epochs):
          model.train()
          for batch_idx, (data, target) in enumerate(train_loader):
              data, target = data.to(device), target.to(device)  # Move data and target to the GPU
              # Zero the gradients
              optimizer.zero_grad()
              # Forward pass
              output = model(data)
              # Calculate loss
              loss = criterion(output, target)
              # Backward pass
              loss.backward()
              # Update parameters
              optimizer.step()
              # Log training loss to TensorBoard
              if batch_idx % 100 == 0:  # Log every 100 batches
                  writer.add_scalar('Loss/train', loss.item(), epoch * len(train_loader) + batch_idx)
                  print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} ({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}')
          # Validate the model and log validation loss and accuracy
          model.eval()
          val_loss = 0
          correct = 0
          with torch.no_grad():  # Do not calculate gradients
              for data, target in val_loader:
                  data, target = data.to(device), target.to(device)  # Move data and target to the GPU
                  output = model(data)
                  val_loss += criterion(output, target).item()  # Accumulate validation loss
                  pred = output.argmax(dim=1, keepdim=True)  # Get predicted label
                  correct += pred.eq(target.view_as(pred)).sum().item()  # Accumulate number of correct predictions
          val_loss /= len(val_loader)  # Calculate average validation loss
          val_accuracy = 100. * correct / len(val_loader.dataset)  # Calculate validation accuracy
          print(f'Validation Loss: {val_loss:.4f}, Accuracy: {correct}/{len(val_loader.dataset)} ({val_accuracy:.0f}%)')
          # Log validation loss and accuracy to TensorBoard
          writer.add_scalar('Loss/validation', val_loss, epoch)
          writer.add_scalar('Accuracy/validation', val_accuracy, epoch)
          # Save the model with the highest validation accuracy
          if val_accuracy > best_val_accuracy:
              best_val_accuracy = val_accuracy
              torch.save(model.state_dict(), '/mnt/data/output/best_model.pth')
              print(f'Model saved with accuracy: {best_val_accuracy:.2f}%')
      # Close the SummaryWriter
      writer.close()
      print('Training complete. writer.close()')
    2. Faça upload do código. Na página de detalhes do dataset, clique em View Data para acessar o OSS console. Em seguida, clique em Upload Object >Select Files > Upload Object para enviar o script de treinamento ao OSS.

3,2 Criar um job do DLC

  1. No painel de navegação à esquerda do PAI console, escolha Deep Learning Containers (DLC) > Create Job.

  2. Configure os seguintes parâmetros principais e mantenha os valores padrão para os demais. Para a lista completa de parâmetros, consulte Create a training job.

    • Image Configuration: Selecione Image Address e insira o endereço de imagem apropriado para sua Region.

      Na barra de navegação superior do console, use o seletor de Region para confirme sua região atual, como China (Hangzhou).

      Region

      Image address

      China (Beijing)

      dsw-registry-vpc.cn-beijing.cr.aliyuncs.com/pai/modelscope:1.28.0-pytorch2.3.1tensorflow2.16.1-gpu-py311-cu121-ubuntu22,04

      China (Shanghai)

      dsw-registry-vpc.cn-shanghai.cr.aliyuncs.com/pai/modelscope:1.28.0-pytorch2.3.1tensorflow2.16.1-gpu-py311-cu121-ubuntu22,04

      China (Hangzhou)

      dsw-registry-vpc.cn-hangzhou.cr.aliyuncs.com/pai/modelscope:1.28.0-pytorch2.3.1tensorflow2.16.1-gpu-py311-cu121-ubuntu22,04

      Other

      Find your region ID e substitua <Region ID> no endereço da imagem para obter a URL completa:

      dsw-registry-vpc.<Region ID>.cr.aliyuncs.com/pai/modelscope:1.28.0-pytorch2.3.1tensorflow2.16.1-gpu-py311-cu121-ubuntu22,04

      Esta imagem usa o mesmo ambiente verificado do tópico Get Started with DSW Interactive Modeling . Ao criar modelos com o PAI, um fluxo de trabalho típico consiste em verificar primeiro o ambiente e desenvolver o código no DSW, usando depois o DLC para treinamento.
    • Dataset Mount: Selecione Custom Dataset e escolha o dataset criado na etapa anterior. O Mount Path padrão é /mnt/data.

    • Startup Command: python /mnt/data/mnist_train.py

      O comando de inicialização é o mesmo usado ao executar o código no DSW ou localmente. No entanto, como o mnist_train.py agora está montado em /mnt/data/ , basta alterar o caminho do código para /mnt/data/mnist_train.py .
    • Source: Selecione Public Resources e escolha ecs.gn7i-c8g1.2xlarge para o Resource Type.

      Se este tipo de instância estiver indisponível, escolha outro tipo de instância com GPU.

    Clique em OK para criar o job. A execução leva cerca de 15 minutos. Durante o processo, clique em Logs para visualizar o progresso do treinamento.

    O log de treinamento mostra que o modelo MNIST atinge uma acurácia de validação de 9886/10000 (aproximadamente 99%) na Época 18. Quando o status do job mudar para Succeeded, o log exibirá a saída final: Training complete. writer.close().

    Após a conclusão do job, o DLC salva o melhor checkpoint do modelo e o log do TensorBoard no caminho output do dataset montado.

    O diretório dlc_mnist/ no seu bucket OSS agora contém um subdiretório dataSet/ para os dados de treinamento, um subdiretório output/ para os artefatos do modelo e o script mnist_train.py.

3,3 (Opcional) Visualizar no TensorBoard

Use a ferramenta de visualização TensorBoard para ver a curva de perda e compreender melhor o processo de treinamento.

Importante

Para usar o TensorBoard em um job do DLC, configure um dataset.

  1. Na página de detalhes do job do DLC, clique na aba Tensorboard e depois em Create TensorBoard.

  2. Defina o Configuration Type como By Task. No campo Summary Path, insira o caminho onde o SummaryWriter salva os dados no código de treinamento: /mnt/data/output/runs/. Clique em OK para iniciar.

    Isso corresponde ao trecho de código: writer = SummaryWriter('/mnt/data/output/runs/mnist_experiment')
  3. Clique em View TensorBoard para visualizar a curva train_loss (perda do conjunto de treinamento) e a curva validation_loss (perda do conjunto de validação).

    image

    (Opcional) Ajustar hiperparâmetros para melhorar o desempenho do modelo

    Avalie o desempenho do seu modelo observando as tendências nos valores de perda:

    • Os valores de train_loss e validation_loss ainda apresentam tendência de queda antes do término do treinamento (underfitting).

      Aumente o num_epochs (número de épocas de treinamento) ou eleve ligeiramente a learning_rate e retreine o modelo para ajustar melhor os dados de treinamento.

    • O train_loss diminui continuamente, mas o validation_loss começa a aumentar antes do fim do treinamento (overfitting).

      Reduza o num_epochs ou diminua levemente a learning_rate e retreine o modelo para evitar overfitting.

    • Tanto o train_loss quanto o validation_loss permanecem estáveis antes da conclusão do treinamento (bom ajuste).

      Quando o modelo estiver nesse estado, prossiga para as próximas etapas.

3,4 Implantar o modelo treinado

Para mais informações, consulte V. Use EAS to deploy model services.

4. Treinamento distribuído com múltiplas GPUs

Se uma única GPU não tiver memória suficiente para seu modelo ou se você quiser acelerar o treinamento, crie um job de treinamento distribuído com múltiplas GPUs ou múltiplos nós.

Esta seção usa duas instâncias, cada uma com uma única GPU, como exemplo. O mesmo procedimento se aplica a outras configurações com múltiplas GPUs e múltiplos nós.

4,1 Preparar o dataset e o script

Se você já criou um dataset na seção 3. Single-node, single-GPU training, basta baixe o arquivo mnist_train_distributed.py e fazer o upload. Caso contrário, primeiro 3,1 Create a dataset e depois envie o código.

Exemplo de código de treinamento multi-GPU em nó único ou multi-GPU em múltiplos nós: mnist_train_distributed.py

import os
import torch
import torch.distributed as dist
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
from torch.utils.data.distributed import DistributedSampler
from torchvision import datasets, transforms
from torch.utils.tensorboard import SummaryWriter
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=3)
        self.fc1 = nn.Linear(20 * 5 * 5, 128)
        self.fc2 = nn.Linear(128, 10)
    def forward(self, x):
        x = F.max_pool2d(F.relu(self.conv1(x)), 2)
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, 20 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x
def main():
    rank = int(os.environ["RANK"])
    world_size = int(os.environ["WORLD_SIZE"])
    local_rank = int(os.environ["LOCAL_RANK"])
    dist.init_process_group(backend='nccl')
    torch.cuda.set_device(local_rank)
    device = torch.device('cuda', local_rank)
    batch_size = 64
    learning_rate = 0.01
    num_epochs = 20
    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.5,), (0.5,))
    ])
    # Only the main process (rank=0) needs to download; other processes must wait for it.
    # Make processes with rank != 0 wait at the barrier.
    if rank != 0:
        dist.barrier()
    # All processes create the dataset.
    # However, only the rank 0 process actually performs the download.
    train_dataset = datasets.MNIST(root='/mnt/data/dataSet', train=True, download=(rank == 0), transform=transform)
    # After the rank 0 process finishes downloading, it also reaches the barrier, releasing all processes.
    if rank == 0:
        dist.barrier()
    # At this point, all processes are synchronized and can continue.
    val_dataset = datasets.MNIST(root='/mnt/data/dataSet', train=False, download=False, transform=transform)
    train_sampler = DistributedSampler(train_dataset, num_replicas=world_size, rank=rank, shuffle=True)
    train_loader = DataLoader(train_dataset, batch_size=batch_size, sampler=train_sampler, num_workers=4, pin_memory=True)
    val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=4, pin_memory=True)
    model = SimpleCNN().to(device)
    model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[local_rank])
    criterion = nn.CrossEntropyLoss().to(device)
    optimizer = optim.SGD(model.parameters(), lr=learning_rate)
    if rank == 0:
        writer = SummaryWriter('/mnt/data/output_distributed/runs/mnist_experiment')
    best_val_accuracy = 0.0
    for epoch in range(num_epochs):
        train_sampler.set_epoch(epoch)
        model.train()
        for batch_idx, (data, target) in enumerate(train_loader):
            data, target = data.to(device, non_blocking=True), target.to(device, non_blocking=True)
            optimizer.zero_grad()
            output = model(data)
            loss = criterion(output, target)
            loss.backward()
            optimizer.step()
            if batch_idx % 100 == 0:
                # Each rank and local_rank prints its own loss.
                print(f"Rank: {rank}, Local_Rank: {local_rank} -- Train Epoch: {epoch} "
                      f"[{batch_idx * len(data) * world_size}/{len(train_loader.dataset)} "
                      f"({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}")
                if rank == 0:
                    writer.add_scalar('Loss/train', loss.item(), epoch * len(train_loader) + batch_idx)
        # Validation
        model.eval()
        val_loss = 0
        correct = 0
        total = 0
        with torch.no_grad():
            for data, target in val_loader:
                data, target = data.to(device, non_blocking=True), target.to(device, non_blocking=True)
                output = model(data)
                val_loss += criterion(output, target).item() * data.size(0)
                pred = output.argmax(dim=1, keepdim=True)
                correct += pred.eq(target.view_as(pred)).sum().item()
                total += target.size(0)
        val_loss_tensor = torch.tensor([val_loss], dtype=torch.float32, device=device)
        correct_tensor = torch.tensor([correct], dtype=torch.float32, device=device)
        total_tensor = torch.tensor([total], dtype=torch.float32, device=device)
        dist.all_reduce(val_loss_tensor, op=dist.ReduceOp.SUM)
        dist.all_reduce(correct_tensor, op=dist.ReduceOp.SUM)
        dist.all_reduce(total_tensor, op=dist.ReduceOp.SUM)
        val_loss = val_loss_tensor.item() / total_tensor.item()
        val_accuracy = 100. * correct_tensor.item() / total_tensor.item()
        if rank == 0:
            print(f'Validation Loss: {val_loss:.4f}, Accuracy: {int(correct_tensor.item())}/{int(total_tensor.item())} ({val_accuracy:.0f}%)')
            writer.add_scalar('Loss/validation', val_loss, epoch)
            writer.add_scalar('Accuracy/validation', val_accuracy, epoch)
            if val_accuracy > best_val_accuracy:
                best_val_accuracy = val_accuracy
                torch.save(model.module.state_dict(), '/mnt/data/output_distributed/best_model.pth')
                print(f'Model saved with accuracy: {best_val_accuracy:.2f}%')
    if rank == 0:
        writer.close()
    dist.destroy_process_group()
    if rank == 0:
        print('Training complete. writer.close()')
if __name__ == "__main__":
    main()

4,2 Criar um job distribuído do DLC

  1. No painel de navegação à esquerda do PAI console, escolha Deep Learning Containers (DLC) > Create Job.

  2. Configure os seguintes parâmetros principais e mantenha os valores padrão para os demais. Para a lista completa de parâmetros, consulte Create a training job.

    • Image Configuration: Selecione Image Address e insira o endereço de imagem apropriado para sua Region.

      Region

      Image address

      China (Beijing)

      dsw-registry-vpc.cn-beijing.cr.aliyuncs.com/pai/modelscope:1.28.0-pytorch2.3.1tensorflow2.16.1-gpu-py311-cu121-ubuntu22,04

      China (Shanghai)

      dsw-registry-vpc.cn-shanghai.cr.aliyuncs.com/pai/modelscope:1.28.0-pytorch2.3.1tensorflow2.16.1-gpu-py311-cu121-ubuntu22,04

      China (Hangzhou)

      dsw-registry-vpc.cn-hangzhou.cr.aliyuncs.com/pai/modelscope:1.28.0-pytorch2.3.1tensorflow2.16.1-gpu-py311-cu121-ubuntu22,04

      Other

      Find your region ID e substitua <Region ID> no endereço da imagem para obter a URL completa:

      dsw-registry-vpc.<Region ID>.cr.aliyuncs.com/pai/modelscope:1.28.0-pytorch2.3.1tensorflow2.16.1-gpu-py311-cu121-ubuntu22,04

      Esta imagem usa o mesmo ambiente verificado do tópico Get Started with DSW Interactive Modeling . Ao criar modelos com o PAI, um fluxo de trabalho típico consiste em verificar primeiro o ambiente e desenvolver o código no DSW, usando depois o DLC para treinamento.
    • Dataset Mount: Selecione Custom Dataset e escolha o dataset criado. O Mount Path padrão é /mnt/data.

    • Startup Command: torchrun --nproc_per_node=1 --nnodes=${WORLD_SIZE} --node_rank=${RANK} --master_addr=${MASTER_ADDR} --master_port=${MASTER_PORT} /mnt/data/mnist_train_distributed.py

      O DLC injeta automaticamente variáveis de ambiente comuns do environment variables , como MASTER_ADDR e WORLD_SIZE , acessíveis pela sintaxe $VARIABLE_NAME .
    • Source: Selecione Public Resources, defina o Number of Nodes como 2 e escolha ecs.gn7i-c8g1.2xlarge para o Resource Type.

      Se este tipo de instância estiver indisponível, escolha outro tipo de instância com GPU.

    Clique em Confirm para criar o job. A execução leva cerca de 10 minutos. Durante o processo, visualize o Log de treinamento de ambas as instâncias na página Overview.

    Após a conclusão do job, o DLC salva o melhor checkpoint do modelo e o log do TensorBoard no caminho output_distributed do dataset montado.

4,3 (Opcional) Visualizar no TensorBoard

Use a ferramenta de visualização TensorBoard para ver a curva de perda e compreender melhor o processo de treinamento.

Importante

Para usar o TensorBoard em um job do DLC, configure um dataset.

  1. Na página de detalhes do job do DLC, clique na aba TensorBoard e depois em Create TensorBoard.

  2. Defina o Configuration Type como By Task. No campo Summary Path, insira o caminho onde o código de treinamento salva os arquivos de resumo: /mnt/data/output_distributed/runs. Clique em OK para iniciar.

    Isso corresponde ao trecho de código: writer = SummaryWriter('/mnt/data/output_distributed/runs/mnist_experiment')
  3. Clique em View TensorBoard para visualizar a curva train_loss (perda do conjunto de treinamento) e a curva validation_loss (perda do conjunto de validação).

    image

    (Opcional) Ajustar hiperparâmetros para melhorar o desempenho do modelo

    Avalie o desempenho do seu modelo observando as tendências nos valores de perda:

    • Antes do término do treinamento, os valores de train_loss e validation_loss ainda estão diminuindo (underfitting).

      Aumente o num_epochs (número de épocas de treinamento) ou eleve ligeiramente a learning_rate e retreine o modelo para ajustar melhor os dados de treinamento.

    • O train_loss diminui continuamente, enquanto o validation_loss começa a aumentar antes do fim do treinamento (overfitting).

      Reduza o num_epochs ou diminua levemente a learning_rate e retreine o modelo para evitar overfitting.

    • Tanto o train_loss quanto o validation_loss permanecem estáveis antes da conclusão do treinamento (bom ajuste).

      Quando o modelo estiver nesse estado, prossiga para as próximas etapas.

4,4 Implantar o modelo treinado

Para mais informações, consulte V. Deploying model services using EAS.

5. Informações relacionadas