为什么即使有大量数据,我的 CNN 验证损失也会立即增加?

数据挖掘 时间序列 美国有线电视新闻网 交叉验证 图像识别 图像预处理
2022-02-14 15:14:53

问题

我一直在研究回归 CNN 实现来预测时间序列数据,并遇到了一个问题,即我的验证损失和训练损失在训练期间立即出现分歧,如下所示:

在此处输入图像描述

在此处输入图像描述

通常,当训练期间验证损失增加时,过度拟合是罪魁祸首,但在这种情况下,验证损失最初似乎并没有减少,这很奇怪。我已经尝试用正常的过度拟合修复来处理这个问题,即增加 dropout 和增加数据量,但无济于事。

作为参考,上面的模型是用大约训练的。60,000 个样本,如混淆矩阵所示(稍后会详细介绍),但我还训练了一个包含超过 250,000 个样本的模型并得到了相同的结果: 在此处输入图像描述

在此处输入图像描述

出于这个原因,在我看来,过度拟合不太可能是唯一的问题,因为我认为将数据增加这么多会产生一些实际影响。

细节

概述

该项目试图通过从特征数据创建图像来使用 CNN 执行时间序列预测。数据有 28 个独立的特征,通过使用 28 天的窗口捕获这些特征(使每行像素为该特征时间序列的 28 天)创建 28x28 图像。这些特征本身是相关的,但不是必须的(例如,有些在 0-1 左右,有些在 100 左右,有些包括负数)

每个图像都有一个标签,它是一个介于 -1 和 1 之间的值,并且标签的符号特别重要,所以这个问题也可以通过将符号作为一个类来框定为分类问题(这就是为什么混淆矩阵是作为度量创建的)。出于这个原因,我还注意到数据集是平衡的,几乎正好有 50% 的正/负。我也使用分类测试了这个项目,但问题仍然存在。

训练图像

以下是我正在生成的图像的一些示例,以及标准化前后(不同)图像中的值示例。

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

预处理

每个特征时间序列在每个图像的范围内使用以下代码在 0,1 之间进行归一化。我在这里的第一个假设是,由于我的特征数据具有趋势,因此最好在图像内进行归一化,而不是在数据集的整个长度上进行归一化(使后面的图像具有更高的值)。我的第二个假设是不可能一次对整个图像阵列进行归一化(例如,对于 MNIST 数据除以 255),因为每一行像素的比例都不同。

def normalizeImages(dataset):
imageList = dataset['trainingImages'].values
for i in range(len(imageList)):
    image = imageList[i]
    for j in range(len(image)):
        row = image[j].reshape(len(image[j]),1)
        minmaxScaler = MinMaxScaler(feature_range=(0, 1))  # 0,1 seems to be standard for image data
        minmaxScaler.fit(row)
        row = minmaxScaler.transform(row)
        image[j] = row.squeeze()
    imageList[i] = image
dataset['trainingImages'] = imageList
return dataset

值得注意的是,在将所有图像拆分为验证/测试集然后打乱之前,这以完全相同的方式应用于所有图像。

除了对图像进行归一化之外,特征本身还使用聚类在图像中排列,以在 Y 轴上将相似的系列组合在一起以增加局部性。

模型

所使用的模型基于 MNIST 分类的一些示例:

 model = Sequential([
    Conv2D(32, (3, 3), input_shape=(inputDim, inputDim, 1), activation='relu'),
    Conv2D(64, (3, 3), activation='relu'),
    MaxPooling2D(pool_size=(4, 4)),
    Flatten(),
    Dense(128, activation='relu'),
    Dropout(0.25),
    Dense(64, activation='relu'),
    Dropout(0.5),
    Dense(1,activation='linear')
])
model.compile(loss='mse', optimizer=keras.optimizers.Adadelta(), metrics=['mse',tf.keras.metrics.RootMeanSquaredError()])

我已经用 100-3000 的时期以及 32-3500 的批量大小训练了这个模型,没有不同的结果。

我还试过什么

以下是我尝试过的其他一些事情,但还有更多,请随时询问任何其他细节。

  • 在整个特征时间序列上规范化图像,而不仅仅是在图像内
  • 使用时间序列上的第一个差异来消除趋势,然后对整个数据集/图像内进行归一化
  • 在 MNIST 数据上进行训练,模型(针对分类进行了修改)将其学习到 98% 的准确率。
  • 将优化器更改为 ADAM
  • 改变 ADADELTA 的学习率:从 0.001 到 0.0001 完全阻止学习
  • 按批次而不是每个时期输出损失,看看我是否只是在前几个时期过度拟合,但看起来不像。

我的理论

  • 我的图像没有很好地归一化,因此即使模型能够学习具有 250,000 个样本的训练集,它们也没有传达要学习的数据?
  • 测试集图像在某种程度上与训练图像不同,尽管它们以完全相同的方式进行归一化。
  • 我仍然以某种方式过度拟合,需要更多数据/辍学/其他?

结论性想法

我绝不是该领域的专家,因此很可能我对标准化和图像处理做出了某种假设/错误,从而阻止了我的模型实际从我的图像中学习。

我已尝试尽可能多地包含我认为与我的问题相关的信息,但我很乐意根据要求提供任何其他信息。

提前感谢您提供的任何建议和知识,以帮助我解决此问题!

编辑

回应 etiennedm

我将密集的 64 层更改为以下内容:

Dense(64, activation='tanh'),

但不幸的是,问题仍然存在: 在此处输入图像描述

我之前曾尝试在没有任何 dropout 的情况下运行,但这并没有什么不同,因为模型似乎可以很好地学习训练数据,我认为删除 dropout 只会使其过拟合更快? 在此处输入图像描述

这是 28 个特征时间序列在其完整数据上而不是仅在 28 天图像内标准化时的结果。我使用以下代码来执行此操作:

minmaxScaler = MinMaxScaler(feature_range=(0,1)) 
    minmaxScaler.fit(trainingSample) 
    featureData = minmaxScaler.transform(featureData)

可能值得注意的是,缩放器仅适用于训练数据,然后应用于整个数据集以消除训练/测试集之间的数据泄漏。结果似乎仍然大致相同:

在此处输入图像描述

1个回答

relu如果您希望网络同时产生正值和负值,则在 n-1 层使用函数可能过于受限。我不确定您的图像预处理,但我会首先尝试将(至少)最后一个激活函数relu更改为leaky relutanh(产生正值和负值的激活函数)。

注意:您也可以删除 dropout,直到您的模型真正学习为止。一旦它正在学习,添加 dropout 以进行正则化。

编辑

您可以尝试规范化您的 28 个特征,因为特征通常是规范化的。这意味着在您的训练集中,获取每个特征的所有值并创建一个缩放器,然后将其应用于您的测试图像。