【问题标题】:PySpark: Get Threshold (cuttoff) values for each point in ROC curvePySpark:获取 ROC 曲线中每个点的阈值(截止)值
【发布时间】:2019-06-22 19:38:56
【问题描述】:

我从 PySpark 开始,构建二元分类模型(逻辑回归),我需要为我的模型找到最佳阈值(截止点)。

我想用ROC曲线找到这个点,但是不知道如何提取这个曲线中每个点的阈值。有没有办法找到这个值?

我发现的东西:

  • This post 展示了如何提取 ROC 曲线,但只提取 TPR 和 FPR 的值。它对于绘图和选择最佳点很有用,但我找不到阈值。
  • 我知道我可以使用 H2O 找到 ROC 曲线中每个点的阈值(我以前做过),但我正在研究 Pyspark。
  • Here 是一篇描述如何使用 R 的帖子...但是,我需要再次使用 Pyspark 进行此操作

其他事实

  • 我使用的是 Apache Spark 2.4.0。
  • 我正在使用数据框(我真的不知道如何使用 RDD,但我并不害怕学习 ;))

【问题讨论】:

    标签: apache-spark pyspark roc


    【解决方案1】:

    如果您特别需要为不同的阈值生成 ROC 曲线,一种方法可能是生成您感兴趣的阈值列表,并针对每个阈值在您的数据集上进行拟合/转换。或者您可以使用来自model.transform(test) 的响应中的probability 字段手动计算每个阈值点的ROC 曲线。

    或者,您可以使用BinaryClassificationMetrics 提取曲线,按阈值绘制各种指标(F1 分数、精度、召回率)。

    不幸的是,PySpark 版本似乎没有实现 Scala 版本的大部分方法,因此您需要将类包装在 Python 中。

    例如:

    from pyspark.mllib.evaluation import BinaryClassificationMetrics
    
    # Scala version implements .roc() and .pr()
    # Python: https://spark.apache.org/docs/latest/api/python/_modules/pyspark/mllib/common.html
    # Scala: https://spark.apache.org/docs/latest/api/java/org/apache/spark/mllib/evaluation/BinaryClassificationMetrics.html
    class CurveMetrics(BinaryClassificationMetrics):
        def __init__(self, *args):
            super(CurveMetrics, self).__init__(*args)
    
        def _to_list(self, rdd):
            points = []
            # Note this collect could be inefficient for large datasets 
            # considering there may be one probability per datapoint (at most)
            # The Scala version takes a numBins parameter, 
            # but it doesn't seem possible to pass this from Python to Java
            for row in rdd.collect():
                # Results are returned as type scala.Tuple2, 
                # which doesn't appear to have a py4j mapping
                points += [(float(row._1()), float(row._2()))]
            return points
    
        def get_curve(self, method):
            rdd = getattr(self._java_model, method)().toJavaRDD()
            return self._to_list(rdd)
    

    用法:

    import matplotlib.pyplot as plt
    
    preds = predictions.select('label','probability').rdd.map(lambda row: (float(row['probability'][1]), float(row['label'])))
    
    # Returns as a list (false positive rate, true positive rate)
    points = CurveMetrics(preds).get_curve('roc')
    
    plt.figure()
    x_val = [x[0] for x in points]
    y_val = [x[1] for x in points]
    plt.title(title)
    plt.xlabel(xlabel)
    plt.ylabel(ylabel)
    plt.plot(x_val, y_val)
    

    结果:

    如果您未与 ROC 结婚,以下是按阈值划分的 F1 分数曲线示例:

    【讨论】:

    • 对不起,'for' 上的'points' 是从哪里来的? x_val = [x[0] for x in points]
    • @AndreCarneiro 有一个错误命名的变量,我更新了答案。
    • 好的,亚历克斯!谢谢!
    【解决方案2】:

    一种方法是使用sklearn.metrics.roc_curve

    首先使用您的拟合模型进行预测:

    from pyspark.ml.classification import LogisticRegression
    
    lr = LogisticRegression(labelCol="label", featuresCol="features")
    model = lr.fit(trainingData)
    predictions = model.transform(testData)
    

    然后收集你的分数和标签1

    preds = predictions.select('label','probability')\
        .rdd.map(lambda row: (float(row['probability'][1]), float(row['label'])))\
        .collect()
    

    现在将preds 转换为与roc_curve 一起使用

    from sklearn.metrics import roc_curve
    
    y_score, y_true = zip(*preds)
    fpr, tpr, thresholds = roc_curve(y_true, y_score, pos_label = 1)
    

    注意事项

    1. 我不能 100% 确定概率向量将始终被排序,这样正标签将位于索引 1。但是,在二元分类问题中,您会立即知道您的 AUC 是否小于 0.5。在这种情况下,只需将1-p 用作概率(因为类概率总和为 1)。

    【讨论】:

      猜你喜欢
      • 2013-04-27
      • 2015-04-27
      • 2019-03-21
      • 2016-09-29
      • 2019-04-19
      • 2022-06-15
      • 2018-07-17
      • 2019-03-10
      • 1970-01-01
      相关资源
      最近更新 更多