[机器学习]PMML预测模型标记语言
?
1.簡(jiǎn)述
??????? PMML全稱預(yù)言模型標(biāo)記模型(Predictive Model Markup Language),以XML 為載體呈現(xiàn)數(shù)據(jù)挖掘模型。PMML 允許您在不同的應(yīng)用程序之間輕松共享預(yù)測(cè)分析模型。因此,您可以在一個(gè)系統(tǒng)中定型一個(gè)模型,在 PMML 中對(duì)其進(jìn)行表達(dá),然后將其移動(dòng)到另一個(gè)系統(tǒng)中,而不需考慮分析和預(yù)測(cè)過程中的具體實(shí)現(xiàn)細(xì)節(jié)。使得模型的部署擺脫了模型開發(fā)和產(chǎn)品整合的束縛。通俗地講, 我有一個(gè)決策樹模型, 使用效果也不錯(cuò), 那么就可以把樹的結(jié)構(gòu)(節(jié)點(diǎn)間的父子關(guān)系, 節(jié)點(diǎn)內(nèi)的豐富信息 等)序列化為PMML文件, 共享給其他人使用.? 這樣無論你的模型是sklearn,R還是Spark MLlib生成的,我們都可以將其轉(zhuǎn)化為標(biāo)準(zhǔn)的XML格式來存儲(chǔ)。當(dāng)我們需要將這個(gè)PMML的模型用于部署的時(shí)候,可以使用目標(biāo)環(huán)境的解析PMML模型的庫來加載模型,并做預(yù)測(cè)。
???????
2. 主要結(jié)構(gòu)
PMML 文件的結(jié)構(gòu)遵從了用于構(gòu)建預(yù)測(cè)解決方案的常用步驟,包括:
這是一種數(shù)據(jù)分析階段的產(chǎn)品,可以識(shí)別和定義哪些輸入數(shù)據(jù)字段對(duì)于解決眼前的問題是最有用的。這可以包括數(shù)值、順序和分類字段。
定義了處理缺少值和離群值的策略。這非常有用,因?yàn)橥ǔG闆r,當(dāng)將模型應(yīng)用于實(shí)踐時(shí),所需的輸入數(shù)據(jù)字段可能為空或者被誤呈現(xiàn)。
定義了將原始輸入數(shù)據(jù)預(yù)處理至派生字段所需的計(jì)算。派生字段(有時(shí)也稱為特征檢測(cè)器)對(duì)輸入字段進(jìn)行合并或修改,以獲取更多相關(guān)信息。例如,為了預(yù)測(cè)停車所需的制動(dòng)壓力,一個(gè)預(yù)測(cè)模型可能將室外溫度和水的存在(是否在下雨?)作為原始數(shù)據(jù)。派生字段可能會(huì)將這兩個(gè)字段結(jié)合起來,以探測(cè)路上是否結(jié)冰。然后結(jié)冰字段被作為模型的直接輸入來預(yù)測(cè)停車所需的制動(dòng)壓力。
定義了用于構(gòu)建模型的結(jié)構(gòu)和參數(shù)。PMML 涵蓋了多種統(tǒng)計(jì)技術(shù)。例如,為了呈現(xiàn)一個(gè)神經(jīng)網(wǎng)絡(luò),它定義了所有的神經(jīng)層和神經(jīng)元之間的連接權(quán)重。對(duì)于一個(gè)決策樹來說,它定義了所有樹節(jié)點(diǎn)及簡(jiǎn)單和復(fù)合謂語。
定義了預(yù)期模型輸出。對(duì)于一個(gè)分類任務(wù)來說,輸出可以包括預(yù)測(cè)類及與所有可能類相關(guān)的概率。
定義了應(yīng)用于模型輸出的后處理步驟。對(duì)于一個(gè)回歸任務(wù)來說,此步驟支持將輸出轉(zhuǎn)變?yōu)槿藗兒苋菀拙涂梢岳斫獾姆謹(jǐn)?shù)(預(yù)測(cè)結(jié)果)。
定義了將測(cè)試數(shù)據(jù)傳遞至模型時(shí)獲得的性能度量標(biāo)準(zhǔn)(與訓(xùn)練數(shù)據(jù)相對(duì))。這些度量標(biāo)準(zhǔn)包括字段相關(guān)性、混淆矩陣、增益圖及接收者操作特征(ROC)曲線圖。
定義了一個(gè)包含輸入數(shù)據(jù)記錄和預(yù)期模型輸出的示例集。這是非常重要的一個(gè)步驟,因?yàn)樵趹?yīng)用程序之間移動(dòng)模型時(shí),該模型需要通過匹配測(cè)試。這樣就可以確保,在呈現(xiàn)相同的輸入時(shí),新系統(tǒng)可以生成與舊系統(tǒng)同樣的輸出。 如果實(shí)際情況是這樣的話,一個(gè)模型將被認(rèn)為經(jīng)過了驗(yàn)證,且隨時(shí)可用于實(shí)踐。
?
一個(gè)通用的PMML文件結(jié)構(gòu)如下(參考http://dmg.org/pmml/v4-3/GeneralStructure.html):
<?xml version="1.0"?> <PMML version="4.3"xmlns="http://www.dmg.org/PMML-4_3"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Header copyright="Example.com"/><DataDictionary> ... </DataDictionary>... a model ...</PMML>?
3 PMML模型的生成和加載相關(guān)類庫
???????? PMML模型的生成相關(guān)的庫需要看我們使用的離線訓(xùn)練庫。如果我們使用的是sklearn,那么可以使用sklearn2pmml這個(gè)python庫來做模型文件的生成,這個(gè)庫安裝很簡(jiǎn)單,使用"pip install sklearn2pmml"即可。如果使用的是Spark MLlib, 這個(gè)庫有一些模型已經(jīng)自帶了保存PMML模型的方法,可惜并不全。如果是R,則需要安裝包"XML"和“PMML”。此外,JAVA庫JPMML可以用來生成R,SparkMLlib,xgBoost,Sklearn的模型對(duì)應(yīng)的PMML文件。github地址是:https://github.com/jpmml/jpmml。
?? 加載PMML模型需要目標(biāo)環(huán)境支持PMML加載的庫,如果是JAVA,則可以用JPMML來加載PMML模型文件。
??????
?????? 可以看出,要使用PMML,需要兩步的工作,第一塊是將離線訓(xùn)練得到的模型轉(zhuǎn)化為PMML模型文件,第二塊是將PMML模型文件載入在線預(yù)測(cè)環(huán)境,進(jìn)行預(yù)測(cè)。這兩塊都需要相關(guān)的庫支持。
????? 不過,當(dāng)訓(xùn)練和預(yù)測(cè)使用同一種開發(fā)語言的時(shí)候,PMML?就沒有必要使用了,因?yàn)槿魏沃虚g格式都會(huì)犧牲掉獨(dú)有的優(yōu)化。
- 整個(gè)流程分為兩部分:離線和在線。
- 離線部分流程是將樣本進(jìn)行特征工程,然后進(jìn)行訓(xùn)練,生成模型。一般離線部分常用 Python 中的 sklearn、R 或者 Spark ML 來訓(xùn)練模型。
- 在線部分是根據(jù)請(qǐng)求得到樣本數(shù)據(jù),對(duì)這些數(shù)據(jù)采用與離線特征工程一樣的方式來處理,然后使用模型進(jìn)行評(píng)估。一般在線部分常用 Java、C++ 來開發(fā)。
- 離線部分與在線部分是通過 PMML 連接的,也就是說離線訓(xùn)練好了模型之后,將模型導(dǎo)出為 PMML 文件,在線部分加載該 PMML 文件生成對(duì)應(yīng)的評(píng)估模型。
我們可以看到,PMML 是連接離線與在線環(huán)節(jié)的關(guān)鍵,一般導(dǎo)出 PMML 文件和 加載 PMML 文件都需要各個(gè)語言來做單獨(dú)的實(shí)現(xiàn)。不過幸運(yùn)的是,已經(jīng)有很多大神實(shí)現(xiàn)了這些,可以參見:https://github.com/jpmml 。
?
4. PMML模型生成和加載示例
??????? 將離線訓(xùn)練得到的模型轉(zhuǎn)化為PMML模型文件
?????? 下面給一個(gè)示例,使用sklearn生成一個(gè)決策樹模型,用sklearn2pmml生成模型文件,用JPMML加載模型文件,并做預(yù)測(cè)。
首先是用用sklearn生成一個(gè)決策樹模型,由于我們是需要保存PMML文件,所以最好把模型先放到一個(gè)Pipeline數(shù)組里面。這個(gè)數(shù)組里面除了我們的決策樹模型以外,還可以有歸一化,降維等預(yù)處理操作,這里作為一個(gè)示例,我們Pipeline數(shù)組里面只有決策樹模型。代碼如下:
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 sklearn2pmmlimport 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)上面這段代碼做了一個(gè)非常簡(jiǎn)單的決策樹分類模型,只有5個(gè)訓(xùn)練樣本,特征有4個(gè),輸出類別有3個(gè)。實(shí)際應(yīng)用時(shí),我們需要將模型調(diào)參完畢后才將其放入PMMLPipeline進(jìn)行保存。運(yùn)行代碼后,在當(dāng)前目錄會(huì)得到一個(gè)PMML的XML文件,可以直接打開看,內(nèi)容大概如下:
<?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><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>可以看到里面就是決策樹模型的樹結(jié)構(gòu)節(jié)點(diǎn)的各個(gè)參數(shù),以及輸入值。我們的輸入被定義為x1-x4,輸出定義為y。
?
將PMML模型文件載入在線預(yù)測(cè)環(huán)境,進(jìn)行預(yù)測(cè)
?
創(chuàng)建一個(gè)Maven或者gradle工程,加入JPMML的依賴,這里給出maven在pom.xml的依賴,gradle的結(jié)構(gòu)是類似的。
<dependency><groupId>org.jpmml</groupId><artifactId>pmml-evaluator</artifactId><version>1.4.1</version></dependency><dependency><groupId>org.jpmml</groupId><artifactId>pmml-evaluator-extension</artifactId><version>1.4.1</version></dependency>接著就是讀取模型文件并預(yù)測(cè)的代碼了,具體代碼如下:
import org.dmg.pmml.FieldName; import org.dmg.pmml.PMML; import org.jpmml.evaluator.*; import org.xml.sax.SAXException;import javax.xml.bind.JAXBException; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map;/*** Created by 劉建平Pinard on 2018/6/24.*/ public class PMMLDemo {private Evaluator loadPmml(){PMML pmml = new PMML();InputStream inputStream = null;try {inputStream = new FileInputStream("D:/demo.pmml");} catch (IOException e) {e.printStackTrace();}if(inputStream == null){return null;}InputStream is = inputStream;try {pmml = org.jpmml.model.PMMLUtil.unmarshal(is);} catch (SAXException e1) {e1.printStackTrace();} catch (JAXBException e1) {e1.printStackTrace();}finally {//關(guān)閉輸入流try {is.close();} catch (IOException e) {e.printStackTrace();}}ModelEvaluatorFactory modelEvaluatorFactory = ModelEvaluatorFactory.newInstance();Evaluator evaluator = modelEvaluatorFactory.newModelEvaluator(pmml);pmml = null;return evaluator;}private int predict(Evaluator evaluator,int a, int b, int c, int d) {Map<String, Integer> data = new HashMap<String, Integer>();data.put("x1", a);data.put("x2", b);data.put("x3", c);data.put("x4", d);List<InputField> inputFields = evaluator.getInputFields();//過模型的原始特征,從畫像中獲取數(shù)據(jù),作為模型輸入Map<FieldName, FieldValue> arguments = new LinkedHashMap<FieldName, FieldValue>();for (InputField inputField : inputFields) {FieldName inputFieldName = inputField.getName();Object rawValue = data.get(inputFieldName.getValue());FieldValue inputFieldValue = inputField.prepare(rawValue);arguments.put(inputFieldName, inputFieldValue);}Map<FieldName, ?> results = evaluator.evaluate(arguments);List<TargetField> targetFields = evaluator.getTargetFields();TargetField targetField = targetFields.get(0);FieldName targetFieldName = targetField.getName();Object targetFieldValue = results.get(targetFieldName);System.out.println("target: " + targetFieldName.getValue() + " value: " + targetFieldValue);int primitiveValue = -1;if (targetFieldValue instanceof Computable) {Computable computable = (Computable) targetFieldValue;primitiveValue = (Integer)computable.getResult();}System.out.println(a + " " + b + " " + c + " " + d + ":" + primitiveValue);return primitiveValue;}public static void main(String args[]){PMMLDemo demo = new PMMLDemo();Evaluator model = demo.loadPmml();demo.predict(model,1,8,99,1);demo.predict(model,111,89,9,11);} }?
代碼里有兩個(gè)函數(shù),第一個(gè)loadPmml是加載模型的,第二個(gè)predict是讀取預(yù)測(cè)樣本并返回預(yù)測(cè)值的。
代碼運(yùn)行結(jié)果如下:
target: y value: {result=2, probability_entries=[0=0.0, 1=0.0, 2=1.0], entityId=5, confidence_entries=[]} 1 8 99 1:2 target: y value: {result=1, probability_entries=[0=0.0, 1=1.0, 2=0.0], entityId=6, confidence_entries=[]} 111 89 9 11:1也就是樣本(1,8,99,1)被預(yù)測(cè)為類別2,而(111,89,9,11)被預(yù)測(cè)為類別1。
?
以上就是PMML生成和加載的一個(gè)示例,使用起來其實(shí)門檻并不高,也很簡(jiǎn)單。
?
5. PMML總結(jié)與思考
??? 第一個(gè)就是PMML為了滿足跨平臺(tái),犧牲了很多平臺(tái)獨(dú)有的優(yōu)化,所以很多時(shí)候我們用算法庫自己的保存模型的API得到的模型文件,要比生成的PMML模型文件小很多。同時(shí)PMML文件加載速度也比算法庫自己獨(dú)有格式的模型文件加載慢很多。
第二個(gè)就是PMML加載得到的模型和算法庫自己獨(dú)有的模型相比,預(yù)測(cè)會(huì)有一點(diǎn)點(diǎn)的偏差,當(dāng)然這個(gè)偏差并不大。比如某一個(gè)樣本,用sklearn的決策樹模型預(yù)測(cè)為類別1,但是如果我們把這個(gè)決策樹落盤為一個(gè)PMML文件,并用JAVA加載后,繼續(xù)預(yù)測(cè)剛才這個(gè)樣本,有較小的概率出現(xiàn)預(yù)測(cè)的結(jié)果不為類別1.
第三個(gè)就是對(duì)于超大模型,比如大規(guī)模的集成學(xué)習(xí)模型,比如xgboost, 隨機(jī)森林,或者tensorflow,生成的PMML文件很容易得到幾個(gè)G,甚至上T,這時(shí)使用PMML文件加載預(yù)測(cè)速度會(huì)非常慢,此時(shí)推薦為模型建立一個(gè)專有的環(huán)境,就沒有必要去考慮跨平臺(tái)了。
此外,對(duì)于TensorFlow,不推薦使用PMML的方式來跨平臺(tái)。可能的方法一是TensorFlow serving,自己搭建預(yù)測(cè)服務(wù),但是會(huì)稍有些復(fù)雜。另一個(gè)方法就是將模型保存為TensorFlow的模型文件,并用TensorFlow獨(dú)有的JAVA庫加載來做預(yù)測(cè)。
?
?
?
參考:
https://blog.csdn.net/hellozhxy/article/details/81563370
https://blog.csdn.net/hellozhxy/article/details/82142700
總結(jié)
以上是生活随笔為你收集整理的[机器学习]PMML预测模型标记语言的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: @Temporal 注解使用
- 下一篇: [机器学习] gcForest 官方代码