华为云用户手册

  • 方式二:通过服务详情页面修改服务信息 登录ModelArts管理控制台,在左侧菜单栏中选择“模型部署”,进入目标服务类型管理页面。 单击目标服务名称,进入服务详情页面。 您可以通过单击页面右上角“修改”,修改服务基本信息,然后根据提示提交修改任务。 当修改了服务的某些参数配置时,系统会自动重启服务使修改生效。在提交修改服务任务时,若涉及重启,会有弹窗提醒。 在线服务参数说明请参见部署为在线服务。修改在线服务还需要配置“最大无效实例数”设置并行升级的最大节点数,升级阶段节点无效。 批量服务参数说明请参见部署为批量服务。 边缘服务参数说明请参见部署为边缘服务。
  • 约束限制 服务升级关系着业务实现,不当的升级操作会导致升级期间业务中断的情况,请谨慎操作。 ModelArts支持部分场景下在线服务进行无损滚动升级。按要求进行升级前准备,做好验证,即可实现业务不中断的无损升级。 表1 支持无损滚动升级的场景 创建AI应用的元模型来源 服务使用的是公共资源池 服务使用的是专属资源池 从训练中选择元模型 不支持 不支持 从容器镜像中选择元模型 不支持 支持,创建AI应用的 自定义镜像 需要满足创建AI应用的自定义镜像规范。 从OBS中选择元模型 不支持 不支持
  • 方式一:通过服务管理页面修改服务信息 登录ModelArts管理控制台,在左侧菜单栏中选择“模型部署”,进入目标服务类型管理页面。 在服务列表中,单击目标服务操作列的“修改”,修改服务基本信息,然后根据提示提交修改任务。 当修改了服务的某些参数配置时,系统会自动重启服务使修改生效。在提交修改服务任务时,若涉及重启,会有弹窗提醒。 在线服务参数说明请参见部署为在线服务。修改在线服务还需要配置“最大无效实例数”设置并行升级的最大节点数,升级阶段节点无效。 批量服务参数说明请参见部署为批量服务。 边缘服务参数说明请参见部署为边缘服务。
  • 推理代码 在模型代码推理文件customize_service.py中,需要添加一个子类,该子类继承对应模型类型的父类,各模型类型的父类名称和导入语句如请参考表1。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 # coding:utf-8 import collections import json from sklearn.externals import joblib from model_service.python_model_service import XgSklServingBaseService class UserService(XgSklServingBaseService): # request data preprocess def _preprocess(self, data): list_data = [] json_data = json.loads(data, object_pairs_hook=collections.OrderedDict) for element in json_data["data"]["req_data"]: array = [] for each in element: array.append(element[each]) list_data.append(array) return list_data # predict def _inference(self, data): sk_model = joblib.load(self.model_path) pre_result = sk_model.predict(data) pre_result = pre_result.tolist() return pre_result # predict result process def _postprocess(self,data): resp_data = [] for element in data: resp_data.append({"predictresult": element}) return resp_data
  • 训练并保存模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import json import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.externals import joblib iris = pd.read_csv('/home/ma-user/work/iris.csv') X = iris.drop(['variety'],axis=1) y = iris[['variety']] # Create a LogisticRegression instance and train model logisticRegression = LogisticRegression(C=1000.0, random_state=0) logisticRegression.fit(X,y) # Save model to local path joblib.dump(logisticRegression, '/tmp/sklearn.m') 训练前请先下载iris.csv数据集,解压后上传至Notebook本地路径/home/ma-user/work/。iris.csv数据集下载地址:https://gist.github.com/netj/8836201。Notebook上传文件操作请参见上传本地文件至Notebook中。 保存完模型后,需要上传到OBS目录才能发布。发布时需要带上“config.json”配置以及“customize_service.py”,定义方式参考模型包规范介绍。
  • 训练并保存模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pyspark.ml import Pipeline, PipelineModel from pyspark.ml.linalg import Vectors from pyspark.ml.classification import LogisticRegression # 创建训练数据,此处通过tuples创建 # Prepare training data from a list of (label, features) tuples. training = spark.createDataFrame([ (1.0, Vectors.dense([0.0, 1.1, 0.1])), (0.0, Vectors.dense([2.0, 1.0, -1.0])), (0.0, Vectors.dense([2.0, 1.3, 1.0])), (1.0, Vectors.dense([0.0, 1.2, -0.5]))], ["label", "features"]) # 创建训练实例,此处使用逻辑回归算法进行训练 # Create a LogisticRegression instance. This instance is an Estimator. lr = LogisticRegression(maxIter=10, regParam=0.01) # 训练逻辑回归模型 # Learn a LogisticRegression model. This uses the parameters stored in lr. model = lr.fit(training) # 保存模型到本地目录 # Save model to local path. model.save("/tmp/spark_model") 保存完模型后,需要上传到OBS目录才能发布。发布时需要带上config.json配置和推理代码customize_service.py。config.json编写请参考模型配置文件编写说明,推理代码请参考推理代码。
  • 推理代码 在模型代码推理文件customize_service.py中,需要添加一个子类,该子类继承对应模型类型的父类,各模型类型的父类名称和导入语句如请参考表1。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 # coding:utf-8 import collections import json import traceback import model_service.log as log from model_service.spark_model_service import SparkServingBaseService from pyspark.ml.classification import LogisticRegression logger = log.getLogger(__name__) class UserService(SparkServingBaseService): # 数据预处理 def _preprocess(self, data): logger.info("Begin to handle data from user data...") # 读取数据 req_json = json.loads(data, object_pairs_hook=collections.OrderedDict) try: # 将数据转换成spark dataframe格式 predict_spdf = self.spark.createDataFrame(pd.DataFrame(req_json["data"]["req_data"])) except Exception as e: logger.error("check your request data does meet the requirements ?") logger.error(traceback.format_exc()) raise Exception("check your request data does meet the requirements ?") return predict_spdf # 模型推理 def _inference(self, data): try: # 加载模型文件 predict_model = LogisticRegression.load(self.model_path) # 对数据进行推理 prediction_result = predict_model.transform(data) except Exception as e: logger.error(traceback.format_exc()) raise Exception("Unable to load model and do dataframe transformation.") return prediction_result # 数据后处理 def _postprocess(self, pre_data): logger.info("Get new data to respond...") predict_str = pre_data.toPandas().to_json(orient='records') predict_result = json.loads(predict_str) return predict_result
  • 推理代码 在模型代码推理文件customize_service.py中,需要添加一个子类,该子类继承对应模型类型的父类,各模型类型的父类名称和导入语句如请参考表1。 # coding:utf-8 import collections import json import xgboost as xgb from model_service.python_model_service import XgSklServingBaseService class UserService(XgSklServingBaseService): # request data preprocess def _preprocess(self, data): list_data = [] json_data = json.loads(data, object_pairs_hook=collections.OrderedDict) for element in json_data["data"]["req_data"]: array = [] for each in element: array.append(element[each]) list_data.append(array) return list_data # predict def _inference(self, data): xg_model = xgb.Booster(model_file=self.model_path) pre_data = xgb.DMatrix(data) pre_result = xg_model.predict(pre_data) pre_result = pre_result.tolist() return pre_result # predict result process def _postprocess(self,data): resp_data = [] for element in data: resp_data.append({"predictresult": element}) return resp_data
  • 训练并保存模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import pandas as pd import xgboost as xgb from sklearn.model_selection import train_test_split # Prepare training data and setting parameters iris = pd.read_csv('/home/ma-user/work/iris.csv') X = iris.drop(['variety'],axis=1) y = iris[['variety']] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234565) params = { 'booster': 'gbtree', 'objective': 'multi:softmax', 'num_class': 3, 'gamma': 0.1, 'max_depth': 6, 'lambda': 2, 'subsample': 0.7, 'colsample_bytree': 0.7, 'min_child_weight': 3, 'silent': 1, 'eta': 0.1, 'seed': 1000, 'nthread': 4, } plst = params.items() dtrain = xgb.DMatrix(X_train, y_train) num_rounds = 500 model = xgb.train(plst, dtrain, num_rounds) model.save_model('/tmp/xgboost.m') 训练前请先下载iris.csv数据集,解压后上传至Notebook本地路径/home/ma-user/work/。iris.csv数据集下载地址:https://gist.github.com/netj/8836201。Notebook上传文件操作请参见上传本地文件至Notebook中。 保存完模型后,需要上传到OBS目录才能发布。发布时需要带上config.json配置和推理代码customize_service.py。config.json编写请参考模型配置文件编写说明,推理代码请参考推理代码。
  • 推理代码 在模型代码推理文件customize_service.py中,需要添加一个子类,该子类继承对应模型类型的父类,各模型类型的父类名称和导入语句如请参考表1。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 from model_service.caffe_model_service import CaffeBaseService import numpy as np import os, json import caffe from PIL import Image class LenetService(CaffeBaseService): def __init__(self, model_name, model_path): # 调用父类推理方法 super(LenetService, self).__init__(model_name, model_path) # 设置预处理 transformer = caffe.io.Transformer({'data': self.net.blobs['data'].data.shape}) # 转换为NCHW格式 transformer.set_transpose('data', (2, 0, 1)) # 归一化处理 transformer.set_raw_scale('data', 255.0) # batch size设为1,只支持一张图片的推理 self.net.blobs['data'].reshape(1, 1, 28, 28) self.transformer = transformer # 设置类别标签 self.label = [0,1,2,3,4,5,6,7,8,9] def _preprocess(self, data): for k, v in data.items(): for file_name, file_content in v.items(): im = caffe.io.load_image(file_content, color=False) # 图片预处理 self.net.blobs['data'].data[...] = self.transformer.preprocess('data', im) return def _postprocess(self, data): data = data['prob'][0, :] predicted = np.argmax(data) predicted = {"predicted" : str(predicted) } return predicted
  • 训练并保存模型 “lenet_train_test.prototxt”文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 name: "LeNet" layer { name: "mnist" type: "Data" top: "data" top: "label" include { phase: TRAIN } transform_param { scale: 0.00390625 } data_param { source: "examples/mnist/mnist_train_lmdb" batch_size: 64 backend: LMDB } } layer { name: "mnist" type: "Data" top: "data" top: "label" include { phase: TEST } transform_param { scale: 0.00390625 } data_param { source: "examples/mnist/mnist_test_lmdb" batch_size: 100 backend: LMDB } } layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 20 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } layer { name: "conv2" type: "Convolution" bottom: "pool1" top: "conv2" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 50 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool2" type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } layer { name: "ip1" type: "InnerProduct" bottom: "pool2" top: "ip1" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 500 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" } layer { name: "ip2" type: "InnerProduct" bottom: "ip1" top: "ip2" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 10 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "accuracy" type: "Accuracy" bottom: "ip2" bottom: "label" top: "accuracy" include { phase: TEST } } layer { name: "loss" type: "SoftmaxWithLoss" bottom: "ip2" bottom: "label" top: "loss" } “lenet_solver.prototxt”文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # The train/test net protocol buffer definition net: "examples/mnist/lenet_train_test.prototxt" # test_iter specifies how many forward passes the test should carry out. # In the case of MNIST, we have test batch size 100 and 100 test iterations, # covering the full 10,000 testing images. test_iter: 100 # Carry out testing every 500 training iterations. test_interval: 500 # The base learning rate, momentum and the weight decay of the network. base_lr: 0.01 momentum: 0.9 weight_decay: 0.0005 # The learning rate policy lr_policy: "inv" gamma: 0.0001 power: 0.75 # Display every 100 iterations display: 100 # The maximum number of iterations max_iter: 1000 # snapshot intermediate results snapshot: 5000 snapshot_prefix: "examples/mnist/lenet" # solver mode: CPU or GPU solver_mode: CPU 执行训练 ./build/tools/caffe train --solver=examples/mnist/lenet_solver.prototxt 训练后生成“caffemodel”文件,然后将“lenet_train_test.prototxt”文件改写成部署用的lenet_deploy.prototxt,修改输入层和输出层。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 name: "LeNet" layer { name: "data" type: "Input" top: "data" input_param { shape: { dim: 1 dim: 1 dim: 28 dim: 28 } } } layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 20 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } layer { name: "conv2" type: "Convolution" bottom: "pool1" top: "conv2" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 50 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool2" type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } layer { name: "ip1" type: "InnerProduct" bottom: "pool2" top: "ip1" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 500 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" } layer { name: "ip2" type: "InnerProduct" bottom: "ip1" top: "ip2" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 10 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "prob" type: "Softmax" bottom: "ip2" top: "prob" }
  • 推理代码 在模型代码推理文件customize_service.py中,需要添加一个子类,该子类继承对应模型类型的父类,各模型类型的父类名称和导入语句如请参考表1。 from PIL import Image import log from model_service.pytorch_model_service import PTServingBaseService import torch.nn.functional as F import torch.nn as nn import torch import json import numpy as np logger = log.getLogger(__name__) import torchvision.transforms as transforms # 定义模型预处理 infer_transformation = transforms.Compose([ transforms.Resize((28,28)), # 需要处理成pytorch tensor transforms.ToTensor() ]) import os class PTVisionService(PTServingBaseService): def __init__(self, model_name, model_path): # 调用父类构造方法 super(PTVisionService, self).__init__(model_name, model_path) # 调用自定义函数加载模型 self.model = Mnist(model_path) # 加载标签 self.label = [0,1,2,3,4,5,6,7,8,9] # 亦可通过文件标签文件加载 # model目录下放置label.json文件,此处读取 dir_path = os.path.dirname(os.path.realpath(self.model_path)) with open(os.path.join(dir_path, 'label.json')) as f: self.label = json.load(f) def _preprocess(self, data): preprocessed_data = {} for k, v in data.items(): input_batch = [] for file_name, file_content in v.items(): with Image.open(file_content) as image1: # 灰度处理 image1 = image1.convert("L") if torch.cuda.is_available(): input_batch.append(infer_transformation(image1).cuda()) else: input_batch.append(infer_transformation(image1)) input_batch_var = torch.autograd.Variable(torch.stack(input_batch, dim=0), volatile=True) print(input_batch_var.shape) preprocessed_data[k] = input_batch_var return preprocessed_data def _postprocess(self, data): results = [] for k, v in data.items(): result = torch.argmax(v[0]) result = {k: self.label[result]} results.append(result) return results def _inference(self, data): result = {} for k, v in data.items(): result[k] = self.model(v) return result class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.hidden1 = nn.Linear(784, 5120, bias=False) self.output = nn.Linear(5120, 10, bias=False) def forward(self, x): x = x.view(x.size()[0], -1) x = F.relu((self.hidden1(x))) x = F.dropout(x, 0.2) x = self.output(x) return F.log_softmax(x) def Mnist(model_path, **kwargs): # 生成网络 model = Net() # 加载模型 if torch.cuda.is_available(): device = torch.device('cuda') model.load_state_dict(torch.load(model_path, map_location="cuda:0")) else: device = torch.device('cpu') model.load_state_dict(torch.load(model_path, map_location=device)) # CPU或者GPU映射 model.to(device) # 声明为推理模式 model.eval() return model
  • 训练模型 from __future__ import print_function import argparse import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchvision import datasets, transforms # 定义网络结构 class Net(nn.Module): def __init__(self): super(Net, self).__init__() # 输入第二维需要为784 self.hidden1 = nn.Linear(784, 5120, bias=False) self.output = nn.Linear(5120, 10, bias=False) def forward(self, x): x = x.view(x.size()[0], -1) x = F.relu((self.hidden1(x))) x = F.dropout(x, 0.2) x = self.output(x) return F.log_softmax(x) def train(model, device, train_loader, optimizer, epoch): model.train() for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) loss = F.cross_entropy(output, target) loss.backward() optimizer.step() if batch_idx % 10 == 0: print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) def test( model, device, test_loader): model.eval() test_loss = 0 correct = 0 with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability correct += pred.eq(target.view_as(pred)).sum().item() test_loss /= len(test_loader.dataset) print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format( test_loss, correct, len(test_loader.dataset), 100. * correct / len(test_loader.dataset))) device = torch.device("cpu") batch_size=64 kwargs={} train_loader = torch.utils.data.DataLoader( datasets.MNIST('.', train=True, download=True, transform=transforms.Compose([ transforms.ToTensor() ])), batch_size=batch_size, shuffle=True, **kwargs) test_loader = torch.utils.data.DataLoader( datasets.MNIST('.', train=False, transform=transforms.Compose([ transforms.ToTensor() ])), batch_size=1000, shuffle=True, **kwargs) model = Net().to(device) optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5) optimizer = optim.Adam(model.parameters()) for epoch in range(1, 2 + 1): train(model, device, train_loader, optimizer, epoch) test(model, device, test_loader)
  • 训练并保存模型 from __future__ import absolute_import, division, print_function, unicode_literals import tensorflow as tf mnist = tf.keras.datasets.mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() 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.Dense(256, activation='relu'), tf.keras.layers.Dropout(0.2), # 对输出层命名output,在模型推理时通过该命名取结果 tf.keras.layers.Dense(10, activation='softmax', name="output") ]) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(x_train, y_train, epochs=10) tf.keras.models.save_model(model, "./mnist")
  • 推理代码 在模型代码推理文件customize_service.py中,需要添加一个子类,该子类继承对应模型类型的父类,各模型类型的父类名称和导入语句如请参考表1。 import logging import threading import numpy as np import tensorflow as tf from PIL import Image from model_service.tfserving_model_service import TfServingBaseService logger = logging.getLogger() logger.setLevel(logging.INFO) class MnistService(TfServingBaseService): def __init__(self, model_name, model_path): self.model_name = model_name self.model_path = model_path self.model = None self.predict = None # label文件可以在这里加载,在后处理函数里使用 # label.txt放在obs和模型包的目录 # with open(os.path.join(self.model_path, 'label.txt')) as f: # self.label = json.load(f) # 非阻塞方式加载saved_model模型,防止阻塞超时 thread = threading.Thread(target=self.load_model) thread.start() def load_model(self): # load saved_model 格式的模型 self.model = tf.saved_model.load(self.model_path) signature_defs = self.model.signatures.keys() signature = [] # only one signature allowed for signature_def in signature_defs: signature.append(signature_def) if len(signature) == 1: model_signature = signature[0] else: logging.warning("signatures more than one, use serving_default signature from %s", signature) model_signature = tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY self.predict = self.model.signatures[model_signature] def _preprocess(self, data): images = [] for k, v in data.items(): for file_name, file_content in v.items(): image1 = Image.open(file_content) image1 = np.array(image1, dtype=np.float32) image1.resize((28, 28, 1)) images.append(image1) images = tf.convert_to_tensor(images, dtype=tf.dtypes.float32) preprocessed_data = images return preprocessed_data def _inference(self, data): return self.predict(data) def _postprocess(self, data): return { "result": int(data["output"].numpy()[0].argmax()) }
  • 保存模型(tf接口) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # 导出模型 # 模型需要采用saved_model接口保存 print('Exporting trained model to', export_path) builder = tf.saved_model.builder.SavedModelBuilder(export_path) tensor_info_x = tf.saved_model.utils.build_tensor_info(x) tensor_info_y = tf.saved_model.utils.build_tensor_info(y) # 定义预测接口的inputs和outputs # inputs和outputs字典的key值会作为模型输入输出tensor的索引键 # 模型输入输出定义需要和推理自定义脚本相匹配 prediction_signature = ( tf.saved_model.signature_def_utils.build_signature_def( inputs={'images': tensor_info_x}, outputs={'scores': tensor_info_y}, method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME)) legacy_init_op = tf.group(tf.tables_initializer(), name='legacy_init_op') builder.add_meta_graph_and_variables( # tag设为serve/tf.saved_model.tag_constants.SERVING sess, [tf.saved_model.tag_constants.SERVING], signature_def_map={ 'predict_images': prediction_signature, }, legacy_init_op=legacy_init_op) builder.save() print('Done exporting!')
  • 保存模型(keras接口) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from keras import backend as K # K.get_session().run(tf.global_variables_initializer()) # 定义预测接口的inputs和outputs # inputs和outputs字典的key值会作为模型输入输出tensor的索引键 # 模型输入输出定义需要和推理自定义脚本相匹配 predict_signature = tf.saved_model.signature_def_utils.predict_signature_def( inputs={"images" : model.input}, outputs={"scores" : model.output} ) # 定义保存路径 builder = tf.saved_model.builder.SavedModelBuilder('./mnist_keras/') builder.add_meta_graph_and_variables( sess = K.get_session(), # 推理部署需要定义tf.saved_model.tag_constants.SERVING标签 tags=[tf.saved_model.tag_constants.SERVING], """ signature_def_map:items只能有一个,或者需要定义相应的key为 tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY """ signature_def_map={ tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: predict_signature } ) builder.save()
  • 训练模型(keras接口) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from keras.models import Sequential model = Sequential() from keras.layers import Dense import tensorflow as tf # 导入训练数据集 mnist = tf.keras.datasets.mnist (x_train, y_train),(x_test, y_test) = mnist.load_data() x_train, x_test = x_train / 255.0, x_test / 255.0 print(x_train.shape) from keras.layers import Dense from keras.models import Sequential import keras from keras.layers import Dense, Activation, Flatten, Dropout # 定义模型网络 model = Sequential() model.add(Flatten(input_shape=(28,28))) model.add(Dense(units=5120,activation='relu')) model.add(Dropout(0.2)) model.add(Dense(units=10, activation='softmax')) # 定义优化器,损失函数等 model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.summary() # 训练 model.fit(x_train, y_train, epochs=2) # 评估 model.evaluate(x_test, y_test)
  • 输出 预测结果以“JSON”格式返回,“JSON”字段说明请参见表3。 表3 JSON字段说明 字段名 类型 描述 data Data结构 包含预测数据。“Data结构”说明请参见表4。 表4 Data结构说明 字段名 类型 描述 resp_data RespData结构数组 预测结果列表。 与“ReqData”一样,“RespData”也是“Object”类型,表示预测结果,其具体组成结构由业务场景决定。建议使用该模式的模型,其自定义的推理代码中的后处理逻辑应输出符合模式所定义的输出格式的数据。 预测结果的“JSON Schema”表示如下: { "type": "object", "properties": { "data": { "type": "object", "properties": { "resp_data": { "type": "array", "items": [{ "type": "object", "properties": {} }] } } } } }
  • 请求样例 该模式下的推理方式均为输入“JSON”格式的待预测数据,预测结果以“JSON”格式返回。示例如下: 页面预测 在服务详情的“预测”页签,输入预测代码,单击“预测”即可获取检测结果。 Postman调REST接口预测 部署上线成功后,您可以从服务详情页的调用指南中获取预测接口地址,预测步骤如下: 选择“Headers”设置请求头部,“Content-Type”的值设为“application/json”,“X-Auth-Token”的值设为用户实际获取的token值。 图1 预测设置请求头部 选择“Body”设置请求体,编辑需要预测的数据,最后单击“send”,发送您的预测请求。
  • 输入 系统预置预测分析输入输出模式,适用于预测分析的模型,使用该模式的模型将被标识为预测分析模型。预测请求路径“/”,请求协议为“HTTP”,请求方法为“POST”,调用方需采用“application/json”内容类型,发送预测请求,请求体以“JSON”格式表示,“JSON”字段说明请参见表1。选择该模式时需确保您的模型能处理符合该输入“JSON Schema”格式的输入数据。“JSON Schema”格式说明请参考官方指导。 表1 JSON字段说明 字段名 类型 描述 data Data结构 包含预测数据。“Data结构”说明请参见表2。 表2 Data结构说明 字段名 类型 描述 req_data ReqData结构数组 预测数据列表。 “ReqData”,是“Object”类型,表示预测数据,数据的具体组成结构由业务场景决定。使用该模式的模型,其自定义的推理代码中的预处理逻辑应能正确处理模式所定义的输入数据格式。 预测请求的“JSON Schema”表示如下: { "type": "object", "properties": { "data": { "type": "object", "properties": { "req_data": { "items": [{ "type": "object", "properties": {} }], "type": "array" } } } } }
  • 请求样例 该模式下的推理方式均为输入一张待处理图片,响应的“JSON”根据模型改变而改变。示例如下: 页面预测 Postman调REST接口预测 部署上线成功后,您可以从服务详情页的调用指南中获取预测接口地址。选择“Body”设置请求体,“key”选择为“images”,选择为“File”类型,接着通过选择文件按钮选择需要处理的图片,最后单击“send”,发送您的预测请求。 图1 调用REST接口
  • 输出 推理结果以“JSON”体的形式返回,具体字段请参见表1。 表1 参数说明 字段名 类型 描述 detection_classes 字符串数组 输出物体的检测类别列表,如["yunbao","cat"] detection_boxes 数组,元素为浮点数数组 输出物体的检测框坐标列表,坐标表示为 detection_scores 浮点数数组 输出每种检测列表的置信度,用来衡量识别的准确度。 推理结果的“JSON Schema”表示如下: { "type": "object", "properties": { "detection_classes": { "items": { "type": "string" }, "type": "array" }, "detection_boxes": { "items": { "minItems": 4, "items": { "type": "number" }, "type": "array", "maxItems": 4 }, "type": "array" }, "detection_scores": { "items": { "type": "string" }, "type": "array" } } }
  • 请求样例 该模式下的推理方式均为输入一张待处理图片,推理结果以“JSON”格式返回。示例如下: 页面预测 在服务详情的“预测”页签,上传需要检测的图片,单击“预测”即可获取检测结果。 Postman调REST接口预测 部署上线成功后,您可以从服务详情页的调用指南中获取预测接口地址,预测步骤如下: 选择“Headers”设置请求头部,“Content-Type”的值设为“multipart/form-data”,“X-Auth-Token”的值设为用户实际获取的token值。 图1 设置请求头部 选择“Body”设置请求体,“key”选择为“images”,选择为“File”类型,接着通过选择文件按钮选择需要处理的图片,最后单击“send”,发送您的预测请求。 图2 设置请求体
  • 模型包规范 模型包必须存储在OBS中,且必须以“model”命名。“model”文件夹下面放置模型文件、模型推理代码。 模型推理代码文件必选,其文件名必须为“customize_service.py”,“model”文件夹下有且只能有1个推理代码文件,模型推理代码编写请参见模型推理代码编写说明。 使用模板导入的模型包结构如下所示: model/ │ ├── 模型文件 //必选,不同的框架,其模型文件格式不同,详细可参考模型包示例。 ├── 自定义Python包 //可选,用户自有的Python包,在模型推理代码中可以直接引用。 ├── customize_service.py //必选,模型推理代码,文件名称必须为“customize_service.py”,否则不视为推理代码。
  • 模型包规范 模型包必须存储在OBS中,且必须以“model”命名。“model”文件夹下面放置模型文件、模型推理代码。 模型推理代码文件必选,其文件名必须为“customize_service.py”,“model”文件夹下有且只能有1个推理代码文件,模型推理代码编写请参见模型推理代码编写说明。 使用模板导入的模型包结构如下所示: model/ │ ├── 模型文件 //必选,不同的框架,其模型文件格式不同,详细可参考模型包示例。 ├── 自定义Python包 //可选,用户自有的Python包,在模型推理代码中可以直接引用。 ├── customize_service.py //必选,模型推理代码,文件名称必须为“customize_service.py”,否则不视为推理代码。
  • 模型包规范 模型包必须存储在OBS中,且必须以“model”命名。“model”文件夹下面放置模型文件、模型推理代码。 模型推理代码文件必选,其文件名必须为“customize_service.py”,“model”文件夹下有且只能有1个推理代码文件,模型推理代码编写请参见模型推理代码编写说明。 使用模板导入的模型包结构如下所示: model/ │ ├── 模型文件 //必选,不同的框架,其模型文件格式不同,详细可参考模型包示例。 ├── 自定义Python包 //可选,用户自有的Python包,在模型推理代码中可以直接引用。 ├── customize_service.py //必选,模型推理代码,文件名称必须为“customize_service.py”,否则不视为推理代码。
  • 模型包规范 模型包必须存储在OBS中,且必须以“model”命名。“model”文件夹下面放置模型文件、模型推理代码。 模型推理代码文件必选,其文件名必须为“customize_service.py”,“model”文件夹下有且只能有1个推理代码文件,模型推理代码编写请参见模型推理代码编写说明。 使用模板导入的模型包结构如下所示: model/ │ ├── 模型文件 //必选,不同的框架,其模型文件格式不同,详细可参考模型包示例。 ├── 自定义Python包 //可选,用户自有的Python包,在模型推理代码中可以直接引用。 ├── customize_service.py //必选,模型推理代码,文件名称必须为“customize_service.py”,否则不视为推理代码。
  • 模型包规范 模型包必须存储在OBS中,且必须以“model”命名。“model”文件夹下面放置模型文件、模型推理代码。 模型推理代码文件必选,其文件名必须为“customize_service.py”,“model”文件夹下有且只能有1个推理代码文件,模型推理代码编写请参见模型推理代码编写说明。 使用模板导入的模型包结构如下所示: model/ │ ├── 模型文件 //必选,不同的框架,其模型文件格式不同,详细可参考模型包示例。 ├── 自定义Python包 //可选,用户自有的Python包,在模型推理代码中可以直接引用。 ├── customize_service.py //必选,模型推理代码,文件名称必须为“customize_service.py”,否则不视为推理代码。
  • 模型包规范 模型包必须存储在OBS中,且必须以“model”命名。“model”文件夹下面放置模型文件、模型推理代码。 模型推理代码文件必选,其文件名必须为“customize_service.py”,“model”文件夹下有且只能有1个推理代码文件,模型推理代码编写请参见模型推理代码编写说明。 使用模板导入的模型包结构如下所示: model/ │ ├── 模型文件 //必选,不同的框架,其模型文件格式不同,详细可参考模型包示例。 ├── 自定义Python包 //可选,用户自有的Python包,在模型推理代码中可以直接引用。 ├── customize_service.py //必选,模型推理代码,文件名称必须为“customize_service.py”,否则不视为推理代码。
共100000条