三、Classification and regression
Spark.mllib包为二分类、多分类和回归分析提供了多样的支持工具
linear models(线性模型)
1)Mathematical formulation(数学公式)
很多标准的机器学习方法都可以表述为凸优化问题,例如:找到依赖于变量向量w的凸函数f的极小值的任务(在代码中被称为权重),通常含有d个输入。形式上,我们可以将其写为优化问题minw∈ℝdf(w),,具有如下形式的目标方程
这里的向量
这里向量xi∈ℝd 是训练数据示例, 其中1≤i≤n, 并且 yi∈ℝ是他们相应的标签, 也是我们想要预测的。我们称方法是线性的如果L(w;x,y) 可以被表示称方程 wTx and y。spark.mllib的几种分类和回归算法都属于此类,在此进行讨论。
目标方程f具有两部分:
The objective function f has two parts: 控制模型复杂度的正则化器,以及测量训练数据上模型误差的损失。损失函数L(w ;.)通常是w中的凸函数。固定的正则化参数λ≥0(代码中的regParam)定义了两个目标之间的权衡,这两个目标是最小化损失(即训练误差)和最小化模型复杂度(即避免过度拟合)。
(1)损失方程
下表总结了spark.mllib支持的方法的损失函数及其梯度或子梯度:
注意,在上面的数学公式中,二进制标记y表示为+1(正)或-1(负),这对于公式方便。
但是,负标记在spark.mllib中由0而不是-1表示,以便与多类标记一致。
(2)Regularizers(正则化器)
正则化器的目的是鼓励简化模型并避免过度拟合。我们在spark.mllib中支持以下正则化器:
这里的sign(w)是由w的所有项的符号(±1)组成的向量。由于平滑,L2正则化的问题通常比L1正则化的问题更容易解决。但是,L1正则化可以帮助促进权重的稀疏性,从而导致模型更小,更易解释,后者对于特征选择非常有用。弹性网是L1和L2正则化的组合。不建议在不进行任何正则化的情况下训练模型,尤其是在训练示例数量很少的情况下。
(3)Optimization(优化)
在幕后,线性方法使用凸优化方法来优化目标函数。 spark.mllib使用两种方法,SGD和L-BFGS,在优化部分中进行了介绍。当前,大多数算法API支持随机梯度下降(SGD),而少数支持L-BFGS。 有关在两种优化方法之间进行选择的指导,请参阅此优化部分。
2)classification(分类)
分类旨在将项目分为几类。最常见的分类类型是二进制分类,其中有两个类别,通常分别命名为肯定和否定。如果类别超过两个,则称为多类别分类。 spark.mllib支持两种线性分类方法:线性支持向量机(SVM)和逻辑回归。线性SVM仅支持二进制分类,而逻辑回归支持二进制和多类分类问题。对于这两种方法,spark.mllib支持L1和L2正则化变量。训练数据集由MLlib中LabeledPoint的RDD表示,其中标签是从零开始的类索引:0,1,2,…。
(1)Linear Support Vector Machines(SVMs)(线性支持向量机)
线性SVM是用于大规模分类任务的标准方法。这是一种线性方法,如上面的公式(1)所述,公式中的损耗函数由铰链损耗给出:
默认情况下,线性SVM使用L2正则化训练。我们还支持替代的L1正则化。在这种情况下,问题就变成了线性程序。线性SVM算法输出一个SVM模型。给定一个新的数据点,用x表示,该模型基于wTx的值进行预测。默认情况下,如果wTx≥0,则结果为正,否则为负。
示例代码
以下代码段说明了如何加载样本数据集,如何使用算法对象中的静态方法对此训练数据执行训练算法,以及如何使用所得模型进行预测以计算训练误差。
有关API的详细信息,请参考SVMWithSGD Scala文档和SVMModel Scala文档。
import org.apache.spark.mllib.classification.{SVMModel, SVMWithSGD}
import org.apache.spark.mllib.evaluation.BinaryClassificationMetrics
import org.apache.spark.mllib.util.MLUtils
// Load training data in LIBSVM format.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split data into training (60%) and test (40%).
val splits = data.randomSplit(Array(0.6, 0.4), seed = 11L)
val training = splits(0).cache()
val test = splits(1)
// Run training algorithm to build the model
val numIterations = 100
val model = SVMWithSGD.train(training, numIterations)
// Clear the default threshold.
model.clearThreshold()
// Compute raw scores on the test set.
val scoreAndLabels = test.map { point =>
val score = model.predict(point.features)
(score, point.label)
}
// Get evaluation metrics.
val metrics = new BinaryClassificationMetrics(scoreAndLabels)
val auROC = metrics.areaUnderROC()
println(s"Area under ROC = $auROC")
// Save and load model
model.save(sc, "target/tmp/scalaSVMWithSGDModel")
val sameModel = SVMModel.load(sc, "target/tmp/scalaSVMWithSGDModel")
(2)Logistic regression
逻辑回归广泛用于预测二进制响应。这是一种线性方法,如上面的等式(1)所述,公式中的损失函数由逻辑损失给出:
对于二进制分类问题,该算法输出二进制logistic回归模型。给定一个新的数据点,用x表示,该模型通过应用逻辑函数进行预测
其中z = wTx。默认情况下,如果f(wTx)> 0.5,则结果为正,否则为负,尽管与线性SVM不同,对数回归模型f(z)的原始输出具有概率解释(即,x的概率是肯定的)。
可以将二进制逻辑回归概括为多项式逻辑回归,以训练和预测多类分类问题。例如,对于K个可能的结果,可以选择其中一个结果作为“枢轴”,而将其他K-1个结果与该枢轴结果分别回归。在spark.mllib中,将第一个类0选择为“ pivot”类。有关参考,请参见《统计学习的要素》的第4.4节。这是详细的数学推导。
对于多类分类问题,该算法将输出一个多项式逻辑回归模型,其中包含针对第一类回归的K-1个二进制逻辑回归模型。给定一个新的数据点,将运行K-1个模型,并且将选择概率最大的类别作为预测类别。
我们实现了两种算法来解决逻辑回归:小批量梯度下降和L-BFGS。我们建议在小批量梯度下降时使用L-BFGS,以加快收敛速度。
示例代码
以下代码说明了如何加载示例多类数据集,将其分为训练和测试,以及如何使用LogisticRegressionWithLBFGS来拟合Logistic回归模型。然后根据测试数据集评估模型并将其保存到磁盘。
有关API的详细信息,请参考LogisticRegressionWithLBFGS Scala文档和LogisticRegressionModel Scala文档。
import org.apache.spark.mllib.classification.{LogisticRegressionModel, LogisticRegressionWithLBFGS}
import org.apache.spark.mllib.evaluation.MulticlassMetrics
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.util.MLUtils
// Load training data in LIBSVM format.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split data into training (60%) and test (40%).
val splits = data.randomSplit(Array(0.6, 0.4), seed = 11L)
val training = splits(0).cache()
val test = splits(1)
// Run training algorithm to build the model
val model = new LogisticRegressionWithLBFGS()
.setNumClasses(10)
.run(training)
// Compute raw scores on the test set.
val predictionAndLabels = test.map { case LabeledPoint(label, features) =>
val prediction = model.predict(features)
(prediction, label)
}
// Get evaluation metrics.
val metrics = new MulticlassMetrics(predictionAndLabels)
val accuracy = metrics.accuracy
println(s"Accuracy = $accuracy")
// Save and load model
model.save(sc, "target/tmp/scalaLogisticRegressionWithLBFGSModel")
val sameModel = LogisticRegressionModel.load(sc,
"target/tmp/scalaLogisticRegressionWithLBFGSModel")
3) regression
(1)Linear least squares , lasso and ridge regression
线性最小二乘法是回归问题的最常见表示。这是一种线性方法,如上面的公式(1)所述,公式中的损失函数由平方损失给出:
通过使用不同类型的正则化来推导各种相关的回归方法:普通最小二乘法或线性最小二乘不使用正则化;岭回归使用L2正则化; Lasso使用L1正则化。对于所有这些模型,平均损失或训练误差
被称为均方误差。
示例代码
下面的示例演示如何加载训练数据,将其解析为LabeledPoint的RDD。然后,该示例使用LinearRegressionWithSGD构建一个简单的线性模型来预测标签值。我们在最后计算均方误差以评估拟合优度。
有关API的详细信息,请参阅LinearRegressionWithSGD Scala文档和LinearRegressionModel Scala文档。
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.regression.LinearRegressionModel
import org.apache.spark.mllib.regression.LinearRegressionWithSGD
// Load and parse the data
val data = sc.textFile("data/mllib/ridge-data/lpsa.data")
val parsedData = data.map { line =>
val parts = line.split(',')
LabeledPoint(parts(0).toDouble, Vectors.dense(parts(1).split(' ').map(_.toDouble)))
}.cache()
// Building the model
val numIterations = 100
val stepSize = 0.00000001
val model = LinearRegressionWithSGD.train(parsedData, numIterations, stepSize)
// Evaluate model on training examples and compute training error
val valuesAndPreds = parsedData.map { point =>
val prediction = model.predict(point.features)
(point.label, prediction)
}
val MSE = valuesAndPreds.map{ case(v, p) => math.pow((v - p), 2) }.mean()
println(s"training Mean Squared Error $MSE")
// Save and load model
model.save(sc, "target/tmp/scalaLinearRegressionWithSGDModel")
val sameModel = LinearRegressionModel.load(sc, "target/tmp/scalaLinearRegressionWithSGDModel")
RidgeRegressionWithSGD和LassoWithSGD的使用方式与LinearRegressionWithSGD相似。
为了运行上述应用程序,请遵循Spark快速入门指南的“自包含应用程序”部分中提供的说明。确保也将spark-mllib作为依赖项包含在构建文件中。
(2)Streaming linear regression(流线性回归)
当数据以流方式到达时,在线拟合回归模型,在新数据到达时更新模型的参数非常有用。 spark.mllib当前支持使用普通最小二乘法进行流线性回归。拟合与离线执行的拟合相似,不同之处在于,每批数据都会进行拟合,因此模型会不断更新以反映流中的数据。
示例
下面的示例演示如何从两个不同的文本文件输入流中加载训练和测试数据,如何将这些流解析为标记点,将线性回归模型在线拟合到第一个流,以及对第二个流进行预测。
示例代码
首先,我们导入必要的类以解析输入数据并创建模型。然后,我们为训练和测试数据创建了输入流。我们假设已经创建了StreamingContext ssc,有关更多信息,请参见Spark Streaming编程指南。在此示例中,我们在训练和测试流中使用带标签的点,但实际上,您可能希望将无标签的向量用于测试数据。
我们通过将权重初始化为零来创建模型,并注册流以进行训练和测试,然后开始工作。将预测与真实标签一起打印可以使我们轻松查看结果。
最后,我们可以将带有数据的文本文件保存到培训或测试文件夹中。每行应该是一个格式为(y,[x1,x2,x3])的数据点,其中y是标签,x1,x2,x3是要素。每当将文本文件放入args(0)时,模型都会更新。任何时候将文本文件放在args(1)中,您都会看到预测。当您将更多数据输入训练目录时,预测会变得更好!
这是一个完整的示例:
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.regression.StreamingLinearRegressionWithSGD
val trainingData = ssc.textFileStream(args(0)).map(LabeledPoint.parse).cache()
val testData = ssc.textFileStream(args(1)).map(LabeledPoint.parse)
val numFeatures = 3
val model = new StreamingLinearRegressionWithSGD()
.setInitialWeights(Vectors.zeros(numFeatures))
model.trainOn(trainingData)
model.predictOnValues(testData.map(lp => (lp.label, lp.features))).print()
ssc.start()
ssc.awaitTermination()
4)Implementation(developer)–实施(开发人员)
在幕后,spark.mllib基于基础的梯度下降原语(如优化部分所述)实现了随机梯度下降(SGD)的简单分布式版本。 所有提供的算法均将正则化参数(regParam)以及与随机梯度下降相关的各种参数(stepSize,numIterations,miniBatchFraction)作为输入。对于它们中的每一个,我们都支持所有三种可能的正则化(无,L1或L2)。
对于Logistic回归,L-BFGS版本在LogisticRegressionWithLBFGS下实现,该版本支持二进制和多项Logistic回归,而SGD版本仅支持二进制Logistic回归。但是,L-BFGS版本不支持L1正则化,而SGD一个支持L1正则化。当不需要L1正则化时,强烈建议使用L-BFGS版本,因为通过使用拟牛顿法近似Hessian逆矩阵,与SGD相比,它收敛更快,更准确。
算法全部在Scala中实现:
- SVMWithSGD
- LogisticRegressionWithLBFGS
- LogisticRegressionWithSGD
- LinearRegressionWithSGD
- RidgeRegressionWithSGD
- LassoWithSGD
naive Bayes(朴素贝叶斯)
朴素贝叶斯是一种简单的多类分类算法,假设每对特征之间具有独立性。朴素贝叶斯可以非常有效地训练。在一次传递训练数据的过程中,它计算给定标签的每个特征的条件概率分布,然后应用贝叶斯定理计算给定观察值的标签的条件概率分布,并将其用于预测。
spark.mllib支持多项式朴素贝叶斯和Bernoulli朴素贝叶斯。这些模型通常用于文档分类。在这种情况下,每个观察值都是一个文档,每个特征都代表一个术语,其值是该术语的出现频率(在多项式朴素贝叶斯中),或者为零或一个,表示该术语是否在文档中找到(在Bernoulli朴素贝叶斯中)。特征值必须为非负数。使用可选参数“ multinomial”或“ bernoulli”(默认值为“ multinomial”)选择模型类型。可以通过设置参数λ(默认为1.0)来使用加法平滑。对于文档分类,输入特征向量通常是稀疏的,应提供稀疏向量作为输入,以利用稀疏性。由于训练数据仅使用一次,因此无需将其缓存。
示例代码
NaiveBayes实现多项式朴素贝叶斯。它以LabeledPoint的RDD和可选的平滑参数lambda作为输入,可选的模型类型参数(默认为“多项式”)作为输入,并输出一个NaiveBayesModel,可用于评估和预测。
有关API的详细信息,请参阅NaiveBayes Scala文档和NaiveBayesModel Scala文档。
import org.apache.spark.mllib.classification.{NaiveBayes, NaiveBayesModel}
import org.apache.spark.mllib.util.MLUtils
// Load and parse the data file.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split data into training (60%) and test (40%).
val Array(training, test) = data.randomSplit(Array(0.6, 0.4))
val model = NaiveBayes.train(training, lambda = 1.0, modelType = "multinomial")
val predictionAndLabel = test.map(p => (model.predict(p.features), p.label))
val accuracy = 1.0 * predictionAndLabel.filter(x => x._1 == x._2).count() / test.count()
// Save and load model
model.save(sc, "target/tmp/myNaiveBayesModel")
val sameModel = NaiveBayesModel.load(sc, "target/tmp/myNaiveBayesModel")
decision trees(决策数)
决策树及其集成是用于机器学习任务的分类和回归的流行方法。决策树被广泛使用,因为它们易于解释,处理分类特征,扩展到多类分类设置,不需要特征缩放以及能够捕获非线性和特征交互。树木分类算法(例如随机森林和boosting)在分类和回归任务中表现最佳。
spark.mllib支持使用连续和分类功能进行二进制和多类分类以及用于回归的决策树。该实现按行对数据进行分区,从而可以对数百万个实例进行分布式训练。
《合集指南》中介绍了树木的合集(随机森林和渐变树)。
1)Basic algorithm(基础算法)
决策树是一种贪婪算法,它执行特征空间的递归二进制分区。该树为每个最底部(叶子)的分区预测相同的标签。通过从一组可能的分割中选择最佳分割来贪婪地选择每个分区,以使树节点的信息增益最大化。换句话说,从集合argmaxsIG(D,s)中选择在每个树节点选择的拆分,其中IG(D,s)是将拆分s应用于数据集D时的信息增益。
(1)Node Impurity and information gain
The node impurity is a measure of the homogeneity of the labels at the node. The current implementation provides two impurity measures for classification (Gini impurity and entropy) and one impurity measure for regression (variance).
(2)Split candidates(候选人拆分)
连续特征
对于单机实施中的小型数据集,每个连续特征的分割候选通常是特征的唯一值。一些实现对特征值进行排序,然后将排序后的唯一值用作候选分割符,以实现更快的树计算。
对于大型分布式数据集,对特征值进行排序非常昂贵。此实现通过对数据的采样部分执行分位数计算来计算一组近似的拆分候选集。有序拆分将创建“箱”,可以使用maxBins参数指定此类箱的最大数量。请注意,bin的数量不能大于实例N的数量(一种罕见的情况,因为默认的maxBins值为32)。如果不满足条件,则树算法会自动减少垃圾箱的数量。
分类特征
对于具有M个可能值(类别)的分类特征,可以提出2M-1-1个拆分候选。对于二进制(0/1)分类和回归,通过按平均标签对分类特征值进行排序,我们可以将分割候选的数量减少到M-1。(有关详细信息,请参见统计机器学习的元素中的9.2.4节。)例如,对于具有一个具有三个类别A,B和C的类别特征的二元分类问题,标签1的相应比例为0.2、0.6和0.4,则类别特征的顺序为A,C,B。这两个拆分候选为A | C,B和A,C | B,其中|表示在哪里分割。
在多类分类中,尽可能使用所有2M-1-1个可能的拆分。当2M-1-1大于maxBins参数时,我们使用类似于二进制分类和回归方法的(启发式)方法。 M个分类特征值按杂质排序,并考虑所得的M-1个分裂候选者。
(3)停止规则
当满足以下条件之一时,递归树构造将在节点处停止:
-节点深度等于maxDepth训练参数。
- 没有分割候选者会导致信息增益大于minInfoGain。
- 没有拆分的候选对象会生成每个都至少具有minInstancesPerNode训练实例的子节点。
2)使用技巧
通过讨论各种参数,我们包括一些使用决策树的准则。以下按重要性从高到低的顺序列出了这些参数。新用户应主要考虑“问题规范参数”部分和maxDepth参数。
(1)问题规范参数
这些参数描述了您要解决的问题和数据集。应该指定它们,并且不需要调整。
- 算法: 决策树的类型,分类或者回归。
- numClasses:类数(仅用于分类)。
- categoricalFeaturesInfo:指定哪些要素是分类的,以及每个要素可以采用多少个分类值。这是从特征索引到特征Arity(类别数量)的映射。该地图中未包含的所有要素均视为连续要素。
- 例如,Map(0-> 2,4-> 10)指定要素0为二进制(取值0或1),要素4具有10个类别(值{0,1,…,9})。请注意,要素索引基于0:要素0和4是实例的要素向量的第1个元素和第5个元素。
- 请注意,您不必指定categoricalFeaturesInfo。该算法仍将运行,并且可以获得合理的结果。但是,如果正确指定分类特征,则性能应更好。
(2)停止条件
这些参数确定树何时停止构建(添加新节点)。调整这些参数时,请小心验证保留的测试数据,以免过度拟合。
- maxDepth:一棵树的最大深度。较深的树更具表现力(可能允许更高的准确性),但它们的训练成本也更高,并且更可能过拟合。
-minInstancesPerNode:为了进一步拆分节点,其每个子节点必须至少接收此数量的训练实例。这通常与RandomForest一起使用,因为它们通常比单独的树更深入地训练。 - minInfoGain:对于要进一步拆分的节点,拆分必须至少改善这么多(就信息增益而言)。
(3)Tunable paramters(可调参数)
这些参数可以调整。调整时请小心验证保留的测试数据,以免过度拟合。
- maxBins:离散化连续特征时使用的仓数。maxBins的增加允许算法考虑更多拆分候选并做出细粒度的拆分决策。但是,它也增加了计算和通信。
- 请注意,对于任何分类功能,maxBins参数必须至少为类别M的最大数量。
- maxMemoryInMB:用于收集足够统计信息的内存量。
- 保守地将默认值选择为256 MB,以使决策算法可以在大多数情况下使用。增加maxMemoryInMB可以通过减少数据传递来加快训练速度(如果有可用的内存)。但是,随着maxMemoryInMB的增加,收益可能会减少,因为每次迭代的通信量可能与maxMemoryInMB成比例。
- 实施细节: 为了更快地进行处理,决策树算法收集有关要拆分的节点组的统计信息(而不是一次分配1个节点)。一组中可以处理的节点数由内存需求(取决于每个功能)决定。 maxMemoryInMB参数以兆字节为单位指定每个工作人员可用于这些统计信息的内存限制。
- subsamplingRate:用于学习决策树的训练数据的分数。此参数与训练树的合奏(使用RandomForest和GradientBoostedTrees)最相关,在此情况下可以对原始数据进行二次采样。对于训练单个决策树,此参数的用处不大,因为训练实例的数量通常不是主要约束。
- 杂质:用于在候选分割之间进行选择的杂质度量(如上所述)。此度量必须匹配算法参数。
(4)缓存和检查点
MLlib 1.2添加了一些功能,可扩展到更大(更深)的树和树组合。将maxDepth设置为较大时,打开节点ID缓存和检查点很有用。当numTrees设置为大时,这些参数对于RandomForest也很有用。
- useNodeIdCache:如果将其设置为true,则算法将避免在每次迭代时将当前模型(一个或多个树)传递给执行程序。
-
这对于深树(加速工作人员的计算)和大型随机森林(减少每次迭代的通信)很有用。
-
实现细节:默认情况下,该算法将当前模型传达给执行者,以便执行者可以将训练实例与树节点进行匹配。启用此设置后,算法将改为缓存此信息。
-
节点ID缓存生成一系列RDD(每次迭代1个)。如此长的血统可能会导致性能问题,但检查点中间的RDD可以缓解这些问题。请注意,仅当useNodeIdCache设置为true时,检查点才适用。
- checkpointDir:用于检查点节点ID缓存RDD的目录。
- checkpointInterval:检查点节点ID缓存RDD的频率。设置得太低将导致写入HDFS的额外开销;如果执行程序失败并且需要重新计算RDD,则将其设置得过高会导致问题。
3)Scaling
计算在训练实例的数量,要素的数量和maxBins参数中近似线性地缩放。通信功能和maxBins的数量大致呈线性比例。实现的算法读取稀疏和密集数据。但是,它并未针对稀疏输入进行优化。
4)示例
(1)分类
下面的示例演示如何加载LIBSVM数据文件,将其解析为LabeledPoint的RDD,然后使用决策树执行分类,该决策树将Gini杂质用作杂质度量,最大树深度为5。计算测试误差以测量算法精度。
示例代码
有关API的详细信息,请参阅DecisionTree Scala文档和DecisionTreeModel Scala文档。
import org.apache.spark.mllib.tree.DecisionTree
import org.apache.spark.mllib.tree.model.DecisionTreeModel
import org.apache.spark.mllib.util.MLUtils
// Load and parse the data file.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split the data into training and test sets (30% held out for testing)
val splits = data.randomSplit(Array(0.7, 0.3))
val (trainingData, testData) = (splits(0), splits(1))
// Train a DecisionTree model.
// Empty categoricalFeaturesInfo indicates all features are continuous.
val numClasses = 2
val categoricalFeaturesInfo = Map[Int, Int]()
val impurity = "gini"
val maxDepth = 5
val maxBins = 32
val model = DecisionTree.trainClassifier(trainingData, numClasses, categoricalFeaturesInfo,
impurity, maxDepth, maxBins)
// Evaluate model on test instances and compute test error
val labelAndPreds = testData.map { point =>
val prediction = model.predict(point.features)
(point.label, prediction)
}
val testErr = labelAndPreds.filter(r => r._1 != r._2).count().toDouble / testData.count()
println(s"Test Error = $testErr")
println(s"Learned classification tree model:\n ${model.toDebugString}")
// Save and load model
model.save(sc, "target/tmp/myDecisionTreeClassificationModel")
val sameModel = DecisionTreeModel.load(sc, "target/tmp/myDecisionTreeClassificationModel")
(2)回归
下面的示例演示如何加载LIBSVM数据文件,将其解析为LabeledPoint的RDD,然后使用决策树执行回归,并以方差作为杂质度量,最大树深度为5。计算均方误差(MSE)最后评估适合度。
import org.apache.spark.mllib.tree.DecisionTree
import org.apache.spark.mllib.tree.model.DecisionTreeModel
import org.apache.spark.mllib.util.MLUtils
// Load and parse the data file.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split the data into training and test sets (30% held out for testing)
val splits = data.randomSplit(Array(0.7, 0.3))
val (trainingData, testData) = (splits(0), splits(1))
// Train a DecisionTree model.
// Empty categoricalFeaturesInfo indicates all features are continuous.
val categoricalFeaturesInfo = Map[Int, Int]()
val impurity = "variance"
val maxDepth = 5
val maxBins = 32
val model = DecisionTree.trainRegressor(trainingData, categoricalFeaturesInfo, impurity,
maxDepth, maxBins)
// Evaluate model on test instances and compute test error
val labelsAndPredictions = testData.map { point =>
val prediction = model.predict(point.features)
(point.label, prediction)
}
val testMSE = labelsAndPredictions.map{ case (v, p) => math.pow(v - p, 2) }.mean()
println(s"Test Mean Squared Error = $testMSE")
println(s"Learned regression tree model:\n ${model.toDebugString}")
// Save and load model
model.save(sc, "target/tmp/myDecisionTreeRegressionModel")
val sameModel = DecisionTreeModel.load(sc, "target/tmp/myDecisionTreeRegressionModel")
Ensembles of decisions trees(决策树的集合)
集成方法是一种学习算法,它创建由一组其他基本模型组成的模型。 spark.mllib支持两种主要的集成算法:GradientBoostedTrees和RandomForest。两者都使用决策树作为其基础模型。
Gradient-Boosted Trees vs. Random Forest
梯度增强树(GBT)和随机森林都是用于学习树的合奏的算法,但是训练过程不同。需要进行一些实际的权衡:
- GBT一次训练一棵树,因此与随机森林相比,它们的训练时间更长。随机森林可以并行训练多棵树。
- 另一方面,与随机森林相比,对GBT使用较小(较浅)的树通常是合理的,并且训练较小的树所需的时间更少。
- 随机森林可能不太适合过度拟合。在随机森林中训练更多的树可以减少过度拟合的可能性,但是使用GBT训练更多的树则可以增加过度拟合的可能性。 (用统计语言,随机森林通过使用更多的树来减少方差,而GBT通过使用更多的树来减少偏差。)
- 随机森林可能更容易调整,因为性能随树的数量单调提高(而如果树的数量太大,GBT的性能可能会开始下降)。
简而言之,两种算法都是有效的,并且选择应基于特定的数据集。
1)随机森林
随机森林是决策树的集合。随机森林是用于分类和回归的最成功的机器学习模型之一。他们结合了许多决策树,以减少过度拟合的风险。像决策树一样,随机森林处理分类特征,扩展到多类分类设置,不需要特征缩放,并且能够捕获非线性和特征交互。
spark.mllib支持使用连续和分类功能对二进制和多类分类以及进行回归的随机森林。 spark.mllib使用现有的决策树实现来实现随机森林。请参阅决策树指南以获取有关树的更多信息。
基础语法
随机森林分别训练一组决策树,因此可以并行进行训练。该算法将随机性注入训练过程中,因此每个决策树都略有不同。合并来自每棵树的预测可以减少预测的方差,从而提高测试数据的性能。
训练
注入训练过程的随机性包括:
- 在每次迭代中对原始数据集进行二次采样以获得不同的训练集(也称为自举)。
- 考虑要在每个树节点上分割的要素的不同随机子集。
除了这些随机化之外,决策树训练的方式与单个决策树的训练方式相同。
预测
为了对新实例进行预测,随机森林必须汇总其决策树集中的预测。对于分类和回归,此聚合的执行方式有所不同。
分类: 多数票。每棵树的预测都算作对一个班级的投票。预计该标签将是获得最多选票的类别。
回归: 平均。每棵树都预测一个真实的价值。标签被预测为树预测的平均值。
使用技巧
通过讨论各种参数,我们包括一些使用随机森林的准则。由于决策树指南中介绍了一些决策树参数,因此我们将其省略。
我们提到的前两个参数是最重要的,对其进行调整通常可以提高性能:
- numTrees: 森林中树的数量
- 增加树的数量将减少预测的方差,从而提高模型的测试时间准确性。
- 训练时间在树木数量上大致呈线性增加。
- maxDepth: 森林中每棵树的最大深度
- 深度的增加使模型更具表现力和功能。但是,深树需要更长的训练时间,也更容易过度拟合。
- 通常,使用随机森林比使用单个决策树训练更深的树是可以接受的。与随机森林相比,一棵树更可能过度拟合(由于对森林中的多棵树进行平均而减少了方差)。
接下来的两个参数通常不需要调整。但是,可以对其进行调整以加快培训速度。
- subsamplingRate: 此参数指定用于训练森林中每棵树的数据集的大小,作为原始数据集大小的一部分。建议使用默认值(1.0),但降低此比例可以加快训练速度。
- featureSubsetStrategy: 用作在每个树节点处分割的候选要素的数量。该数字指定为特征总数的分数或函数。减少此数字将加快培训速度,但如果过低,有时会影响性能。
示例代码
分类
下面的示例演示如何加载LIBSVM数据文件,将其解析为LabeledPoint的RDD,然后使用随机森林进行分类。计算测试误差以测量算法准确性。
import org.apache.spark.mllib.tree.RandomForest
import org.apache.spark.mllib.tree.model.RandomForestModel
import org.apache.spark.mllib.util.MLUtils
// Load and parse the data file.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split the data into training and test sets (30% held out for testing)
val splits = data.randomSplit(Array(0.7, 0.3))
val (trainingData, testData) = (splits(0), splits(1))
// Train a RandomForest model.
// Empty categoricalFeaturesInfo indicates all features are continuous.
val numClasses = 2
val categoricalFeaturesInfo = Map[Int, Int]()
val numTrees = 3 // Use more in practice.
val featureSubsetStrategy = "auto" // Let the algorithm choose.
val impurity = "gini"
val maxDepth = 4
val maxBins = 32
val model = RandomForest.trainClassifier(trainingData, numClasses, categoricalFeaturesInfo,
numTrees, featureSubsetStrategy, impurity, maxDepth, maxBins)
// Evaluate model on test instances and compute test error
val labelAndPreds = testData.map { point =>
val prediction = model.predict(point.features)
(point.label, prediction)
}
val testErr = labelAndPreds.filter(r => r._1 != r._2).count.toDouble / testData.count()
println(s"Test Error = $testErr")
println(s"Learned classification forest model:\n ${model.toDebugString}")
// Save and load model
model.save(sc, "target/tmp/myRandomForestClassificationModel")
val sameModel = RandomForestModel.load(sc, "target/tmp/myRandomForestClassificationModel")
回归
下面的示例演示如何加载LIBSVM数据文件,将其解析为LabeledPoint的RDD,然后使用随机森林执行回归。最后计算均方误差(MSE)以评估拟合优度。
import org.apache.spark.mllib.tree.RandomForest
import org.apache.spark.mllib.tree.model.RandomForestModel
import org.apache.spark.mllib.util.MLUtils
// Load and parse the data file.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split the data into training and test sets (30% held out for testing)
val splits = data.randomSplit(Array(0.7, 0.3))
val (trainingData, testData) = (splits(0), splits(1))
// Train a RandomForest model.
// Empty categoricalFeaturesInfo indicates all features are continuous.
val numClasses = 2
val categoricalFeaturesInfo = Map[Int, Int]()
val numTrees = 3 // Use more in practice.
val featureSubsetStrategy = "auto" // Let the algorithm choose.
val impurity = "variance"
val maxDepth = 4
val maxBins = 32
val model = RandomForest.trainRegressor(trainingData, categoricalFeaturesInfo,
numTrees, featureSubsetStrategy, impurity, maxDepth, maxBins)
// Evaluate model on test instances and compute test error
val labelsAndPredictions = testData.map { point =>
val prediction = model.predict(point.features)
(point.label, prediction)
}
val testMSE = labelsAndPredictions.map{ case(v, p) => math.pow((v - p), 2)}.mean()
println(s"Test Mean Squared Error = $testMSE")
println(s"Learned regression forest model:\n ${model.toDebugString}")
// Save and load model
model.save(sc, "target/tmp/myRandomForestRegressionModel")
val sameModel = RandomForestModel.load(sc, "target/tmp/myRandomForestRegressionModel")
2)Gradient-Boosted Trees
梯度增强树(GBT)是决策树的集合。 GBT反复训练决策树,以使损失函数最小化。像决策树一样,GBT可以处理分类特征,扩展到多类分类设置,不需要特征缩放,并且能够捕获非线性和特征交互。
spark.mllib使用连续和分类功能支持GBT用于二进制分类和回归。 spark.mllib使用现有的决策树实现来实现GBT。请参阅决策树指南以获取有关树的更多信息。
注意:GBT尚不支持多类分类。对于多类问题,请使用决策树或随机森林
基础算法
梯度提升以迭代方式训练决策树序列。在每次迭代中,该算法使用当前整体预测每个训练实例的标签,然后将预测结果与真实标签进行比较。重新标记数据集,以将更多的重点放在预测效果较差的训练实例上。因此,在下一次迭代中,决策树将有助于纠正先前的错误。
重新标记实例的特定机制由损失函数定义(如下所述)。每次迭代,GBT都会进一步减少训练数据上的损失函数。
损失
下表列出了spark.mllib中GBT当前支持的损失。请注意,每种损失都适用于分类或回归之一,但不能同时适用于两者。
使用技巧
通过讨论各种参数,我们包括一些使用GBT的准则。由于决策树指南中介绍了一些决策树参数,因此我们将其省略。
- 损失:有关损失及其对任务的适用性(分类与回归)的信息,请参见上文。根据数据集的不同,不同的损失可能会产生明显不同的结果
- numIterations:设置集合中树木的数量。每次迭代都会生成一棵树。增加此数字可使模型更具表现力,从而提高训练数据的准确性。但是,如果测试时间精度太大,则可能会降低测试时间精度。
- learningRate: 此参数不需要调整。如果算法行为似乎不稳定,则减小该值可以提高稳定性。
- algo: 使用树[Strategy]参数设置算法或任务(分类与回归)。
在训练时验证
当训练更多树木时,梯度提升可能会过拟合。为了防止过度拟合,在训练时进行验证很有用。提供了方法runWithValidation来使用此选项。它以一对RDD作为参数,第一个是训练数据集,第二个是验证数据集。
当验证错误的改善不超过某个公差(由BoostingStrategy中的validationTol参数提供)时,训练将停止。在实践中,验证误差最初会减小,随后会增大。在某些情况下,验证错误不会单调变化,建议用户设置足够大的负公差,并使用valuateEachIteration(给出每次迭代的错误或损失)来检查验证曲线,以调整迭代次数。
示例代码
分类
import org.apache.spark.mllib.tree.GradientBoostedTrees
import org.apache.spark.mllib.tree.configuration.BoostingStrategy
import org.apache.spark.mllib.tree.model.GradientBoostedTreesModel
import org.apache.spark.mllib.util.MLUtils
// Load and parse the data file.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split the data into training and test sets (30% held out for testing)
val splits = data.randomSplit(Array(0.7, 0.3))
val (trainingData, testData) = (splits(0), splits(1))
// Train a GradientBoostedTrees model.
// The defaultParams for Classification use LogLoss by default.
val boostingStrategy = BoostingStrategy.defaultParams("Classification")
boostingStrategy.numIterations = 3 // Note: Use more iterations in practice.
boostingStrategy.treeStrategy.numClasses = 2
boostingStrategy.treeStrategy.maxDepth = 5
// Empty categoricalFeaturesInfo indicates all features are continuous.
boostingStrategy.treeStrategy.categoricalFeaturesInfo = Map[Int, Int]()
val model = GradientBoostedTrees.train(trainingData, boostingStrategy)
// Evaluate model on test instances and compute test error
val labelAndPreds = testData.map { point =>
val prediction = model.predict(point.features)
(point.label, prediction)
}
val testErr = labelAndPreds.filter(r => r._1 != r._2).count.toDouble / testData.count()
println(s"Test Error = $testErr")
println(s"Learned classification GBT model:\n ${model.toDebugString}")
// Save and load model
model.save(sc, "target/tmp/myGradientBoostingClassificationModel")
val sameModel = GradientBoostedTreesModel.load(sc,
"target/tmp/myGradientBoostingClassificationModel")
回归
import org.apache.spark.mllib.tree.GradientBoostedTrees
import org.apache.spark.mllib.tree.configuration.BoostingStrategy
import org.apache.spark.mllib.tree.model.GradientBoostedTreesModel
import org.apache.spark.mllib.util.MLUtils
// Load and parse the data file.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split the data into training and test sets (30% held out for testing)
val splits = data.randomSplit(Array(0.7, 0.3))
val (trainingData, testData) = (splits(0), splits(1))
// Train a GradientBoostedTrees model.
// The defaultParams for Regression use SquaredError by default.
val boostingStrategy = BoostingStrategy.defaultParams("Regression")
boostingStrategy.numIterations = 3 // Note: Use more iterations in practice.
boostingStrategy.treeStrategy.maxDepth = 5
// Empty categoricalFeaturesInfo indicates all features are continuous.
boostingStrategy.treeStrategy.categoricalFeaturesInfo = Map[Int, Int]()
val model = GradientBoostedTrees.train(trainingData, boostingStrategy)
// Evaluate model on test instances and compute test error
val labelsAndPredictions = testData.map { point =>
val prediction = model.predict(point.features)
(point.label, prediction)
}
val testMSE = labelsAndPredictions.map{ case(v, p) => math.pow((v - p), 2)}.mean()
println(s"Test Mean Squared Error = $testMSE")
println(s"Learned regression GBT model:\n ${model.toDebugString}")
// Save and load model
model.save(sc, "target/tmp/myGradientBoostingRegressionModel")
val sameModel = GradientBoostedTreesModel.load(sc,
"target/tmp/myGradientBoostingRegressionModel")
isotonic regression
等渗回归属于回归算法家族。形式等渗回归是一个问题,其中给定有限的实数集Y = y1,y2,…,yn表示观察到的响应,而X = x1,x2,…,xn未知的响应值要拟合以找到一个函数最小化
spark.mllib支持池相邻违规者算法,该算法使用一种使等渗回归并行化的方法。训练输入是三个双精度值的元组的RDD,三个值依次表示标签,特征和权重。此外,IsotonicRegression算法具有一个称为isotonic的可选参数,默认为true。该参数指定等渗回归是等渗(单调增加)还是反渗(单调减少)。
训练返回一个IsotonicRegressionModel,该模型可用于预测已知和未知特征的标签。等渗回归的结果被视为分段线性函数。因此,预测规则为:
- 如果预测输入与训练功能完全匹配,则返回关联的预测。如果有多个具有相同特征的预测,则返回其中之一。哪一个未定义(与java.util.Arrays.binarySearch相同)。
- 如果预测输入低于或高于所有训练特征,则分别返回具有最低或最高特征的预测。如果存在多个具有相同特征的预测,则分别返回最低或最高。
- 如果预测输入介于两个训练特征之间,则将预测视为分段线性函数,并根据两个最接近特征的预测来计算内插值。如果有多个具有相同特征的值,则使用与上一点相同的规则。
示例代码
import org.apache.spark.mllib.regression.{IsotonicRegression, IsotonicRegressionModel}
import org.apache.spark.mllib.util.MLUtils
val data = MLUtils.loadLibSVMFile(sc,
"data/mllib/sample_isotonic_regression_libsvm_data.txt").cache()
// Create label, feature, weight tuples from input data with weight set to default value 1.0.
val parsedData = data.map { labeledPoint =>
(labeledPoint.label, labeledPoint.features(0), 1.0)
}
// Split data into training (60%) and test (40%) sets.
val splits = parsedData.randomSplit(Array(0.6, 0.4), seed = 11L)
val training = splits(0)
val test = splits(1)
// Create isotonic regression model from training data.
// Isotonic parameter defaults to true so it is only shown for demonstration
val model = new IsotonicRegression().setIsotonic(true).run(training)
// Create tuples of predicted and real labels.
val predictionAndLabel = test.map { point =>
val predictedLabel = model.predict(point._2)
(predictedLabel, point._1)
}
// Calculate mean squared error between predicted and real labels.
val meanSquaredError = predictionAndLabel.map { case (p, l) => math.pow((p - l), 2) }.mean()
println(s"Mean Squared Error = $meanSquaredError")
// Save and load model
model.save(sc, "target/tmp/myIsotonicRegressionModel")
val sameModel = IsotonicRegressionModel.load(sc, "target/tmp/myIsotonicRegressionModel")
来源:CSDN
作者:TIAN_R
链接:https://blog.csdn.net/pt798633929/article/details/103850202