在Spark MLlib中处理不平衡数据集



我正在研究一个具有高度不平衡数据集的特定二进制分类问题,我想知道是否有人尝试使用Spark的MLlib在分类问题中实现处理不平衡数据集(如SMOTE)的特定技术。

我正在使用MLLib的随机森林实现,并且已经尝试了最简单的随机欠采样较大类的方法,但它没有像我预期的那样工作。

我将非常感谢您对类似问题的任何反馈。

谢谢,

class weight with Spark ML

到目前为止,随机森林算法的类权重仍在开发中(见这里)

但是如果你愿意尝试其他分类器-这个功能已经添加到逻辑回归

考虑一个我们在数据集中有80%阳性(label == 1)的情况,所以理论上我们想要"欠采样"阳性类。逻辑损失目标函数应以较高的权重处理负类(label == 0)。

下面是在Scala中生成此权重的示例,我们为数据集中的每条记录添加一个新列到数据框:

def balanceDataset(dataset: DataFrame): DataFrame = {
    // Re-balancing (weighting) of records to be used in the logistic loss objective function
    val numNegatives = dataset.filter(dataset("label") === 0).count
    val datasetSize = dataset.count
    val balancingRatio = (datasetSize - numNegatives).toDouble / datasetSize
    val calculateWeights = udf { d: Double =>
      if (d == 0.0) {
        1 * balancingRatio
      }
      else {
        (1 * (1.0 - balancingRatio))
      }
    }
    val weightedDataset = dataset.withColumn("classWeightCol", calculateWeights(dataset("label")))
    weightedDataset
  }

然后,我们创建一个分类器,如下所示:

new LogisticRegression().setWeightCol("classWeightCol").setLabelCol("label").setFeaturesCol("features")

更多详细信息,请看这里:https://issues.apache.org/jira/browse/SPARK-9610

- Predictive Power

你应该检查的另一个问题-你的特征是否有"预测能力"对于你试图预测的标签。在欠采样之后,你仍然有低精度的情况下,也许这与你的数据集本质上是不平衡的事实无关。


我会做一个探索性数据分析——如果分类器没有比随机选择做得更好,那么就有可能在特征和类之间没有联系。

  • 对标签中的每个特性执行相关性分析
  • 为特征生成特定于类的直方图(即绘制给定的每个类的数据直方图)特征在同一轴上)也可以是一个很好的方式来显示如果特征可以很好地区分这两类。

过拟合——训练集的低误差和测试集的高误差可能表明你使用了过于灵活的特征集过拟合。


偏差方差-检查您的分类器是否存在高偏差或高方差问题。

  • 训练误差与验证误差-将验证误差和训练集误差绘制为训练样例的函数(进行增量学习)
    • 如果这些线似乎收敛到相同的值并且在最后很接近,那么你的分类器有高偏差。在这种情况下,增加更多的数据是没有用的。将分类器更改为具有更高方差的分类器,或者简单地降低当前分类器的正则化参数。
    • 另一方面,如果这些线相距很远,并且你的训练集误差低但验证误差高,那么你的分类器有太高的方差。在这种情况下,获得更多的数据很可能会有所帮助。如果在获得更多数据后方差仍然太高,可以增加正则化参数。

我使用了@Serendipity的解决方案,但是我们可以优化balanceDataset函数以避免使用udf。我还添加了更改所使用的标签列的功能。这是我最终得到的函数版本:

def balanceDataset(dataset: DataFrame, label: String = "label"): DataFrame = {
  // Re-balancing (weighting) of records to be used in the logistic loss objective function
  val (datasetSize, positives) = dataset.select(count("*"), sum(dataset(label))).as[(Long, Double)].collect.head
  val balancingRatio = positives / datasetSize
  val weightedDataset = {
    dataset.withColumn("classWeightCol", when(dataset(label) === 0.0, balancingRatio).otherwise(1.0 - balancingRatio))
  }
  weightedDataset
}

我们用如下语句创建分类器:

new LogisticRegression().setWeightCol("classWeightCol").setLabelCol("label").setFeaturesCol("features")

@dbakr你在不平衡数据集上的偏差预测得到答案了吗?

虽然我不确定这是你最初的计划,请注意,如果你首先通过比率r对数据集的大多数类进行子采样,那么,为了获得Spark逻辑回归的无基础预测,你可以:—使用transform()函数提供的rawPrediction,用log(r)调整截距-或者你可以使用.setWeightCol("classWeightCol")来训练你的权重回归(参见这里引用的文章,以找出必须在权重中设置的值)。

最新更新