Keras 神经网络中 val-acc 和预测精度之间的巨大差异

Posted

技术标签:

【中文标题】Keras 神经网络中 val-acc 和预测精度之间的巨大差异【英文标题】:Big difference between val-acc and prediction accuracy in Keras Neural Network 【发布时间】:2020-07-10 21:28:57 【问题描述】:

我有一个用于在 Keras 中制作 NN 模型的数据集,我从该数据集中取了 2000 行作为验证数据,这 2000 行应该添加到 .predict 函数中。

我为 Keras NN 编写了一个代码,现在它运行良好,但我注意到一些对我来说非常奇怪的东西。它给了我超过 83% 的非常好的准确率,损失在 0.12 左右,但是当我想用看不见的数据(那 2000 行)进行预测时,它只能预测平均 65% 的正确率。 当我添加 Dropout 层时,它只会降低准确性。

然后我添加了EarlyStopping,它给了我大约 86% 的准确率,损失在 0.10 左右,但是当我用看不见的数据进行预测时,我得到了 67% 的最终预测准确率。

这是否意味着模型在 87% 的情况下做出了正确的预测?我的逻辑是,如果我在 .predict 函数中添加 100 个样本,那么该程序应该对 87/100 个样本或该范围内的某个位置(假设超过 80 个)做出良好的预测?我尝试在我的.predict 函数中添加 100、500、1000、1500 和 2000 个样本,它总是在 65-68% 的样本中做出正确的预测。

为什么,我做错了什么? 我尝试过使用层数、节点数、不同的激活函数和不同的优化器,但它只会将结果改变 1-2%。 我的数据集如下所示:

DataFrame shape (59249, 33)
x_train shape (47399, 32)
y_train shape (47399,)
x_test shape (11850, 32)
y_test shape (11850,)
testing_features shape (1000, 32)

这是我的神经网络模型:

model = Sequential()
model.add(Dense(64, input_dim = x_train.shape[1], activation = 'relu')) # input layer requires input_dim param
model.add(Dropout(0.2))
model.add(Dense(32, activation = 'relu'))
model.add(Dropout(0.2))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(1, activation='sigmoid')) # sigmoid instead of relu for final probability between 0 and 1

# compile the model, adam gradient descent (optimized)
model.compile(loss="binary_crossentropy", optimizer= "adam", metrics=['accuracy'])


# call the function to fit to the data training the network)
es = EarlyStopping(monitor='val_loss', min_delta=0.0, patience=1, verbose=0, mode='auto')
model.fit(x_train, y_train, epochs = 15, shuffle = True, batch_size=32, validation_data=(x_test, y_test), verbose=2, callbacks=[es])

scores = model.evaluate(x_test, y_test)
print(model.metrics_names[0], round(scores[0]*100,2), model.metrics_names[1], round(scores[1]*100,2))

这些是结果:

Train on 47399 samples, validate on 11850 samples
Epoch 1/15
 - 25s - loss: 0.3648 - acc: 0.8451 - val_loss: 0.2825 - val_acc: 0.8756
Epoch 2/15
 - 9s - loss: 0.2949 - acc: 0.8689 - val_loss: 0.2566 - val_acc: 0.8797
Epoch 3/15
 - 9s - loss: 0.2741 - acc: 0.8773 - val_loss: 0.2468 - val_acc: 0.8849
Epoch 4/15
 - 9s - loss: 0.2626 - acc: 0.8816 - val_loss: 0.2416 - val_acc: 0.8845
Epoch 5/15
 - 10s - loss: 0.2566 - acc: 0.8827 - val_loss: 0.2401 - val_acc: 0.8867
Epoch 6/15
 - 8s - loss: 0.2503 - acc: 0.8858 - val_loss: 0.2364 - val_acc: 0.8893
Epoch 7/15
 - 9s - loss: 0.2480 - acc: 0.8873 - val_loss: 0.2321 - val_acc: 0.8895
Epoch 8/15
 - 9s - loss: 0.2450 - acc: 0.8886 - val_loss: 0.2357 - val_acc: 0.8888
11850/11850 [==============================] - 2s 173us/step
loss 23.57 acc 88.88

这是为了预测:

#testing_features are 2000 rows that i extracted from dataset (these samples are not used in training, this is separate dataset thats imported)

prediction = model.predict(testing_features , batch_size=32)

res = []
for p in prediction:
    res.append(p[0].round(0))


# Accuracy with sklearn - also much lower 
acc_score = accuracy_score(testing_results, res)
print("Sklearn acc", acc_score)    

result_df = pd.DataFrame("label":testing_results,
                          "prediction":res)


result_df["prediction"] = result_df["prediction"].astype(int)

s = 0
for x,y in zip(result_df["label"], result_df["prediction"]):
    if x == y:
        s+=1

print(s,"/",len(result_df))
acc = s*100/len(result_df)
print('TOTAL ACC:', round(acc,2))

问题是......现在我得到了 sklearn 52% 和 my_acc 52% 的准确度。 为什么我在验证时得到如此低的准确度,而它说它要大得多?

【问题讨论】:

您的模型可能过拟合。了解避免过度拟合的方法 请在这个新的测试集中包含预测的代码,并且损失不是百分比。 @MatiasValdenegro 完成了,我也改变了它,所以损失不是 %。请给我建议我做错了什么。我的训练集和测试集是 0.75/0.25 您没有正确计算准确率,只需使用model.evaluate,返回元组中的第二个元素是准确率 对不起,我没听懂你的意思。我已经做到了,我得到了大约 87% 的准确率,然后我用 2000 个样本进行了预测,我想看看我的模型的表现如何,它说它在 67% 的时间内做出了正确的预测 【参考方案1】:

您发布的训练数据提供了很高的验证准确度,所以我有点困惑您从哪里获得 65%,但一般来说,当您的模型在训练数据上的表现比在看不见的数据上好得多时,这意味着您'是over fitting。这是机器学习中一个反复出现的大问题,没有办法保证可以防止这种情况发生,但您可以尝试以下几件事:

调整网络的权重,例如使用 l2 正则化 在训练期间使用随机正则化技术,例如 drop-out early stopping 降低模型复杂度(但你说你已经尝试过了)

【讨论】:

我知道什么是过拟合。我有一个我没有训练的 2000 个样本的数据集,我的数据集中没有用于训练的数据(但它们是同一类型,该数据来自同一数据集,我刚刚手动排除它以便我可以拥有它用于预测),当我对该数据运行 .predict 时,它给了我大约 65%。我有辍学层,我刚刚尝试过 kernel_regularizer 和bias_regularizer,但同样只有 65%。而且我不知道如何申请提前停止 尝试增加 dropout,或者在不同的密集层之间添加更多的 dropout 层。可以通过回调 (tensorflow.org/api_docs/python/tf/keras/callbacks/EarlyStopping) 提前停止。但是,如果您的验证集提供的结果比最终测试集好得多,那么如果您在创建训练、验证和测试集时犯了错误,则可能值得一看。在创建分区之前,您是否对所有数据进行了洗牌?您的训练数据和验证数据之间可能存在重叠吗? 是的,我已经对数据进行了洗牌,训练集和验证集之间没有重叠。我设法将 acc 提高到 87% 左右并将损失降低到 8-9%,但是,当我预测结果时,我仍然得到 67-69% 的匹配 我的逻辑是,如果我在我的 .predict 函数中添加 100 个样本,该程序应该对 87/100 个样本做出良好的预测,还是在那个范围内?我尝试在我的 .predict 函数中添加 100、500、1000、1500 和 2000 个样本,它总是对 65-69% 的样本进行预测【参考方案2】:

我将列出我在您的模型上看到的问题/建议。

    您要预测什么?您在最后一层使用sigmoid 激活函数,这似乎是一个二进制分类,但在您的loss 函数中您使用了mse,这似乎很奇怪。您可以为您的模型尝试binary_crossentropy 而不是mse 损失函数。 您的模型似乎存在过拟合问题,因此您可以增加概率。的 Dropout 并在其他隐藏层之间添加新的 Dropout,或者您可以删除其中一个隐藏层,因为您的模型似乎太复杂了。 您可以在层中更改神经元数量,例如更窄 => 64 -> 32 -> 16 -> 1 或尝试不同的 NN 架构。 尝试使用adam 优化器而不是sgd。 如果您有57849 样本,您可以在训练+验证中使用 47000 个样本,其余的将是您的测试集。 不要使用相同的集合进行评估和验证。首先将您的数据拆分为训练集和测试集。然后,当您拟合模型时,请提供 validation_split_ratio,然后它会自动从您的训练集中提供验证集。

【讨论】:

binary_crossentropy 有助于增加 acc,但当我应用 Dropouts 时,acc 会降低到 70%。然后我保留 binary_crossentropy 并设置像 64 -> 32 -> 16 -> 1 这样的层,我的损失从 0.08 增加到 0.25 能否请您提供培训和验证的准确性?您还修复了验证和测试部分吗?您可以尝试这样的 NN=> 64->dropout(0.2)->32->dropout(0.2)->16->1,使用 binary_crossentropy 和 'adam' 优化器。然后请报告训练/验证和测试准确性 我认为您的测试集准确率计算存在一些问题。因为您具有良好的训练和验证测试准确性。请确保您的测试数据和基本事实 y 具有相同的索引。如果你使用 scikit-learn 准确度方法会更好。 做了,结果还是一样 就像我之前提到的,我看不出您的训练和验证准确性有任何问题。所以可能只有两种可能。首先,您的测试分布与训练和验证测试非常不同(也许您的数据集不平衡,您可以尝试分层拆分)。其次,您计算的准确度分数错误。例如,testing_results 和 res 变量的索引不同,因此它给出了错误的准确性。

以上是关于Keras 神经网络中 val-acc 和预测精度之间的巨大差异的主要内容,如果未能解决你的问题,请参考以下文章

Keras 模型给出了 1.0 的测试精度

Keras 精度和实际精度正好相反

在 Keras 分类神经网络中进行精度交易以获得更好的召回率

为啥 Keras/tensorflow 的 sigmoid 和交叉熵精度低?

深度学习入门,keras实现回归模型

Keras,比分对预测值