标签:模型部署
PMML
对于算法工程师来说,如何将训练好的模型成功部署上线也是工作的重要环节之一。当然,对于python训练的模型,在python环境下部署是个很自然的想法,但是在我本人大部分的实际项目中,都是需要实现模型的跨平台(比如java环境)部署,而PMML(Predictive Model Markup Language, 预测模型标记语言)就是解决跨平台部署的一种方式。
1. PMML是什么? 🤔
PMML是一套基于XML标准,与平台和环境无关的模型表示语言。它主要是通过了XML schema定义和储存了一个算法模型的核心元素:
- 数据字典:描述输入数据
- 数据转换:定义了原始数据数据预处理的方式,比如标准化、缺失值处理、哑变量的生成等
- 模型定义:模型的类型及参数,比如树模型的分裂节点等
- 模型输出:模型的输出结果
不难看出,通过定义PMML中的所有核心元素,可以完成数据挖掘的所有流程,即后端开发人员部署时,只需要将数据读取后,调用PMML文件,然后就能得到输出数据,而不用关注数据转化、模型参数等问题,加快了模型的部署效率。
2. Python怎么实现PMML文件打包? 💻
本文采用Iris
鸢尾花数据集作为例子,通过Pipeline的方式训练一个XGBoost分类器,然后将其打包输出PMML文件。Pipeline的方式是指,将许多不同的数据预处理步骤和一个或者多个模型,像拼接管道一样按顺序合并在一起,从而完成数据挖掘的所有阶段,简化模型的训练和部署。
Iris
鸢尾花数据集,包含4个特征变量(sepal length, sepal width, petal length和petal width)以及1个类别标签(0-setosa, 1-versicolor, 2-virginica),我们的模型目标是:基于特征变量,将3类鸢尾花区分开。
Step 0: 导入所需包
# import packages
import pandas as pd
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn_pandas import DataFrameMapper
from sklearn.preprocessing import StandardScaler
from sklearn2pmml import PMMLPipeline
from sklearn2pmml import sklearn2pmml
from xgboost import XGBClassifier
Step 1: 使用python训练一个模型
# read data
iris = datasets.load_iris()
X = pd.DataFrame(iris.data, columns=iris.feature_names)
y = iris.target
# split train and test dataset
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=123)
# define datamapper
mapper = DataFrameMapper([
(["sepal length (cm)", "sepal width (cm)"], StandardScaler()),
(["petal length (cm)"], None)
], df_out=True)
# define a model
xgb = XGBClassifier(n_estimators=5, seed=123)
# make a pipeline
pip_model = PMMLPipeline([
('mapper', mapper),
("classifier", xgb)]
)
# train a model & predict on test data
pip_model.fit(X_train, y_train)
pred_prob_pip = pip_model.predict_proba(X_test)
在例子中,我们先使用DataFrameMapper
定义了输入的特征变量只包含3个(去除了petal width),并且只对sepal length和sepal width进行标准化处理,而petal length则不做任何处理。然后定义了一个XGBClassifier
,最后通过PMMLPipeline
将两个数据处理流程和模型结合在一起进行训练。
Step 2: 保存为PMML文件
# save model
sklearn2pmml(pip_model, "iris_model.pmml", with_repr = True)
输出的PMML文件可以由任何编辑器打开,形式如下:
Step 3: python中调用PMML校验结果
from pypmml import Model
model = Model.fromFile('iris_model.pmml')
pred_prob_reloaded_model = model.predict(X_test)
重新读取PMML文件和原Pipeline预测结果在python环境下应该保持一致。
3. 可能会遇到什么坑?😂
上面的例子实现很简单,但是真正部署时,总会遇到一些坑,毕竟大家都是在踩坑中成长的嘛。下面就列出我在部署PMML过程中遇见的一些问题,仅供参考。
-
坑1: 可以训练模型但是却不能生成pmml文件
可能是由于环境原因导致,sklearn/sklearn2pmml/sklearn_pandas需要适配,而且当sklearn2pmml版本过高而打jar版本过低,则会导致文件无法读取的问题,本文使用版本信息如下:
- sklearn2pmml = 0.53.0
- sklearn = 0.23.1
- sklearn_pandas = 2.2.0
-
坑2: Java调用PMML和原Pipeline模型预测不一致(正常情况下,小数点后5位应该保持一致)
导致这个问题的原因可能是两个: (1)数据类型不一致;(2)缺失值的处理。
-
数据类型不一致,可以尝试以下2个解决方案:
- 因为生成pmml文件时,所有的变量默认是double类型,而有些分类变量是string类型,所以可以考虑对分类变量进行labelencoder处理成数值型
- 将pmml文件中double改成string
-
缺失值的处理:当数据中存在缺失值,且不是为nan,而是以-9999代替时,PMML文件需要在datamapper和模型中同时定义
-9999
为缺失值, 否则就会出现预测不一致现象。此时应该# define datamapper mapper = DataFrameMapper([ (["sepal length (cm)", "sepal width (cm)"], StandardScaler()), (["petal length (cm)"], ContinuousDomain(missing_values=-9999.0, with_data = False)) ], df_out=True) # define a model xgb = XGBClassifier(n_estimators=5, seed=123, missing=-9999.0)
注:使用
ContinuousDomain
后会根据训练数据找到每个特征的数据范围,而当测试数据数显超出此范围的数值后会报错,因此需要设置with_data=False
来去除这一限制。
-
-
坑3: 原Pipeline模型和python读取PMML文件模型预测结果不一致
这种情况很大程度上也和缺失值有关。如果在正确处理坑2的后,还出现此类问题,可能是由于python读取PMML文件模型预测的时候,对于缺失特征也传入并赋值
-9999
。正确做法是,使用pypmml调用PMML文件,应该对缺失特征不传入,或者赋值为nan,这样pypmml调用PMML文件和原Pipeline模型预测结果就能保持一致。
敬请期待
- Python构造Pipeline模型
- 使用Scala调用python训练的PMML
禁止无授权转载,转载请联系作者,否则作者将保留一切合法权利。