[机器学习系列三]机器学习部署—PMML与TensorFlow serving

离线训练好的模型有时需要在线上部署,这里介绍PMML与tensorflow serving两种方式,模型都选择线性回归。

1. 基于PMML文件部署

预测模型标记语言(Predictive Model Markup Language,PMML)是一种可以呈现预测分析模型的事实标准语言。通过制定标准,各种开发语言都可以使用相应的包,把模型文件转成这种中间格式,而另外一种开发语言,可以使用相应的包导入该文件做线上预测。
基于PMML文件部署流程主要分为以下两步:

  1. 离线训练、导出模型
  2. 线上部署线上部署两步
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自带的模型序列化函数可将训练好的模型参数和结构保存至某文件路径。
主要过程如下所示:

  1. 准备protobuf模型文件。
  2. 安装tensorflow serving。
  3. 启动tensorflow serving 服务。
  4. 向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]]
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容