算法开发

最近更新时间:2020-04-16 17:40:37

平台中的算法运行依赖于容器运行环境,理论上能制作成Docker镜像的算法都能在平台上运行。要开发出能在平台上安装运行的算法,需要对算法进行以下几项适配:

  1. 将算法所需的运行环境(如package依赖),输入输出数据等以environment.yml文件进行描述。
  2. 如算法有需要经常调整的超参数,将超参数以parameter.yml文件进行描述。
  3. 为使用以上两个配置文件,需对代码进行少量改写,届时会用到两个Python工具包:kpl-helperkpl-dataset

environment.yml

完整的environment.yml示例

version: 1  
image:  
  from_image: hub.kce.ksyun.com/kpl_k8s/pytorch:cuda10.0-py3-v1.2.0  
  runs:  
    - pip3 install kpl-dataset kpl-helper matplotlib==2.2.4 Pillow==5.0.0 
                   terminaltables==3.1.0 -i https://pypi.tuna.tsinghua.edu.cn/simple  

train:  
  resource: 
    limits:
      cpu: 4
      gpu: 1
      memory: 10240   # 单位MB
      shm: 1024   # 共享内存,单位MB
    requests:    #  requests选填
      cpu: 4
      gpu: 1
      memory: 10240
      shm: 1024
  inputs:  
    - key: data  
      name: 数据  
  - key: model  
      name: 预训练模型
  outputs:  
    - key: save  
      name: 模型  
  cmd: python3 train.py  

infer:  
  resource: 
    limits:
      cpu: 4
      gpu: 1
      memory: 10240
  inputs:  
    - key: data  
      name: 数据  
  - key: model  
      name: 模型  
  outputs:  
    - key: save  
      name: 推理输出  
  output: /output  
  cmd: python3 infer.py  

test:
  格式与以上一致

deploy:  
  格式与以上一致

compile:  
  格式与以上一致

environment.yml > version

environment.yml文件配置格式的版本号,以上格式配置的版本为1

environment.yml > image

image下的参数用于描述运行算法所需的Docker镜像,from_image含义等同于Dockerfile中的FROMruns下的每一条配置等同于Dockerfile中的RUN

environment.yml > train{test, infer, deploy, compile}

一般算法可以分为5个功能:训练,测试,批量推理,API服务,编译SDK。train, test, infer, deploy, compile分别与之对应,用于描述不同功能参数配置。5个功能的参数配置都是几乎相同的,以train为例进行说明。 train下包含共包含4部分:运行所需资源描述resrouce,输入描述inputs, 输出描述ouputs,启动训练命令cmd

parameter.yml

只要符合yaml文件的格式即可,字段与字段的值完全由用户自定义,在下述Helper包中会说明parameter.yml的使用。

Helper Package

kpl-helper包提供了用于在平台中运行算法的工具函数,如:获取数据的路径,传递和解析超参数等。 安装方法:pip3 install kpl-helper 使用方法会在下面代码改写小节中以例子说明。

Dataset Package

在平台中,数据的序列化格式是由平台来定义的(类似TensorFlow的TFRecords,但是会更简单),该序列化方式既方便使用者脱离平台使用又能友好的在平台上由系统解析在Web上呈现内容。kpl-dataset就是用于创建和读取序列化数据集的工具包(可脱离平台使用)。 安装方法:pip3 install kpl-dataset

写数据

from kpl_dataset import ReadOnlyDataset, WriteOnlyDataset, BasicType
# 定义要写入的字段结构和类型,结构支持key: value的层次化结构(推荐结构不要太深),key必须为字符串。
record_type = {
    "height": BasicType.Int,
    "width": BasicType.Int,
    "image": BasicType.ByteArray,
    "object": [{
          "name": BasicType.String,  
          "xmin": BasicType.Int,  
          "xmax": BasicType.Int,  
          "ymin": BasicType.Int,  
          "ymax": BasicType.Int
    }]
}
writer = WriteOnlyDataset("/tmp/", data_name="data_name", record_type=record_type)
for _ in range(10:
    writer.write({
       "height": 200,
       "width": 200,
       "image": b"image file",
       "object": [
           {
               "name": "dog",
               "xmin": 100,
               "ymin": 100,
               "xmax": 200,
               "ymax": 200,
           },
            {
               "name": "dog",
               "xmin": 100,
               "ymin": 100,
               "xmax": 200,
               "ymax": 200,
           }
       ] 
    })
# 最后一定不要忘了关闭数据
writer.close()

读数据

from kpl_dataset import ReadOnlyDataset, WriteOnlyDataset, BasicType
reader = ReadOnlyDataset("/tmp", data_name=None)
# 迭代
record = next(reader)
print(record)

for record in reader:
    print(record)

# 以index索引
index = random.randint(0, len(reader))
record = reader[index]
print(record)

# 获取数据中record的数量
print(len(reader))

# 迭代结束后重置数据
reader.reset()

# 关闭数据
reader.close()

代码改写

如果你已经有一个可以在本地运行的算法,那么让该算法能在平台上运行,你需要对代码进行如下改写。

  1. 编写environment.yml和parameter.yml文件,并存放于代码根路径下。
  2. 在算法代码执行前后调用ready()和done() (必要)
import kpl_helper as helper
if __name__ == "__main__":
    helper.ready()
    # your algorithm code
    helper.done()
  1. 获取超参数和使用超参数(必要)

    '''
    如parameter.yml文件内容如下:
    KEY1:
    KEY2: VALUE
    '''
    import kpl_helper as helper
    parameter = helper.get_parameter(default="./parameter.yml")
    print(parameter.KEY1.KEY2)   # 输出结果为VALUE
  2. 获取输入路径(必要)

你将使用到的数据和模型,会由系统挂载到运行时的容器中去,对应的路径需要使用helper包来获取。

'''
如environment.yml文件内容如下:
...
train:
  inputs:
    - key: data
      name: 输入数据
    - key: pretrain_model
      name: 预训练模型
...
'''
import kpl_helper as helper
data_path = helper.io.get_input_path(key="data", default="your local path")
pretrain_model_path = helper.io.get_input_path(key="model", default="your local path")
  1. 获取输出路径(必要)

    '''
    如environment.yml文件内容如下:
    ...
    train:
    outputs:
    - key: save_model
      name: 训练出的模型
    ...
    '''
    import kpl_helper as helper
    save_path = helper.io.get_output_path(key="save_model", default="your local path")
  2. 使用kpl-dataset读取输入数据(必要)

除了模型之外,所有的数据都需要使用kpl_dataset包进行读取。

from kpl_dataset import ReadOnlyDataset, WriteOnlyDataset, BasicType
import kpl_helper as helper
data_path = helper.io.get_input_path(key="data")
reader = ReadOnlyDataset(data_path, data_name=None)
# method 1. iteration
for record in reader:
    print(record)

# method 2
record = next(reader)
print(record)

# method 3. read by index
index = random.randint(0, len(reader))
record = reader[index]
print(record)
  1. 使用kpl-dataset保存输出数据(必要)

在批量推理保存数据时,数据的保存格式一定要使用kpl_dataset包进行序列化保存。

from kpl_dataset import ReadOnlyDataset, WriteOnlyDataset, BasicType
from kpl_dataset.common import RectangleBoxObjectDetectionDatasetDefine
import kpl_helper as helper
save_path = helper.io.get_output_path(key="save_model")
# 以一般矩形框检测数据的组织方式为例
writer = WriteOnlyDataset(save_path, data_name="data_name",
                          record_type=RectangleBoxObjectDetectionDatasetDefine)
for _ in range(10:
    writer.write({
       "path": "file path (String)",
       "content": b"I am image file (Bytes)",
       "contentType": "image/jpeg",
       "object": [
           {
               "name": "dog",
               "xmin": 100,
               "ymin": 100,
               "xmax": 200,
               "ymax": 200,
               "difficult": 0,
               "score": 0.9
           }
       ] 
    })
writer.close()
  1. 发送Metric数据(可选)

如果希望在Web端可视化Loss等曲线,可以通过helper包提供的MetricFigure来实现,如:

from kpl_helper import metric
figure = metric.MetricFigure("title", y1_name="loss")  # 默认横坐标为迭代次数
iter_num = 100
for no in range(1, iter_num):
    ce_loss = 10.0 / no
    figure.push_metric(x=no, y1=ce_loss)
  1. 发送进度信息(可选)

如果希望系统能对剩余时长进行更好的估计,可以将内部算法当前进行进度通过send_progress函数发出来,如

from kpl_helper.progress import send_progress
iter_num = 100
for no in range(1, iter_num):
    send_progress(float(no) / iter_num)

QA:

金山云,开启您的云计算之旅

免费注册