离线训练好的模型有时需要在线上部署,这里介绍PMML与tensorflow serving两种方式,模型都选择线性回归。
1. 基于PMML文件部署
预测模型标记语言(Predictive Model Markup Language,PMML)是一种可以呈现预测分析模型的事实标准语言。通过制定标准,各种开发语言都可以使用相应的包,把模型文件转成这种中间格式,而另外一种开发语言,可以使用相应的包导入该文件做线上预测。
基于PMML文件部署流程主要分为以下两步:
- 离线训练、导出模型
- 线上部署线上部署两步
1.1 离线训练、导出模型
训练使用scikit-learn,使用sklearn2pmml将模型导出成pmml格式。
from sklearn import linear_model
import numpy as np
from sklearn.metrics import mean_squared_error
from sklearn2pmml import PMMLPipeline
# 训练数据
x_train = np.array([1.0, 3.0, 5.0, 7.0, 9.0]).reshape(-1, 1)
y_train = np.array([5.69277859, 7.6934948, 13.6772203, 15.488286, 21.2844448])
# 评估数据
x_eval = np.array([2.0, 4.0, 5.0, 8.0, 10.0]).reshape(-1, 1)
y_eval = np.array([5.68863153, 10.7271547, 14.6714172, 20.2158928, 24.5952759])
#训练模型
model = linear_model.LinearRegression()
pipeline = PMMLPipeline([("linearRegression", model)])
pipeline.fit(x_train,y_train)
#打印回归系数(斜率)
print('coef_:%.3f' % model.coef_)
# coef_:1.949
#打印截距
print('intercept_:%.3f' % model.intercept_)
# intercept_:3.023
# 预测数据
y_predict = pipeline.predict(x_eval)
print(y_predict)
# [ 6.92052636 10.81833872 12.7672449 18.61396344 22.5117758 ]
# 打印误差((y_test-LR.predict(X_test))**2).mean()
print('Mean squared error: %.3f' % mean_squared_error(y_eval, y_predict))
# Mean squared error: 2.412
#打印准确率accuracy
print('score: %.3f' % model.score(x_eval,y_eval))
# score: 0.946
# 导出模型文件
from sklearn2pmml import sklearn2pmml
sklearn2pmml(pipeline, "LinearRegression.pmml", with_repr = True)
生成的LinearRegression.pmml如下所示,因为是线性回归,所以模型比较简单,一个输入x1,一个输出y,coefficient是1.9489061810000003,intercept是3.022713993。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PMML xmlns="http://www.dmg.org/PMML-4_4" xmlns:data="http://jpmml.org/jpmml-model/InlineTable" version="4.4">
<Header>
<Application name="JPMML-SkLearn" version="1.6.35"/>
<Timestamp>2022-06-05T10:05:53Z</Timestamp>
</Header>
<MiningBuildTask>
<Extension name="repr">PMMLPipeline(steps=[('linearRegression', LinearRegression())])</Extension>
</MiningBuildTask>
<DataDictionary>
<DataField name="y" optype="continuous" dataType="double"/>
<DataField name="x1" optype="continuous" dataType="double"/>
</DataDictionary>
<RegressionModel functionName="regression" algorithmName="sklearn.linear_model._base.LinearRegression">
<MiningSchema>
<MiningField name="y" usageType="target"/>
<MiningField name="x1"/>
</MiningSchema>
<RegressionTable intercept="3.022713993">
<NumericPredictor name="x1" coefficient="1.9489061810000003"/>
</RegressionTable>
</RegressionModel>
</PMML>
1.2 线上部署
需要引入如下依赖
<dependency>
<groupId>org.jpmml</groupId>
<artifactId>pmml-evaluator</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.jpmml</groupId>
<artifactId>pmml-evaluator-extension</artifactId>
<version>1.5.1</version>
</dependency>
示例代码如下所示, 首先加载模型,然后构建参数进行评估。当x1是2.0是,预测值为6.920526355000001。
public class PMMLDemo {
public static void main(String[] args) throws Exception {
Map<String, Object> paramData = new HashMap<>();
paramData.put("x1", 2.0);
File file = new ClassPathResource("LinearRegression.pmml").getFile();
FileInputStream inputStream = new FileInputStream(file);
//解析pmml文件,实际上是用JAXB做xml的解析
PMML pmml = PMMLUtil.unmarshal(inputStream);
//生成评估器
ModelEvaluator<?> evaluate = new ModelEvaluatorBuilder(pmml).build();
//构建输入参数
Map<FieldName, FieldValue> arguments = new LinkedHashMap<>();
List<InputField> inputFields = evaluate.getInputFields();
for (InputField inputField : inputFields) {
//将参数通过模型对应的名称进行添加
FieldName inputFieldName = inputField.getName(); //获取模型中的参数名
Object paramValue = paramData.get(inputFieldName.getValue()); //获取模型参数名对应的参数值
FieldValue fieldValue = inputField.prepare(paramValue); //将参数值填入模型中的参数中
arguments.put(inputFieldName, fieldValue); //存放在map列表中
}
//开始评估
Map<FieldName, ?> target = evaluate.evaluate(arguments);
//获取评估结果
List<TargetField> targetFields = evaluate.getTargetFields();
Object targetFieldValue = target.get(targetFields.get(0).getFieldName());
System.out.println("targetFieldValue: " + targetFieldValue);
// targetFieldValue: 6.920526355000001
System.out.println("target: " + target);
// target: {y=6.920526355000001}
}
}
2. tensorflow serving
TensorFlow Serving是TensorFlow推出的原生的模型serving服务器。本质上讲TensorFlow Serving的工作流程和PMML类的工具的流程是一致的。不同之处在于TensorFlow定义了自己的模型序列化标准。利用TensorFlow自带的模型序列化函数可将训练好的模型参数和结构保存至某文件路径。
主要过程如下所示:
- 准备protobuf模型文件。
- 安装tensorflow serving。
- 启动tensorflow serving 服务。
- 向API服务发送请求,获取预测结果。
2.1 训练并保存模型
import numpy as np
import tensorflow as tf
from tensorflow.keras import models,layers,optimizers
from keras.models import Sequential
from keras.layers import Dense
# 训练数据
x_train = np.array([1.0, 3.0, 5.0, 7.0, 9.0]).reshape(-1, 1)
y_train = np.array([5.69277859, 7.6934948, 13.6772203, 15.488286, 21.2844448])
# 评估数据
x_eval = np.array([2.0, 4.0, 5.0, 8.0, 10.0]).reshape(-1, 1)
y_eval = np.array([5.68863153, 10.7271547, 14.6714172, 20.2158928, 24.5952759])
model = Sequential()
model.add(Dense(1, input_dim=1, activation="linear")) # 简单线性
sgd=optimizers.SGD(lr=0.01) # 激励函数: SGD
model.compile(loss="mse", optimizer=sgd, metrics=["mse"])
model.fit(x_train, y_train, epochs=1000, batch_size=32, verbose=0)
# 训练模型
mse, mae = model.evaluate(x_eval, y_eval)
tf.print("w = ",model.layers[0].kernel)
tf.print("b = ",model.layers[0].bias)
print("mse = {}, mae = {}", mse, mae)
print(model.predict(x_eval))
# 将模型保存成pb格式文件
export_path = "./data/linear_model/"
version = "1" #后续可以通过版本号进行模型版本迭代与管理
model.save(export_path+version, save_format="tf")
最终保存的模型如下所示:
# ls data/linear_model/1/
assets keras_metadata.pb saved_model.pb variables
2.2 安装并启动tensorflow serving
官方极力推荐通过docker形式安装并启动tensorflow serving,首先拉取镜像,然后启动镜像
# 01 拉取镜像
docker pull tensorflow/serving
# 02 启动镜像
docker run -p 8501:8501 \
--mount type=bind,\
source=${PWD}/data/linear_model/,\
target=/models/linear_regression \
-e MODEL_NAME=linear_regression -t tensorflow/serving:latest &
2.3 发送请求,预测结果
对于x_eval = np.array([2.0, 4.0, 5.0, 8.0, 10.0])
, 预测的结果为[6.90578794], [10.8100748], [12.7622185], [18.6186485], [22.5229359]
。
import numpy as np
x_eval = np.array([2.0, 4.0, 5.0, 8.0, 10.0]).reshape(-1, 1).tolist()
y_eval = np.array([5.68863153, 10.7271547, 14.6714172, 20.2158928, 24.5952759])
import json
data = json.dumps({"signature_name": "serving_default", "instances": x_eval})
import requests
headers = {"content-type": "application/json"}
json_response = requests.post('http://localhost:8501/v1/models/linear_regression:predict', data=data, headers=headers)
predictions = json.loads(json_response.text)['predictions']
print(predictions)
# [[6.90578794], [10.8100748], [12.7622185], [18.6186485], [22.5229359]]