Spark 高级分析:第四章第7节
问题导读1.AUC指标是什么?
2.决策树现实时数据集要分成几份?
3.DecisionTree有哪几个方法?
http://www.aboutyun.com/static/image/hrline/4.gif
上一篇:Spark 高级分析:第四章第5,6节
http://www.aboutyun.com/forum.php?mod=viewthread&tid=23789&extra=
首先,数据将按原样使用。决策树的实现,如Spark MLlib中的几个,需要以LabeledPoint对象的形式输入:
import org.apache.spark.mllib.linalg._
import org.apache.spark.mllib.regression._
val rawData = sc.textFile("hdfs:///user/ds/covtype.data")
val data = rawData.map { line =>
val values = line.split(',').map(_.toDouble)
val featureVector = Vectors.dense(values.init)
val label = values.last - 1
LabeledPoint(label, featureVector)
}
在前一章中,我们对所有可用的数据建立了一个推荐模型。这就产生了一个可以被任何有音乐知识的人检查的推荐:看一个用户的倾听习惯和建议,我们可以感觉到它正在产生好的结果。但在这里,这是不可能的。我们不知道如何对科罗拉多州的一块新土地进行新的54个特征描述,也不知道从这样一个小块土地有什么样的森林覆盖。
相反,为了评估结果模型,有必要直接跳过一些数据。在此之前,AUC指标用于评估从推荐中得到的倾听数据和预测之间的一致性。这里的原理是一样的,尽管评价指标是不同的:精度。这次,数据将被分成三个子集:训练,交叉验证(CV)和测试。80%的数据用于训练,10%用于交叉验证和测试。
val Array(trainData, cvData, testData) =
data.randomSplit(Array(0.8, 0.1, 0.1))
trainData.cache()
cvData.cache()
testData.cache()
与ALS的实现一样,决策树的实现有几个超参数可供选择。因此,就像以前一样,训练集和CV集用于为这个数据集选择一个良好的超参数设置。这里,第三个集合,测试集,然后被用来对一个用这些超参数构建的模型的预期精度进行无偏评估。仅在交叉验证集上的模型的准确性倾向于偏倚和稍微过于乐观。本章将对测试集的最终模型进行额外的评估。
但首先,尝试在训练集上构建一个决策树,并使用一些默认参数,并使用CV集计算结果模型的一些指标:
import org.apache.spark.mllib.evaluation._
import org.apache.spark.mllib.tree._
import org.apache.spark.mllib.tree.model._
def getMetrics(model: DecisionTreeModel, data: RDD):
MulticlassMetrics = {
val predictionsAndLabels = data.map(example =>
(model.predict(example.features), example.label)
)
new MulticlassMetrics(predictionsAndLabels)
}
val model = DecisionTree.trainClassifier(
trainData, 7, Map(), "gini", 4, 100)
val metrics = getMetrics(model, cvData)
在这里,使用trainClassifier代替trainRegressor表明每个LabeledPoint中的目标值应该被视为一个不同的类别号,而不是一个数字特征值。(对回归问题类似的trainRegressor也不会在本章中单独讨论。)
此时,我们必须指定它将遇到的目标值的数量:7。该Map包含了关于分类特征的信息;稍后将讨论“gini”的含义,最大深度为4,最大bin count为100。
MulticlassMetrics计算标准指标,以不同的方式衡量分类器的预测的质量,这里已经在CV集上运行。理想情况下,分类器应该可以预测CV集中每个示例的正确目标类别。这里的指标可以以不同的方式衡量这种正确性。
其伴生类BinaryClassificationMetrics包含类似的评价指标的实现,常见的分类目标只有两个值。在这里不能直接使用它,因为目标具有许多值。
先快速浏览一下混乱矩阵可能会有帮助:
metrics.confusionMatrix
...
14019.0 6630.0 15.0 0.0 0.0 1.0 391.0
5413.0 22399.0 438.0 16.0 0.0 3.0 50.0
0.0 457.0 2999.0 73.0 0.0 12.0 0.0
0.0 1.0 163.0 117.0 0.0 0.0 0.0
0.0 872.0 40.0 0.0 0.0 0.0 0.0
0.0 500.0 1138.0 36.0 0.0 48.0 0.0
1091.0 41.0 0.0 0.0 0.0 0.0 891.0
因为有7个目标分类值,这是一个7×7矩阵,每一行对应一个实际的正确值,每列都对应一个预测值。第i行和第j列的条目数的次数是i被预测为第j类的一个例子,所以正确的预测是沿着对角线计算的,而不正确的预测则是其他的。在这里,似乎是沿着对角线的高度,这是一个好迹象。然而,肯定有一些错误分类,例如,第5类根本就没有被预测到。
用一个数字来总结准确性是很有帮助的。一个明显的起点是计算所有正确预测的例子的分数:
metrics.precision
...
0.7030630195577938
大约70%的例子被正确分类,这听起来是个不错的开始。这通常被称为准确性,但是在Spark的多类度量中被称为精度。这是一个术语轻度超载。
精度实际上是二进制分类问题的常用度量,其中有两个类别值,而不是几个。在二进制分类问题中,有一些正负类,精度是分类器标记为正的例子的分数。它常常伴随着度量召回。这是为什么所有例子的分数都是正的,因为分类器是正的。
例如,假设在一个50个例子的数据集中有20个实例。分类器标记了20个正面中的10个,而这10个,4个实际上是正的(正确分类)。精度为4 /10 = 0.4,在这种情况下,召回率是4 /20 = 0.2。
这些概念可以应用到这个多类问题上,将每个类别独立地视为正类,而其他所有的都是否定的。例如,为了计算每个类别和其他类别的精度和撤销:
(0 until 7).map(
cat => (metrics.precision(cat), metrics.recall(cat))
).foreach(println)
...
(0.6805931840866961,0.6809492105763744)
(0.7297560975609756,0.7892237892589596)
(0.6376224968044312,0.8473952434881087)
(0.5384615384615384,0.3917910447761194)
(0.0,0.0)
(0.7083333333333334,0.0293778801843318)
(0.6956168831168831,0.42828585707146427)
这表明每个类的准确性各不相同。在这里,没有理由认为一个类别的准确性比另一个更重要,因此,例子将把整体的多类精度作为一个良好的,单一的测量精度的预测。
虽然70%的准确率听起来不错,但目前还不清楚它是否出色或准确性差。一个简单的方法如何才能建立一个基线?例如,就像一个坏了的钟一天两次正确,随机猜测每个例子的分类也会偶尔产生正确的答案。
这样的“分类器”可以根据其在训练集中的流行程度,随机选择一个类来构造。每一种分类都与CV集中的流行程度成正比。例如,一个占训练集20%的类和10%的CV集将贡献10%的20%,即2%,以达到整体的准确性。10%的会用来猜测正确地“分类”,20%的时间用来猜测。可以用这些概率乘积来求精度。
import org.apache.spark.rdd._
def classProbabilities(data: RDD): Array = {
val countsByCategory = data.map(_.label).countByValue()
val counts = countsByCategory.toArray.sortBy(_._1).map(_._2)
counts.map(_.toDouble / counts.sum)
}
val trainPriorProbabilities = classProbabilities(trainData)
val cvPriorProbabilities = classProbabilities(cvData)
trainPriorProbabilities.zip(cvPriorProbabilities).map {
case (trainProb, cvProb) => trainProb * cvProb
}.sum
...
0.37737764750734776
随机猜测达到了37%的准确率,这使得70%看起来是一个好的结果。但是这个结果是用DecisionTree.trainClassifier()默认参数来决定的。当然,通过探究这些参数——这些超参数——对树的构建过程的意义,我们可以做得更好。
页:
[1]