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

Dealing with unbalanced datasets in Spark MLlib

我正在处理具有高度不平衡数据集的特定二进制 class化问题,我想知道是否有人尝试实施特定技术来处理不平衡数据集(例如 SMOTE) class使用 Spark 的 MLlib 的化问题。

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

如有任何关于您在类似问题上的体验的反馈,我将不胜感激。

谢谢,

Class Spark ML 权重

截至目前,随机森林算法的class权重仍在开发中(参见here

但如果您愿意尝试其他 class 工具 - 此功能 has been already added to the Logistic Regression

考虑这样一种情况,我们在数据集中有 80% 的阳性(标签 == 1),因此理论上我们想要 "under-sample" 阳性 class。 logistic loss objective 函数应该以更高的权重处理负数 class(标签 == 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
  }

然后,我们创建一个class层如下:

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

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

- 预测能力

您应该检查的另一个问题 - 您的特征是否具有 "predictive power" 作为您尝试预测的标签。如果欠采样后你的精度仍然很低,这可能与你的数据集天生不平衡的事实无关。


我会进行 探索性数据分析 - 如果 classifier 的表现不如随机选择,则存在根本不存在的风险特征与 class 之间的联系。

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

过度拟合 - 训练集上的低错误率和测试集上的高错误率可能表明您使用过于灵活的特征集过度拟合。


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

  • 训练误差与验证误差 - 绘制验证误差和训练集误差,作为训练示例的函数(进行增量学习)
    • 如果这些线看起来收敛到相同的值并且在最后很接近,那么你的 classifier 有高偏差。在这种情况下,添加更多数据将无济于事。 更改 class 具有更高方差的 ifier,或者简单地降低当前的 regularization 参数。
    • 如果另一方面,这些线相距很远,并且您的训练集错误率较低但验证错误率很高,那么您的 classifier 方差过高。在这种情况下,获取更多数据很可能会有所帮助。如果获取更多数据后方差仍然过高,可以增加正则化参数。

我使用了@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 对数据集的大部分 class 进行子采样,那么,为了获得 Spark 逻辑回归的无基预测,您可以: - 使用 transform() 函数提供的 rawPrediction 并使用 log(r) 调整截距 - 或者您可以使用 .setWeightCol("classWeightCol") 使用权重训练回归(请参阅引用的文章 here 以计算出必须在权重中设置的值)。