【问题标题】:using confusion matrix as scoring metric in cross validation in scikit learn在 scikit learn 的交叉验证中使用混淆矩阵作为评分指标
【发布时间】:2017-02-24 16:55:58
【问题描述】:

我正在 scikit learn 中创建一个管道,

pipeline = Pipeline([
    ('bow', CountVectorizer()),  
    ('classifier', BernoulliNB()), 
])

并使用交叉验证计算准确性

scores = cross_val_score(pipeline,  # steps to convert raw messages      into models
                     train_set,  # training data
                     label_train,  # training labels
                     cv=5,  # split data randomly into 10 parts: 9 for training, 1 for scoring
                     scoring='accuracy',  # which scoring metric?
                     n_jobs=-1,  # -1 = use all cores = faster
                     )

如何报告混淆矩阵而不是“准确性”?

【问题讨论】:

    标签: python machine-learning scikit-learn


    【解决方案1】:

    您可以使用cross_val_predict(See the scikit-learn docs) 代替cross_val_score

    而不是这样做:

    from sklearn.model_selection import cross_val_score
    scores = cross_val_score(clf, x, y, cv=10)
    

    你可以这样做:

    from sklearn.model_selection import cross_val_predict
    from sklearn.metrics import confusion_matrix
    y_pred = cross_val_predict(clf, x, y, cv=10)
    conf_mat = confusion_matrix(y, y_pred)
    

    【讨论】:

    • 特别提到here“将这些预测传递到评估指标中是不合适的。使用cross_validate来衡量泛化误差。”
    • 请注意,我们在这里没有使用任何评估指标。如here 所述,“cross_val_predict”可用于“从不同模型获得的预测的可视化”,这就是我们正在做的事情。但是,它不能与“cross_val_score”结合使用,因为预测不一样。
    • confusion_matrix 是一个评估指标。
    • 我一头雾水,这个函数的输出是什么?是每次运行的总和?
    • 我认为(不过需要检查)每个样本只预测一次,因为它在测试集中只出现一次。
    【解决方案2】:

    简短的回答是“你不能”。

    您需要了解cross_val_score 与作为模型选择方法的交叉验证之间的区别。 cross_val_score 顾名思义,仅适用于scores。混淆矩阵不是分数,它是对评估过程中发生的事情的一种总结。一个主要区别是分数应该返回可排序对象,特别是在 scikit-learn 中 - float。因此,根据分数,您可以通过简单地比较 b 是否具有更大的分数来判断方法 b 是否比 a 更好。你不能用混淆矩阵来做到这一点,顾名思义,它是一个矩阵。

    如果您想获得多次评估运行的混淆矩阵(例如交叉验证),您必须手动执行此操作,这在 scikit-learn 中并没有那么糟糕 - 它实际上是几行代码。

    kf = cross_validation.KFold(len(y), n_folds=5)
    for train_index, test_index in kf:
    
       X_train, X_test = X[train_index], X[test_index]
       y_train, y_test = y[train_index], y[test_index]
    
       model.fit(X_train, y_train)
       print confusion_matrix(y_test, model.predict(X_test))
    

    【讨论】:

    • 如果我在 jupyter notebook 中使用它,这不会覆盖每个混淆矩阵吗?
    • @FlixRo 这不会在打印每个混淆矩阵时覆盖每个混淆矩阵。如果不使用打印,情况就是这样:只有最后一个矩阵会输出到笔记本中。
    • @CharlesG - 这确实会在每次运行中覆盖混淆矩阵。您可以将每个混淆矩阵存储在列表中,然后计算其平均值:stackoverflow.com/a/59316264/2119941
    【解决方案3】:

    我认为您真正想要的是从每次交叉验证运行中获得的混淆矩阵的平均值。 @lejlot 已经很好地解释了原因,我将通过计算混淆矩阵的平均值来升级他的答案:

    在每次交叉验证运行中计算混淆矩阵。你可以使用这样的东西:

    conf_matrix_list_of_arrays = []
    kf = cross_validation.KFold(len(y), n_folds=5)
    for train_index, test_index in kf:
    
       X_train, X_test = X[train_index], X[test_index]
       y_train, y_test = y[train_index], y[test_index]
    
       model.fit(X_train, y_train)
       conf_matrix = confusion_matrix(y_test, model.predict(X_test))
       conf_matrix_list_of_arrays .append(conf_matrix)
    

    最后,您可以使用以下方法计算 numpy 数组(混淆矩阵)列表的平均值:

    mean_of_conf_matrix_arrays = np.mean(conf_matrix_list_of_arrays, axis=0)
    

    【讨论】:

      【解决方案4】:

      你可以做的是定义一个使用某些值的记分器来自混淆矩阵。见here [link]。只是引用代码:

      def confusion_matrix_scorer(clf, X, y):
          y_pred = clf.predict(X)
          cm = confusion_matrix(y, y_pred)
          return {'tn': cm[0, 0], 'fp': cm[0, 1],
                  'fn': cm[1, 0], 'tp': cm[1, 1]}
      cv_results = cross_validate(svm, X, y, cv=5,
                                  scoring=confusion_matrix_scorer)
      

      这将为这四个评分器中的每一个执行交叉验证并返回评分字典cv_results,例如,键test_tptest_tn 等包含来自每个交叉验证拆分的混淆矩阵的值.

      据此,您可以重建平均混淆矩阵,但 Xemacross_val_predict 似乎更优雅。

      请注意,这实际上不适用于cross_val_score;你需要cross_validate(在 scikit-learn v0.19 中引入)。

      旁注:您可以使用这些记分器中的 一个(即矩阵的一个元素)通过网格搜索进行超参数优化。

      【讨论】:

      • 我认为基于 here 混淆矩阵中的示例返回的顺序与您在函数中使用的顺序不同:例如TN 位于 [0,0],TP 位于 [1,1]。
      • @Celdor 是正确的。请参阅 scikit-learn 文档:“在二进制分类中,真阴性的计数是 C[0,0],假阴性是 C[1,0],真阳性是 C[1,1],假阳性是 C[0 ,1]"。 source
      • 感谢@Celdor 和 fpersyn。我实际上已经更新了它,所以我无法发现这些不一致之处;我更新了代码 sn-p 以反映第一个链接之后文档中的最新状态。显然,自从我最初的复制粘贴以来,文档中的这一部分有两个变化:commit in 2018commit in 2020
      • 为什么.predict() 是在整个输入数据X 中完成的?不应该只在某些测试集x_test 中完成吗?而且由于在每一折中,从cross_validate 获得不同的测试集,我不明白我们如何将X 传递给confusion_matrix_scorer().predict()
      【解决方案5】:

      我是机器学习的新手。如果我理解正确的话,混淆矩阵可以从 4 个值中获得,它们是 TP、FN、FP 和 TN。这4个值不能直接从评分中获得,但它隐含在accuracy、precision和recall中。

      现在它有4个未知的TP、FN、FP和TN。

      方程1:tp/(tp+fp)=P

      方程2:tp/(tp+fn)=R

      方程3:(tp+tn)/(tp+fn+fp+tn)=A

      [1]: https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%7Btp%7D%7Btp%2Bfp%7D%3DP
      [2]: https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%7Btp%7D%7Btp%2Bfn%7D%3DR
      [3]: https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%7Btp%2Btn%7D%7Btp%2Bfn%2Bfp%2Btn%7D%3DA
      

      假设其中一个未知数是 1,那么它就变成了 3 个未知数和 3 个方程。相对值可以通过方程组求解。

      1. P R A 可以从评分中获得

      2. cross_validate 可以一次获取所有 3 个源

      def calculate_confusion_matrix_by_assume_tp_equal_to_1(r, p, a):
          # tp/(tp+fp)=P, tp/(tp+fn)=R, (tp+tn)/(tp+fn+fp+tn)=A
          fn = (1 / r) - 1
          fp = (1 / p) - 1
          tn = (1 - a - a * fn - a * fp) / (a - 1)
          return fn, fp, tn
      

      【讨论】:

      • 有人可以帮我张贴图片吗?由于信用,我无法这样做。
      猜你喜欢
      • 2017-03-03
      • 2016-05-12
      • 2014-04-19
      • 2014-07-30
      • 2018-05-22
      • 2012-04-01
      • 2017-06-23
      • 2020-05-30
      • 1970-01-01
      相关资源
      最近更新 更多