为什么 MXNet 报告的验证准确性不正确?

Why MXNet is reporting the incorrect validation accuracy?

我是 MXNet 的新手,想解决一个使用 1 层网络解决数字分类问题的简单示例。我的程序如下:

import math
import numpy as np
import mxnet as mx
import matplotlib.pyplot as plt
import logging
logging.getLogger().setLevel(logging.DEBUG)
#============================================================
with np.load("notMNIST.npz") as data:

    images, labels = data["images"], data["labels"]

# Reshape the images from 28x28 into 784 1D-array and flaten the labels. 
images = images.reshape(784, 18720) labels = labels.reshape(18720)

# Apply one-hot encoding. 
Images = images.T.astype(np.float32) 
Labels = np.zeros((18720, 10)).astype(np.float32) 
Labels[np.arange(18720), labels] = 1

# Segment the data into training, evaluation and testing. 
X_train = Images[0 : 15000] 
y_train = Labels[0 : 15000]

X_eval = Images[15000 : 16000] 
y_eval = Labels[ 1200 :  2200] # IMPORTANT!!!

X_test = Images[16000 : 18720] 
y_test = Labels[16000 : 18720]

train_iter = mx.io.NDArrayIter(X_train, y_train, 100, shuffle=False)
_eval_iter = mx.io.NDArrayIter(X_eval , y_eval , 100, shuffle=False)
#============================================================
# Variables
X = mx.sym.Variable(name='data')

# Neural Network Layers
fully_connected_layer = mx.sym.FullyConnected(data=X, name='fc1', num_hidden=10)

# Outputs
lro = mx.sym.SoftmaxOutput(data=fully_connected_layer, name="softmax")
#============================================================

model = mx.mod.Module(symbol=lro)

model.fit(train_data=train_iter, eval_data=_eval_iter, 
          optimizer='sgd', optimizer_params={
              'learning_rate' : 1e-5, 
              'momentum' : 0.1}, 
          eval_metric="acc",
          num_epoch=500)

在 运行 带有评估标签 1500016000 的程序之后,最后一步是报告 97% 的验证准确度,我个人认为这太高了对于 1 层网络。因此,我特意将评估标签从1200改为2200,看到程序仍然报准确率在83~86%左右(一开始以为是巧合,尝试了几种不同的评估标签,但仍然得到相似的结果。

我的程序犯了什么错误?

TLDR;

如果你停止进行单热编码,你就可以解决这个问题。

不传递 Labels[0:15000]、Labels[15000:16000] 和 Labels[16000:18720],而是传递 labels[0:15000]、labels[15000:16000] 和 labels[16000:18720]。

这会使您在适当的评估标签上的准确性降低到中等的 0.796000,并在您的 "random" 评估标签上降低到 0.095000。

详细解答

由于 mxnet.metric.Accuracy 的误导性计算,您得到如此高的准确度。在内部,准确性指标可以在 2 "modes" 中工作,具体取决于提供的参数 "preds" 和 "labels":

的形状
  1. 如果 "preds" 和 "labels" 的形状不匹配,准确性会将 "preds" 的每一行解释为样本属于每个 class 的概率。 class 被定义为数组中的项目索引。

例如,如果您有 preds=[[0.1, 0.9], [0.8, 0.2]] 那么这意味着:

  • 第一个示例属于 class 0,概率为 0.1,属于 class 1,概率为 0.9
  • 第二个示例属于 class 0,概率为 0.8,属于 class 1,概率为 0.2

在这种模式下工作,"labels" 应该是一个真正的 class 数组。在我们的例子中,假设模型是绝对正确的,"labels" 数组应该是 [1, 0].

2) 如果 "preds" 和 "labels" 的形状匹配,则 Accuracy 将数组视为预测的 classes 和真实的 classes。因此,每个项目都被视为一个样本的 class。然后进行计算,将 "preds" "labels" 中具有相同索引的项目进行比较。

当您将 one-hot 编码应用于标签时,将使用第二种计算模式,因为模型的预测形状与 one-hot 编码的形状相匹配。准确性将数组中的每个项目解释为一个独立的样本并将它们相互比较。

在内部,Accuracy converts float array to int,对于小于 1 的浮点数,它总是产生 0。这种行为实质上将所有预测都转换为 0,除了 class 概率为 1.0 的罕见情况。所以在大多数情况下,我们得到 preds = [0, 0, ..., 0]。

One-hot 编码数组包含除 0 以外的所有项。这意味着我们会有类似 [0, 1, 0, ..., 0] 的内容。

Accuracy在比较这两个数组时,发现除了一个地方,大部分都相等,返回错误的高准确率。

这是一个简单的复制示例:

import mxnet as mx
predicts = mx.nd.array([[1.29206967e-09,   3.40120096e-05,   2.23299547e-12,   3.98692492e-07,
    1.21151755e-10,   2.59370694e-08,   1.95488334e-02,   1.13474562e-05,
    9.80405331e-01,   3.51648767e-12]])
labels = mx.nd.array([[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]])
acc = mx.metric.Accuracy()
acc.update(preds=predicts, labels=labels)
print(acc.get())

这会给我们

('accuracy', 0.90000000000000002)

因为单热编码恰好包含 1 个非零元素。