如何在训练时纠正不稳定的损失和准确率?(二元分类)

18

我目前正在使用tensorflow的新keras API开展一个小型二元分类项目。该问题是Kaggle.com几年前发布的Higgs Boson挑战赛的简化版本。数据集的形状为2000x14,其中每行的前13个元素构成输入向量,第14个元素是相应的标签。以下是所述数据集的样本:

86.043,52.881,61.231,95.475,0.273,77.169,-0.015,1.856,32.636,202.068, 2.432,-0.419,0.0,0
138.149,69.197,58.607,129.848,0.941,120.276,3.811,1.886,71.435,384.916,2.447,1.408,0.0,1
137.457,3.018,74.670,81.705,5.954,775.772,-8.854,2.625,1.942,157.231,1.193,0.873,0.824,1

我对机器学习和tensorflow比较新,但熟悉损失函数、优化器和激活函数等高级概念。我尝试根据网上找到的二元分类问题示例构建各种模型,但训练模型时遇到了困难。在训练过程中,有时损失会在同一轮内增加,导致学习不稳定。准确率在70%左右停滞不前。我尝试改变学习率和其他超参数,但无济于事。与此相比,我已经硬编码了一个完全连接的前馈神经网络,在同样的问题上达到了80-85%的准确率。

这是我的当前模型:

import tensorflow as tf
from tensorflow.python.keras.layers.core import Dense
import numpy as np
import pandas as pd

def normalize(array):
    return array/np.linalg.norm(array, ord=2, axis=1, keepdims=True)

x_train = pd.read_csv('data/labeled.csv', sep='\s+').iloc[:1800, :-1].values
y_train = pd.read_csv('data/labeled.csv', sep='\s+').iloc[:1800, -1:].values

x_test = pd.read_csv('data/labeled.csv', sep='\s+').iloc[1800:, :-1].values
y_test = pd.read_csv('data/labeled.csv', sep='\s+').iloc[1800:, -1:].values

x_train = normalize(x_train)
x_test = normalize(x_test)

model = tf.keras.Sequential()
model.add(Dense(9, input_dim=13, activation=tf.nn.sigmoid)
model.add(Dense(6, activation=tf.nn.sigmoid))
model.add(Dense(1, activation=tf.nn.sigmoid))

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=50)
model.evaluate(x_test, y_test)

正如提到的那样,一些时代的起始准确性高于结束准确性,导致学习不稳定。

  32/1800 [..............................] - ETA: 0s - loss: 0.6830 - acc: 0.5938
1152/1800 [==================>...........] - ETA: 0s - loss: 0.6175 - acc: 0.6727
1800/1800 [==============================] - 0s 52us/step - loss: 0.6098 - acc: 0.6861
Epoch 54/250

  32/1800 [..............................] - ETA: 0s - loss: 0.5195 - acc: 0.8125
1376/1800 [=====================>........] - ETA: 0s - loss: 0.6224 - acc: 0.6672
1800/1800 [==============================] - 0s 43us/step - loss: 0.6091 - acc: 0.6850
Epoch 55/250
这个简单模型中学习波动的原因可能是什么?谢谢。

编辑:

我已经根据评论中的建议对模型进行了一些修改。它现在看起来更像这样:

model = tf.keras.Sequential()
model.add(Dense(250, input_dim=13, activation=tf.nn.relu))
model.add(Dropout(0.4))
model.add(Dense(200, activation=tf.nn.relu))
model.add(Dropout(0.4))
model.add(Dense(100, activation=tf.nn.relu))
model.add(Dropout(0.3))
model.add(Dense(50, activation=tf.nn.relu))
model.add(Dense(1, activation=tf.nn.sigmoid))

model.compile(optimizer='adadelta',
              loss='binary_crossentropy',
              metrics=['accuracy'])

你能提供数据集的链接吗?这样问题就可以使用相同的数据和模型进行复现了。 - theberzi
3个回答

48

振荡

这些振荡显然与您的网络规模有关;每次通过的批次都会改变您的神经网络,因为它没有足够的神经元来表示关系。 它可以对一个批次进行良好的工作,对另一个批次更新权重,并有效地更改以前学习的连接,导致“取消学习”。这就是为什么损失也会跳动,因为网络试图适应您给定的任务。

Sigmoid激活及其饱和可能也会给您带来麻烦(因为梯度被压缩到小范围内,大多数梯度更新为零)。快速修复 - 使用下面描述的ReLU激活。

此外,神经网络并不关心准确性,只关心最小化损失值(它大多数情况下尝试这样做)。假设它预测概率:[0.55, 0.55, 0.55, 0.55, 0.45],用于类别[1, 1, 1, 1, 0],因此其准确性为100%,但它相当不确定。现在,假设下一次更新将网络推向概率预测:[0.8, 0.8, 0.8, 0.8, 0.55]。在这种情况下,损失将下降,但准确性也会下降,从100%80%

顺便说一下。您可能希望检查逻辑回归的分数,并查看它在此任务上的表现(因此仅具有输出的单个层)。

考虑的一些事情

1. 神经网络的规模

始终从简单模型开始,如果需要再逐步扩大(不建议相反)。你可以先检查一小部分数据样本(例如2/3个批次,约160个元素)是否能够学会输入和输出之间的关系。

在你的情况下,我怀疑模型将无法通过您提供的层大小学习这些关系。尝试增加大小,特别是在较早的层中(起始点为50/100),看看它的表现如何。

2. 激活函数

Sigmoid很容易饱和(变化发生的范围很小,大多数值几乎为0或1)。它现在很少被用作瓶颈(最后一层)前的激活函数。现在最常见的是ReLU或其变体,它不容易饱和(至少当输入是正数时),这可能也有所帮助。

3. 学习率

对于每个数据集和神经网络模型,最优的学习率选择是不同的。默认设置通常效果一般,但当学习率过小时,可能会陷入局部极小值(它的泛化效果将更差),而值过大将使您的网络不稳定(损失会高度振荡)。

您可能想了解循环学习率(或Leslie N. Smith的原始研究论文)。在那里,您可以找到关于如何启发式地选择好的学习率并设置一些简单的学习率调度程序的信息。这些技术被fast.ai团队在CIFAR10比赛中使用,并取得了非常好的成绩。或者在他们库的文档中,您可以找到 One Cycle Policy 和学习率查找器(基于上述研究人员的工作)。我认为这应该可以帮助您开始进入这个领域。

4. 规范化

不确定,但对我来说,这种规范化看起来相当非标准化(从未见过像这样做的方式)。 良好的规范化是神经网络收敛的基础(除非数据已经非常接近正态分布)。通常,对于每个特征,我们会将平均值减去并除以标准差。您可以在scikit-learn中检查一些方案。

5. 深度

这个不应该成为问题,但如果你的输入过于复杂,考虑增加神经网络的层数(现在它几乎肯定太薄了)。这将使其能够学习更抽象的特征并更好地转换输入空间。

过拟合

当网络对数据过度拟合时,您可以采用一些正则化技术(很难告诉哪些技术可能有帮助,您应该自行测试),其中一些包括:

  • 使用批标准化平滑学习空间的更高学习率。
  • 较少数量的神经元(网络学习的关系直观上必须更好地代表数据分布)。
  • 小批量大小也具有正则化效果。
  • Dropout,尽管很难确定好的丢弃率。最后再考虑它。此外,已知其与批标准化技术相冲突(虽然有结合它们的技术,请参见这里这里,您可以在网上找到更多信息)。
  • L1/L2 正则化,第二种方法更广泛应用(除非您有特定的知识表明 L1 可能表现更好)。
  • 数据增强 - 我建议先尝试这个,主要是出于好奇心。由于您的特征是连续的,您可能希望在批次之间添加一些随机噪声,该噪声生成自高斯分布。噪声应该很小,标准偏差约为1e-21e-3,您需要通过实验来测试这些值。
  • 早期停止 - 在验证集中经过N次训练轮数没有改进的情况下,结束训练。这是非常常见的技术,几乎每次都应该使用。记得保存验证集上最好的模型,并将patience(上述N)设置为适度大小的值(不要将它设置为1轮或更少的时间,神经网络可能会在5轮或更多轮之后容易提高性能)。
  • 此外还有大量其他技术可供选择。检查哪些技术直觉上比较合理并且您最喜欢的技术,然后测试其性能。


    嗨,Szymon,我已经遵循了你的大部分建议,并添加了一个新版本的模型,如果你有兴趣可以看一下。损失的行为更加稳定,测试集上的准确率达到了80-85%。我仍然需要阅读并实现动态学习率,我会尽快完成。我只是想知道你是否有任何最后的建议来对抗过拟合?训练集上的准确率倾向于1,但测试集上的准确率最高只有85%,即使使用了dropout层。增加神经元和层数似乎只会加剧问题。再次感谢! - Mustfled
    @ÉricPfleiderer 你可以尝试检查数据以去除异常值,还有其他一些方法,比如将训练集的大小略微缩小到测试集的大小、稍微降低dropout率,并尝试使用“提前停止”来帮助您的模型。 - theberzi
    @ÉricPfleiderer 添加了适当的部分。Federico S 提出的使用异常值的建议也是可行的选择(同时也包括提前停止)。另一方面,我会反对降低丢失率(如果我理解正确,这将使您更倾向于过度拟合)。除了像装袋这样的方法之外,减少训练集可能会带来更多的伤害,因为每个训练样本对网络都很珍贵。 - Szymon Maszke

    3
    我曾经训练过一个Siamese网络,发现如果我使用更高的学习率,训练损失会平稳下降(因为这就是神经网络正在学习的内容),但验证损失却出现了巨大的波动。在我使用较低的学习率(约为1e-05)时,从未出现过这种情况。我认为训练损失实际上是虚假的,因为最近的论文已经证明,大型神经网络(我的意思是更复杂的神经网络)可以完美地学习随机数据集,尽管它们在验证时表现极差。我附上了下面的论文供您参考,其中清楚地解释了与过度拟合相关的现象。因此,仅通过观察训练数据就不能得出整个模型的性能结论。虽然上述其他参数也很重要,但我想在这种情况下应该先开始调整学习率,然后再调整模型本身。论文链接:https://arxiv.org/pdf/1611.03530。如果我有错,请纠正我...

    0
    Symon提出的所有观点都很好,但还有另一个可能的原因:你是否对数据集进行了洗牌?如果没有,并且你的数据包含一些有序偏差,那么你的模型可能会调整自己到数据集的一个“端”,只在另一个“端”表现不佳。

    我认为是这样的。根据TensorFlow文档,fit()方法默认情况下会在每个epoch中对训练集进行洗牌。 - Mustfled

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接