ML管道:MLlib的一个新的高级API
2015年1月7日 在工程的博客
MLlib的目标是让实用的机器学习(ML)变得可扩展和简单。除了我们在每个版本中看到的新算法和性能改进之外,我们还花费了大量的时间和精力来制作MLlib容易.与Spark Core类似,MLlib提供了三种语言的api: Python、Java和Scala,以及用户指南和示例代码,以简化来自不同背景的用户的学习曲线。在Apache Spark 1.2中,Databricks与加州大学伯克利分校的AMPLab合作,通过向MLlib引入管道API来简化实际ML管道的创建和调优,继续了这一努力。
一个实际的ML管道通常涉及一系列数据预处理、特征提取、模型拟合和验证阶段。例如,对文本文档进行分类可能涉及到文本分割和清理、提取特征以及使用交叉验证训练分类模型。尽管我们可以为每个阶段使用许多库,但将这些点连接起来并不像看起来那么容易,特别是对于大规模的数据集。大多数ML库不是为分布式计算设计的,或者它们不提供管道创建和调优的本机支持。不幸的是,这个问题在学术界经常被忽略,在工业中,它在很大程度上得到了特别的处理,在工业中,开发往往发生在手动的一次性管道实现中。
在这篇文章中,我们简要地描述了在MLlib中解决ML管道的工作,MLlib是Databricks和AMPLab, UC Berkeley的一项联合工作,受到scikit-learn项目和一些早期工作多层互连.
数据抽象
在新的管道设计中,数据集由Spark SQL的SchemaRDD表示,ML管道由数据集转换序列表示。(更新: SchemaRDD在Spark 1.3中被重命名为DataFrame)。每个转换接受一个输入数据集并输出转换后的数据集,该数据集成为下一阶段的输入。我们利用Spark SQL有几个原因:数据导入/导出,灵活的列类型和操作,以及执行计划优化。
数据导入/导出是ML管道的开始/结束点。MLlib目前为几个特定于应用程序的类型提供了导入/导出实用程序:用于分类和回归的LabeledPoint,用于协同过滤的Rating,等等。然而,真实的数据集可能包含许多类型,例如用户/项目id、时间戳或原始记录。目前的实用程序无法轻松处理这些类型组合的数据集,而且它们使用了从其他ML库中采用的低效文本存储格式。
特征转换通常构成实际ML管道的大部分。特性转换可以看作是添加从现有列创建的新列。例如,文本标记化将文档分解为一袋单词,tf-idf将一袋单词转换为一个特征向量,而在转换过程中需要保留标签以进行模型拟合。更复杂的特征转换在实践中很常见。因此,数据集需要支持不同类型的列,包括密集向量和稀疏向量,以及从现有列创建新列的操作。
在上面的例子中,id、text和words在转换过程中被保留。它们对模型拟合是不必要的,但在预测和模型检验中是有用的。如果预测数据集只包含预测的标签,它不会提供太多信息。如果我们想检查预测结果,例如,检查假阳性,查看预测的标签以及原始输入文本和标记化的单词是非常有用的。每个阶段所需的柱子都有很大不同。理想的情况是底层执行引擎可以为我们进行优化,并且只加载所需的列。
幸运的是,Spark SQL已经提供了大部分所需的函数,我们不需要重新发明轮子。Spark SQL支持从Parquet导入/导出SchemaRDDs, Parquet是一种高效的柱状存储格式,支持rdd和SchemaRDDs之间的简单转换。它还支持可插拔的外部数据源,如Hive和Avro.使用用户定义函数很容易从现有列创建(或者更准确地说,是声明)新列。SchemaRDD的实体化是懒惰的。Spark SQL知道如何根据请求的列优化执行计划,这很符合我们的需求。SchemaRDD支持标准数据类型。为了使它更适合ML,我们与Spark SQL团队合作,添加了Vector类型作为用户定义的类型,支持密集和稀疏特征向量。
我们展示了一个简单的Scala代码示例,用于ML数据集导入/导出和简单操作。中更完整的数据集示例Scala而且Python能在下面找到吗例子/
Spark存储库的文件夹。我们向用户推荐Spark SQL的用户指南以了解BOB低频彩更多关于SchemaRDD及其支持的操作。
val sqlContext = sqlContext (sc)
进口sqlContext。_ //隐式转换
//加载LIBSVM文件到RDD[LabeledPoint]。
val labeledPointRDD: RDD[LabeledPoint] =
MLUtils.loadLibSVMFile(“/道路/ / libsvm”)
//将其保存为一个带有隐式转换的Parquet文件
//从RDD[LabeledPoint]到SchemaRDD。
labeledPointRDD.saveAsParquetFile(“/道路/ /拼花”)
//将parquet文件加载回SchemaRDD。
val dataset = parquetFile("/path/to/parquet")
//收集特征向量并打印。
dataset.select(特性).collect () .foreach println ()
管道
新的管道API位于名为“spark.ml”的新包下。管道由一系列阶段组成。管道阶段有两种基本类型:Transformer和Estimator。Transformer接受一个数据集作为输入,并生成一个增强数据集作为输出。例如,tokenizer是一个Transformer,它将包含文本的数据集转换为包含标记化单词的数据集。Estimator必须首先适合输入数据集以生成模型,该模型是转换输入数据集的Transformer。例如,逻辑回归是一个在带有标签和特征的数据集上训练并产生逻辑回归模型的估计器。
创建管道很简单:只需声明它的阶段,配置它们的参数,并将它们链接到一个管道对象中。例如,下面的代码创建了一个简单的文本分类管道,该管道由一个标记器、一个哈希术语频率特征提取器和逻辑回归组成。
val tokenizer = new tokenizer ()
.setInputCol(“文本”)
.setOutputCol(“单词”)
val hashingTF = new hashingTF ()
.setNumFeatures (1000)
.setInputCol (tokenizer.getOutputCol)
.setOutputCol(“特性”)
val lr = new LogisticRegression()
.setMaxIter (10)
.setRegParam (0.01)
val pipeline = new pipeline ()
.setStages(Array(tokenizer, hashingTF, lr))
管道本身就是一个Estimator,因此我们可以很容易地在整个管道上调用fit。
val model = pipeline.fit(trainingDataset)
拟合模型由标记器、散列TF特征提取器和拟合的逻辑回归模型组成。下图绘制了工作流程,其中虚线只在管道安装过程中出现。
拟合的管道模型是一个可用于预测、模型验证和模型检验的变压器。
model.transform (testDataset)
.select('text, 'label, 'prediction)
.collect ()
.foreach println ()
ML算法的一个不幸特征是它们有许多必须调优的超参数。这些超参数(例如正则化程度)与MLlib优化的模型参数不同。如果没有对数据和算法的专家知识,很难猜测超参数的最佳组合。即使拥有专业知识,随着管道的大小和超参数数量的增长,它也可能变得不可靠。在实践中,为了获得有意义的结果,超参数调优(根据保留数据的性能选择参数)通常是必要的。例如,我们在下面的管道中有两个超参数要调优,我们为每个超参数设置了三个候选值。因此,总共有9个组合(下图中有4个),我们希望找到一个组合,从而得到评价结果最好的模型。
我们支持超参数调优的交叉验证。我们将交叉验证视为一种元算法,它试图用用户指定的参数组合来拟合底层估计器,交叉评估拟合的模型,并输出最佳模型。请注意,对底层估计器没有特定的要求,它可以是一个管道,只要它可以与从预测中输出标量度量(例如精度)的Evaluator配对即可。调优管道很简单:
//创建一个参数网格。
val paramGrid = new ParamGridBuilder()
.addGrid (hashingTF。numFeatures, Array(10, 20, 40)
.addGrid (lr)。regParam,数组(0.01,0.1,1.0)
.build ()
//设置交叉验证。
val cv =新的交叉验证器()
.setNumFolds (3)
.setEstimator(管道)
.setEstimatorParamMaps (paramGrid)
.setEvaluator(新BinaryClassificationEvaluator)
//用交叉验证来拟合模型。
val cvModel = cv.fit(trainingDataset)
需要注意的是,用户可以将他们自己的转换器或估计器嵌入到ML管道中,只要他们实现了管道接口。这个API使得在MLlib之外维护的代码易于使用和共享。中更完整的代码示例Java而且Scala可以在Spark存储库的“examples/”文件夹下找到。我们建议用户参考火花。Ml用户指南有关管道API的更多信息。
结束语
这篇博文描述了Spark 1.2中引入的ML管道API及其背后的原理。这项工作由几个jira负责:火星- 3530,火星- 3569,火星- 3572,火星- 4192,火星- 4209.我们建议用户参考张贴在每个JIRA页面上的设计文档,以获得关于设计选择的更多信息。我们要感谢所有参与讨论并提供宝贵反馈的人。
也就是说,在Spark 1.2中,管道API还处于实验阶段,工作还远远没有完成。例如,更多的特性转换器可以帮助用户快速组装管道。我们想提一下与管道API相关的一些正在进行的工作:
该管道API是Spark 1.2的一部分,可以从以下网站下载https://spark.apache.org/.我们期待收到您的回复,并欢迎您的贡献和反馈。