本帖最后由 喵十八 于 2018-7-12 08:06 编辑
问题导读
1.跨语言,跨平台之间的机器学习模型如何共享交互?
2.机器学习模型如何上线?
PMML简介
模型预测标记语言(Predictive Model Markup Language)是由Dr. Robert Lee Grossman 提出的一种基于XML的存储模型的格式标准。
这里的模型是指那些由数据挖掘和机器学习算法生成的预测模型。PMML为不同的数据分析软件或者编程语言,提供了一种轻松共享预测分析模型的方式。它支持常见的模型,比如逻辑回归和前馈神经网络等。
常见的就是,公司的数据分析人员使用的是R,线上的生产的环境是Java,分析人员在R中将模型训练好之后,就可以将模型导出为PMML,然后在Java中导入,实现模型在R与Java之间的共享与快速上线。
PMML的第一个版本 Version 0.7 是在1997年7月发布的。 第二个版本 Version 0.9 在1998年7月发布的。随后的版本由Data Mining Group 组织维护开发。
因为PMML是一个基于XML的格式标准,所有定义都是和XML schema保持一致的。PMML本身也是一个成熟的标准,已经有超过30个组织声明他们的产品支持PMML。
机器学习模型上线过程与PMML的作用
一个机器学习的上线过程,主要包括:分析、特征工程、模型训练、调优、上线。
其中,PMML的便捷性,主要体现在模型上线的过程中。
模型上线是将训练好的模型有效地应用于生产环境的过程。一般而言,生产环境和数据分析人员所使用的环境差距巨大。以我们目前的实践为例,线上DMP是基于Hadoop的,计算框架用的Spark + MR。线下人员建模主要使用的是R、python 中的sklearn库 以及 部分使用keras。(这里需要吐槽下Spark MLlib 库中的优化算法,效率和精度都差sklearn太多了。)
如果没有PMML,因为不同的系统采用不同的方式呈现其计算,模型上线时就必须经历一个漫长的、容易出现错误和误呈现的翻译过程。
此外,还需要对模型结构了解非常深入的工程人员。
比如,将R中的LR迁移至Java中,就需要工程人员根据训练好模型的参数,裸写一个java类。
以最简单的LR为例,就是一个数据表示权重向量,权重向量和参数向量相乘之后求一次激活函数。
但复杂一点的模型,就要求对模型结构了解非常深入的工程人员,严格按照模型的计算逻辑,来编写该类。
有了PMML,就可以从应用程序A到B再到C轻松共享模型,并且在训练完成之后,迅速将模型上线(只需替换模型文件即可)。现在的实践就是,线下分析人员,使用R等训练好一版的模型之后,导出为PMML,在线上只需替换该PMML文件即可。
PMML 基础知识
注:以下内容基于PMML 4.3版本
PMML 包含数据预处理和数据后处理(即模型预测结果的处理)和预测模型本身,见下图:
PMML文件的结构遵从了用于构建预测模型的常用步骤。
文头件:包含了PMML文档的基本信息,例如模型的版权信息,模型的描述,以及生成该文件所用软件的信息(比如软件的名字和版本)。头文件中也会包含该PMML文件的生成时间。
数据字典:包含了模型可能用到的所有字段名。并定义了字段的类型,可以是连续型(continuous)、类别型(categorical)、定序型(ordinal)。和字段值的类型,如String、Double。
数据挖掘模式:数据挖掘模式,可以看做是模型的一个看门人。所有进入模型的数据,必须经过数据挖掘模式。每个模型都包含且只包含一个数据挖掘模式,用于列出该模型使用的数据。数据挖掘模式包含针对特定模型不同的信息,相对的,数据字典中定义则是稳定的,不会随模型变化而变化。数据挖掘模式的主要目的是列出使用模型需要的数据。
数据挖掘模式也定义了每个字段的使用用途(激活、追加、目标)以及针对空值、非法数据的策略。
数据转化:数据转化操作可以用于对进入模型之前的数据进行预处理。类比python sklearn中的DataFrameMapper,以及Spark中特征预处理相关算子。
PMML定义了如下简单的数据转化操作:标准化、离散化、值映射、自定义函数、聚合
模型:包含了模型的定义和结构信息。
输出:定义了模型输出。对于一个分类任务来说,输出可以包括预测类及与所有可能类相关的概率。
目标:定义了应用于模型输出的后处理步骤。对于一个回归任务来说,此步骤支持将输出转变为人们很容易就可以理解的分数(预测结果)。
模型解释:定义了将测试数据传递至模型时获得的性能度量标准(与训练数据相对)。这些度量标准包括字段相关性、混淆矩阵、增益图及接收者操作特征(ROC)曲线图。
模型验证:定义了一个包含输入数据记录和预期模型输出的示例集。这是非常重要的一个步骤,因为在应用程序之间移动模型时,该模型需要通过匹配测试。这样就可以确保,在呈现相同的输入时,新系统可以生成与旧系统同样的输出。如果实际情况是这样的话,一个模型将被认为经过了验证,且随时可用于实践。
实例解析
在这里,我们使用R语言生成一个LR模型。
其中使用了r2pmml工具,使用参见r2pmml
使用的数据集是经典的iris。可以说是机器学习中入门的helloworld了,感兴趣的可以查看。
R的代码如下
[mw_shl_code=python,true]# 二分类实例
# 去掉setosa类
index <- which(iris$Species == 'setosa')
iris <- iris[- index,]
training <-iris
#抽样方法
ind<-sample(2,nrow(training),replace=TRUE,prob=c(0.7,0.3)) #对数据分成两部分,70%训练数据,30%检测数据nrow(training)行数
traindata<- training [ind==1,] #训练集
testdata<- training [ind==2,]
#二项分布族binomial--正态、指数、gamma、逆高斯、Poisson、二项
fit <- glm(Species ~.,family=binomial(link='logit'),data=traindata)
predict <- predict(fit,type='response',newdata=testdata)
real <- testdata$Species
res <- data.frame(real,predict =ifelse(predict>0.5,'virginca','versicorlor'))
table(real,predict =ifelse(predict>0.5,'virginca','versicorlor'))
# 存成pmml
library("r2pmml")
r2pmml(fit,"iris.pmml")[/mw_shl_code]
生成的PMML文档,有三个子元素
文件头
[mw_shl_code=xml,true] <Header>
<Application name="JPMML-R" version="1.2.20"/>
<Timestamp>2018-06-26T12:10:20Z</Timestamp>
</Header>[/mw_shl_code]
表名该PMML文件是用1.2.20版本的JPMML-R生成的。
生成时间为2018-06-26T12:10:20Z
数据字典
[mw_shl_code=xml,true] <DataDictionary>
<DataField name="Species" optype="categorical" dataType="string">
<Value value="versicolor"/>
<Value value="virginica"/>
</DataField>
<DataField name="Sepal.Length" optype="continuous" dataType="double"/>
<DataField name="Sepal.Width" optype="continuous" dataType="double"/>
<DataField name="Petal.Length" optype="continuous" dataType="double"/>
<DataField name="Petal.Width" optype="continuous" dataType="double"/>
</DataDictionary>[/mw_shl_code]
该模型共涉及5个字段,其中Species字段为类别类型,共有两个可能值。 数据集应该是在三个,为了实现,在代码中删除了一个。
其余四个字段分别为连续型,值都是double型的。
模型部分
[mw_shl_code=xml,true]<GeneralRegressionModel modelType="generalizedLinear" functionName="classification" linkFunction="logit" distribution="binomial">
具体定义
</GeneralRegressionModel>[/mw_shl_code]
模型是一个 普通的线性二分类模型。激活函数式logit。具体内容定义在其子元素中。
数据挖掘模式
[mw_shl_code=xml,true] <MiningSchema>
<MiningField name="Species" usageType="target"/>
<MiningField name="Sepal.Length"/>
<MiningField name="Sepal.Width"/>
<MiningField name="Petal.Length"/>
<MiningField name="Petal.Width"/>
</MiningSchema>[/mw_shl_code]
定义了该模型使用到的字段。数据字典中的5个字段都使用了,其中Species是作为结果的。其余都是模型输入
这里是将数据字典中的所有字段都用上了,实际操作过程中,存在数据字典中的部分字段,对最后的结果无影响,在数据挖掘模式中,便不会包括这部分字段。
输出
[mw_shl_code=xml,true]
<Output>
<OutputField name="probability(versicolor)" optype="continuous" dataType="double" feature="probability" value="versicolor"/>
<OutputField name="probability(virginica)" optype="continuous" dataType="double" feature="probability" value="virginica"/>
</Output>[/mw_shl_code]
定义了模型预测结果的输出格式,包括判断为某一分类的概率,是一个连续型,值为double型。
参数部分
[mw_shl_code=xml,true] <ParamMatrix>
<PCell targetCategory="virginica" parameterName="p0" beta="-732.0440201835713"/>
<PCell targetCategory="virginica" parameterName="p1" beta="-23.885379989132485"/>
<PCell targetCategory="virginica" parameterName="p2" beta="-205.83327893055016"/>
<PCell targetCategory="virginica" parameterName="p3" beta="129.4711031033915"/>
<PCell targetCategory="virginica" parameterName="p4" beta="494.71600981992856"/>
</ParamMatrix>[/mw_shl_code]
这个便是训练好的LR模型的权重参数向量。
Java中使用
在Java中使用pmml,主要是用到一个开源的包[jpmml](https://github.com/jpmml/jpmml-evaluator)
先load 模型
[mw_shl_code=java,true]
PMML pmml;
try(InputStream is = ...){
pmml = org.jpmml.model.PMMLUtil.unmarshal(is);
}[/mw_shl_code]
将输入参数的形式调整为模型所需的形式
[mw_shl_code=java,true]Map<FieldName, FieldValue> arguments = new LinkedHashMap<>();
List<InputField> inputFields = evaluator.getInputFields();
for(InputField inputField : inputFields){
FieldName inputFieldName = inputField.getName();
// The raw (ie. user-supplied) value could be any Java primitive value
Object rawValue = ...;
// The raw value is passed through: 1) outlier treatment, 2) missing value treatment, 3) invalid value treatment and 4) type conversion
FieldValue inputFieldValue = inputField.prepare(rawValue);
arguments.put(inputFieldName, inputFieldValue);
}[/mw_shl_code]
进行预测
[mw_shl_code=java,true]Map<FieldName, ?> results = evaluator.evaluate(arguments);[/mw_shl_code]
总结
PMML主要用于跨平台,跨语言之间的模型共享和交互,对于模型快速上线,具有非常重大的意义。
Reference
|
|