在modelarts里使用自定义算法进行训练、导入模型、部署服务

举报
黄生 发表于 2021/05/28 17:58:55 2021/05/28
【摘要】 本文介绍在modelarts里使用自定义算法进行训练、导入模型、部署服务的过程。如果你用过了modelarts的自动学习,然后你觉得自动学习过于自动了,想试试不那么自动的;或者你用过了Modelarts的市场订阅算法(以前叫预置算法),然后你觉得只是把别人写好的攻略再重复一遍,你还想尝试一点更新的东西;那你也许可能对本文有一点感兴趣,虽然这里的过程结束后,没有生产出有任何业务意义的服务,但是...

本文介绍在modelarts里使用自定义算法进行训练、导入模型、部署服务的过程。
如果你用过了modelarts的自动学习,然后你觉得自动学习过于自动了,想试试不那么自动的;
或者你用过了Modelarts的市场订阅算法(以前叫预置算法),然后你觉得只是把别人写好的攻略再重复一遍,你还想尝试一点更新的东西;
那你也许可能对本文有一点感兴趣,虽然这里的过程结束后,没有生产出有任何业务意义的服务,但是你知道了这个流程,不管有没有业务意义,都是这样的流程。

首先,自定义算法,就是自己可以比较自由的控制的算法。
这里用的算法,是一个最最简单的,一元一次方程的线性回归的算法。
数据?为简单起见,不需要准备,不需要输入,算法自己包含了。
算法的思路是这样的:
1.自己准备数据,就是一些数字,先是x轴上的100个数字。然后是y轴上的100个数字,y和x的关系是:y=2x+1+一个噪声值
2.假装我们不知道x和y之间的关系,我们的算法就是来寻找这个关系。每一对x和y的值,都是算法的输入,而算法的目标,就是找出系数2和常量1。在tf里,x和y就是占位符,而要找出的系数和常量,就是变量。
3.算法的寻找的方法,是损失函数,和优化器。谈到损失函数,那这个应该是有监督学习吧,没有标签,怎么计算损失呢?
4.有了方法后,就是去做,一遍遍的做,反正机器也是不知道累的,这个算法简单,秒完。

有了数据,然后数据配合算法跑完,变量/参数就算出来了,这个时候要把成果保存下来。
好了,上面的这些,写道一个py文件tf_modelart.py里,就可以作为自定义算法来到Modelarts里做训练了。

# -*- coding: utf-8 -*-

import os
import numpy as np

import tensorflow.compat.v1 as tf
tf.disable_eager_execution()

tf.flags.DEFINE_integer('max_steps', 1000, 'number of training iterations.')
tf.flags.DEFINE_string('data_url', '', 'dataset directory.')
tf.flags.DEFINE_string('train_url', '', 'saved model directory.')
FLAGS = tf.flags.FLAGS

def model(x,w,b):
    return tf.multiply(x,w)+b
    
def main(*args):
    #输入数据目录这里没有使用;因为代码里自己生成了数据
    print('^_^the data_url is ',FLAGS.data_url)
    np.random.seed(5)
    #生成100个点,取值范围[-1,1]
    x_data=np.linspace(-1,1,100)
    #后面是加一个噪声 不然是一条直线   ; *是把元组展开 print(*x_data.shape)
    y_data=2*x_data + 1.0 + np.random.randn(*x_data.shape)*0.4
    
    print('^_^start training...')
    #二个占位符是训练时传入的
    x=tf.placeholder('float',name='x')
    y=tf.placeholder('float',name='y')
    #这二个变量是来保存和更新参数的
    w=tf.Variable(1.0,name='w0')
    b=tf.Variable(0.0,name='b0')
    pred=model(x,w,b)

    #设置训练超参
    train_epochs=2
    learning_rate=0.05

    #损失函数
    loss_func=tf.reduce_mean(tf.square(y-pred))
    #优化器
    optimizer=tf.train.GradientDescentOptimizer(learning_rate).minimize(loss_func)

    #可以开始跑训练了
    sess=tf.Session()
    init=tf.global_variables_initializer()
    sess.run(init)

    for epoch in range(train_epochs):
        for xs,ys in zip(x_data,y_data):
            _,loss=sess.run([optimizer,loss_func],feed_dict={x:xs,y:ys})
    print('^_^Done training!')
    
    #保存模型
    signature_key = 'test_signature'
    input_key = 'input_x'
    output_key = 'output'
    builder=tf.saved_model.builder.SavedModelBuilder(os.path.join(FLAGS.train_url,'model'))
    inputs = {input_key : tf.saved_model.utils.build_tensor_info(x)}
    outputs = {output_key : tf.saved_model.utils.build_tensor_info(pred)}
    signature = tf.saved_model.signature_def_utils.build_signature_def(inputs, outputs, 'test_sig_name')
    builder.add_meta_graph_and_variables(sess,['igraphtag'],{signature_key:signature})
    builder.save()
    print('^_^export train dir is ',FLAGS.train_url)

if __name__ == '__main__':
    tf.app.run(main=main)

训练管理>训练作业>创建截图

这里train_url和data_url是根据上面的选择带出的,还可以增加其他可配置化的参数,比如一些超参,训练的epoch和学习率等,这里为了简化,都略过。

运行成功后,保存了模型到OBS里

然后,在modelarts里,有一个模型导入的步骤要做,这样会将模型封进image,以利于后续部署。

而在模型导入之前,还需要做一件事件,那就是确定模型对外服务时的输入输出接口(入参/出参),以及推理的代码,确定了这二项后,在导入模型时,modelarts会用到。如下图

那么config.json和推理代码是长上面样子的呢?

如下:config.json

{
    "model_type": "TensorFlow",
	"runtime": "tf2.1-python3.7",
    "model_algorithm": "predict_analysis",
    "apis": [
        {
            "protocol": "http",
            "url": "/",
            "method": "post",
            "request": {
                "Content-type": "application/json",
                "data": {
                    "type": "object"
                "properties": {
                        "input_x": {"type": "string"}
                    },
                    "required": ["input_x"]    
                }
            },
            "response": {
                "Content-type": "application/json",
                "data": {
                    "type": "object",
					"properties": {
                        "result": {"type": "string"}
                    },
                    "required": ["result"]
                }
            }
        }],   
}

推理代码customize_service.py:

# -*- coding: utf-8 -*-
from model_service.tfserving_model_service import TfServingBaseService
import tensorflow.compat.v1 as tf
tf.disable_eager_execution()

class my_service(TfServingBaseService):
    def __init__(self, model_name, model_path):
        # these three parameters are no need to modify
        self.model_name = model_name
        self.model_path = model_path
        self.signature_key = 'test_signature'

        # add the input and output key of your pb model here,
        # these keys are defined when you save a pb file
        self.input_key_1 = 'input_x'
        self.output_key_1 = 'output'
        config = tf.ConfigProto(allow_soft_placement=True)
        with tf.get_default_graph().as_default():
            self.sess = tf.Session(graph=tf.Graph(), config=config)
            meta_graph_def = tf.saved_model.loader.load(self.sess, ["igraphtag"], self.model_path)
            self.signature = meta_graph_def.signature_def

            # define input and out tensor of your model here
            input_images_tensor_name = self.signature[self.signature_key].inputs[self.input_key_1].name
            output_score_tensor_name = self.signature[self.signature_key].outputs[self.output_key_1].name
            self.input_images = self.sess.graph.get_tensor_by_name(input_images_tensor_name)
            self.output_score = self.sess.graph.get_tensor_by_name(output_score_tensor_name)
            print('^_^_init_ tensor in is',self.input_images)
            print('^_^_init_ tensor out is',self.output_score)

    def _inference(self, data):
        """
        model inference function
        Here are a inference example of resnet, if you use another model, please modify this function
        """
        print('^_^inference data is',data)
        img=data
        
        pred_score = self.sess.run(self.output_score, feed_dict={self.input_images: img})
        result = {'result':str(pred_score)}
        return result
            
    def _preprocess(self, data):
        print('^_^pre data is ',data)
        preprocessed_data = data
        return preprocessed_data

    def _postprocess(self, data):
        print('^_^post data is ',data)
        infer_output = data
        return infer_output

注:这里的代码参考了垃圾分类,可以从这里下载

要按照modelarts的模型包规范,写好apis的json配置文件,和推理的代码。参考文档:模型包规范介绍 

推理的代码,是用来做对外服务的业务处理。部署为服务后,可以对外提供restful风格的http服务,这样你的服务可以以大家喜闻乐见的方式提供给大众来调用。
config.json就是用来定义服务的输入输出接口。
推理的思路很简单,和算法思路的最后一部分,保存模型的对应起来的。要注意的是不要对差了,否则错一小点又要重来。
1.加载模型,并按约定提取出用于输入输出的张量。
2.建立会话运行模型里的预测功能,获取预测结果
3.组织返回

再然后,模型导入成功了,下面就是部署。部署就是准备环境、拉镜像image、然后启动。虽然是很小的模型,但是架子很大,所以还是比较慢,要几分钟。
部署没什么好说的,就是免费规格排队很慢,可能很久都排不到,可以用收费的,用完了记得停止服务。
服务启动成功后,来测试一下服务,看服务返回是不是正确,如果不正确,报错什么的,要去看日志排查具体原因,找到原因修改后,再从导入模型来过~

有点奇怪的时,输入接口咋不能输入json格式的数据呢?

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。