
# 开发用于预置框架训练的代码
在使用ModelArts平台完成算法开发时，开发者需要利用预置框架来构建和训练模型。然而，预置框架虽然提供了基础功能，但可能无法完全满足特定的业务需求，需要进行定制化开发。为了确保预置框架能够与具体的业务需求完美结合，开发者需要在使用预置框架创建算法之前，预先完成算法代码的开发工作。通过预先开发，可以确保预置框架能够高效地支持模型训练和优化，从而实现更高效的算法开发流程。
#### 配置路径说明
创建算法时，您需要在创建页面提供**代码目录路径** 、**代码目录路径中的启动文件** 、**训练输入路径参数** 和**训练输出路径参数**。这四种输入搭建了用户代码和ModelArts后台交互的桥梁。
- **代码目录路径**
  您需要在OBS桶中指定代码目录，并将训练代码、依赖安装包或者预生成模型等训练所需文件上传至该代码目录下。训练作业创建完成后，ModelArts会将代码目录及其子目录下载至后台容器中。
  例如：OBS路径"obs://obs-bucket/training-test/demo-code"作为代码目录，OBS路径下的内容会被自动下载至训练容器的"${MA_JOB_DIR}/demo-code"目录中，demo-code为OBS存放代码路径的最后一级目录，用户可以根据实际修改。
  请注意不要将训练数据放在代码目录路径下。训练数据比较大，训练代码目录在训练作业启动后会下载至后台，可能会有下载失败的风险。建议训练代码目录大小小于或等于50MB。
  
- **代码目录路径中的启动文件**
  代码目录路径中的启动文件作为训练启动的入口，当前只支持python格式。预置框架启动文件的启动流程说明请参见[预置框架启动文件的启动流程说明](https://support.huaweicloud.com/usermanual-standard-modelarts/develop-modelarts-1415.html)。
  
- **训练输入路径参数**
  训练数据需上传至OBS桶或者存储至[数据集](https://support.huaweicloud.com/dataprepare-modelarts/dataprepare-modelarts-0032.html)中。在训练代码中，用户需解析输入路径参数。系统后台会自动下载输入参数路径中的训练数据至训练容器的本地目录。请保证您设置的桶路径有读取权限。在训练作业启动后，ModelArts会挂载硬盘至"/cache"目录，用户可以使用此目录来存储临时文件。"/cache"目录大小请参考[训练环境中不同规格资源"/cache"目录的大小](https://support.huaweicloud.com/modelarts_faq/modelarts_05_0090.html)。
  
- **训练输出路径参数**
  建议设置一个空目录为训练输出路径。在训练代码中，您需要解析输出路径参数。系统后台会自动上传训练输出至指定的训练输出路径，请保证您设置的桶路径有写入权限和读取权限。
  
在ModelArts中，训练代码需包含[（可选）引入依赖]和[解析和设置输入路径参数、输出路径参数]步骤。
 #### （可选）引入依赖
当您使用自定义脚本创建算法的时候，如果您的模型引用了其他依赖，您需要在"算法管理 \> 创建算法"的"代码目录"下放置相应的文件或安装包。
- 安装python依赖包请参考[模型中引用依赖包时，如何创建训练作业？](https://support.huaweicloud.com/modelarts_faq/modelarts_05_0063.html)
- 安装C++的依赖库请参考[如何安装C++的依赖库？](https://support.huaweicloud.com/modelarts_faq/modelarts_05_0088.html)
- 在预训练模型中加载参数请参考[如何在训练中加载部分训练好的参数？](https://support.huaweicloud.com/modelarts_faq/modelarts_05_0091.html)
 
 #### 解析和设置输入路径参数、输出路径参数
运行在ModelArts的训练作业会读取存储在OBS服务的数据，或者输出训练结果至OBS服务指定路径，输入和输出数据需要配置2个地方：
1. 训练代码中需解析输入路径参数和输出路径参数。ModelArts推荐以下方式实现参数解析。
   ```
   import argparse
   # 创建解析
   parser = argparse.ArgumentParser(description='train mnist')
   # 添加参数
   parser.add_argument('--data_url', type=str, default="./Data/mnist.npz", help='path where the dataset is saved')
   parser.add_argument('--train_url', type=str, default="./Model", help='path where the model is saved')
   # 解析参数
   args = parser.parse_args()
   ```
   完成参数解析后，用户使用"data_url"、"train_url"代替算法中数据来源和数据输出所需的路径。
   
2. 在创建训练作业时，填写输入路径和输出路径。 训练输入选择对应的OBS路径或者数据集路径；训练输出选择对应的OBS路径。
   
 
#### 训练代码完整示例
训练代码示例中涉及的代码与您使用的AI引擎密切相关，以下案例以TensorFlow框架为例。案例中使用到的"mnist.npz"文件需要提前[下载](https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz)并上传至OBS桶中，训练输入为"mnist.npz"所在OBS路径。
以下训练代码样例中包含了保存模型代码。
```
import os
import argparse
import tensorflow as tf
parser = argparse.ArgumentParser(description='train mnist')
parser.add_argument('--data_url', type=str, default="./Data/mnist.npz", help='path where the dataset is saved')
parser.add_argument('--train_url', type=str, default="./Model", help='path where the model is saved')
args = parser.parse_args()
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data(args.data_url)
x_train, x_test = x_train / 255.0, x_test / 255.0
model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Dense(10)
])
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
model.compile(optimizer='adam',
              loss=loss_fn,
              metrics=['accuracy'])
model.fit(x_train, y_train, epochs=5)
model.save(os.path.join(args.train_url, 'model'))
```
