confusion_matrix()
我使用scikit-learn为我的分类器计算了一个混淆矩阵。混淆矩阵的对角线元素表示预测标签等于真实标签的点的数量,而非对角线元素是那些被分类器错误标记的点。
我想标准化我的混淆矩阵,使其仅包含 0 到 1 之间的数字。我想从矩阵中读取正确分类样本的百分比。
我找到了几种方法来规范化矩阵(行和列规范化),但我对数学知之甚少,不确定这是否是正确的方法。
confusion_matrix()
我使用scikit-learn为我的分类器计算了一个混淆矩阵。混淆矩阵的对角线元素表示预测标签等于真实标签的点的数量,而非对角线元素是那些被分类器错误标记的点。
我想标准化我的混淆矩阵,使其仅包含 0 到 1 之间的数字。我想从矩阵中读取正确分类样本的百分比。
我找到了几种方法来规范化矩阵(行和列规范化),但我对数学知之甚少,不确定这是否是正确的方法。
假设
>>> y_true = [0, 0, 1, 1, 2, 0, 1]
>>> y_pred = [0, 1, 0, 1, 2, 2, 1]
>>> C = confusion_matrix(y_true, y_pred)
>>> C
array([[1, 1, 1],
[1, 2, 0],
[0, 0, 1]])
然后,要找出每个类有多少样本收到了正确的标签,您需要
>>> C / C.astype(np.float).sum(axis=1)
array([[ 0.33333333, 0.33333333, 1. ],
[ 0.33333333, 0.66666667, 0. ],
[ 0. , 0. , 1. ]])
对角线包含所需的值。计算这些的另一种方法是意识到你正在计算的是每类的召回:
>>> from sklearn.metrics import precision_recall_fscore_support
>>> _, recall, _, _ = precision_recall_fscore_support(y_true, y_pred)
>>> recall
array([ 0.33333333, 0.66666667, 1. ])
类似地,如果你除以总和axis=0
,你会得到精度(具有真实标签的类k
预测的分数k
):
>>> C / C.astype(np.float).sum(axis=0)
array([[ 0.5 , 0.33333333, 0.5 ],
[ 0.5 , 0.66666667, 0. ],
[ 0. , 0. , 0.5 ]])
>>> prec, _, _, _ = precision_recall_fscore_support(y_true, y_pred)
>>> prec
array([ 0.5 , 0.66666667, 0.5 ])
来自 sklearn 文档(绘图示例)
cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
其中 cm 是 sklearn 提供的混淆矩阵。
使用 Seaborn,您可以使用健康图轻松打印标准化且漂亮的混淆矩阵:
from sklearn.metrics import confusion_matrix
import seaborn as sns
cm = confusion_matrix(y_test, y_pred)
# Normalise
cmn = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
fig, ax = plt.subplots(figsize=(10,10))
sns.heatmap(cmn, annot=True, fmt='.2f', xticklabels=target_names, yticklabels=target_names)
plt.ylabel('Actual')
plt.xlabel('Predicted')
plt.show(block=False)
我假设那M[i,j]
代表Element of real class i was classified as j
. 如果反过来,您将需要转置我所说的所有内容。我还将使用以下矩阵作为具体示例:
1 2 3
4 5 6
7 8 9
您基本上可以做两件事:
你可以问的第一件事是i
这里分类为每个类的真实类元素的百分比。为此,我们采用固定的行i
并将每个元素除以行中元素的总和。在我们的示例中,来自第 2 类的对象被分类为第 1 类 4 次,被正确分类为第 2 类 5 次,被分类为第 3 类 6 次。要找到百分比,我们只需将所有内容除以总和 4 + 5 + 6 = 15
4/15 of the class 2 objects are classified as class 1
5/15 of the class 2 objects are classified as class 2
6/15 of the class 2 objects are classified as class 3
您可以做的第二件事是查看分类器的每个结果,并询问这些结果中有多少来自每个真实类。它与另一种情况类似,但使用列而不是行。在我们的示例中,当原始类为 1 时,我们的分类器返回“1”1 次,当原始类为 2 时返回 4 次,当原始类为 3 时返回 7 次。要找到百分比,我们除以总和 1 + 4 + 7 = 12
1/12 of the objects classified as class 1 were from class 1
4/12 of the objects classified as class 1 were from class 2
7/12 of the objects classified as class 1 were from class 3
--
当然,我给出的两种方法一次只适用于单行列,我不确定以这种形式实际修改混淆矩阵是否是个好主意。但是,这应该给出您正在寻找的百分比。
如今,scikit-learn 的混淆矩阵自带了一个normalize
参数;来自文档:
规范化:{'true','pred','all'},默认=无
在真实(行)、预测(列)条件或所有总体上标准化混淆矩阵。如果没有,混淆矩阵将不会被归一化。
因此,如果您希望对所有样本的值进行归一化,您应该使用
confusion_matrix(y_true, y_pred, normalize='all')
sklearn 输出的矩阵confusion_matrix()
是这样的
C_{i, j} 等于已知在组 i 中但预测在组 j 中的观察数
因此,要获得每个类的百分比(通常称为二进制分类中的特异性和敏感性),您需要按行进行归一化:将一行中的每个元素替换为自身除以该行元素的总和。
请注意,sklearn 有一个可用的汇总函数,可以从混淆矩阵中计算指标:classification_report。它输出精度和召回率,而不是特异性和灵敏度,但通常这些信息通常被认为更具信息性(尤其是对于不平衡的多类分类。)
我认为最简单的方法是这样做:
c = sklearn.metrics.confusion_matrix(y, y_pred)
normed_c = (c.T / c.astype(np.float).sum(axis=1)).T
scikit-learn 本身提供了一个用于绘制图形的库。它基于 matplotlib 并且应该已经安装以继续进行。
pip install scikit-plot
现在,只需将normalize参数设置为true:
import scikitplot as skplt
skplt.metrics.plot_confusion_matrix(Y_TRUE, Y_PRED, normalize=True)
对于您拥有TOTALS的情况。像这样的东西:
0 1 2 Total
0 5434084 567 3460 5438111
1 458896 4717484 115297 5291677
2 189553 8305 13962602 14160460
Total 6082533 4726356 14081359 24890248
我的解决方案是:
cm = (cm.astype('float').T / cm.drop('Total', axis=1).sum(axis=1)).T