快速提交PAI-DLC训练任务

更新时间: 2024-04-12 18:01:35

learn

手动配置

40

教程简介

在本教程中,您将学习如何使用阿里云机器学习平台PAI控制台提交分布式训练(DLC)任务。

PAI-DLC(Deep Learning Containers)是基于云原生容器服务的深度学习训练平台,为您提供灵活、稳定、易用和较优性能的深度学习训练环境。PAI平台同时还提供了完善的SDK和OpenAPI,实现了基于代码提交任务,使得PAI-DLC更灵活地应用到您的日常生产中。

我能学到什么

学会使用控制台界面提交DLC训练任务的流程。

操作难度

所需时间

40分钟

使用的阿里云产品

容器训练(DLC)

所需费用

阿里云免费试用在华北2(北京)、华东2(上海)、华东1(杭州)、华南1(深圳)为您提供了一定数量的DLC免费资源包供您使用,超出免费额度后将收取费用。DLC的计费详情请参见通用计算资源(DSW/DLC)计费说明

【重要】PAI-DLC免费资源包只适用于本教程中的PAI-DLC产品。如果您领取了PAI-DLC资源包后,使用了PAI-DLC及PAI的其他产品功能(如PAI-DSW、PAI-EAS等),PAI-DLC产品产生的费用由资源包抵扣,其他产品功能产生的费用无法抵扣,会产生对应的费用账单。

准备环境及资源

10

开始教程前,请按以下步骤准备环境和资源:

【重要】PAI-DLC免费资源包只适用于本教程中的PAI-DLC产品。如果您领取了PAI-DLC资源包后,使用了PAI-DLC及PAI的其他产品功能(如PAI-DSW、PAI-EAS等),PAI-DLC产品产生的费用由资源包抵扣,其他产品功能产生的费用无法抵扣,会产生对应的费用账单。

  1. 访问阿里云免费试用。单击页面右上方的登录/注册按钮,并根据页面提示完成账号登录(已有阿里云账号)、账号注册(尚无阿里云账号)或实名认证(根据试用产品要求完成个人实名认证或企业实名认证)。

  2. 申请免费试用OSS,并创建Bucket。

    【说明】本教程以使用OSS创建数据集来存储代码文件为例,来说明如何提交训练任务。

    1. 领取对象存储OSS资源抵扣包。

      进入OSS免费资源包申请页面,单击立即试用,在确认并了解相关信息后,根据页面提示申请试用。

      【注意】:如果您的对象存储OSS资源抵扣包已使用完毕或无领取资格,开通PAI试用后,将正常收取费用,计费详情请参见OSS按量付费

    2. 创建OSS Bucket。

      登录OSS控制台,在Bucket列表页面单击创建Bucket开始创建Bucket。本教程的核心配置参数如下,其他参数可保持默认值,详细创建步骤请参见创建有地域属性Bucket

      • 地域:选择有地域属性华南1(深圳)地域。

      • 其他参数:保持默认值即可。

  3. 申请开通免费试用PAI。

    1. 进入模型训练PAI-DLC免费资源包申请页面,并在模型训练PAI-DLC卡片上单击立即试用

      【说明】:如果您此前已申请过试用PAI的免费资源包,此时界面会提示为已试用,您可以直接单击已试用按钮,进入PAI的控制台。

    2. 模型训练PAI-DLC面板,勾选服务协议后,单击立即试用,进入免费开通页面。

    3. 开通机器学习PAI并创建默认工作空间。其中关键参数配置如下,更多详情内容,请参见开通并创建默认工作空间。如果您后续使用RAM用户来提交DLC训练任务,您需要将RAM用户添加为默认工作空间的成员,并配置管理员角色,详情请参见管理工作空间成员

      • 本教程选择的地域为:华南1(深圳)

      • 单击免费开通并创建默认工作空间:在弹出的开通页面中配置订单详情。配置要点如下。

        • 本教程不需要开通除OSS产品外的其他产品,您需要在组合开通配置模块,去勾选其他产品的复选框。

        • 服务角色授权模块单击去授权,根据界面提示为PAI完成授权,然后返回开通页面,刷新页面,继续开通操作。

      328913ece355816e8a05c85286b30653.png

    4. 开通成功后,单击进入PAI控制台

    【重要】以下几种情况可能产生额外费用

    • 使用了除免费资源类型外的计费资源类型:

      您申请试用的是PAI-DLC免费资源包,但您提交DLC训练任务使用的资源类型非阿里云免费试用提供的资源类型。当前可申请免费使用的资源类型有:ecs.gn6v-c8g1.2xlarge、ecs.g6.xlarge、ecs.gn7i-c8g1.2xlarge。

    • 申请试用的免费资源包与使用的产品资源不对应:

      • 您提交了DLC训练任务,但您申请试用的是PAI-DSW或PAI-EAS产品的免费资源包。您使用DLC产品产生的费用无法使用免费资源包抵扣,会产生后付费账单。

      • 您申请试用的是PAI-DLC免费资源包,但您使用的产品是PAI-DSW或PAI-EAS。使用PAI-DSW和PAI-EAS产品产生的费用无法使用DLC免费资源包抵扣,会产生后付费账单。

    • 免费额度用尽或超出试用期:

      领取免费资源包后,请在免费额度和有效试用期内使用。如果免费额度用尽或试用期结束后,若继续使用计算资源,将会产生后付费账单。

      • 如果您使用的是DLCDSW产品,请前往资源实例管理页面,查看免费额度使用量和过期时间,如下图所示。image

      • 如果您使用的是EAS产品,请前往节省计划页面,查看抵扣包剩余金额和过期时间,如下图所示。image..png

准备训练脚本

5

  1. 在本地创建torch_ddp_sample_code.py(Python)代码文件,文件内容如下。

    import datetime
    import logging
    import os
    import argparse
    
    from math import ceil
    from random import Random
    
    import torch
    import torch.distributed as dist
    import torch.nn as nn 
    import torch.nn.functional as F
    import torch.optim as optim 
    import torch.utils.data
    import torch.utils.data.distributed
    from torch._utils import _flatten_dense_tensors, _unflatten_dense_tensors
    from torch.autograd import Variable
    from torch.nn.modules import Module
    from torchvision import datasets, transforms
    
    gbatch_size = 128
    epochs = 10
    world_size = os.environ.get("WORLD_SIZE", "{}")
    rank = os.environ.get("RANK", "{}")
    
    class DistributedDataParallel(Module):
      def __init__(self, module):
        super(DistributedDataParallel, self).__init__()
        self.module = module
        self.first_call = True
    
        def allreduce_params():
          if self.needs_reduction:
            self.needs_reduction = False 
            buckets = {}
            for param in self.module.parameters():
              if param.requires_grad and param.grad is not None:
                tp = type(param.data)
                if tp not in buckets:
                  buckets[tp] = []
                buckets[tp].append(param)
            for tp in buckets:
              bucket = buckets[tp]
              grads = [param.grad.data for param in bucket]
              coalesced = _flatten_dense_tensors(grads)
              dist.all_reduce(coalesced)
              coalesced /= dist.get_world_size()
              
              for buf, synced in zip(grads, _unflatten_dense_tensors(coalesced, grads)):
                buf.copy_(synced)
    
        for param in list(self.module.parameters()):
          def allreduce_hook(*unused): 
            Variable._execution_engine.queue_callback(allreduce_params)  
    
          if param.requires_grad:
            param.register_hook(allreduce_hook)
    
      def weight_broadcast(self):
        for param in self.module.parameters():
          dist.broadcast(param.data, 0)
    
      def forward(self, *inputs, **kwargs):  
        if self.first_call:
          logging.info("first broadcast start")
          self.weight_broadcast()
          self.first_call = False
          logging.info("first broadcast done")
        self.needs_reduction = True  
        return self.module(*inputs, **kwargs)
    
    class Net(nn.Module):
      def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.conv2_drop = nn.Dropout2d()
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)
    
      def forward(self, x): 
        x = F.relu(F.max_pool2d(self.conv1(x), 2))
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
        x = x.view(-1, 320)
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        return F.log_softmax(x)
    
    
    def partition_dataset(rank):
      dataset = datasets.MNIST(
        './data{}'.format(rank),
        train=True,
        download=True,
        transform=transforms.Compose([
          transforms.ToTensor(),
          transforms.Normalize((0.1307,), (0.3081,))
        ]))
      size = dist.get_world_size()
      bsz = int(gbatch_size / float(size))
      train_sampler = torch.utils.data.distributed.DistributedSampler(dataset)
      train_set = torch.utils.data.DataLoader(
        dataset, batch_size=bsz, shuffle=(train_sampler is None), sampler=train_sampler)
      return train_set, bsz
    
    
    def average_gradients(model):
      size = float(dist.get_world_size())
      group = dist.new_group([0])
      for param in model.parameters():
        dist.all_reduce(param.grad.data, op=dist.ReduceOp.SUM, group=group)
        
        param.grad.data /= size
    
    
    def run(gpu):
      rank = dist.get_rank()
      torch.manual_seed(1234)
      train_set, bsz = partition_dataset(rank)
      model = Net()
      if gpu:
        model = model.cuda()
        model = torch.nn.parallel.DistributedDataParallel(model)
      else:
        model = DistributedDataParallel(model)
      optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
    
      num_batches = ceil(len(train_set.dataset) / float(bsz))
      logging.info("num_batches = %s", num_batches)
      time_start = datetime.datetime.now()
      for epoch in range(epochs):
        epoch_loss = 0.0
        for data, target in train_set:
          if gpu:
            data, target = Variable(data).cuda(), Variable(target).cuda()
          else:
            data, target = Variable(data), Variable(target)
          optimizer.zero_grad()
          output = model(data)
          loss = F.nll_loss(output, target)
          epoch_loss += loss.item()
          loss.backward()
          average_gradients(model)
          optimizer.step()
        logging.info('Epoch {} Loss {:.6f} Global batch size {} on {} ranks'.format(
          epoch, epoch_loss / num_batches, gbatch_size, dist.get_world_size()))
      if gpu:
        logging.info("GPU training time= {}".format(  
          str(datetime.datetime.now() - time_start)))  
      else:
        logging.info("CPU training time= {}".format(  
          str(datetime.datetime.now() - time_start))) 
    
    
    if __name__ == "__main__":
      logging.basicConfig(level=logging.INFO,
                          format=('%(levelname)s|%(asctime)s'
                                  '|%(pathname)s|%(lineno)d| %(message)s'),
                          datefmt='%Y-%m-%dT%H:%M:%S',
                          )
      logging.getLogger().setLevel(logging.INFO)
    
      parser = argparse.ArgumentParser(description='Train Pytorch model using DDP')
      parser.add_argument('--gpu', action='store_true',
                          help='Use GPU and CUDA')
      parser.set_defaults(gpu=False)
      args = parser.parse_args()
      if args.gpu:
        logging.info("\n======= CUDA INFO =======")
        logging.info("CUDA Availability: %s", torch.cuda.is_available())
        if torch.cuda.is_available():
          logging.info("CUDA Device Name: %s", torch.cuda.get_device_name(0))
          logging.info("CUDA Version: %s", torch.version.cuda)
        logging.info("=========================\n")
      dist.init_process_group(backend='gloo', init_method='env://',world_size=int(world_size),rank=int(rank))
      run(gpu=False)
      dist.destroy_process_group()
  2. 支持使用以下两种方式保存代码数据,供后续模型训练使用。本教程以创建数据集为例。

  3. 创建数据集

    创建阿里云对象存储(OSS)类型数据集,其中关键参数配置如下,其他参数取默认配置即可。更多详细内容,请参见创建及管理数据集

    参数

    描述

    数据集名称

    自定义数据集名称,本教程配置为:dataset-oss。

    属性

    选择文件夹

    从阿里云云存储创建

    按照以下操作步骤选择代码文件所在的Bucket路径。

    1. 单击image,选择已创建的Bucket。

    2. 单击新建目录,输入目录名称,单击确定

    3. 进入新创建的目录中,单击上传文件,按照界面操作指引上传步骤1中在本地创建的torch_ddp_sample_code.py代码文件。

    4. 选中步骤b创建的目录,单击确定

    默认挂载路径

    保持默认配置:/mnt/data/

    创建代码集

    1. 将步骤1创建的代码文件保存到您的Git地址。

      【说明】您可以将代码文件保存到您自己的Git地址;您也可以使用阿里云云效基础版保存代码文件,该版本免费使用,但有代码库容量限制,如果您使用云效其他版本,会收取费用,具体计费详情请参见计费说明

    2. 创建代码集,其中关键参数配置如下,其他参数取默认配置即可。更多详细内容,请参见代码配置

      参数

      描述

      名称

      代码集名称。

      Git地址

      您在步骤a中配置的Git地址。

提交训练任务

15

  1. 进入新建任务页面。

    1. 登录PAI控制台

    2. 在页面左上方,选择工作空间所在地域。本教程选择:华南1(深圳)。

    3. 在左侧导航栏单击工作空间列表,在工作空间列表页面中单击默认工作空间名称,进入对应工作空间内。

    4. 在工作空间页面的左侧导航栏选择模型开发与训练>分布式训练(DLC),在分布式训练(DLC)页面,单击新建任务

  2. 配置训练任务的参数。

    1. 基本信息区域配置以下关键参数,其他参数取默认配置即可,更多详细内容,请参见创建训练任务

      参数

      描述

      任务名称

      自定义任务名称,本教程配置为:doc_test_for_dlc

      节点镜像

      目前PAI-DLC支持使用PAI官方镜像用户自定义镜像

      本教程选择PAI官方镜像pytorch-training:1.8PAI-gpu-py36-cu101-ubuntu18.04

      数据集配置

      单击添加按钮,配置类型选择按数据集

      • 数据集:选择已创建的数据集dataset-oss

      • 挂载路径:配置为/mnt/data/

      代码配置

      本教程无需配置该参数。

      【说明】如果您将代码文件保存为代码集,需要配置该参数。

      执行命令

      本教程配置为:python /mnt/data/torch_ddp_sample_code.py

      【说明】如果您将代码文件保存为代码集,需要根据实际情况来配置执行命令。

    2. 资源配置区域,配置以下关键参数。

      参数

      描述

      资源配额

      本教程选择:公共资源(后付费)

      框架

      本教程选择:PyTorch

      任务资源

      单击image,配置资源规格。本教程选择FreeTier > ecs.gn6v-c8g1.2xlarge。阿里云免费试用提供的节点规格包括以下几种规格:

      • ecs.gn7i-c8g1.2xlarge

      • ecs.g6.xlarge

      • ecs.gn6v-c8g1.2xlarge

  3. 单击确定,大约需要持续10分钟,任务运行成功。

完成

5

完成以上操作后,您的DLC训练任务已提交成功。您可以在分布式训练(DLC)页面查看已提交的训练任务。

image

清理及后续

5

清理

  • 领取免费资源包后,请在免费额度和有效试用期内使用。如果免费额度用尽或试用期结束后,继续使用计算资源,会产生后付费账单。

    您可前往资源实例管理页面,查看免费额度使用量和过期时间。

  • 如果无需继续使用DLC训练任务,您可以删除DLC任务。

    分布式训练(DLC)页面,当目标任务状态已成功已停止已失败,您可以单击操作列下的删除,来删除目标任务。当目标任务状态为其他状态时,您可以先单击操作列下的停止,再单击操作列下的删除,即可删除任务。image.png

  • 对象存储OSS资源清理

    删除对象存储空间,详情请参见删除存储空间

后续

在试用有效期期间,您还可自行提交DLC训练任务进行模型训练。

总结

常用知识点

问题1:新建任务页面,本教程必须完成的配置有哪些?(单选题)

  • 任务名称&节点镜像&任务类型&执行命令&数据集配置

  • 代码配置

  • 最长运行时长

正确答案是任务名称&节点镜像&任务类型&执行命令&数据集配置。

问题2:提交DLC训练任务后,可以到哪个页面查看已提交的训练任务?(单选题)

  • 分布式训练(DLC)

  • 镜像

  • 数据集

正确答案是分布式训练(DLC)。

延伸阅读