cochrans_q: Cochran的Q检验用于比较多个分类器

Cochran的Q检验用于比较多个分类器的性能。

> `from mlxtend.evaluate import cochrans_q`

概述

Cochran的Q检验可以被视为McNemar检验的推广版本,适用于评估多个分类器。从某种意义上说,Cochran的Q检验类似于针对二元结果的方差分析(ANOVA)。

为了比较两个以上的分类器,我们可以使用Cochran的Q检验,其检验统计量$Q$近似于(类似于McNemar检验)服从自由度为$L-1$的卡方分布,其中L是我们评估的模型数量(由于McNemar检验中$L=2$,McNemar的检验统计量近似于自由度为1的卡方分布)。

更正式地,Cochran的Q检验检验的假设是分类准确性之间没有差异 [1]:

$$p_i: H_0 = p_1 = p_2 = \cdots = p_L.$$

设${D_1, \dots , D_L}$是一组在同一数据集上进行测试的分类器。如果这L个分类器的表现没有差异,那么如下的Q统计量近似服从自由度为$L-1$的“卡方”分布:

$$Q_C = (L-1) \frac{L \sum^{L}{i=1}G{i}^{2} - T^2}{LT - \sum^{N_{ts}}_{j=1} (L_j)^2}.$$

这里,$G_i$是$D_i= 1, \dots L$中正确分类的对象数量;$L_j$是来自$L$的分类器中正确分类对象$\mathbf{z}j \in \mathbf{Z}{ts}$的数量,其中$\mathbf{Z}{ts} = {\mathbf{z}_1, ... \mathbf{z}{N_{ts}}}$是分类器测试的测试数据集;$T$是在$L$个分类器中正确投票的总数 [2]:

$$ T = \sum_{i=1}^{L} G_i = \sum^{N_{ts}}_{j=1} L_j.$$

为了执行Cochran的Q检验,我们通常将分类器预测组织成一个二元的$N_{ts} \times L$矩阵。该矩阵的$ij$项为0,如果分类器$D_j$错误分类了一个数据示例(向量)$\mathbf{z}_i$,否则为1(如果分类器正确预测了类别标签$l(\mathbf{z}_i)$)[2]。

以下来自[2]的示例说明了如何组织分类结果。例如,假设我们拥有测试数据集的真实标签y_true和3个分类器的以下预测(y_model_1y_model_2y_model_3):

y_true = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0])

y_model_1 = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0])

y_model_2 = np.array([1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0])

y_model_3 = np.array([1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      1, 1])

正确(1)和错误(0)分类的表格可能如下所示:

$D_1$ (模型 1) $D_2$ (模型 2) $D_3$ (模型 3) 出现次数
1 1 1 80
1 1 0 2
1 0 1 0
1 0 0 2
0 1 1 9
0 1 0 1
0 0 1 3
0 0 0 3
准确率 84/100*100% = 84% 92/100*100% = 92% 92/100*100% = 92%

通过将各自的值代入到前面的公式中,我们得到如下$Q$值 [2]:

$$Q_c = 2 \times \frac{3 \times (84^2 + 92^2 + 92^2) - 268^2}{3\times 268-(80 \times 9 + 11 \times 4 + 6 \times 1)} \approx 7.5294.$$

(请注意,[2]中的$Q$值列出为3.7647,这是由于与作者讨论后的笔误,7.5294是正确的值。)

现在,$Q$值(近似于$\chi^2$)对应于自由度为$L-1 = 2$的卡方分布的p值大约为0.023。假设我们选择了显著性水平$\alpha=0.05$,我们将拒绝零假设,即所有分类器表现相同,因为$0.023 < \alpha$。

在实践中,如果我们成功拒绝了零假设,我们可以进行多个事后成对检验——例如,对不同人口比例的McNemar检验,并进行Bonferroni校正——以确定哪些对之间存在差异。

参考文献

示例 1 - Cochran的Q检验

import numpy as np
from mlxtend.evaluate import cochrans_q
from mlxtend.evaluate import mcnemar_table
from mlxtend.evaluate import mcnemar

# #数据集:

# 测试数据集的地面实况标签:

y_true = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0])


# 由3个分类器(`y_model_1`、`y_model_2` 和 `y_model_3`)做出的预测:

y_model_1 = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0])

y_model_2 = np.array([1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0])

y_model_3 = np.array([1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                      1, 1])

假设显著性水平为 $\alpha=0.05$,我们可以进行 Cochran 的 Q 检验,如下所示,以检验原假设:分类准确率没有差异,即 $p_i: H_0 = p_1 = p_2 = \cdots = p_L$:

q, p_value = cochrans_q(y_true, 
                        y_model_1, 
                        y_model_2, 
                        y_model_3)

print('Q: %.3f' % q)
print('p-value: %.3f' % p_value)

Q: 7.529
p-value: 0.023

由于p值小于$\alpha$,我们可以拒绝零假设,并得出分类准确率之间存在差异的结论。如前面介绍中提到的,我们现在可以进行多次事后成对检验——例如,使用Bonferroni校正的McNemar检验——以确定哪些对具有不同的人口比例。

最后, 让我们说明Cochran的Q检验确实只是McNemar检验的一个推广版本:

chi2, p_value = cochrans_q(y_true, 
                           y_model_1, 
                           y_model_2)

print('Cochran\'s Q Chi^2: %.3f' % chi2)
print('Cochran\'s Q p-value: %.3f' % p_value)

Cochran's Q Chi^2: 5.333
Cochran's Q p-value: 0.021
chi2, p_value = mcnemar(mcnemar_table(y_true, 
                                      y_model_1, 
                                      y_model_2),
                        corrected=False)

print('McNemar\'s Chi^2: %.3f' % chi2)
print('McNemar\'s p-value: %.3f' % p_value)

McNemar's Chi^2: 5.333
McNemar's p-value: 0.021

API

cochrans_q(y_target, y_model_predictions)*

Cochran's Q test to compare 2 or more models.

Parameters

Returns

Examples

For usage examples, please see https://rasbt.github.io/mlxtend/user_guide/evaluate/cochrans_q/