
# 创建Workflow模型注册节点
#### 功能介绍
通过对ModelArts模型管理的能力进行封装，实现将训练后的结果注册到模型管理中，便于后续服务部署、更新等步骤的执行。主要应用场景如下：
- 注册ModelArts训练作业中训练完成的模型。
- 注册自定义镜像中的模型。
 
#### 属性总览
您可以使用**ModelStep** 来构建模型注册节点，**ModelStep**结构如下：
表1**ModelStep** 
| 属性           | 描述                                                                                 | 是否必填 | 数据类型                        |
|:---|:---|:---|:---|
| name         | 模型注册节点的名称。只能包含英文字母、数字、下划线（_）、中划线（-），并且只能以英文字母开头，长度限制为64字符，一个Workflow里的两个step名称不能重复 | 是    | str                         |
| inputs       | 模型注册节点的输入列表                                                                        | 否    | ModelInput或者ModelInput的列表   |
| outputs      | 模型注册节点的输出列表                                                                        | 是    | ModelOutput或者ModelOutput的列表 |
| title        | title信息，主要用于前端的名称展示                                                                | 否    | str                         |
| description  | 模型注册节点的描述信息                                                                        | 否    | str                         |
| policy       | 节点执行的policy                                                                        | 否    | StepPolicy                  |
| depend_steps | 依赖的节点列表                                                                            | 否    | Step或者Step的列表               |
   
表2**ModelInput** 
| 属性   | 描述                                                                           | 是否必填 | 数据类型                                                                                                                          |
|:---|:---|:---|:---|
| name | 模型注册节点的输入名称，只能包含英文字母、数字、下划线（_）、中划线（-），并且只能以英文字母开头，长度限制为64字符。同一个Step的输入名称不能重复 | 是    | str                                                                                                                           |
| data | 模型注册节点的输入数据对象                                                                | 是    | OBS、SWR或订阅模型相关对象，当前仅支持OBSPath、SWRImage、OBSConsumption、OBSPlaceholder、SWRImagePlaceholder、DataConsumptionSelector、GalleryModel |
   
表3**ModelOutput** 
| 属性           | 描述                                                                           | 是否必填 | 数据类型        |
|:---|:---|:---|:---|
| name         | 模型注册节点的输出名称，只能包含英文字母、数字、下划线（_）、中划线（-），并且只能以英文字母开头，长度限制为64字符。同一个Step的输出名称不能重复 | 是    | str         |
| model_config | 模型注册相关配置信息                                                                   | 是    | ModelConfig |
   
表4**ModelConfig** 
| 属性                | 描述                                                                                                                                                                               | 是否必填 | 数据类型             |
|:---|:---|:---|:---|
| model_type        | 模型的类型，支持的格式有（"TensorFlow", "MXNet", "Caffe", "Spark_MLlib", "Scikit_Learn", "XGBoost", "Image", "PyTorch", "Template"，"Custom"）默认为TensorFlow。                                    | 是    | str              |
| model_name        | 模型的名称，支持1-64位可见字符（含中文），名称可以包含字母、中文、数字、中划线、下划线。                                                                                                                                   | 否    | str、Placeholder  |
| model_version     | 模型的版本，格式需为"数值.数值.数值"，其中数值为1-2位正整数。该字段不填时，版本号自动增加。 注意： 版本不可以出现例如01.01.01等以0开头的版本号形式。 | 否    | str、Placeholder  |
| runtime           | 模型运行时环境，runtime可选值与model_type相同。                                                                                                                                                 | 否    | str、Placeholder  |
| description       | 模型备注信息，1-100位长度，不能包含\&!'"\<\>=                                                                                                                                                   | 否    | str              |
| execution_code    | 执行代码存放的OBS地址，默认值为空，名称固定为"customize_service.py"。 推理代码文件需存放在模型"model"目录。该字段不需要填，系统也能自动识别出model目录下的推理代码。                             | 否    | str              |
| dependencies      | 推理代码及模型需安装的包，默认为空。从配置文件读取。                                                                                                                                                       | 否    | str              |
| model_metrics     | 模型精度信息，从配置文件读取。                                                                                                                                                                  | 否    | str              |
| apis              | 模型所有的apis入参出参信息（选填），从配置文件中解析出来。                                                                                                                                                  | 否    | str              |
| initial_config    | 模型配置相关数据。                                                                                                                                                                        | 否    | dict             |
| template          | 模板的相关配置项，使用模板导入模型（即model_type为Template）时必选                                                                                                                                       | 否    | Template         |
| dynamic_load_mode | 动态加载模式，当前仅支持"Single"                                                                                                                                                             | 否    | str、Placeholder  |
| prebuild          | 模型是否提前构建，默认为False                                                                                                                                                                | 否    | bool、Placeholder |
| install_type      | 模型的安装类型，支持"real_time", "edge", "batch"，该字段不填时默认均支持                                                                                                                               | 否    | list\[str\]      |
   
表5**Template** 
| 属性              | 描述                         | 是否必填 | 数据类型                          |
|:---|:---|:---|:---|
| template_id     | 所使用的模板ID，模板中会内置一个输入输出模式    | 是    | str、Placeholder               |
| infer_format    | 输入输出模式ID，提供时覆盖模板中的内置输入输出模式 | 否    | str、Placeholder               |
| template_inputs | 模板输入项配置，即配置模型的源路径          | 是    | list of TemplateInputs object |
   
表6**Template** **Inputs** 
| 属性       | 描述                                                                                                     | 是否必填 | 数据类型                    |
|:---|:---|:---|:---|
| input_id | 输入项ID，从模板详情中获取                                                                                         | 是    | str、Placeholder         |
| input    | 模板输入路径，可以是OBS文件路径或OBS目录路径。使用多输入项的模板创建模型时，如果模板定义的目标路径input_properties是一样的，则此处输入的OBS目录或者OBS文件不能重名，否则会覆盖。 | 是    | str、Placeholder、Storage |
   
#### 使用案例
主要包含六种场景的用例：
- 基于JobStep的输出注册模型
- 基于OBS数据注册模型
- 使用模板方式注册模型
- 使用自定义镜像注册模型
- 使用自定义镜像+OBS的方式注册模型
- 使用订阅模型+OBS的方式注册模型
 
#### 从训练作业中注册模型（模型输入来源JobStep的输出）
```
import modelarts.workflow as wf
# 构建一个OutputStorage对象，对训练输出目录做统一管理
storage = wf.data.OutputStorage(name="storage_name", title="title_info", description="description_info") # name字段必填，title, description可选填 
# 定义输入的数据集对象
dataset = wf.data.DatasetPlaceholder(name="input_dataset")
# 通过JobStep来定义一个训练节点，输入使用数据集，并将训练结果输出到OBS
job_step = wf.steps.JobStep(
    name="training_job", # 训练节点的名称，命名规范(只能包含英文字母、数字、下划线（_）、中划线（-），并且只能以英文字母开头，长度限制为64字符)，一个Workflow里的两个step名称不能重复
    title="图像分类训练", # 标题信息，不填默认使用name
    algorithm=wf.AIGalleryAlgorithm(
        subscription_id="subscription_id", # 算法订阅ID，也可直接填写版本号
        item_version_id="item_version_id", # 算法订阅版本ID，也可直接填写版本号
        parameters=[
            wf.AlgorithmParameters(
                name="parameter_name", 
                value=wf.Placeholder(name="parameter_name", placeholder_type=wf.PlaceholderType.STR, default="fake_value",description="description_info")
            ) # 算法超参的值使用Placeholder对象来表示，支持int, bool, float, str四种类型
        ]
    ),  # 训练使用的算法对象，示例中使用AIGallery订阅的算法；部分算法超参的值如果无需修改，则在parameters字段中可以不填写，系统自动填充相关超参值
    
    inputs=wf.steps.JobInput(name="data_url", data=dataset), # JobStep的输入在运行时配置；data字段也可使用wf.data.Dataset(dataset_name="fake_dataset_name", version_name="fake_version_name")表示
    outputs=wf.steps.JobOutput(name="train_url", obs_config=wf.data.OBSOutputConfig(obs_path=storage.join("directory_path"))), # JobStep的输出
    spec=wf.steps.JobSpec(
        resource=wf.steps.JobResource(
            flavor=wf.Placeholder(name="train_flavor", placeholder_type=wf.PlaceholderType.JSON, description="训练资源规格")
           
        )
    )# 训练资源规格信息
)
# 通过ModelStep来定义一个模型注册节点，输入来源于JobStep的输出
# 定义模型名称参数
model_name = wf.Placeholder(name="placeholder_name", placeholder_type=wf.PlaceholderType.STR)
model_registration = wf.steps.ModelStep(
    name="model_registration",  # 模型注册节点的名称，命名规范(只能包含英文字母、数字、下划线（_）、中划线（-），并且只能以英文字母开头，长度限制为64字符)，一个Workflow里的两个step名称不能重复
    title="模型注册",  # 标题信息
    inputs=wf.steps.ModelInput(name='model_input', data=job_step.outputs["train_url"].as_input()),  # ModelStep的输入来源于依赖的JobStep的输出
    outputs=wf.steps.ModelOutput(name='model_output',model_config=wf.steps.ModelConfig(model_name=model_name, model_type="TensorFlow")), # ModelStep的输出
    depend_steps=job_step # 依赖的作业类型节点对象
)
# job_step是wf.steps.JobStep的 实例对象，train_url是wf.steps.JobOutput的name字段值
workflow = wf.Workflow(
    name="model-step-demo",
    desc="this is a demo workflow",
    steps=[job_step, model_registration],
    storages=[storage]
)
```
#### 从训练作业中注册模型（模型输入来源OBS路径，训练完成的模型已存储到OBS路径）
```
import modelarts.workflow as wf
# 通过ModelStep来定义一个模型注册节点，输入来源于OBS中
# 定义OBS数据对象
obs = wf.data.OBSPlaceholder(name = "obs_placeholder_name", object_type = "directory" ) # object_type必须是file或者directory
# 定义模型名称参数
model_name = wf.Placeholder(name="placeholder_name", placeholder_type=wf.PlaceholderType.STR)
model_registration = wf.steps.ModelStep(
    name="model_registration",  # 模型注册节点的名称，命名规范(只能包含英文字母、数字、下划线（_）、中划线（-），并且只能以英文字母开头，长度限制为64字符)，一个Workflow里的两个step名称不能重复
    title="模型注册",  # 标题信息
    inputs=wf.steps.ModelInput(name='model_input', data=obs),  # ModelStep的输入在运行时配置；data字段的值也可使用wf.data.OBSPath(obs_path="fake_obs_path")表示
    outputs=wf.steps.ModelOutput(name='model_output',model_config=wf.steps.ModelConfig(model_name=model_name, model_type="TensorFlow"))# ModelStep的输出
)
workflow = wf.Workflow(
    name="model-step-demo",
    desc="this is a demo workflow",
    steps=[model_registration]
)
```
#### 使用模板的方式注册模型
```
import modelarts.workflow as wf
# 通过ModelStep来定义一个模型注册节点，并通过预置模板进行注册
# 定义预置模板对象，Template对象中的字段可使用Placeholder表示
template = wf.steps.Template(
    template_id="fake_template_id",
    infer_format="fake_infer_format",
    template_inputs=[
        wf.steps.TemplateInputs(
            input_id="fake_input_id",
            input="fake_input_file"
        )
    ]
)
# 定义模型名称参数
model_name = wf.Placeholder(name="placeholder_name", placeholder_type=wf.PlaceholderType.STR)
model_registration = wf.steps.ModelStep(
    name="model_registration",  # 模型注册节点的名称，命名规范(只能包含英文字母、数字、下划线（_）、中划线（-），并且只能以英文字母开头，长度限制为64字符)，一个Workflow里的两个step名称不能重复
    title="模型注册",  # 标题信息
    outputs=wf.steps.ModelOutput(
          name='model_output',
          model_config=wf.steps.ModelConfig(
              model_name=model_name, 
              model_type="Template",
              template=template
          )
    )# ModelStep的输出
)
workflow = wf.Workflow(
    name="model-step-demo",
    desc="this is a demo workflow",
    steps=[model_registration]
)
```
#### 从自定义镜像中注册模型
```
import modelarts.workflow as wf
# 通过ModelStep来定义一个模型注册节点，输入来源于自定义镜像地址
# 定义镜像数据
swr = wf.data.SWRImagePlaceholder(name="placeholder_name")
# 定义模型名称参数
model_name = wf.Placeholder(name="placeholder_name", placeholder_type=wf.PlaceholderType.STR)
model_registration = wf.steps.ModelStep(
    name="model_registration",  # 模型注册节点的名称，命名规范(只能包含英文字母、数字、下划线（_）、中划线（-），并且只能以英文字母开头，长度限制为64字符)，一个Workflow里的两个step名称不能重复
    title="模型注册",  # 标题信息
    inputs=wf.steps.ModelInput(name="input",data=swr), # ModelStep的输入在运行时配置；data字段的值也可使用wf.data.SWRImage(swr_path="fake_path")表示
    outputs=wf.steps.ModelOutput(name='model_output',model_config=wf.steps.ModelConfig(model_name=model_name, model_type="TensorFlow"))# ModelStep的输出
)
workflow = wf.Workflow(
    name="model-step-demo",
    desc="this is a demo workflow",
    steps=[model_registration]
)
```
#### 使用自定义镜像+OBS的方式注册模型
```
import modelarts.workflow as wf
# 通过ModelStep来定义一个模型注册节点，输入来源于自定义镜像地址
# 定义镜像数据
swr = wf.data.SWRImagePlaceholder(name="placeholder_name")
# 定义OBS模型数据
model_obs = wf.data.OBSPlaceholder(name = "obs_placeholder_name", object_type = "directory" ) # object_type必须是file或者directory
# 定义模型名称参数
model_name = wf.Placeholder(name="placeholder_name", placeholder_type=wf.PlaceholderType.STR)
model_registration = wf.steps.ModelStep(
    name="model_registration",  # 模型注册节点的名称，命名规范(只能包含英文字母、数字、下划线（_）、中划线（-），并且只能以英文字母开头，长度限制为64字符)，一个Workflow里的两个step名称不能重复
    title="模型注册",  # 标题信息
    inputs=[
        wf.steps.ModelInput(name="input",data=swr), # ModelStep的输入在运行时配置；data字段的值也可使用wf.data.SWRImage(swr_path="fake_path")表示
        wf.steps.ModelInput(name="input",data=model_obs) # ModelStep的输入在运行时配置；data字段的值也可使用wf.data.OBSPath(obs_path="fake_obs_path")表示
    ], 
    outputs=wf.steps.ModelOutput(
        name='model_output',
        model_config=wf.steps.ModelConfig(
            model_name=model_name, 
            model_type="Custom",
            dynamic_load_mode="Single"
        )
    )# ModelStep的输出
)
workflow = wf.Workflow(
    name="model-step-demo",
    desc="this is a demo workflow",
    steps=[model_registration]
)
```
#### 使用订阅模型+OBS的方式注册模型
该方式本质上与自定义镜像+OBS的方式没有区别，只是自定义镜像变成从订阅模型中获取。
具体使用案例：
```
import modelarts.workflow as wf
# 定义订阅的模型对象
base_model = wf.data.GalleryModel(subscription_id="fake_subscription_id", version_num="fake_version") # 从订阅的模型，一般由开发者自行创建发布
# 定义OBS模型数据
model_obs = wf.data.OBSPlaceholder(name = "obs_placeholder_name", object_type = "directory" ) # object_type必须是file或者directory
# 定义模型名称参数
model_name = wf.Placeholder(name="placeholder_name", placeholder_type=wf.PlaceholderType.STR)
model_registration = wf.steps.ModelStep(
    name="model_registration",  # 模型注册节点的名称，命名规范(只能包含英文字母、数字、下划线（_）、中划线（-），并且只能以英文字母开头，长度限制为64字符)，一个Workflow里的两个step名称不能重复
    title="模型注册",  # 标题信息
    inputs=[
        wf.steps.ModelInput(name="input",data=base_model) # ModelStep的输入使用订阅的模型
        wf.steps.ModelInput(name="input",data=model_obs) # ModelStep的输入在运行时配置；data字段的值也可使用wf.data.OBSPath(obs_path="fake_obs_path")表示
    ], 
    outputs=wf.steps.ModelOutput(
        name='model_output',
        model_config=wf.steps.ModelConfig(
            model_name=model_name, 
            model_type="Custom",
            dynamic_load_mode="Single"
        )
    )# ModelStep的输出
)
workflow = wf.Workflow(
    name="model-step-demo",
    desc="this is a demo workflow",
    steps=[model_registration]
)
```
上述案例中，系统会自动获取订阅模型中的自定义镜像，然后结合输入的OBS模型路径，注册生成一个新的模型，其中model_obs可以替换成JobStep的动态输出。
![](https://support.huaweicloud.com/usermanual-standard-modelarts/public_sys-resources/note_3.0-zh-cn.png)
model_type支持的类型有："**TensorFlow** "、"**MXNet** "、"**Caffe** "、 "**Spark_MLlib** "、 "**Scikit_Learn** "、"**XGBoost** "、 "**Image** "、"**PyTorch** "、"**Template** "、"**Custom**"。
在wf.steps.ModelConfig对象中，如果model_type字段未填写，则表示默认使用"**TensorFlow**"。
- 如果您构建的工作流对注册的模型类型没有修改的需求，则按照上述示例使用即可。
- 如果您构建的工作流需要多次运行可以修改模型类型，则可使用占位符参数的方式进行编写： model_type = wf.Placeholder(name="placeholder_name", placeholder_type=wf.PlaceholderType.ENUM, default="TensorFlow", enum_list=\["TensorFlow", "MXNet", "Caffe", "Spark_MLlib", "Scikit_Learn", "XGBoost", "Image", "PyTorch", "Template", "Custom"\], description="模型类型")
  
 
