研究机器学习用例:
在所有的这些例子中,如果有了模型的持久性,那么保存和加载模型的问题将变得更容易解决。在即将到来的2.0版本中,通过基于DataFrame的API,Spark机器学习库MLlib将实现几乎完整的ML持久性支持。本文将提前透露有关代码示例,以及MLlib API持久性的一些细节。
ML持久性的关键特性包括:
感谢所有为MLlib带来巨大发展的社区贡献者们!在JIRAs中可以看到为Scala,Java, Python和R做出贡献的完整人员名单。
在Apache Spark 2.0里,对于MLlib来说基于DataFrame的API在关于Spark的ML中占据了首要位置。该API模仿被人们所熟知的Spark Data Source API,提供保存和加载模型的功能。
下面将采用流行的MNIST数据集进行手写体数字识别,并在几种语言上演示保存和加载模型的功能(LeCun等著,1998;可从LIBSVM数据页面获取)。这个数据集包含了手写数字0-9,以及地面实况标签。这里有些例子:
我们的最终目的是为了拍摄新的手写数字图像并进行数字识别。在下面的笔记中就完整地演示了数据载入,以及模型训练、保存和加载的代码。
首先将展示如何保存和加载单一模型以促进语言共享,例子中首先会通过Python来训练一个Random Forest Classifier并保存下来,然后再利用Scala加载相同的模型。
training = sqlContext.read... # data: features, label rf = RandomForestClassifier(numTrees=20) model = rf.fit(training)
为了简化,这里将保存模型称为save方法,把加载模型称为load方法:
model.save("myModelPath") sameModel = RandomForestClassificationModel.load("myModelPath")
我们还可以将同样的模型(已保存在Python的)加载到一个Scala或者Java应用程序中:
// Load the model in Scala val sameModel = RandomForestClassificationModel.load("myModelPath")
这个方法既可以用于小型的本地模型例如K-Means模型(为了集群),也可以用于大型的分布式模型例如ALS模型(为了推荐)。因为加载的模型具有相同的参数设置和数据,所以即使加载的是一个完全不同的Spark部署,它也会给出相同的预测。
到目前为止只演示了单一ML模型的保存和加载,但在实际过程中,ML的工作流其实包含着许多阶段,从特征的提取和转换到模型的训练和调优都在其中。MLlib还提供了Pipelines来帮助用户更好地构建这些工作流。
同时,MLlib还允许用户保存和加载整个Pipelines。下面通过一个Pipeline案例看一下它是采用了哪些步骤实现的:
下面是建立Pipeline的一个片段:
// Construct the Pipeline: Binarizer + Random Forest val pipeline = new Pipeline().setStages(Array(binarizer, rf)) // Wrap the Pipeline in CrossValidator to do model tuning. val cv = new CrossValidator().setEstimator(pipeline) ...
在管道训练之前,我们会演示将整个工作流保存下来的过程(训练前)。而且这个工作流可以在另一个数据集上,或者是在另一个Spark集群上等地方加载运行。
cv.save("myCVPath") val sameCV = CrossValidator.load("myCVPath")
最后,我们就可以进行Pipeline训练,再将其保存和加载。这不仅可以节省特征提取的步骤,还可以省去使用Cross-Validation调整Random Forest模型以及从模型调优中提取数据的过程。
val cvModel = cv.fit(training) cvModel.save("myCVModelPath") val sameCVModel = CrossValidatorModel.load("myCVModelPath")
Python调优
很遗憾,Python调优将缺席Spark 2.0版本。就目前情况来看,Python还不支持保存和加载用于优化hyperparameters模型的CrossValidator和TrainValidationSplit;这个问题也正是Spark 2.1版本需要解决的。但是,它仍然有可能被用来保存Python中的CrossValidator和TrainValidationSplit结果。例如,使用Cross-Validation来调整Random Forest并将调整过程中发现的最好模型保存起来。
# Define the workflow rf = RandomForestClassifier() cv = CrossValidator(estimator=rf, ...) # Fit the model, running Cross-Validation cvModel = cv.fit(trainingData) # Extract the results, i.e., the best Random Forest model bestModel = cvModel.bestModel # Save the RandomForest model bestModel.save("rfModelPath")
可交换的存储格式
在内部,我们可以把模型的元数据和参数保存为JSON,把数据保存为Parquet。这些存储格式是可交换的,还可以使用其他库读取。Parquet不仅可以存储小的模型(例如Naive Bayes for classification),还可以存储大型的分布式模型(例如ALS for recommendation)。任何被Dataset/DataFrame支持的URI 都可以保存和加载存储路径,包括S3路径、本地存储等等。
语言的跨平台兼容性
利用Scala、Java和Python可以很容易地保存和加载模型,但是R却有两个局限性。一方面,R并不是支持所有的MLlib模型,其他语言所训练的模型也不是都可以被加载到R。另一方面,目前的R模型格式需要存储一些配合R使用的数据,这样给其他语言加载R所训练和存储的模型增加了困难。相信更好的跨语言支持R会在不久的将来被补足。
随着2.0版本的即将发布,DataFrame-based MLlib API将几乎完全覆盖持久化的模型和Pipelines。对于团队间共享模型、多语言ML工作流创建以及将模型用于生产这些,持久性发挥着至关重要的作用。这个特性也将会推动MLlib API(DataFrame-based)最终转变为Apache Spark机器学习的重要API。
未来的话,更高优先级的项目将会包括完整的持久性覆盖、Python模型优化算法以及R和其他语言API之间的兼容性改进。