【问题标题】:How to extract classes from prefetched dataset in Tensorflow for confusion matrix如何在 Tensorflow 中为预取数据集绘制混淆矩阵
【发布时间】:2021-02-13 17:34:39
【问题描述】:

我试图用以下代码为我的图像分类器绘制一个混淆矩阵,但我收到一条错误消息:'PrefetchDataset' object has no attribute 'classes'

Y_pred = model.predict(validation_dataset)
y_pred = np.argmax(Y_pred, axis=1)

print('Confusion Matrix')
print(confusion_matrix(validation_dataset.classes, y_pred)) # ERROR message generated

【问题讨论】:

    标签: python tensorflow machine-learning image-processing keras


    【解决方案1】:

    免责声明:这不适用于打乱的数据集。我会尽快更新这个答案。

    您可以使用tf.stack 连接所有数据集值。像这样:

    true_categories = tf.concat([y for x, y in test_dataset], axis=0)
    

    为了重现性,假设您有一个数据集、一个神经网络和一个训练循环:

    import tensorflow_datasets as tfds
    import tensorflow as tf
    from sklearn.metrics import confusion_matrix
    
    data, info = tfds.load('iris', split='train',
                           as_supervised=True,
                           shuffle_files=True,
                           with_info=True)
    
    AUTOTUNE = tf.data.experimental.AUTOTUNE
    
    train_dataset = data.take(120).batch(4).prefetch(buffer_size=AUTOTUNE)
    test_dataset = data.skip(120).take(30).batch(4).prefetch(buffer_size=AUTOTUNE)
    
    model = tf.keras.Sequential([
        tf.keras.layers.Dense(8, activation='relu'),
        tf.keras.layers.Dense(16, activation='relu'),
        tf.keras.layers.Dense(info.features['label'].num_classes, activation='softmax')
        ])
    
    model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', 
                  metrics='accuracy')
    
    history = model.fit(train_dataset, validation_data=test_dataset, epochs=50, verbose=0)
    

    现在你的模型已经拟合好了,你可以预测测试集了:

    y_pred = model.predict(test_dataset)
    
    array([[2.2177568e-05, 3.0841196e-01, 6.9156587e-01],
           [4.3539176e-06, 1.2779665e-01, 8.7219906e-01],
           [1.0816366e-03, 9.2667454e-01, 7.2243840e-02],
           [9.9921310e-01, 7.8686583e-04, 9.8775059e-09]], dtype=float32)
    

    这将是一个(n_samples, 3) 数组,因为我们正在处理三个类别。我们想要sklearn.metrics.confusion_matrix(n_samples, 1) 数组,所以取argmax:

    predicted_categories = tf.argmax(y_pred, axis=1)
    
    <tf.Tensor: shape=(30,), dtype=int64, numpy=
    array([2, 2, 2, 0, 2, 2, 2, 2, 1, 1, 2, 0, 0, 2, 1, 1, 1, 2, 0, 2, 1, 2,
           1, 0, 2, 0, 1, 2, 1, 0], dtype=int64)>
    

    然后,我们可以从预取数据集中获取所有 y 值:

    true_categories = tf.concat([y for x, y in test_dataset], axis=0)
    
    [<tf.Tensor: shape=(4,), dtype=int64, numpy=array([1, 1, 1, 0], dtype=int64)>,
     <tf.Tensor: shape=(4,), dtype=int64, numpy=array([2, 2, 2, 2], dtype=int64)>,
     <tf.Tensor: shape=(4,), dtype=int64, numpy=array([1, 1, 1, 0], dtype=int64)>,
     <tf.Tensor: shape=(4,), dtype=int64, numpy=array([0, 2, 1, 1], dtype=int64)>,
     <tf.Tensor: shape=(4,), dtype=int64, numpy=array([1, 2, 0, 2], dtype=int64)>,
     <tf.Tensor: shape=(4,), dtype=int64, numpy=array([1, 2, 1, 0], dtype=int64)>,
     <tf.Tensor: shape=(4,), dtype=int64, numpy=array([2, 0, 1, 2], dtype=int64)>,
     <tf.Tensor: shape=(2,), dtype=int64, numpy=array([1, 0], dtype=int64)>]
    

    那么,你就可以得到混淆矩阵了:

    confusion_matrix(predicted_categories, true_categories)
    
    array([[ 9,  0,  0],
           [ 0,  9,  0],
           [ 0,  2, 10]], dtype=int64)
    

    (9 + 9 + 10) / 30 = 0.933 是准确度得分。对应model.evaluate(test_dataset)

    8/8 [==============================] - 0s 785us/step - loss: 0.1907 - accuracy: 0.9333
    

    结果也和sklearn.metrics.classification_report一致:

                  precision    recall  f1-score   support
               0       1.00      1.00      1.00         8
               1       0.82      1.00      0.90         9
               2       1.00      0.85      0.92        13
        accuracy                           0.93        30
       macro avg       0.94      0.95      0.94        30
    weighted avg       0.95      0.93      0.93        30
    

    这是完整的代码:

    import tensorflow_datasets as tfds
    import tensorflow as tf
    from sklearn.metrics import confusion_matrix
    
    data, info = tfds.load('iris', split='train',
                           as_supervised=True,
                           shuffle_files=True,
                           with_info=True)
    
    AUTOTUNE = tf.data.experimental.AUTOTUNE
    
    train_dataset = data.take(120).batch(4).prefetch(buffer_size=AUTOTUNE)
    test_dataset = data.skip(120).take(30).batch(4).prefetch(buffer_size=AUTOTUNE)
    
    model = tf.keras.Sequential([
        tf.keras.layers.Dense(8, activation='relu'),
        tf.keras.layers.Dense(16, activation='relu'),
        tf.keras.layers.Dense(info.features['label'].num_classes, activation='softmax')
        ])
    
    model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', 
                  metrics='accuracy')
    
    history = model.fit(train_dataset, validation_data=test_dataset, epochs=50, verbose=0)
    
    y_pred = model.predict(test_dataset)
    
    predicted_categories = tf.argmax(y_pred, axis=1)
    
    true_categories = tf.concat([y for x, y in test_dataset], axis=0)
    
    confusion_matrix(predicted_categories, true_categories)
    

    【讨论】:

    • @Nicholas Sir,谢谢,它可以工作,但没有按预期工作... model.evaluate() 返回的准确度为 0.82,而分类报告中的准确度为 0.20,使用与你提到...
    • 什么是分类报告?请参阅我的更新答案。它确实对应于model.evaluate()的结果。
    • 只是为了确保,您知道mode.evaluate() 返回(loss, accuracy) 对吗?
    • 没有随机播放它按预期工作。但是使用随机播放,例如train_dataset = data.take(120).shuffle(120).batch(4).prefetch(buffer_size=AUTOTUNE)混淆矩阵的结果变得奇怪(接近随机),而训练的准确率和损失是正常的。
    • @shahryar 很有道理,有机会我会更新答案
    【解决方案2】:

    此代码适用于打乱的 tf.data.Dataset

    y_pred = []  # store predicted labels
    y_true = []  # store true labels
    
    # iterate over the dataset
    for image_batch, label_batch in dataset:   # use dataset.unbatch() with repeat
       # append true labels
       y_true.append(label_batch)
       # compute predictions
       preds = model.predict(image_batch)
       # append predicted labels
       y_pred.append(np.argmax(preds, axis = - 1))
    
    # convert the true and predicted labels into tensors
    correct_labels = tf.concat([item for item in y_true], axis = 0)
    predicted_labels = tf.concat([item for item in y_pred], axis = 0)
    

    【讨论】:

    • 这段代码在洗牌后的 image_dataset_from_directory 数据集上运行良好,但得到更多选票的答案并不是因为洗牌。非常感谢。
    • 这可以更好地处理image_dataset_from_directory 执行的洗牌。谢谢!
    • 这是正确的答案,因为要处理随机播放。
    猜你喜欢
    • 2016-01-31
    • 2016-06-04
    • 2021-06-12
    • 2020-08-10
    • 2017-10-02
    • 2018-09-26
    • 2018-06-06
    • 1970-01-01
    相关资源
    最近更新 更多