在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格式的数据呢?
- 点赞
- 收藏
- 关注作者
评论(0)