https://zhuanlan.zhihu.com/p/429842534
PMML全称预言模型标记模型(Predictive Model Markup Language),以XML 为载体呈现数据挖掘模型。
PMML的一个优点是可以跨语言跨平台,而不需考虑分析和预测过程中的具体实现细节。但其实对于很多灵活的自定义操作,还是需要考虑具体的实现细节。
本质上来说,pmml的角色类似于csv,python中的数据以csv的格式存储,然后java可以使用csvreader或c++ 使用csv-parser 来读取csv文件,从而实现不同编程语言之间的数据传输。
再说的简单点,模型的训练过程中虽然存在很多不确定性,但是目前业界应用的模型的推断的过程中大部分情况下是确定性的,既然是确定性的就可以通过语言来描述,比如说xgb模型,预测某个样本,先判断落在哪个叶子结点上,然后叶子结点权重加权求和再加个activation就可以得到预测结果了,既然可以通过语言描述,自然可以通过代码来描述,使用不同语言的代码来实现这个逻辑就可以了,但是这样明显太麻烦了,所以jpmml以及sklearn2pmml的项目出现了,把这些繁琐的逻辑都封装成现成的函数,实现的过程是类似的,只不过可以通过api来一键完成。
pmml使得模型的部署省去了很多的麻烦。主要原因在于jpmml以及其下属的sklearn2pmml提供了直接从python的sklearn api 到java代码的快速转换。说白了有人给你写了转换的接口,所以才能这么方便快速,简单来说,其实jpmml做的事情是把sklearn里的大部分api都用java实现了一遍,然后sklearn2pmml 转化的时候直接进行匹配,因此,一些自定义的py操作是没法进行正常转换的因为没有对应的java代码实现。如下
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import pandas as pd
from sklearn import tree
from sklearn2pmml.pipeline import PMMLPipeline
from sklearn2pmml import sklearn2pmml
import os
os.environ["PATH"] += os.pathsep + 'C:/Program Files/Java/jdk1.8.0_171/bin'
X=[[1,2,3,1],[2,4,1,5],[7,8,3,6],[4,8,4,7],[2,5,6,9]]
y=[0,1,0,2,1]
pipeline = PMMLPipeline([("classifier", tree.DecisionTreeClassifier(random_state=9))]);
pipeline.fit(X,y)
sklearn2pmml(pipeline, ".\demo.pmml", with_repr = True)
如果是sklearn2pmml转化的结果,很多时候包含3部分,注释直接写下面了:
##################### 文件头部分,不必在意##################
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PMML xmlns="http://www.dmg.org/PMML-4_3" version="4.3">
<Header>
<Application name="JPMML-SkLearn" version="1.5.3"/>
<Timestamp>2018-06-24T05:47:17Z</Timestamp>
</Header>
########### MiningBuildTask 根据你在py中定义的pmmlpipline自动生成相应的pipeline描述信息##############
<MiningBuildTask>
<Extension>PMMLPipeline(steps=[('classifier', DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False, random_state=9,
splitter='best'))])</Extension>
</MiningBuildTask>
################## 定义你建模过程中使用到的全部的变量以及对应类型等#########################
<DataDictionary>
<DataField name="y" optype="categorical" dataType="integer">
<Value value="0"/>
<Value value="1"/>
<Value value="2"/>
</DataField>
<DataField name="x3" optype="continuous" dataType="float"/>
<DataField name="x4" optype="continuous" dataType="float"/>
</DataDictionary>
############################# 定义数据预处理和特征工程部分 ###########################
<TransformationDictionary>
<DerivedField name="double(x3)" optype="continuous" dataType="double">
<FieldRef field="x3"/>
</DerivedField>
<DerivedField name="double(x4)" optype="continuous" dataType="double">
<FieldRef field="x4"/>
</DerivedField>
</TransformationDictionary>
######################## 模型逻辑部分,每个模型的描述信息差异很大,但是本质上都是转化为具体的一大堆的类似规则
一样的字符串了##################################################################
<TreeModel functionName="classification" missingValueStrategy="nullPrediction" splitCharacteristic="multiSplit">
<MiningSchema>
<MiningField name="y" usageType="target"/>
<MiningField name="x3"/>
<MiningField name="x4"/>
</MiningSchema>
<Output>
<OutputField name="probability(0)" optype="continuous" dataType="double" feature="probability" value="0"/>
<OutputField name="probability(1)" optype="continuous" dataType="double" feature="probability" value="1"/>
<OutputField name="probability(2)" optype="continuous" dataType="double" feature="probability" value="2"/>
</Output>
<Node>
<True/>
<Node>
<SimplePredicate field="double(x3)" operator="lessOrEqual" value="3.5"/>
<Node score="1" recordCount="1.0">
<SimplePredicate field="double(x3)" operator="lessOrEqual" value="2.0"/>
<ScoreDistribution value="0" recordCount="0.0"/>
<ScoreDistribution value="1" recordCount="1.0"/>
<ScoreDistribution value="2" recordCount="0.0"/>
</Node>
<Node score="0" recordCount="2.0">
<True/>
<ScoreDistribution value="0" recordCount="2.0"/>
<ScoreDistribution value="1" recordCount="0.0"/>
<ScoreDistribution value="2" recordCount="0.0"/>
</Node>
</Node>
<Node score="2" recordCount="1.0">
<SimplePredicate field="double(x4)" operator="lessOrEqual" value="8.0"/>
<ScoreDistribution value="0" recordCount="0.0"/>
<ScoreDistribution value="1" recordCount="0.0"/>
<ScoreDistribution value="2" recordCount="1.0"/>
</Node>
<Node score="1" recordCount="1.0">
<True/>
<ScoreDistribution value="0" recordCount="0.0"/>
<ScoreDistribution value="1" recordCount="1.0"/>
<ScoreDistribution value="2" recordCount="0.0"/>
</Node>
</Node>
</TreeModel>
</PMML>
可以看到里面就是决策树的树结构节点的各个参数,以及输入值。我们的输入被定义为x1-x4,输出定义为y;
这些不是纯粹自动生成的,而是开发预先写好的转换逻辑,就类似于开发人员写了一个字典,定义了py代码到pmml的映射。因此,你要自己用numpy实现了一个model,需要自己去考虑model转pmml的逻辑(那特么还不如自己用java来写model)
现阶段,传统的机器学习模型和表格数据的应用走jpmml的这一套流程没啥大问题,复杂的逻辑有一个比较好的方式就是python实现一遍,然后按照jpmml的官方提供的实现的java代码的模版把逻辑用java重新写一遍,然后测试二者的输出结果是否相同(java可以使用ijava来实现交互式编程,测试的时候会比较方便)。类似于在sklearn中通过集成其base下的各种父类然自定义sklearn api。
深度这一套就不要走pmml了,太坑,直接走tf或torch官方提供的案例撸一遍熟悉一下就行了。