
# 使用SDK调测单机训练作业
代码中涉及到的OBS路径，请用户替换为自己的实际OBS路径。
代码是以PyTorch为例编写的，不同的AI框架之间，整体流程是完全相同的，仅需修改[6]和[10]中的framework_type参数值即可，例如：MindSpore框架，此处framework_type=Ascend-Powered-Engine。
1. Session初始化。
   
   代码如下：这里只列出最常用的一种方式，更多方式请参考《[Session鉴权章节](https://support.huaweicloud.com/sdkreference-modelarts/modelarts_04_0123.html)》
   ```
   from modelarts.session import Session 
   session = Session()
   ```
   
   

2. 准备训练数据，这里支持三种形式，用户可根据自己的情况选择一种。
   
   ```
   import os
   from modelarts.train_params import InputData
   base_bucket_path = "obs://modelarts-xxx-a0de02a6/dis-train/cifar10/"
   base_local_path = "/home/ma-user/work/cifar10/"
   # 形式1，数据在OBS上，且是一个压缩文件
   obs_path = os.path.join(base_bucket_path, "dataset-zip/dataset.zip")
   data_local = os.path.join(base_local_path, "dataset/") 
   input_data = InputData(obs_path=obs_path, local_path=data_local, is_local_source=False)
   # 形式2，数据在OBS上，且是一个目录
   #obs_path = os.path.join(base_bucket_path, "dataset/")
   #data_local = os.path.join(base_local_path, "dataset/") 
   #input_data = InputData(obs_path=obs_path, local_path=data_local, is_local_source=False)
   # 形式3，数据在Notebook中，且是一个目录，一般是使用SFS挂载磁盘的场景
   #obs_path = os.path.join(base_bucket_path, "dataset-local/")
   #data_local = os.path.join(base_local_path, "dataset/") 
   #input_data = InputData(obs_path=obs_path, local_path=data_local, is_local_source=True)
   ```
   参数解释：
   - is_local_source：可选参数，默认为False，指定训练数据的保存位置。
     - False：训练数据保存在参数obs_path指定的位置中；
     
     - True：训练数据保存在Notebook中，由local_path指定。
      
   
   - obs_path：obs地址。根据is_local_source值的变化，有不同的含义。
     - is_local_source=False，此时是必选参数，代表训练数据位置，支持文件夹和压缩文件。
     
     - is_local_source=True，此时是可选参数。如果用户填写了该参数，则开始训练时会将Notebook中的训练数据压缩并上传到该位置，不可重复上传。如果第一次上传后，建议将is_local_source修改为False，obs_path指向刚才上传的压缩数据文件位置；如果用户没有填写，则不会进行压缩上传。
      
   
   - local_path：必选参数，Notebook中的路径。用户的训练脚本需要从该目录中读取数据，完成训练任务。根据is_local_source值的变化，有不同的含义。
     - is_local_source=True，此时代表训练数据位置，仅支持文件夹。
     
     - is_local_source=False，训练过程中SDK会帮助用户将数据下载到该位置，如果训练数据是压缩文件，下载完成后会进行解压缩。
      
   
   
   
   

3. 准备训练脚本。
   
   ```
   from modelarts.train_params import TrainingFiles
   code_dir = os.path.join(base_local_path, "train/") 
   # 这里提前将训练脚本放在了obs中，实际上训练脚本可以是任何来源，只要能够放到Notebook里边就行
   session.obs.download_file(os.path.join(base_bucket_path, "train/test-pytorch.py"), code_dir)
   training_file = TrainingFiles(code_dir=code_dir, boot_file="test-pytorch.py", obs_path=base_bucket_path + 'train/')
   ```
   参数解释：
   - code_dir：必选参数，训练脚本所在的目录。在训练任务调测的情况下，必须是Notebook中的目录，不能是OBS目录。
   
   - boot_file：必选参数，训练启动文件路径，路径格式为基于code_dir目录的相对路径，如实例代码中boot_file的完整路径为/home/ma-user/work/cifar10/train/test-pytorch.py，这里就只需要填写test-pytorch.py。
   
   - obs_path：可选参数，一个OBS目录。仅在本地单机调试时不需要该参数，提交远程训练时必选，会将训练脚本压缩并上传到该路径。
   
   
   
   

4. 准备训练输出，如果用户不需要将训练输出上传到OBS，可以省略这一步。
   
   ```
   from modelarts.train_params import OutputData
   output = OutputData(local_path=os.path.join(base_local_path, "output/"), obs_path=os.path.join(base_bucket_path, 'output/'))
   ```
   - local_path：必选参数，一个Notebook中的路径，训练脚本需要将输出的模型或其他数据保存在该目录下。
   
   - obs_path：必选参数，一个OBS目录。SDK会将local_path中的模型文件自动上传到这里。
   
   
   
   

5. 查看训练支持的AI框架。 
   ```
   from modelarts.estimatorV2 import Estimator
   Estimator.get_framework_list(session)
   ```
   参数session即是第一步初始化的数据。如果用户知道要使用的AI框架，可以略过这一步。
   
   

6. Estimator初始化。
   
   ```
   from modelarts.estimatorV2 import Estimator
   parameters = []
   parameters.append({"name": "data_url", "value": data_local})
   parameters.append({"name": "output_dir", "value": os.path.join(base_local_path, "output/")}) 
   parameters.append({"name": "epoc_num", "value": 2}) 
   estimator = Estimator(session=session,
                         training_files=training_file,
                         outputs=[output],
                         parameters=parameters,
                         framework_type='PyTorch',  
                         train_instance_type='local', 
                         train_instance_count=1,
                         script_interpreter="/home/ma-user/anaconda3/envs/PyTorch-1.4/bin/python",
                         log_url=base_bucket_path + 'log/',
                         job_description='This is a image net train job')
   ```
   参数解释：
   - session：必选参数，[1]中初始化的参数。
   
   - training_files：必选参数，[3]中初始化的训练文件。
   
   - outputs：可选参数，这里传入的是一个list，每个元素都是[4]中初始化的训练输出。
   
   - parameters：可选参数，一个list，每个元素都是一个字典，包含"name"和"value"两个字段，以"--name=value"的形式传递给训练启动文件。value支持字符串，整数，布尔等类型。对于布尔类型，建议用户在训练脚本中使用action='store_true'的形式来解析。
   
   - framework_type：必选参数，训练作业使用的AI框架类型，可参考步骤5查询的返回结果。
   
   - train_instance_type：必选参数，训练实例类型，这里指定'local'即为在Notebook中进行训练。
   
   - train_instance_count：必选参数，训练使用的worker个数，单机训练时为1，训练作业只在当前使用的Notebook中运行。
   
   - script_interpreter：可选参数，指定使用哪个python环境来执行训练任务，如果未指定，会默认使用当前的kernel。
   
   - log_url：可选参数，一个OBS地址，训练过程中，SDK会自动将训练的日志上传到该位置。但是如果训练任务运行在Ascend上，则是必选参数。
   
   - job_description：可选参数，训练任务的描述。
   
   
   
   

7. 开始训练。 
   ```
   estimator.fit(inputs=[input_data], job_name="cifar10-dis")
   ```
   参数解释：
   - inputs：可选参数，一个list，每个元素都是[2]生成的实例。
   
   - job_name：可选参数，训练任务名，便于区分和记忆。
   
   
   本地单机调试训练任务开始后，SDK会依次帮助用户完成以下流程：
   1. 初始化训练作业，如果[2]指定的训练数据在OBS上，这里会将数据下载到local_path中。
   
   2. 执行训练任务，用户的训练代码需要将训练输出保存在[4]中指定的local_path中。
   
   3. 将训练任务得到的输出上传到[4]指定的obs_path中，日志上传到第六步指定的log_url中。
    
   同时，可以在任务名后增加时间后缀，区分不同的任务名称。
   ```
   from datetime import datetime, timedelta
   import time
   base_name = "cifar10-dis"
   job_name = base_name + '-' + (datetime.now() + timedelta(hours=8)).strftime('%Y%m%d-%H%M%S')
   estimator.fit(inputs=[input_data], job_name=job_name)
   ```
   
   
   

8. 多次调试。
   
   上一步执行过程中，训练脚本的日志会实时打印到控制台，如果用户的代码或者参数有误的话，可以很方便地看到。在Notebook中经过多次调试，得到想要的结果后，可以进行下一步。
   
   

9. 查询训练支持的计算节点类型和最大个数。
   
   ```
   from modelarts.estimatorV2 import Estimator 
   Estimator.get_spec_list(session=session)
   ```
   参数session即是[1]初始化的数据。返回的是一个字典，其中flavors值是一个列表，描述了训练服务支持的所有规格的信息。每个元素中flavor_id是可直接用于远程训练任务的计算规格，max_num是该规格的最大节点数。如果用户知道要使用的计算规格，可以略过这一步。
   
   

10. 提交远程训练作业。
    
    ```
    from modelarts.estimatorV2 import Estimator
    parameters = []
    parameters.append({"name": "data_url", "value": data_local})
    parameters.append({"name": "output_dir", "value": os.path.join(base_local_path, "output/")}) 
    parameters.append({"name": "epoch_num", "value": 2}) 
    estimator = Estimator(session=session,
                          training_files=training_file,
                          outputs=[output],
                          parameters=parameters,
                          framework_type='PyTorch',  
                          train_instance_type='modelarts.vm.cpu.8u', 
                          train_instance_count=1,
                          script_interpreter="/home/ma-user/anaconda3/envs/PyTorch-1.4/bin/python",
                          log_url=base_bucket_path + 'log/',
                          job_description='This is a image net train job')
    estimator.fit(inputs=[input_data], job_name="cifar10-dis")
    ```
    在本地调测完成的基础上，只需要Estimator初始化时将参数train_instance_type修改为训练服务支持的规格即可（即第10步查询出来的flavor_id的值）。执行fit函数后，即可提交远程训练任务。
    训练任务提交后，SDK会依次帮助用户完成以下流程：
    1. 将训练脚本打包成zip文件，上传到[3]中指定的obs_path中。
    
    2. 当训练数据保存在Notebook中，则将其打包成zip文件并上传到指定的obs_path中。
    
    3. 向ModelArts训练服务提交自定义镜像训练作业，使用的镜像为当前Notebook的镜像，这样保证了远程训练作业和在Notebook中的训练作业使用的运行环境一致。
    
    4. 训练任务得到的输出上传到[4]指定的obs_path中，日志上传到这一步log_url指定的位置中。
       在这一步中需要注意的一个问题：
       如果用户在自己的训练脚本中要创建新的目录或文件，请在以下几种目录中创建：
       - /home/ma-user/work；
       
       - /cache；
       
       - inputs或者outputs中指定的local_path，如在步骤2中初始化InputData时，填写了local_path="/home/ma-user/work/xx/yy/"，则在该目录下也可以创建新目录或文件。
        
    
    
    
    
 
