写在前面: 目前做的项目中核心功能是可视化建模,是通过Spark ML改造而来。本文通过一个简单的建模流程来解读Spark ML代码框架,了解了优秀的框架是如何抽象和解剖复杂问题之后,再简单说明了解了架构之后可以在这个基础上做什么。
举个建模中的小例子
假设我们现在要建立一个评分卡模型,最简单的步骤就是“数据处理 -> 特征分箱 -> iv特征过滤 -> 建立评分卡”,数据处理咱们就只做“类型转换”,那么建模流程如下图:
每一个步骤我们都起个名字叫做stage,然后我们看每个stage的数据流:
图中,“类型转换”的输入和输出均为数据,输入的是原始数据,输出的是转换类型后的数据。而“特征分箱”因为涉及到计算woe和iv等值,会生成“分箱模型”。特征数据会命重“分箱模型”的具体分箱,然后输出特征分箱数据。所以“特征分箱”的输入为数据,输出为一个模型。而生成的“分箱模型”的输入输出均为数据。
另一边“iv特征过滤”的输入和输出均为数据,输入的是特征分箱数据,输出的是经过iv过滤的特征数据。而“评分卡”的计算可以使用逻辑回归等模型进行训练(不仅限该方法)得到“评分卡模型”。所以“评分卡”的输入是数据,输出是模型。“评分卡模型”的输入输出均为数据。
按照上述流程,可以将stage分为两类,一类是输入输出均为数据,用于数据的转换处理;另一类是将数据转换为模型,用于数据的推演计算。咱们暂且将这两种stage称之为Transformer和Estimator。
接下来我们得到了两个完整的模型,便可以直接在业务上使用:
显然此时该模型的输入输出均为数据。
Spark ML架构
其实上述的stage、Transformer和Estimator就是对应Spark ML里的PipelineStage、Transformer和Estimator。简单的继承关系如下图:
Spark ML中Transformer提供了transform()方法,将输入的Dataframe转换为输出的Dataframe;Estimator提供了fit()方法将输入的Dataframe训练为输出的Transformer。可以简单的理解为Transformer用于做数据处理,而Estimator用于生成模型。实际上Estimator的fit()方法生成的是一个Model,也就是我们说的模型,该类继承自Transformer也是靠transform()方法来做数据处理。想到我们生成的模型,最终就是做一个数据的处理再输出为一个结果,上面的描述也就能明白了。
另外,刚才我们将“类型转换”、“特征分箱”、“iv值过滤”和“评分卡”这些PipelineStage连接在一起,就能组成一个Pipeline。可以看到Pipeline继承自Estimator,所以也提供了fit()方法。方法中回循环执行每个PipelineStage的transform()和fit()方法,最终生成一个PipelineModel。过程如下图:
此处贴上Spark源码片段,可能会更直观:
theStages.view.zipWithIndex.foreach { case (stage, index) =>
if (index <= indexOfLastEstimator) {
val transformer = stage match {
case estimator: Estimator[_] =>
estimator.fit(curDataset)
case t: Transformer =>
t
case _ =>
throw new IllegalArgumentException(
s"Does not support stage $stage of type ${stage.getClass}")
}
if (index < indexOfLastEstimator) {
curDataset = transformer.transform(curDataset)
}
transformers += transformer
} else {
transformers += stage.asInstanceOf[Transformer]
}
}
生成的PipelineModel继承自Model,所以提供了transform()方法,提供模型服务。过程如下图:
理解Spark ML架构后可以做什么
- 可以自定义
PipelineStage,只要按照业务需求实现fit()和transform()方法,也能放进Pipeline中使用; Pipeline和PipelineModel均为单链路,也就是一个输入对应一个输出。同样可以修改fit()和transform()方法的入参出参,来实现拓扑结构;- Spark ML使用的数据集为
Dataframe,而Dataframe是一种分布式数据集。所以Spark ML注定只能做离线或准实时的批量数据处理。可以考虑在Transform中新增一个接口,它的输入和输出均只有一条数据; PipelineStage的运算比较黑盒,一次运行后只能看到处理结果,无法看到中间数据或某些具体算法的数据统计。比如“评分卡模型”,如果想看当前模型的评分区间,其实是看不到的。所以可以考虑新增一个接口方法,专门用于输出中间数据或数据统计。