机器学习库(MLlib)指南
MLlib是Spark的机器学习(ML)库。 它的目标是使实用的机器学习可扩展和容易。它提供了一个高水平的工具,如:
ML算法:常见的学习算法,如分类,回归,聚类和协同过滤
特征:特征提取和选择,变换,降维
管道:用于构建,评估和调整ML管道的工具
持久性:保存和加载算法,模型和管道
工具:线性代数,统计,数据处理等
一、ML Pipelines:
在本节中,我们介绍ML管线的概念。ML管道提供一套统一的高级API,它建立在DataFrames之上,帮助用户创建和调整实用的机器学习管道。
1、管道中的主要概念
1.1 DataFrame
1.2 管道组件
1.2.1 Transformers
1.2.2 Estimators
1.2.3 管道组件的性能
1.3 Pipeline
1.3.1 How it works
1.3.2 Details
1.4 参数
1.5 保存和加载管道
2、代码示例
2.1 示例:Estimator, Transformer, and Param
2.2 示例:Pipeline
2.3 Model selection (hyperparameter tuning)
1、管道中的主要概念
MLlib为机器学习算法标准化API,以便更容易将多个算法合并到单个管道或工作流中。 本部分涵盖Pipelines API引入的关键概念,其中管道概念
主要受scikit-learn项目的启发。
DataFrame:这个是ML的API使用Spark SQL中的DataFrame作为ML数据集,它可以容纳各种数据类型。 例如,DataFrame可以具有存储文本,特征向量,
真实标签和预测的不同列。
变换器:变换器是一种可以将一个DataFrame转换为另一个DataFrame的算法。 例如,ML模型是将具有特征的DataFrame转换为具有预测的DataFrame
的Transformer。
估计器:估计器是一种可以适合DataFrame以产生变换器的算法。 例如,学习算法是在DataFrame上训练并产生模型的估计器。
管道:管道将多个变换器和估计器链接在一起,以指定ML工作流。
参数:所有变换器和估计器现在共享用于指定参数的公共API。
1.1 DataFrame
机器学习可应用于各种各样的数据类型,例如向量,文本,图像和结构化数据。 此API采用Spark SQL中的DataFrame以支持各种数据类型。
DataFrame支持许多基本和结构化类型; 相关的支持类型,请参阅Spark SQL数据类型参考。 除了Spark SQL指南中列出的类型之外,
DataFrame还可以使用ML Vector类型。可以从常规RDD隐式或显式创建DataFrame。 请参阅下面的代码示例和Spark SQL编程指南的示例。
DataFrame中的列已命名。 下面的代码示例使用名称,如“text”,“功features”和“label”。
1.2 Pipeline components
1.2.1 Transformers
Transformers是包括特征变换和学习模型的一种抽象。 技术上,Transformer实现了一个方法transform(),它通过附加一个或多个列将一个DataFrame
转换为另一个DataFrame。 例如:
特征变换器可以采用DataFrame,读取列(例如,文本),将其映射到新列(例如,特征向量),并且输出具有附加的映射列的新DataFrame。
学习模型可以采用DataFrame,读取包含特征向量的列,预测每个特征向量的标签,并输出新的DataFrame,其中预测的标签作为列添加。
1.2.2 Estimators
Estimators是学习算法或任何算法对数据进行填充和训练的概念。 从技术上讲,Estimator实现了一个方法fit(),它接受一个DataFrame并产生一个Model,可以说是
一个一个Transformer。例如,诸如LogisticRegression的学习算法是Estimators,并且调用fit()训练LogisticRegressionModel,因为LogisticRegressionModel
是一个模型,因此Estimators也是一个Transformers。
1.2.3 Properties of pipeline components
Transformer.transform()和Estimator.fit()都是无状态的。 在将来,可以经由替代概念来支持状态算法。
Transformer或Estimator的每个实例都有一个唯一的ID,在指定参数(下面讨论)时很有用。
1.3 Pipeline
在机器学习中,通常运行一系列算法来处理和学习数据。 例如,简单的文本文档处理工作流可以包括以下几个阶段:
(1)将每个文档的文本拆分为单词。
(2)将每个文档的单词转换为数字特征向量。
(3)使用特征向量和标签了解预测模型。
MLlib将包括以特定顺序运行的PipelineStages(Transformers和Estimators)序列的工作流表示为Pipeline。
在本节中,我们将使用这个简单的工作流程作为一个运行的例子。
1.3.1 How it works
Pipeline被指定为阶段序列,并且每个阶段是Transfomer或Estimator。这些阶段按顺序运行,并且输入DataFrame在其通过每个阶段时被转换。 对于Transformer阶段,
在DataFrame上调用transform()方法。 对于Estimator阶段,调用fit()方法以产生Transformer(它成为PipelineModel的一部分或拟合的管道),并且
Transformer的transform()方法在DataFrame上调用。
我们说明这个简单的文本文档工作流。 下图是管道的训练时间用法。
上面,第一行表示具有三个阶段的Pipeline。 前两个(Tokenizer和HashingTF)是Transformers(蓝色),第三个(LogisticRegression)
是Estimator(红色)。 第二行表示流过Pipeline的数据,其中圆柱表示DataFrames。 在原始DataFrame上调用Pipeline.fit()方法,它具
有原始文本文档和标签。 Tokenizer.transform()方法将原始文本文档拆分为单词,向DataFrame添加一个带有单词的新列。
HashingTF.transform()方法将字列转换为特征向量,向这些向量添加一个新列到DataFrame。 现在,由于LogisticRegression是一个Estimator,
Pipeline首先调用LogisticRegression.fit()产生一个LogisticRegressionModel。 如果流水线有更多的阶段,则在将DataFrame传递到下一阶段
之前,将在DataFrame上调用LogisticRegressionModel的transform()方法。
Pipeline是一个Estimator。 因此,在Pipeline的fit()方法运行之后,它产生一个PipelineModel,它是一个Transformer。 这个管道模型在测试时使用;
下图说明了这种用法。
在上图中,PipelineModel具有与原始流水线相同的级数,但是原始流水线中的所有Estimator都变为Transformer。 当在test数据集上调用PipelineModel的transform()
方法时,数据按顺序通过拟合Pipiline。 每个阶段的transform()方法更新数据集并将其传递到下一个阶段。
Pipelines and PipelineModels有助于确保 training和test数据通过相同的特征处理步骤。
1.3.2 Details
DAG管道:Pipeline的阶段被指定为有序数组。这里给出的示例全部用于线性Pipeline,即其中每个级使用由前一级产生的数据的Pipeline。只要数据流图形形成定向非循环图(DAG),
就可以创建非线性Pipeline。此图形当前基于每个阶段的输入和输出列名称(通常指定为参数)隐式指定。如果Pipeline形成DAG,则这个阶段必须以拓扑顺序指定。
运行时检查:由于Pipelines可以对不同类型的DataFrames进行操作,因此它们不能使用编译时类型检查。 Pipelines和PipelineModel而不是在实际运行管道之前进行运行时检查。
此类型检查是使用DataFrame模式完成的,DataFrame模式是DataFrame中列的数据类型的描述。
独特的流水线阶段:流水线的阶段应该是唯一的实例。例如,相同的实例myHashingTF不应该插入管道两次,因为管道阶段必须具有唯一的ID。
然而,不同的实例myHashingTF1和myHashingTF2(两者类型HashingTF)可以放入同一流水线,因为不同的实例将创建与不同的ID。
1.4 参数
MLlib的Estimators和Transformers使用统一的API来指定参数。Param是一个包含文档的命名参数。 ParamMap是一组(parameter,value)对。
将参数传递给算法有两种主要方法: 设置实例的参数。 例如,如果lr是LogisticRegression的一个实例,可以调用lr.setMaxIter(10)使lr.fit()最多使用10次迭代。
此API类似于spark.mllib包中使用的API。 将paramMap传递给fit()或transform()。 ParamMap中的任何参数都将覆盖先前通过setter方法指定的参数。
参数属于Estimators和Transformers的特定实例。 例如,如果我们有两个LogisticRegression实例lr1和lr2,
那么我们可以创建一个具有指定的maxIter参数的ParamMap:ParamMap(lr1.maxIter - > 10,lr2.maxIter - > 20)。
如果在流水线中有两个算法带有maxIter参数,这是有用的。
1.5 保存和加载管道
通常,值得将模型或管道保存到磁盘以备后用。 在Spark 1.6中,模型导入/导出功能已添加到Pipeline API。 支持大多数基本变压器以及一些更基本的ML模型。
请参阅算法的API文档,以了解是否支持保存和加载。
2、代码示例
本节给出了说明上述功能的代码示例。 有关详细信息,请参阅API文档(Scala,Java和Python)。
2.1 示例:Estimator, Transformer, and Param
此示例涵盖Estimator,Transformer和Param的概念。
#Python版
from pyspark.ml.linalg import Vectors
from pyspark.ml.classification import LogisticRegression
#从(label,features)元组列表中准备训练数据
training = spark.createDataFrame([
(1.0, Vectors.dense([0.0, 1.1, 0.1])),
(0.0, Vectors.dense([2.0, 1.0, -1.0])),
(0.0, Vectors.dense([2.0, 1.3, 1.0])),
(1.0, Vectors.dense([0.0, 1.2, -0.5]))], ["label", "features"])
#创建LogisticRegression实例。 此实例是一个估计器
lr = LogisticRegression(maxIter=10,regParam = 0.01)
#打印参数,文档和任何默认值
print("LogisticRegression parameters:\n" + lr.explainParams() + "\n")
#学习LogisticRegression模型,使用存储在lr中的参数
model = lr.fit(training)
#由于model是一个模型(即由Estimator生成的transformer),
#我们可以查看在fit()期间使用的参数。
#打印参数(名称:值)对,其中名称是唯一的ID
#LogisticRegression实例
print("Model 1 was fit using parameters: ")
print(model.extractParamMap())
#我们还可以使用Python字典作为paramMap指定参数
paramMap = {lr.maxIter: 20}
paramMap[lr.maxIter] = 30 # 指定1 Param,覆盖原始maxIter
paramMap.update({lr.regParam: 0.1, lr.threshold: 0.55}) # 指定多个参数
#你可以结合paramMaps,这是python词典
paramMap2 = {lr.probabilityCol: "myProbability"} # 改变输出列的名称
paramMapCombined = paramMap.copy()
paramMapCombined.update(paramMap2)
#现在使用paramMapCombined参数学习一个新模型
#paramMapCombined通过lr.set *方法覆盖之前设置的所有参数
model2 = lr.fit(training, paramMapCombined)
print("Model 2 was fit using parameters: ")
print(model2.extractParamMap())
#准备测试数据
test = spark.createDataFrame([
(1.0, Vectors.dense([-1.0, 1.5, 1.3])),
(0.0, Vectors.dense([3.0, 2.0, -0.1])),
(1.0, Vectors.dense([0.0, 2.2, -1.5]))], ["label", "features"])
#使用Transformer.transform()方法对测试数据进行预测。
#LogisticRegression.transform将只使用“features”列。
#请注意,model2.transform()输出“myProbability”列,而不是通常的
#'probability'列,因为我们以前重命名了lr.probabilityCol参数。
prediction = model2.transform(test)
result = prediction.select("features", "label", "myProbability", "prediction") \
.collect()
for row in result:
print("features=%s, label=%s -> prob=%s, prediction=%s"
% (row.features, row.label, row.myProbability, row.prediction))
输出:
features=[-1.0,1.5,1.3], label=1.0 -> prob=[0.0570730417103,0.94292695829], prediction=1.0
features=[3.0,2.0,-0.1], label=0.0 -> prob=[0.92385223117,0.0761477688296], prediction=0.0
features=[0.0,2.2,-1.5], label=1.0 -> prob=[0.109727761148,0.890272238852], prediction=1.0
2.2 示例:Pipeline
#Python版
from pyspark.ml import Pipeline
from pyspark.ml.classification import LogisticRegression
from pyspark.ml.feature import HashingTF, Tokenizer
#从(id,text,label)的元组列表准备训练文档
training = spark.createDataFrame([
(0, "a b c d e spark", 1.0),
(1, "b d", 0.0),
(2, "spark f g h", 1.0),
(3, "hadoop mapreduce", 0.0)
], ["id", "text", "label"])
#配置ML的Pipeline,包括三个阶段:tokenizer,hashingTF和lr。
tokenizer = Tokenizer(inputCol="text", outputCol="words")
hashingTF = HashingTF(inputCol=tokenizer.getOutputCol(), outputCol="features")
lr = LogisticRegression(maxIter=10, regParam=0.001)
pipeline = Pipeline(stages=[tokenizer, hashingTF, lr])
#拟合数据
model = pipeline.fit(training)
#准备测试文档,它们是未标记(id,text)元组。
test = spark.createDataFrame([
(4, "spark i j k"),
(5, "l m n"),
(6, "spark hadoop spark"),
(7, "apache hadoop")
], ["id", "text"])
#预测和打印
prediction = model.transform(test)
selected = prediction.select("id", "text", "probability", "prediction")
for row in selected.collect():
rid, text, prob, prediction = row
print("(%d, %s) --> prob=%s, prediction=%f" % (rid, text, str(prob), prediction))
输出:
(4, spark i j k) --> prob=[0.159640773879,0.840359226121], prediction=1.000000
(5, l m n) --> prob=[0.837832568548,0.162167431452], prediction=0.000000
(6, spark hadoop spark) --> prob=[0.0692663313298,0.93073366867], prediction=1.000000
(7, apache hadoop) --> prob=[0.982157533344,0.0178424666556], prediction=0.000000
2.3 Model selection (hyperparameter tuning)
使用ML管道的一个大好处是超参数优化。 有关自动模型选择的更多信息,请参阅ML Tuning Guide。