如何从 softmax 中获取单个值而不是概率并获取混淆矩阵

How to get a single value from softmax instead of probability & get confusion matrix

test_generator = test_datagen.flow_from_directory(
    test_dir,
    target_size=(150, 150),
    batch_size=20,
    class_mode='categorical')
test_loss, test_acc = model.evaluate_generator(test_generator, steps=28)
print('test acc:', test_acc)

predict = model.predict_generator(test_generator,steps =28, verbose=0)
print('Prediction: ', predict)

test_imgs, test_labels = next(test_generator)

print(test_labels)

cm =confusion_matrix(test_labels, predict)

我从上面的代码中得到了 2 个问题。首先,我得到一个错误,在我的 test_labels 和预测之间有不同数量的样本。我的 test_labels 只存储 20 个样本(如批量大小中所写。同时,我从 model.predict_generator 预测的总共有 560 张图像(20*28 步)

ValueError: Found input variables with inconsistent numbers of samples: [20, 560]

第二个问题是,如何更改我的 softmax 值(从我的 4 图像 classes 的概率 float 到 int)?当我将步骤更改为 1 时出现错误(仅测试 20 个样本而不是上述问题中的 560 个样本)

ValueError: Classification metrics can't handle a mix of multilabel-indicator and continuous-multioutput targets

我认为这是错误的,因为当我进行预测时,我得到了一些 4-d 列表(来自 4 classes),例如

Prediction:  [[2.9905824e-12 5.5904431e-10 1.8195983e-11 1.0000000e+00]
 [2.7073351e-21 1.0000000e+00 8.3221777e-21 4.9091786e-22]
 [4.2152173e-05 6.1331893e-04 3.7486094e-05 9.9930704e-01]

无论如何我可以得到哪个是我的模型预测的确切 class (例如在我的测试损失和测试准确性中)。

或者还有其他我不知道的在 Keras 中获取混淆矩阵的简单方法吗? :(

Edit1(从 desertnaut 获得) test_labels个变量返回如下

array([[0., 0., 0., 1.],
   [0., 0., 0., 1.],
   [0., 1., 0., 0.],
   [0., 1., 0., 0.],
   [0., 1., 0., 0.],
   [0., 0., 0., 1.],
   [1., 0., 0., 0.],
   [0., 0., 0., 1.],
   [0., 1., 0., 0.],
   [0., 0., 0., 1.],
   [0., 0., 0., 1.],
   [0., 1., 0., 0.],
   [0., 0., 0., 1.],
   [0., 0., 0., 1.],
   [0., 0., 1., 0.],
   [0., 1., 0., 0.],
   [0., 1., 0., 0.],
   [0., 0., 0., 1.],
   [0., 0., 0., 1.],
   [0., 0., 1., 0.]], dtype=float32), array([[1., 0., 0., 0.],
   [0., 0., 0., 1.],

^ 这仅适用于 1 个周期(总共 28 个,此列表中还有 27 个)。此快照位于输出中间的某个位置。列表太长,无法显示最顶部的数组(无法滚动到 Spyder 输出框的顶部)。我尝试使用 argmax 尝试作为上面的第二个问题。例如

test_class = np.argmax(test_labels, axis=1)
test_class = test_class.tolist()
print(test_class)

但是我没有得到正确答案。我认为是因为循环不同。我认为您给出的 predict_class 的输出是 1 个列表,其中包含所有 560 个样本预测。但是对于 test_label 它算作 28 个不同的循环。 predict_class 的输出是这样的。例如

[3, 1, 1, 2, 0, 0, 3, 1, 2, 0, 0, 1, 2, 2, 1, 3, 2, 2, 0, 2, 0, 3, 0, 1, 3, 3, 1, 2, 0, 1, 1, 0, 2, 1, 0, 2, 1, 3, 1, 0, 1, 2, 2, 2, 1, 2, 2, 2, 2, 3, 2, 3, 1, 3, 1, 1, 3, 2, 2, 0, 1, 1, 0, 2, 1, 3, 3, 2, 0, 1, 1, 0, 3, 0, 0, 2, 3, 2, 1, 1, 2, 3, 0, 0, 2, 1, 3, 2, 3, 1, 0, 0, 3, 0, 3, 1, 1, 3, 1, 0, 1, 2, 0, 0, 0, 0, 3, 2, 2, 3, 3, 1, 3, 0, 3, 2, 0, 0, 0, 2, 1, 0, 2, 2, 1, 0, 1, 2, 2, 2, 3, 2, 1, 2, 2, 0, 0, 2, 3, 3, 1, 2, 2, 3, 0, 2, 1, 1, 3, 0, 1, 0, 1, 3, 3, 1, 3, 0, 1, 3, 0, 2, 1, 1, 3, 0, 1, 0, 1, 1, 3, 2, 3, 3, 0, 1, 1, 3, 2, 0, 3, 2, 0, 1, 3, 3, 2, 1, 1, 1, 0, 2, 0, 2, 2, 0, 2, 2, 0, 0, 1, 2, 2, 0, 0, 1, 1, 1, 0, 2, 2, 0, 3, 0, 3, 2, 2, 0, 1, 1, 1, 3, 0, 2, 2, 1, 3, 3, 3, 1, 2, 0, 3, 0, 0, 3, 1, 1, 3, 0, 2, 2, 2, 2, 3, 0, 2, 3, 0, 3, 2, 3, 2, 3, 3, 0, 0, 2, 3, 2, 0, 0, 3, 1, 3, 0, 0, 1, 1, 0, 1, 0, 0, 3, 0, 0, 1, 1, 3, 1, 3, 2, 1, 0, 1, 0, 2, 3, 0, 1, 2, 1, 2, 2, 2, 2, 0, 2, 2, 1, 3, 2, 2, 2, 1, 3, 3, 2, 0, 3, 0, 1, 2, 2, 2, 3, 1, 0, 2, 3, 2, 1, 0, 1, 2, 0, 2, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 3, 3, 2, 1, 0, 0, 3, 0, 0, 2, 1, 0, 2, 3, 2, 3, 2, 1, 3, 0, 2, 1, 0, 0, 0, 1, 2, 2, 3, 2, 3, 2, 0, 3, 2, 1, 0, 0, 3, 2, 3, 0, 2, 0, 1, 0, 0, 3, 2, 3, 1, 3, 2, 2, 2, 0, 1, 2, 0, 2, 0, 0, 0, 3, 1, 3, 2, 3, 2, 1, 2, 3, 3, 1, 3, 3, 0, 1, 1, 2, 0, 1, 2, 3, 0, 2, 2, 2, 0, 0, 3, 0, 3, 3, 3, 3, 3, 3, 0, 1, 3, 0, 2, 3, 1, 0, 2, 3, 2, 3, 1, 1, 2, 1, 2, 3, 0, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 0, 0, 2, 0, 1, 0, 3, 1, 0, 0, 2, 1, 2, 3, 3, 2, 2, 1, 2, 2, 0, 2, 0, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 0, 3, 2, 2, 3, 0, 1, 3, 2, 3, 3, 0, 3, 1, 2, 3, 3, 0, 3, 3, 3, 2, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 2, 2, 3, 0, 0, 1, 1, 0, 2, 0, 2, 0, 3, 3, 1, 0, 2, 2, 1, 0, 0, 3, 0, 3, 3, 3]

^ 1 个包含 560 个样本的列表。

test_class 的输出(使用 argmax 编辑)。例如

[[7, 3, 0, 2], [9, 3, 2, 0], [0, 2, 9, 6], [0, 2, 3, 1], [2, 3, 0, 1], [6, 0, 1, 4], [5, 0, 1, 2], [1, 3, 2, 0], [0, 2, 3, 5], [0, 1, 3, 7], [1, 0, 8, 4], [3, 7, 1, 0], [3, 5, 0, 2], [9, 0, 3, 1], [0, 2, 1, 9], [8, 5, 1, 0], [2, 0, 1, 8], [0, 5, 1, 3], [0, 17, 1, 4], [2, 1, 7, 0], [0, 4, 5, 1], [1, 2, 0, 4], [0, 2, 3, 1], [2, 0, 1, 3], [3, 2, 1, 0], [0, 2, 7, 6], [5, 0, 18, 2], [2, 0, 7, 1]]

numpy 或 scipy 中是否有函数使其成为 560 个样本的 1 个列表而不是 28 个列表*20 批次。

编辑2

谢谢!两者现在都在 1 个列表中。但是,无论如何要检查样本是否以相同的方式洗牌?我获得了 87.8% class 化准确率。但是我得到的 conf_matrix 非常非常低。

[[33 26 35 46]
 [43 25 41 31]
 [38 36 36 30]
 [32 30 39 39]]

对于你的第二个问题,由于你的预测是 one-hot 编码的,你应该简单地得到最大参数;以您显示的 3 个预测为例:

import numpy as np
# your shown predictions:
predict = np.array( [[2.9905824e-12, 5.5904431e-10, 1.8195983e-11 ,1.0000000e+00],
                     [2.7073351e-21, 1.0000000e+00, 8.3221777e-21, 4.9091786e-22],
                     [4.2152173e-05, 6.1331893e-04, 3.7486094e-05, 9.9930704e-01]])
predict_class = np.argmax(predict, axis=1)
predict_class = predict_class.tolist()
predict_class
# [3, 1, 3]

关于你的第一个问题:我假设你不能独立地得到你的整个数据集的 test_labels (否则你可能会使用这个长度为 560 的数组作为你的混淆矩阵);如果是这样,您可以使用类似 [OP 编辑​​后更新]:

test_labels = []
for i in range(28):
    test_imgs, batch_labels = next(test_generator)
    batch_labels = np.argmax(batch_labels, axis=1).tolist()
    test_labels = test_labels + batch_labels

之后你的 test_labelspredict_class 都是长度为 560 的列表,你应该能够得到整个测试集的混淆矩阵

cm =confusion_matrix(test_labels, predict_class)

为确保预测和测试标签确实对齐,您应该将 shuffle=False 参数添加到 test_datagen.flow_from_directory()(默认值为 True - docs) .

鉴于混淆矩阵,如果您需要进一步的分类措施,如精度、召回率等,请查看

您也可以使用sklearn

import numpy as np
# your shown predictions:
predict = np.array( [[2.9905824e-12, 5.5904431e-10, 1.8195983e-11 ,1.0000000e+00],
                     [2.7073351e-21, 1.0000000e+00, 8.3221777e-21, 4.9091786e-22],
                     [4.2152173e-05, 6.1331893e-04, 3.7486094e-05, 9.9930704e-01]])

labels = ['class1', 'class2', 'class3', 'class4']
from sklearn.preprocessing import LabelBinarizer
lb.fit(labels)

predict_class = lb.inverse_transform(predict)
print(predict_class)
# ['class4', 'class2', 'class4']