我正在Apache Spark上工作,使用MLib提供的LogisticRegressionWithLBFGS()类构建LRM。一旦建立了模型,我们就可以使用所提供的预测函数,它只给出二进制标签作为输出。我还想计算相同的概率。
有一个与
相同的实现https://github.com/apache/spark/blob/master/mllib/src/main/scala/org/apache/spark/mllib/classification/LogisticRegression.scalaoverride protected def predictPoint(
dataMatrix: Vector,
weightMatrix: Vector,
intercept: Double) = {
require(dataMatrix.size == numFeatures)
// If dataMatrix and weightMatrix have the same dimension, it's binary logistic regression.
if (numClasses == 2) {
val margin = dot(weightMatrix, dataMatrix) + intercept
val score = 1.0 / (1.0 + math.exp(-margin))
threshold match {
case Some(t) => if (score > t) 1.0 else 0.0
case None => score
}
}
此方法未公开,概率也不可用。我能知道如何用这个函数得到概率吗。在上述函数中使用的dot方法也没有公开,它存在于BLAS包中,但不是公共的。
调用myModel.clearThreshold
来获取原始预测而不是0/1标签。
注意,这只适用于二进制逻辑回归(numClasses == 2)。
我在试图获得一个倍数问题的原始预测时遇到了类似的问题。对我来说,最好的解决方案是通过借用和定制Spark MLlib Logistic Regression src来创建一个方法。你可以创建一个类似的:
object ClassificationUtility {
def predictPoint(dataMatrix: Vector, model: LogisticRegressionModel):
(Double, Array[Double]) = {
require(dataMatrix.size == model.numFeatures)
val dataWithBiasSize: Int = model.weights.size / (model.numClasses - 1)
val weightsArray: Array[Double] = model.weights match {
case dv: DenseVector => dv.values
case _ =>
throw new IllegalArgumentException(
s"weights only supports dense vector but got type ${model.weights.getClass}.")
}
var bestClass = 0
var maxMargin = 0.0
val withBias = dataMatrix.size + 1 == dataWithBiasSize
val classProbabilities: Array[Double] = new Array[Double](model.numClasses)
(0 until model.numClasses - 1).foreach { i =>
var margin = 0.0
dataMatrix.foreachActive { (index, value) =>
if (value != 0.0) margin += value * weightsArray((i * dataWithBiasSize) + index)
}
// Intercept is required to be added into margin.
if (withBias) {
margin += weightsArray((i * dataWithBiasSize) + dataMatrix.size)
}
if (margin > maxMargin) {
maxMargin = margin
bestClass = i + 1
}
classProbabilities(i+1) = 1.0 / (1.0 + Math.exp(-(margin - maxMargin)))
}
return (bestClass.toDouble, classProbabilities)
}
}
注意,它与原始方法只有轻微的不同,它只是将逻辑计算为输入特征的函数。它还定义了一些val和vars,它们最初是私有的,包含在该方法之外。最后,它将分数索引到一个数组中,并将其与最佳答案一起返回。我像这样调用我的方法:
// Compute raw scores on the test set.
val predictionAndLabelsAndProbabilities = test
.map { case LabeledPoint(label, features) =>
val (prediction, probabilities) = ClassificationUtility
.predictPoint(features, model)
(prediction, label, probabilities)}
然而:
似乎Spark贡献者不鼓励使用MLlib
而支持ML
。ML逻辑回归API目前不支持多重分类。我现在使用的是OneVsRest,它作为一个对所有分类的包装。我正在进行类似的定制以获得原始分数。
我相信呼叫是myModel.clearThreshold()
;即没有括号的myModel.clearThreshold
失败。请参阅此处的线性SVM示例。