Python Keras'fit_generator'的验证精度较低,但不适用于'fit'`

Python Keras'fit_generator'的验证精度较低,但不适用于'fit'`,python,machine-learning,keras,deep-learning,Python,Machine Learning,Keras,Deep Learning,我有一个二元分类问题的数据集,其中两个类的表示是相等的。由于数据集不适合内存(400万个数据点),我将其存储为HDF5文件,通过fit\u generator以增量方式读取并馈送到简单的Keras模型中。问题是我使用fit\u generator的验证精度很低,而如果我只使用fit,一切都正常。我确实提到了数据集不适合内存,但出于调试目的,在本文的其余部分,我只使用了100k的4M数据点 由于目标是对整个数据集进行10倍的分层CV,因此我手动将数据集索引划分为用于训练、验证和评估集的索引。我使用

我有一个二元分类问题的数据集,其中两个类的表示是相等的。由于数据集不适合内存(400万个数据点),我将其存储为HDF5文件,通过
fit\u generator
以增量方式读取并馈送到简单的Keras模型中。问题是我使用
fit\u generator
的验证精度很低,而如果我只使用
fit
,一切都正常。我确实提到了数据集不适合内存,但出于调试目的,在本文的其余部分,我只使用了100k的4M数据点

由于目标是对整个数据集进行10倍的分层CV,因此我手动将数据集索引划分为用于训练、验证和评估集的索引。我使用生成函数调用
fit_generator
,生成成批的训练(或验证)样本和标签,涵盖HDF5文件第一季度的指定索引,然后是第二季度的指定索引,以此类推

我知道
fit\u generator
的验证部分使用
test\u on\u batch
在发动机罩下,以及
evaluation\u generator
。我还尝试了一种解决方案,使用
train\u on\u batch
test\u on\u batch
方法,但结果相同:如果数据集一次加载到内存中,那么
fit\u生成器的验证精度较低,而
fit
的验证精度较高。在这两种情况下,模型是相同的(
fit
vs
fit\u生成器

数据集和模型 我的调试数据集有~100k个样本和标签(类0中~50k,类1中~50k)。对75%的数据进行培训和验证(我有大约60k个样本用于培训,15k用于验证)。这两个类在培训和验证样本中平均分布

以下是我使用的非常简单的模型:

input\u layer=input(shape=(2581),dtype='float32')
隐藏的_层=密集(512,activation='relu',input_shape=(2581,1))(input_层)
输出层=密集(1,激活='sigmoid')(隐藏层)
模型=模型(输入=[输入层],输出=[输出层])
compile(优化器='rmsprop',loss='binary\u crossentropy',metrics=['accurity'])
fit
非常适合。。。 由于这个小数据集很容易放入内存,下面是我如何使用上面创建的模型直接使用
fit
train_idx
是训练集的索引,
valid_idx
是验证集的索引:

model.fit(特征[train_idx],标签[train_idx],
批次大小=128,时代=5,
洗牌=正确,
验证\u数据=(特征[valid\u idx],标签[valid\u idx]))
这是我用
fit
得到的
val\u acc

58847/58847 [==============================] - 4s 70us/step - loss: 0.4075 - acc: 0.8334 - val_loss: 0.3259 - val_acc: 0.8828
Epoch 2/5
58847/58847 [==============================] - 4s 61us/step - loss: 0.2757 - acc: 0.8960 - val_loss: 0.2686 - val_acc: 0.9039
Epoch 3/5
58847/58847 [==============================] - 4s 61us/step - loss: 0.2219 - acc: 0.9212 - val_loss: 0.2162 - val_acc: 0.9227
Epoch 4/5
58847/58847 [==============================] - 4s 61us/step - loss: 0.1855 - acc: 0.9353 - val_loss: 0.1992 - val_acc: 0.9314
Epoch 5/5
58847/58847 [==============================] - 4s 60us/step - loss: 0.1583 - acc: 0.9456 - val_loss: 0.1763 - val_acc: 0.9390
... 但是
fit\u生成器
没有 我希望使用
fit\u generator
也能得到同样的结果:

model.fit_生成器(生成_数据(hdf5_文件、序列idx、批量大小),
每历元步数=len(列车idx)//批量大小,
纪元=5,
shuffle=False,
验证\数据=生成\数据(hdf5 \文件、有效\ idx、批次\大小),
验证\u步骤=len(有效\u idx)//批量大小)
对于每个时代,我得到的都是相同的
val_acc
,就好像只有一个类是不断预测的:

460/460 [==============================] - 8s 17ms/step - loss: 0.3230 - acc: 0.9447 - val_loss: 6.9277 - val_acc: 0.4941
Epoch 2/5
460/460 [==============================] - 6s 14ms/step - loss: 0.9536 - acc: 0.8627 - val_loss: 7.1385 - val_acc: 0.4941
Epoch 3/5
460/460 [==============================] - 6s 14ms/step - loss: 0.8764 - acc: 0.8839 - val_loss: 7.0521 - val_acc: 0.4941
Epoch 4/5
460/460 [==============================] - 6s 13ms/step - loss: 0.9005 - acc: 0.8885 - val_loss: 7.0459 - val_acc: 0.4941
Epoch 5/5
460/460 [==============================] - 6s 14ms/step - loss: 0.9259 - acc: 0.8907 - val_loss: 7.0880 - val_acc: 0.4941
请注意:

  • generate_data
    生成器用于培训和验证
  • 使用
    shuffle=False
    调用
    fit\u生成器
    ,因为它是处理洗牌的生成器(在任何情况下,指定
    shuffle=True
    不会更改
    val\u acc
生成器方法 拼图的最后一块:发电机。此处,
n\u parts
是HDF5文件拆分为要加载的部分数。然后,我只保留HDF5文件当前加载的
部分中实际属于所选
索引的行。保留的特征(
partial_features
)和标签(
partial_labels
)实际上是HDF5文件中索引
partial_index
处的行

def生成数据(hdf5文件、索引、批量大小、n\u部分=4):
部分=0
将h5py.File(hdf5_文件,'r')作为h5:
dset=h5.get('features')
零件尺寸=dset.shape[0]//n零件
尽管如此:
将h5py.File(hdf5_文件,'r')作为h5:
dset=h5.get('features')
dset_开始=零件*零件尺寸
dset_end=(零件+1)*零件尺寸(如果零件如果dset_start我最终解决了这个问题,我将发布我的发现供将来参考,以防其他人偶然发现类似的问题:生成器不是问题所在,但HDF5文件中样本的顺序是

该模型用于二元分类问题,其中数据集中的标签为0或1。问题在于HDF5文件最初包含所有标记为1的样本,然后是所有标记为0的样本(其中正样本和负样本的数量大致相同)。这意味着,当生成器函数将HDF5文件拆分为4个部分时,前两部分仅包含正样本,后两部分仅包含负样本

如果样本以随机顺序写入HDF5文件,使得文件的任何连续部分包含大致相同数量的正样本和负样本,则可以修复此问题。通过这种方式,模型中的正负数据大致相等