在本教程中,您将学习在训练自己的自定义深度神经网络时,验证损失可能低于训练损失的三个主要原因。

我的验证损失低于训练损失!

怎么可能呢?

  • 我是否意外地将训练和验证loss绘图的标签切换了? 潜在地。 我没有像matplotlib这样的绘图库,因此将丢失日志通过管道传输到CSV文件,然后在Excel中进行绘图。 绝对容易发生人为错误。
  • 我的代码中有错误吗? 几乎可以确定。 我同时在自学Java和机器学习-该代码中肯定存在某种错误。
  • 我只是因为太疲倦而无法理解我的大脑吗? 也很有可能。 我一生中的睡眠时间不多,很容易错过一些明显的事情。

但是,事实证明,上述情况都不是——我的验证损失确实比我的训练损失低。

要了解您的验证loss可能低于训练loss的三个主要原因,请继续阅读!

为什么我的验证loss低于训练loss?

在本教程的第一部分中,我们将讨论神经网络中“loss”的概念,包括loss代表什么以及我们为什么对其进行测量。

在此,我们将实现一个基础的CNN和训练脚本,然后使用新近实现的CNN进行一些实验(这将使我们的验证损失低于我们的训练损失)。

根据我们的结果,我将解释您的验证loss可能低于训练loss的三个主要原因。

训练神经网络时的“loss”是什么?

[1] 机器/深度学习的中的“loss”是什么? 为什么我的验证loss低于训练loss?

在最基本的层次上,loss函数可量化给定预测变量对数据集中输入数据点进行分类的“好”或“坏”程度。

loss越小,分类器在建模输入数据和输出目标之间的关系方面的工作就越好。

就是说,在某种程度上我们可以过度拟合我们的模型-通过过于紧密地建模训练数据(modeling the training data too closely),我们的模型将失去泛化的能力。

因此,我们寻求:

  1. 尽可能降低loss,从而提高模型精度。
  2. 尽可能快地这样子做,并减少超参数的更新/实验次数。
  3. 所有这些都没有过度拟合我们的网络,也没有将训练数据建模得过于紧密。。

这是一种平衡,我们选择loss函数和模型优化器会极大地影响最终模型的质量,准确性和通用性。

典型的损失函数(也称为“目标函数”或“评分函数”)包括:

  • Binary cross-entropy
  • Categorical cross-entropy
  • Sparse categorical cross-entropy
  • Mean Squared Error (MSE)
  • Mean Absolute Error (MAE)
  • Standard Hinge
  • Squared Hinge

对loss函数的全面回顾不在本文的范围内,但就目前而言,只需了解对于大多数任务:

  • loss衡量你的模型的“好(goodness)”
  • loss越小越好
  • 但你要小心别过拟合

要了解在训练自己的自定义神经网络时loss函数的作用,请确保:

  • 阅读参数化学习和线性分类简介。https://www.pyimagesearch.com/2016/08/22/an-intro-to-linear-classification-with-python/
  • 请阅读以下有关SoftMax分类器的教程。https://www.pyimagesearch.com/2016/09/12/softmax-classifiers-explained/
  • 关于多类SVM损失,请参阅本指南。https://www.pyimagesearch.com/2016/09/05/multi-class-svm-loss/

文件结构

从那里,通过tree命令检查项目/目录结构:

  $ tree --dirsfirst.├── pyimagesearch│   ├── __init__.py│   └── minivggnet.py├── fashion_mnist.py├── plot_shift.py└── training.pickle​1 directory, 5 files

今天我们将使用一个称为MiniVGGNet的更小版本的vggnet。pyimagesearch模块包括这个CNN。

我们的fashion_mnist.py脚本在fashion MNIST数据集上训练MiniVGGNet。我在之前的一篇博文中写过关于在时尚mnist上训练MiniVGGNet,所以今天我们不会详细讨论。

https://www.pyimagesearch.com/2019/02/11/fashion-mnist-with-keras-and-deep-learning/

今天的训练脚本将生成一个training.pickle文件,其中包含训练精度/loss历史记录。在下面的原因部分中,我们将使用plot_shift.py将训练loss图移动半个epoch,以证明当验证loss低于训练loss时,测量loss的时间起作用。现在让我们深入探讨三个原因来回答这个问题:“为什么我的验证loss比训练loss低?“。

原因1:在训练中应用正则化,但在验证/测试中未应用正则化

[2] Aurélien在他的Twitter提要上回答了一个问题:“大家都想知道为什么验证loss>训练loss吗?”。 第一个原因是在训练过程中应用了正则化,但在验证/测试过程中未进行正则化。

在训练深度神经网络时,我们经常应用正则化来帮助我们的模型:

  1. 获得更高的验证/测试精度
  2. 理想情况下,为了更好地泛化验证和测试集之外的数据

正则化方法通常会牺牲训练准确性来提高验证/测试准确性——在某些情况下,可能导致您的验证loss低于训练loss。

其次,请记住,在验证/测试时不应用诸如dropout之类的正则化方法。

作为的Aurelien显示在图2中,原因验证loss应正则化(例如,在验证/测试时应用dropout)可以让你的训练/验证loss曲线看起来更相似。

原因2:训练loss是在每个epoch测量的,而验证loss是在每个epoch后测量的

[3] 验证loss的原因2有时小于训练损失,这与进行测量的时间有关

您可能会看到验证loss低于训练loss的第二个原因是由于如何测量和报告loss值:

  1. 训练loss在每个epoch过程中测量的
  2. 而验证loss是在每个epoch后测量的

在整个epoch内,您的训练loss将不断得到报告;但是,仅在当前训练epoch完成后,才根据验证集计算验证指标。

这意味着,平均而言,训练loss要提前半个epoch来衡量。

如果您将训练loss向左移动半个epoch,您会发现训练和验证loss值之间的差距要小得多。

有关此行为的示例,请阅读以下部分。

执行我们的训练脚本

我们将实现一个简单的Python脚本,以在Fashion MNIST数据集上训练类似于VGG的小型网络(称为MiniVGGNet)。在训练期间,我们会将训练和验证loss保存到磁盘中。然后,我们将创建一个单独的Python脚本,以比较未变动和变动后的loss图。

让我们开始执行loss脚本:

  # import the necessary packagesfrom pyimagesearch.minivggnet import MiniVGGNetfrom sklearn.metrics import classification_reportfrom tensorflow.keras.optimizers import SGDfrom tensorflow.keras.datasets import fashion_mnistfrom tensorflow.keras.utils import to_categoricalimport argparseimport pickle​# construct the argument parser and parse the argumentsap = argparse.ArgumentParser()ap.add_argument("-i", "--history", required=True,help="path to output training history file")args = vars(ap.parse_args())

第2-8行导入了我们所需的包,模块,类和函数。 即,我们导入MiniVGGNet(我们的CNN),fashion_mnist(我们的数据集)和pickle(确保可以序列化我们的训练历史以使用单独的脚本来处理绘图)。

命令行参数--history指向单独的.pickle文件,该文件将很快包含我们的训练历史记录(第11-14行)。

然后,我们初始化一些超参数,即我们要训练的epoch数,初始学习率和批量大小:

  # initialize the number of epochs to train for, base learning rate,# and batch sizeNUM_EPOCHS = 25INIT_LR = 1e-2BS = 32

然后,我们继续加载和预处理我们的Fashion MNIST数据:

  # grab the Fashion MNIST dataset (if this is your first time running# this the dataset will be automatically downloaded)print("[INFO] loading Fashion MNIST...")((trainX, trainY), (testX, testY)) = fashion_mnist.load_data()# we are using "channels last" ordering, so the design matrix shape# should be: num_samples x rows x columns x depthtrainX = trainX.reshape((trainX.shape[0], 28, 28, 1))testX = testX.reshape((testX.shape[0], 28, 28, 1))# scale data to the range of [0, 1]trainX = trainX.astype("float32") / 255.0testX = testX.astype("float32") / 255.0# one-hot encode the training and testing labelstrainY = to_categorical(trainY, 10)testY = to_categorical(testY, 10)# initialize the label nameslabelNames = ["top", "trouser", "pullover", "dress", "coat","sandal", "shirt", "sneaker", "bag", "ankle boot"]

第3-13行加载并预处理训练/验证数据。

第16和17行将我们的类别标签二值化,而第20和21行则列出了人类可读的类别标签名称,以供日后分类报告之用。

从这里,我们拥有编译和训练Fashion MNIST数据上的MiniVGGNet模型所需的一切:

  # initialize the optimizer and modelprint("[INFO] compiling model...")opt = SGD(lr=INIT_LR, momentum=0.9, decay=INIT_LR / NUM_EPOCHS)model = MiniVGGNet.build(width=28, height=28, depth=1, classes=10)model.compile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])​# train the networkprint("[INFO] training model...")H = model.fit(trainX, trainY,validation_data=(testX, testY),batch_size=BS, epochs=NUM_EPOCHS)

第3-6行初始化并编译MiniVGGNet模型。

然后,第10-12行拟合/训练模型。

从这里我们将评估我们的模型并序列化我们的训练历史:

  # make predictions on the test set and show a nicely formatted# classification reportpreds = model.predict(testX)print("[INFO] evaluating network...")print(classification_report(testY.argmax(axis=1), preds.argmax(axis=1),target_names=labelNames))​# serialize the training history to diskprint("[INFO] serializing training history...")f = open(args["history"], "wb")f.write(pickle.dumps(H.history))f.close()

第3-6行对测试集进行预测,并将分类报告打印到终端。

10-12行将我们的训练准确性/损失历史序列化为.pickle文件。 我们将在单独的Python脚本中使用训练历史记录来绘制损耗曲线,包括一个显示二分之一epoch偏移的图。

从那里打开一个终端,然后执行以下命令:

  $ python fashion_mnist.py --history training.pickle[INFO] loading Fashion MNIST...[INFO] compiling model...[INFO] training model...Train on 60000 samples, validate on 10000 samples   Epoch 1/2560000/60000 [==============================] - 200s 3ms/sample - loss: 0.5433 - accuracy: 0.8181 - val_loss: 0.3281 - val_accuracy: 0.8815Epoch 2/2560000/60000 [==============================] - 194s 3ms/sample - loss: 0.3396 - accuracy: 0.8780 - val_loss: 0.2726 - val_accuracy: 0.9006Epoch 3/2560000/60000 [==============================] - 193s 3ms/sample - loss: 0.2941 - accuracy: 0.8943 - val_loss: 0.2722 - val_accuracy: 0.8970Epoch 4/2560000/60000 [==============================] - 193s 3ms/sample - loss: 0.2717 - accuracy: 0.9017 - val_loss: 0.2334 - val_accuracy: 0.9144Epoch 5/2560000/60000 [==============================] - 194s 3ms/sample - loss: 0.2534 - accuracy: 0.9086 - val_loss: 0.2245 - val_accuracy: 0.9194...Epoch 21/2560000/60000 [==============================] - 195s 3ms/sample - loss: 0.1797 - accuracy: 0.9340 - val_loss: 0.1879 - val_accuracy: 0.9324Epoch 22/2560000/60000 [==============================] - 194s 3ms/sample - loss: 0.1814 - accuracy: 0.9342 - val_loss: 0.1901 - val_accuracy: 0.9313Epoch 23/2560000/60000 [==============================] - 193s 3ms/sample - loss: 0.1766 - accuracy: 0.9351 - val_loss: 0.1866 - val_accuracy: 0.9320Epoch 24/2560000/60000 [==============================] - 193s 3ms/sample - loss: 0.1770 - accuracy: 0.9347 - val_loss: 0.1845 - val_accuracy: 0.9337Epoch 25/2560000/60000 [==============================] - 194s 3ms/sample - loss: 0.1734 - accuracy: 0.9372 - val_loss: 0.1871 - val_accuracy: 0.9312[INFO] evaluating network...precision    recall  f1-score   support​top       0.87      0.91      0.89      1000trouser       1.00      0.99      0.99      1000pullover       0.91      0.91      0.91      1000dress       0.93      0.93      0.93      1000coat       0.87      0.93      0.90      1000sandal       0.98      0.98      0.98      1000shirt       0.83      0.74      0.78      1000sneaker       0.95      0.98      0.97      1000bag       0.99      0.99      0.99      1000ankle boot       0.99      0.95      0.97      1000​accuracy                           0.93     10000macro avg       0.93      0.93      0.93     10000weighted avg       0.93      0.93      0.93     10000​[INFO] serializing training history...

检查工作目录的内容,您应该有一个名为training.pickle的文件-该文件包含我们的训练历史日志。

  $ ls *.pickletraining.pickle

在下一节中,我们将学习如何绘制这些值并将训练信息向左移动半个epoch,从而使我们的训练/验证loss曲线看起来更加相似。

平移我们的训练loss值

我们的plot_shift.py脚本用于绘制来自fashion_mnist.py的训练历史记录。 使用此脚本,我们可以研究将训练损失向左移动半个世纪如何使我们的训练/验证图看起来更相似。

打开plot_shift.py文件并插入以下代码:

  # import the necessary packagesimport matplotlib.pyplot as pltimport numpy as npimport argparseimport pickle# construct the argument parser and parse the argumentsap = argparse.ArgumentParser()ap.add_argument("-i", "--input", required=True,help="path to input training history file")args = vars(ap.parse_args())

第2-5行导入matplotlib(用于绘制),NumPy(用于简单的数组创建操作),argparse(命令行参数)和pickle(加载我们的序列化训练历史记录)。

第8-11行解析--input命令行参数,该参数指向磁盘上的.pickle训练历史记录文件。

让我们继续加载数据并初始化绘图:

  # load the training historyH = pickle.loads(open(args["input"], "rb").read())# determine the total number of epochs used for training, then# initialize the figureepochs = np.arange(0, len(H["loss"]))plt.style.use("ggplot")(fig, axs) = plt.subplots(2, 1)

第2行使用--input命令行参数加载序列化的训练历史记录.pickle文件。

第6行为我们的x轴腾出了空间,该空间从零到训练历史中的epoch数。

第7行和第8行将我们的绘图图设置为同一图像中的两个堆叠绘图:

  • top plot将按原样包含loss曲线。
  • 另一方面,bottom plot将包括训练loss(但不包括验证loss)的偏移。 训练loss将按照Aurélien的推文向左移动半个epoch。 然后,我们将能够观察绘图线的排列是否更加紧密。

让我们生成top plot

  # plot the *unshifted* training and validation lossplt.style.use("ggplot")axs[0].plot(epochs, H["loss"], label="train_loss")axs[0].plot(epochs, H["val_loss"], label="val_loss")axs[0].set_title("Unshifted Loss Plot")axs[0].set_xlabel("Epoch #")axs[0].set_ylabel("Loss")axs[0].legend()

然后绘制bottom plot

  # plot the *shifted* training and validation lossaxs[1].plot(epochs - 0.5, H["loss"], label="train_loss")axs[1].plot(epochs, H["val_loss"], label="val_loss")axs[1].set_title("Shifted Loss Plot")axs[1].set_xlabel("Epoch #")axs[1].set_ylabel("Loss")axs[1].legend()# show the plotsplt.tight_layout()plt.show()

请注意,在第2行上,训练损失向左移动了0.5个epoch,即本例的核心。

现在,让我们分析我们的训练/验证图。

打开一个终端并执行以下命令:

  python plot_shift.py --input training.pickle

[4] 将训练损失图向左移动1/2个epoch,可以得到更多类似的图。 显然,测量时间回答了一个问题:“为什么我的验证loss低于训练loss?”。

如您所见,将训练loss值向左(底部)移动一个半个epoch,使训练/验证曲线与未移动(顶部)图更加相似。

原因#3:验证集可能比训练集更容易(否则可能会泄漏(leaks))

[5] 考虑如何获取/生成验证集。 常见的错误可能导致验证loss少于训练loss。

验证loss低于训练loss的最终最常见原因是由于数据本身分布的问题。

考虑如何获取验证集:

  • 您可以保证验证集是从与训练集相同的分布中采样的吗?
  • 您确定验证示例与您的训练图像一样具有挑战性吗?
  • 您是否可以确保没有“数据泄漏”(即训练样本与验证/测试样本意外混入)?
  • 您是否确信自己的代码正确创建了训练集,验证集和测试集?

每位深度学习从业者在其职业中都至少犯过一次以上错误。

是的,它确实会令人尴尬-但这很重要-确实会发生,所以现在就花点时间研究您的代码。

BONUS: Are you training hard enough?

[6] 如果您想知道为什么验证损失低于训练loss,也许您没有“足够努力地训练”。

Aurélien在推文中没有提及的一个方面是“足够努力地训练(training hard enough)”的概念。

在训练深度神经网络时,我们最大的担心几乎总是过拟合——为了避免过拟合,我们引入了正则化技术(在上面的原因1中进行了讨论)。我们用以下形式应用正则化:

  • Dropout
  • L2权重衰减
  • 减少模型容量(即更浅的模型)

我们的学习率也趋于保守一些,以确保我们的模型不会在亏损形势下超越亏损较低的领域。

一切都很好,但是有时候我们最终会过度规范我们的模型 (over-regularizing our models)

如果您经历了验证loss低于上述详细说明的训练loss的所有三个原因,则可能是您的模型over-regularized了。通过以下方法开始放宽正则化约束:

  • 降低L2权重衰减强度。
  • 减少申请的dropout数量。
  • 增加模型容量(即,使其更深)。

您还应该尝试以更高的学习率进行训练,因为您可能对此过于保守。

总结

今天的教程深受作者AurélienGeron的以下推文启发。

在线程中,Aurélien简洁明了地解释了训练深度神经网络时验证损失可能低于训练损失的三个原因:

  1. 原因1:在训练期间应用正则化,但在验证/测试期间未进行正则化。如果在验证/测试期间添加正则化损失,则损失值和曲线将看起来更加相似。
  2. 原因2:训练损失是在每个epoch期间测量的,而验证损失是在每个epoch后测量的。平均而言,训练损失的测量时间是前一个时期的1/2。如果将训练损失曲线向左移动半个epoch,则损失会更好。
  3. 原因3:您的验证集可能比训练集更容易,或者代码中的数据/错误泄漏。确保您的验证集大小合理,并且是从与您的训练集相同的分布(和难度)中抽取的。
  4. 奖励:您的模型可能over-regularizing 。尝试减少正则化约束,包括增加模型容量(即通过更多参数使其更深),减少dropout,降低L2权重衰减强度等。

希望这有助于消除对为什么您的验证损失可能低于培训损失的困惑!

英文原文链接:https://www.pyimagesearch.com/2019/10/14/why-is-my-validation-loss-lower-than-my-training-loss/

一个值得深思的问题?为什么验证集的loss会小于训练集的loss相关推荐

  1. 训练softmax分类器实例_一个值得深思的问题?为什么验证集的loss会小于训练集的loss...

    编辑:zero 关注 搜罗最好玩的计算机视觉论文和应用,AI算法与图像处理 微信公众号,获得第一手计算机视觉相关信息 在本教程中,您将学习在训练自己的自定义深度神经网络时,验证损失可能低于训练损失的三 ...

  2. 男子因微信被封从腾讯公司坠亡 前快播CEO王欣说出一个值得深思的问题

    男子因微信被封从腾讯公司坠亡 前快播CEO王欣说出一个值得深思的问题 8 月 27 日,有媒体报道称,有市民反映,自己 21 岁弟弟从腾讯用户接待中心顶楼坠亡.据悉,坠亡者此前被微信封号,多次申诉无果 ...

  3. 十折交叉验证10-fold cross validation, 数据集划分 训练集 验证集 测试集

    机器学习 数据挖掘 数据集划分 训练集 验证集 测试集 Q:如何将数据集划分为测试数据集和训练数据集? A:three ways: 1.像sklearn一样,提供一个将数据集切分成训练集和测试集的函数 ...

  4. 为什么神经网络模型在测试集上的准确率高于训练集上的准确率?

    为什么神经网络模型在测试集上的准确率高于训练集上的准确率? 种花家的奋斗兔 2020-03-21 17:28:37  5847  已收藏 11 分类专栏: Deep Learning 文章标签: dr ...

  5. 分页携带请求参数_一个值得深思的小问题 请求中的参数值为空要不要携带该参数?...

    最近一个朋友疯狂的和我吐槽公司的后端,说很常规.很普通的一个事儿,也就是验证一下子的事儿,非要搞的那么复杂,治标不治本,技术玩来玩去不但没进步还倒退了. 这是怎么回事呢?咱们就来聊聊这件"小 ...

  6. 项目是否应该开源, 这是一个值得深思的问题

    毫无疑问,开源哲学在编写代码和生产软件方面的优点.现代计算核心的许多软件包,从Linux操作系统到MySQL,都是使用开放共享和协作开发的模式创建的.四十年来,在开放哲学的滋养下,伟大的代码解决了任何 ...

  7. 3.2 实战项目二(手工分析错误、错误标签及其修正、快速地构建一个简单的系统(快速原型模型)、训练集与验证集-来源不一致的情况(异源问题)、迁移学习、多任务学习、端到端学习)

    手工分析错误 手工分析错误的大多数是什么 猫猫识别,准确率90%,想提升,就继续猛加材料,猛调优?     --应该先做错误分析,再调优! 把识别出错的100张拿出来, 如果发现50%是"把 ...

  8. 训练集准确率很高,验证集准确率低问题

    训练集在训练过程中,loss稳步下降,准确率上升,最后能达到97% 验证集准确率没有升高,一直维持在50%左右(二分类问题,随机概率) 测试集准确率57% 在网上搜索可能打的原因: 1.learnin ...

  9. 机器学习中的训练集 验证集 测试集的关系

    1.划分测试集目的 为了了解一个模型对新样本的泛化能力,唯一的办法是:让已经训练好的模型真正的处理新的样本. 解决方法: 将原始数据划分成两个部分:训练集 测试集.可以使用训练集来训练模型,然后用测试 ...

最新文章

  1. 服务器邮箱备份文件在哪里,如何轻松将数据文件备份到电子邮箱?
  2. 浅谈JS原型与原型链(一)
  3. 用例设计:判定表驱动法
  4. html5 移动端单页面布局
  5. Bailian3248 最大公约数【数论】
  6. c++数学函数运算,浮点数据相等判断
  7. 编程菜鸟的日记-初学尝试编程-C++ Primer Plus 第6章编程练习6
  8. 互联网晚报 | 1月21日 星期五 | 中兴手机官宣吴京为新代言人;58同城旗下“赶集网”更名“赶集直招”;罗永浩年后回归科技界...
  9. win10从网络访问计算机没有guest,简单几步解决win10没有权限访问网络资源的问题...
  10. 燕过留声:由 Activity 和 Fragment 的通信方法想到的【WIP】
  11. 使用微信号开通检测软件的成功案例(一)
  12. 危险在线旅游,为何依然有人冒死买卖?
  13. 怎么在一台电脑上同时打开运行两个微信号
  14. 一张图带你看完图论第一章(包含定义、定理、公式、推导证明和例题)
  15. 表格里面出现个别边框线粗细不同的解决
  16. h5调起QQ客服的坑(PC端、移动端)
  17. React 兼容IE8 项目
  18. APACHE+PHP+MySQL 绿色版安装教程
  19. python数值运算m op n_M OP N数值运算问题
  20. 单代号网络图计算例题_工程人如何绘制进度计划?(双代号网络图、横道图等)...

热门文章

  1. Hive用户权限管理
  2. Mac系统服务器管理工具倾情推荐
  3. 【电子学会】2020年09月图形化三级 -- 魔术表演“开花”
  4. 轴线图层设置_定位轴线图层怎么设计
  5. OpenStake组件
  6. Rust 高级编程 变性的直观解释
  7. Android 相机 LED 做手电筒
  8. k均值聚类算法(K Means)及其实战案例
  9. 双11小黑盒很炫酷?咱们用CSS变量来改进一下!
  10. root改名字,root重命名