PMML讲解及使用

1. PMML概述

PMML全称预言模型标记语言(Predictive Model Markup Language),利用XML描述和存储数据挖掘模型,是一个已经被W3C所接受的标准。使用pmml储存好模型之后,任何软件栈都可以调用pmml储存好的模型。主要用于跨平台的机器学习模型部署。

 

2. PMML模型的生成和加载相关类库

PMML模型的生成相关的库需要看我们使用的离线训练库。如果我们使用的是sklearn,那么可以使用sklearn2pmml这个python库来做模型文件的生成,这个库安装很简单,使用"pip install sklearn2pmml"即可,相关的使用我们后面会有一个demo。如果使用的是Spark MLlib, 这个库有一些模型已经自带了保存PMML模型的方法,可惜并不全。如果是R,则需要安装包"XML"和“PMML”。此外,JAVA库JPMML可以用来生成R,SparkMLlib,xgBoost,Sklearn的模型对应的PMML文件。github地址是:https://github.com/jpmml/jpmml。

加载PMML模型需要目标环境支持PMML加载的库,如果是JAVA,则可以用JPMML来加载PMML模型文件。

                         PMML åå«æ°æ®é¢å¤çåæ°æ®åå¤ç以åé¢æµæ¨¡åæ¬èº«

pmml支持的model有 :

3. PMML模型生成和加载示例

下面我们给一个示例,使用sklearn生成一个决策树模型,用sklearn2pmml生成模型文件,用JPMML加载模型文件,并做预测。

首先是用用sklearn生成一个决策树模型,由于我们是需要保存PMML文件,所以最好把模型先放到一个Pipeline数组里面。这个数组里面除了我们的决策树模型以外,还可以有归一化,降维等预处理操作,这里作为一个示例,我们Pipeline数组里面只有决策树模型。代码如下:

  1.  
    import numpy as np
  2.  
    import matplotlib.pyplot as plt
  3.  
    %matplotlib inline
  4.  
    import pandas as pd
  5.  
    from sklearn import tree
  6.  
    from sklearn2pmml.pipeline import PMMLPipeline
  7.  
    from sklearn2pmml import sklearn2pmml
  8.  
     
  9.  
    import os
  10.  
    os.environ["PATH"] += os.pathsep + 'C:/Program Files/Java/jdk1.8.0_171/bin'
  11.  
     
  12.  
    X=[[1,2,3,1],[2,4,1,5],[7,8,3,6],[4,8,4,7],[2,5,6,9]]
  13.  
    y=[0,1,0,2,1]
  14.  
    pipeline = PMMLPipeline([("classifier", tree.DecisionTreeClassifier(random_state=9))]);
  15.  
    pipeline.fit(X,y)
  16.  
     
  17.  
    sklearn2pmml(pipeline, ".\demo.pmml", with_repr = True)

上面这段代码做了一个非常简单的决策树分类模型,只有5个训练样本,特征有4个,输出类别有3个。实际应用时,我们需要将模型调参完毕后才将其放入PMMLPipeline进行保存。运行代码后,我们在当前目录会得到一个PMML的XML文件,可以直接打开看,内容大概如下:

  1.  
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  2.  
    <PMML xmlns="http://www.dmg.org/PMML-4_3" version="4.3">
  3.  
    <Header>
  4.  
    <Application name="JPMML-SkLearn" version="1.5.3"/>
  5.  
    <Timestamp>2018-06-24T05:47:17Z</Timestamp>
  6.  
    </Header>
  7.  
    <MiningBuildTask>
  8.  
    <Extension>PMMLPipeline(steps=[('classifier', DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None,
  9.  
    max_features=None, max_leaf_nodes=None,
  10.  
    min_impurity_decrease=0.0, min_impurity_split=None,
  11.  
    min_samples_leaf=1, min_samples_split=2,
  12.  
    min_weight_fraction_leaf=0.0, presort=False, random_state=9,
  13.  
    splitter='best'))])</Extension>
  14.  
    </MiningBuildTask>
  15.  
    <DataDictionary>
  16.  
    <DataField name="y" optype="categorical" dataType="integer">
  17.  
    <Value value="0"/>
  18.  
    <Value value="1"/>
  19.  
    <Value value="2"/>
  20.  
    </DataField>
  21.  
    <DataField name="x3" optype="continuous" dataType="float"/>
  22.  
    <DataField name="x4" optype="continuous" dataType="float"/>
  23.  
    </DataDictionary>
  24.  
    <TransformationDictionary>
  25.  
    <DerivedField name="double(x3)" optype="continuous" dataType="double">
  26.  
    <FieldRef field="x3"/>
  27.  
    </DerivedField>
  28.  
    <DerivedField name="double(x4)" optype="continuous" dataType="double">
  29.  
    <FieldRef field="x4"/>
  30.  
    </DerivedField>
  31.  
    </TransformationDictionary>
  32.  
    <TreeModel functionName="classification" missingValueStrategy="nullPrediction" splitCharacteristic="multiSplit">
  33.  
    <MiningSchema>
  34.  
    <MiningField name="y" usageType="target"/>
  35.  
    <MiningField name="x3"/>
  36.  
    <MiningField name="x4"/>
  37.  
    </MiningSchema>
  38.  
    <Output>
  39.  
    <OutputField name="probability(0)" optype="continuous" dataType="double" feature="probability" value="0"/>
  40.  
    <OutputField name="probability(1)" optype="continuous" dataType="double" feature="probability" value="1"/>
  41.  
    <OutputField name="probability(2)" optype="continuous" dataType="double" feature="probability" value="2"/>
  42.  
    </Output>
  43.  
    <Node>
  44.  
    <True/>
  45.  
    <Node>
  46.  
    <SimplePredicate field="double(x3)" operator="lessOrEqual" value="3.5"/>
  47.  
    <Node score="1" recordCount="1.0">
  48.  
    <SimplePredicate field="double(x3)" operator="lessOrEqual" value="2.0"/>
  49.  
    <ScoreDistribution value="0" recordCount="0.0"/>
  50.  
    <ScoreDistribution value="1" recordCount="1.0"/>
  51.  
    <ScoreDistribution value="2" recordCount="0.0"/>
  52.  
    </Node>
  53.  
    <Node score="0" recordCount="2.0">
  54.  
    <True/>
  55.  
    <ScoreDistribution value="0" recordCount="2.0"/>
  56.  
    <ScoreDistribution value="1" recordCount="0.0"/>
  57.  
    <ScoreDistribution value="2" recordCount="0.0"/>
  58.  
    </Node>
  59.  
    </Node>
  60.  
    <Node score="2" recordCount="1.0">
  61.  
    <SimplePredicate field="double(x4)" operator="lessOrEqual" value="8.0"/>
  62.  
    <ScoreDistribution value="0" recordCount="0.0"/>
  63.  
    <ScoreDistribution value="1" recordCount="0.0"/>
  64.  
    <ScoreDistribution value="2" recordCount="1.0"/>
  65.  
    </Node>
  66.  
    <Node score="1" recordCount="1.0">
  67.  
    <True/>
  68.  
    <ScoreDistribution value="0" recordCount="0.0"/>
  69.  
    <ScoreDistribution value="1" recordCount="1.0"/>
  70.  
    <ScoreDistribution value="2" recordCount="0.0"/>
  71.  
    </Node>
  72.  
    </Node>
  73.  
    </TreeModel>
  74.  
    </PMML>


1、sklearn生成pmml文件 :

  1.  
    pipeline = PMMLPipeline([
  2.  
     
  3.  
    ('mapper', mapper),
  4.  
    ("classifier", linear_model.LinearRegression())
  5.  
    ])
  6.  
    pipeline.fit(heart_data[heart_data.columns.difference(["chd"])], heart_data["chd"])
  7.  
     
  8.  
    sklearn2pmml(pipeline, "lrHeart.xml", with_repr = True)

2、jpmml加载pmml文件 
先添加maven依赖,

  1.  
    <dependency>
  2.  
    <groupId>org.jpmml</groupId>
  3.  
    <artifactId>pmml-evaluator</artifactId>
  4.  
    <version>1.4.2</version>
  5.  
    </dependency>
  6.  
     
  7.  
    <dependency>
  8.  
     
  9.  
    <groupId>org.jpmml</groupId>
  10.  
    <artifactId>pmml-evaluator-extension</artifactId>
  11.  
    <version>1.4.2</version>
  12.  
    </dependency>

 然后加载pmml模型和调用

  1.  
    PMML pmml;
  2.  
    try(InputStream is = ...){
  3.  
     
  4.  
    pmml = org.jpmml.model.PMMLUtil.unmarshal(is);
  5.  
    }

4. PMML 深度解析

您已了解了何为 PMML 及其重要性,现在让我们来深入探究这种语言本身。如上所述,PMML 的结构反映了常用于创建预测解决方案的八大步骤,从在 “数据词典” 步骤中定义原始输入数据字段到在 “模型验证” 步骤中验证模型是否得到正确部署。

清单 1 展示了一个含有三个字段的解决方案中 PMML 元素 DataDictionary 的定义,这三个字段是:数值型输入字段 Value、分类输入字段 Element 和数值型输出字段 Risk

清单 1. DataDictionary 元素

1

2

3

4

5

6

7

8

9

10

11

12

<DataDictionary numberOfFields="3">

    <DataField dataType="double" name="Value" optype="continuous">

        <Interval closure="openClosed" rightMargin="60" />

    </DataField>

    <DataField dataType="string" name="Element" optype="categorical">

        <Value property="valid" value="Magnesium" />

        <Value property="valid" value="Sodium" />

        <Value property="valid" value="Calcium" />

        <Value property="valid" value="Radium" />

    </DataField>

    <DataField dataType="double" name="Risk" optype="continuous" />

</DataDictionary>

请注意,对于字段 Value,范围从负无穷大到 60 的值是有效值。高于 60 的值被定义为无效值。(尽管在此没有显示,您可以使用 PMML 元素 MiningSchema 为无效值和遗漏值定义合适的处理方法。)考虑到字段 Element 是分类的,有效值被明确地列出。如果该特定字段的数据提要包含元素 Iron,将该元素作为无效值处理。

图 2 展示了神经网络模型的图形表示,其中输入层包含 3 个神经元,隐藏层包含 2 个神经元,输出层包含 1 个神经元。如您所期望的,PMML 可以完全呈现这样一个结构。

图 2. 一个简单的神经网络模型,其中在对预测进行计算之前,数据经过一系列层

一个简单的神经网络模型,其中在对预测进行计算之前,数据经过一系列层

清单 2 展示了隐藏层及其神经元以及输入层(0、1 和 2)和隐藏层(3 和 4)中神经元的连接权重的定义。

清单 2. 在 PMML 中定义神经层及其神经元

1

2

3

4

5

6

7

8

9

10

11

12

<NeuralLayer numberOfNeurons="2">

    <Neuron id="3" bias="-3.1808306946637">

        <Con from="0" weight="0.119477686963504" />

        <Con from="1" weight="-1.97301278112877" />

        <Con from="2" weight="3.04381251760906" />

    </Neuron>

    <Neuron id="4" bias="0.743161353729323">

        <Con from="0" weight="-0.49411146396721" />

        <Con from="1" weight="2.18588757615864" />

        <Con from="2" weight="-2.01213331163562" />

    </Neuron>

</NeuralLayer>

PMML 不是一件艰难的事。其复杂程度反映了其呈现的建模技术的复杂程度。事实上,它揭开了许多人感到神秘的预测分析的秘密和黑匣子。利用 PMML,任何预测解决方案都可以采用同样的顺序用同一种语言元素呈现。

在公司中,PMML 不仅可以作为应用程序之间也可以作为部门、服务提供商及外部供应商之间的混合语。在这种情况下,PMML 就成为定义预测解决方案交流的单一、清晰流程的一个标准。

5. PMML总结与思考

PMML的确是跨平台的利器,但是是不是就没有缺点呢?肯定是有的!

    第一个就是PMML为了满足跨平台,牺牲了很多平台独有的优化,所以很多时候我们用算法库自己的保存模型的API得到的模型文件,要比生成的PMML模型文件小很多。同时PMML文件加载速度也比算法库自己独有格式的模型文件加载慢很多。

    第二个就是PMML加载得到的模型和算法库自己独有的模型相比,预测会有一点点的偏差,当然这个偏差并不大。比如某一个样本,用sklearn的决策树模型预测为类别1,但是如果我们把这个决策树落盘为一个PMML文件,并用JAVA加载后,继续预测刚才这个样本,有较小的概率出现预测的结果不为类别1.

    第三个就是对于超大模型,比如大规模的集成学习模型,比如xgboost, 随机森林,或者tensorflow,生成的PMML文件很容易得到几个G,甚至上T,这时使用PMML文件加载预测速度会非常慢,此时推荐为模型建立一个专有的环境,就没有必要去考虑跨平台了。

    此外,对于TensorFlow,不推荐使用PMML的方式来跨平台。可能的方法一是TensorFlow serving,自己搭建预测服务,但是会稍有些复杂。另一个方法就是将模型保存为TensorFlow的模型文件,并用TensorFlow独有的JAVA库加载来做预测。

【转载】:https://www.cnblogs.com/pinard/p/9220199.html

                  https://www.jianshu.com/p/0eb9b2c904a9

                  https://www.ibm.com/developerworks/cn/opensource/ind-PMML1/

posted @ 2020-09-29 23:45  牧之丨  阅读(3563)  评论(0编辑  收藏  举报