PMML模型文件在机器学习的实践经验 - CSDN博客

标签: | 发表时间:2018-08-30 15:34 | 作者:
出处:https://blog.csdn.net

算法工程师和业务开发工程师,所掌握的技能容易在长期的工作中出现比较深的鸿沟,算法工程师辛辛苦苦调参的成果,业务工程师可能不清楚如何使用,如何为线上决策给予支持。本文介绍一种基于PMML的模型上线方法。

这种方案,在本次参加 QCon 大会时,Paypal的机器学习平台中也有所提及:

PMML

预测模型标记语言(Predictive Model Markup Language,PMML)是一种可以呈现预测分析模型的事实标准语言。标准东西的好处就是,各种开发语言都可以使用相应的包,把模型文件转成这种中间格式,而另外一种开发语言,可以使用相应的包导入该文件做线上预测。

不过,当训练和预测使用同一种开发语言的时候,PMML 就没有必要使用了,因为任何中间格式都会牺牲掉独有的优化。本文介绍的内容是采用Python语言做模型训练,线上采用 Java 载入模型做预测。在模型训练端,分别介绍了 python sk-learn 和 xgboost 训练模型。

正式开始之前,首先总结下模型训练和线上预测的流程图

离线部分负责模型训练和导出模型,线上导入模型并且做预测。当然特征工程部分主要做特征变换,例如 分桶,单值编码,归一化等。

SK-Learn

该开源项目支持 sk-learn模型转成PMML,项目地址: jpmml/sklearn2pmml,扩充了一个例子定义如何使用sk-learn导出带有特征工程的模型文件

        heart_data=pandas.read_csv("heart.csv")#用Mapper定义特征工程mapper=DataFrameMapper([(['sbp'],MinMaxScaler()),(['tobacco'],MinMaxScaler()),('ldl',None),('adiposity',None),(['famhist'],LabelBinarizer()),('typea',None),('obesity',None),('alcohol',None),(['age'],FunctionTransformer(np.log)),])#用pipeline定义使用的模型,特征工程等pipeline=PMMLPipeline([('mapper',mapper),("classifier",linear_model.LinearRegression())])pipeline.fit(heart_data[heart_data.columns.difference(["chd"])],heart_data["chd"])#导出模型文件sklearn2pmml(pipeline,"lrHeart.xml",with_repr=True)

heart.csv定义结构如下:

sbp,tobacco,ldl,adiposity,famhist,typea,obesity,alcohol,age,chd
160,12,5.73,23.11,Present,49,25.3,97.2,52,1
144,0.01,4.41,28.61,Absent,55,28.87,2.06,63,1
118,0.08,3.48,32.28,Present,52,29.14,3.81,46,0
170,7.5,6.41,38.03,Present,51,31.99,24.26,58,1
134,13.6,3.5,27.78,Present,60,25.99,57.34,49,1
132,6.2,6.47,36.21,Present,62,30.77,14.14,45,0
142,4.05,3.38,16.2,Absent,59,20.81,2.62,38,0
114,4.08,4.59,14.6,Present,62,23.11,6.72,58,1
114,0,3.83,19.4,Present,49,24.86,2.49,29,0
132,0,5.8,30.96,Present,69,30.11,0,53,1
206,6,2.95,32.27,Absent,72,26.81,56.06,60,1
134,14.1,4.44,22.39,Present,65,23.09,0,40,1

模型文件导出后,可以把文件存储在公司内部文件存储,实在不行可以打包在 jar 包中,供线上调用。

Java端采用  jpmml/jpmml-evaluator项目,载入 PMML 文件,然后准备线上所需数据,示例代码如下:

        //准备画像数据-key和原始特征一致即可
lrHeartInputMap.put("sbp", 142);
lrHeartInputMap.put("tobacco", 2);
lrHeartInputMap.put("ldl", 3);
lrHeartInputMap.put("adiposity", 30);
lrHeartInputMap.put("famhist", "Present");
lrHeartInputMap.put("typea", 83);
lrHeartInputMap.put("obesity", 23);
lrHeartInputMap.put("alcohol", 90);
lrHeartInputMap.put("age", 30);

//预测核心代码
public static void predictLrHeart() throws Exception {

    PMML pmml;
    //模型导入
    File file = new File("lrHeart.xml");
    InputStream inputStream = new FileInputStream(file);
    try (InputStream is = inputStream) {
        pmml = org.jpmml.model.PMMLUtil.unmarshal(is);

        ModelEvaluatorFactory modelEvaluatorFactory = ModelEvaluatorFactory.newInstance();
        ModelEvaluator<?> modelEvaluator = modelEvaluatorFactory.newModelEvaluator(pmml);
        Evaluator evaluator = (Evaluator) modelEvaluator;

        List<InputField> inputFields = evaluator.getInputFields();
        //过模型的原始特征,从画像中获取数据,作为模型输入
        Map<FieldName, FieldValue> arguments = new LinkedHashMap<>();
        for (InputField inputField : inputFields) {
            FieldName inputFieldName = inputField.getName();
            Object rawValue = lrHeartInputMap.get(inputFieldName.getValue());
            FieldValue inputFieldValue = inputField.prepare(rawValue);
            arguments.put(inputFieldName, inputFieldValue);
        }

        Map<FieldName, ?> results = evaluator.evaluate(arguments);
        List<TargetField> targetFields = evaluator.getTargetFields();
        //获得结果,作为回归预测的例子,只有一个输出。对于分类问题等有多个输出。
        for (TargetField targetField : targetFields) {
            FieldName targetFieldName = targetField.getName();
            Object targetFieldValue = results.get(targetFieldName);
            System.out.println("target: " + targetFieldName.getValue() + " value: " + targetFieldValue);
        }
    }
}

特征工程的说明

如流程图所示,对于原始样本数据,首先要做特征工程输入算法需要数据。特征工程可以线上线下,分开单独做,也可以用 DataFrameMapper 的方式实现特征工程,导出到模型文件中,这样线上就不需要再实现一次特征工程,但缺点是只可以使用Sklearn包中提供的方法,自拓展的方法无法支持(例如 Bucketizer)

支持的特征工程方法

Sklearn preprocessing 暂时不支持 分桶,该操作不久会被支持,已经有开源贡献者提交了 Merge Request,在Sk支持后,相信不久 JPMML项目也会支持

https://github.com/scikit-learn/scikit-learn/pull/9342

XGBoost

模型原理的理解可以参考这篇文章: GBDT的原理和应用

XGBoost 输入的文件格式是 SVMLib 文件格式。

1 3:1 10:1 11:1 21:1 30:1 34:1 36:1 40:1 41:1 53:1 58:1 65:1 69:1 77:1 86:1 88:1 92:1 95:1 102:1 105:1 117:1 124:1
0 3:1 10:1 20:1 21:1 23:1 34:1 36:1 39:1 41:1 53:1 56:1 65:1 69:1 77:1 86:1 88:1 92:1 95:1 102:1 106:1 116:1 120:1
0 1:1 10:1 19:1 21:1 24:1 34:1 36:1 39:1 42:1 53:1 56:1 65:1 69:1 77:1 86:1 88:1 92:1 95:1 102:1 106:1 116:1 122:1
1 3:1 9:1 19:1 21:1 30:1 34:1 36:1 40:1 42:1 53:1 58:1 65:1 69:1 77:1 86:1 88:1 92:1 95:1 102:1 105:1 117:1 124:1

第一列是 label,后面的是特征Id对应的值。和他一起使用的,有个特征文件,定义了特征工程方法。

0 cap-shape=bell i
1 cap-shape=conical i
2 cap-shape=convex i
3 cap-shape=flat i
4 cap-shape=knobbed i

i: indicator, 二值特征
q: quantitative, 数值特征,例如年龄等
int: int means this feature is integer value (when int is hinted, the decision boundary will be integer)

模型训练完毕后,导出模型文件 (.model) 。使用  jpmml/jpmml-xgboost 转模型文件成PMML格式。 可以和feature map文件作为输入,这样线上就不需要重新定义特征工程。

示例命令如下: java -jar target/converter-executable-1.2-SNAPSHOT.jar --model-input 0002.model --fmap-input featmap.txt --target-name mpg --pmml-output xgboost.pmml

线上使用的方法和前例基本一致,准备好原始特征值,过模型即可

        xgBoostInputMap.put("mpg","1");
xgBoostInputMap.put("bruises?", "no");
xgBoostInputMap.put("odor", "creosote");
xgBoostInputMap.put("gill-spacing", "close");
xgBoostInputMap.put("gill-size", "narrow");
xgBoostInputMap.put("stalk-root", "club");
xgBoostInputMap.put("stalk-surface-below-ring", "smooth");
xgBoostInputMap.put("spore-print-color", "orange");

总结

因为PMML格式的通用性,所以会丧失特殊模型的特殊优化,例如上线XGBoost模型,也可以使用XGBoost4J,该包会链接一个本地环境编译的 .so 文件,C++实现的核心代码效率很高。不过PMML格式通用,在效率要求不高的场景可以发挥很大作用。

对于特征工程部分的计算,PMML对特征工程对支持有限,线上、线下可以单独实现,PMML文件只负责模型部分,这样既可以做丰富的特征工程,也实现了模型的共用。

传送门:https://zhuanlan.zhihu.com/p/30378213

相关 [pmml 模型 文件] 推荐:

PMML模型文件在机器学习的实践经验 - CSDN博客

- -
算法工程师和业务开发工程师,所掌握的技能容易在长期的工作中出现比较深的鸿沟,算法工程师辛辛苦苦调参的成果,业务工程师可能不清楚如何使用,如何为线上决策给予支持. 本文介绍一种基于PMML的模型上线方法. 这种方案,在本次参加 QCon 大会时,Paypal的机器学习平台中也有所提及:. 预测模型标记语言(Predictive Model Markup Language,PMML)是一种可以呈现预测分析模型的事实标准语言.

模型制作

- 小鱼儿 - 非正常人类研究中心 – Mtime时光网
1.材料:一大袋的一次性筷子(花了60块钱);5支502胶水;5张粗砂纸;记号笔一只;锋利的美工刀片若干,破剪刀一把. 就是这种屌毛筷子,质量也太他妈的差了点,80%都是弯的 . 随便提一下:我的脚丫子还是蛮性感滴 . 开始动工了!!  先做门框跟房子的底架. 3.不好意思,忘了交代一下了,我是先画图纸的,看到那张纸了没有.

MapReduce编程模型

- - CSDN博客云计算推荐文章
MapReduce是一个Google发明的编程模型,也是一个处理和生成超大规模数据集的算法模型的相关实现. 用户首先创建一个Map函数处理一个基于对的数据集合,输出的中间结果基于对的数据集合,然后再创建一个Reduce函数用来合并所有的具有相同中间Key值的中间Value值.

关于BOM模型

- - CSDN博客编程语言推荐文章
当我们使用浏览器打开一个网页程序时,那么,js系统会自动创建对象,首先创建浏览器对象window,然后再为window对象创建它的子级对象,最后形成一个树状模型,这个就是BOM模型. BOM定义了JavaScript可以进行操作的浏览器的各个功能部件的接口. BOM 主要处理浏览器窗口和框架,不过通常浏览器特定的 JavaScript 扩展都被看做 BOM 的一部分.

对象的消息模型

- loudly - 酷壳 - CoolShell.cn
[ ———— 感谢 Todd 同学 投递本文,原文链接 ———— ]. 话题从下面这段C++程序说起,你认为它可以顺利执行吗. 试试的确可以顺利运行输出hello world,奇怪吗. 其实并不奇怪,根据C++对象模型,类的非虚方法并不会存在于对象内存布局中,实际上编译器是把Hello方法转化成了类似这样的全局函数:.

JS三维模型库 Three.js

- Le - 开源中国社区最新软件
Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象. 你可以在它的主页上看到许多精采的演示. 不过,这款引擎目前还处在比较不成熟的开发阶段,其不够丰富的 API 以及匮乏的文档增加了初学者的学习难度(尤其是文档的匮乏) 演示:http://mrdoob.github.com/three.js/.

论NoSQL的数据模型

- - NoSQLFan
本文内容是对《 NoSQL Data Modeling Techniques》一文的简单概述,原文对NoSQL的几种 数据模型进行了详细深入的讨论. 是了解NoSQL数据模型不过错过的全面资料. NoSQL的一些非功能性的特性,比如扩展性、性能以及一致性的讨论,目前已经有很多. 而对于NoSQL产品内部数据模型相关的知识一直比较欠缺,本文就希望能够系统地对NoSQL数据模型进行一些探讨.

深入Java内存模型

- - ImportNew
你可以在网上找到一大堆资料让你了解JMM是什么东西,但大多在你看完后仍然会有很多疑问. happen-before是怎么工作的呢. 用volatile会导致缓存的丢弃吗. 为什么我们从一开始就需要内存模型. 通过这篇文章,读者可以学习到足以回答以上所有问题的知识. 它包含两大部分:第一部分是硬件层次的大体架构,第二部分是深入OpenJdk源代码和实现.

[转]Geodatabase数据模型

- - 小鸥的博客
1  Geodatabase概念.   Geodatabase是ArcInfo8引入的一种全新的面向对象的空间数据模型,是建立在DBMS之上的统一的、智能的空间数据模型. “统一”是指,Geodatabase之前的多个空间数据模型都不能在一个统一的模型框架下对地理空间要素信息进行统一的描述,而Geodatabase做到了这一点;“智能化”是指,在Geodatabase模型中,对空间要素的描述和表达较之前的空间数据模型更接近我们的现实世界,更能清晰、准确地反映现实空间对象的信息.

Apache与Nginx网络模型

- - CSDN博客互联网推荐文章
      Nginx的高并发得益于其采用了epoll模型,与传统的服务器程序架构不同,epoll是linux内核2.6以后才出现的. 下面通过比较Apache和Nginx工作原理来比较.       传统Apache都是多进程或者多线程来工作,假设是多进程工作(prefork),apache会先生成几个进程,类似进程池的工作原理,只不过这里的进程池会随着请求数目的增加而增加.