Python model.evaluate()由生成器馈送时,根据批次大小更改结果

Python model.evaluate()由生成器馈送时,根据批次大小更改结果,python,tensorflow,keras,deep-learning,Python,Tensorflow,Keras,Deep Learning,在colab中使用默认的tensorflow和keras版本(打印tensorflow 2.2.0-rc2、keras 2.3.0-tf) 我犯了一个非常奇怪的错误。基本上,model.evaluate()的结果取决于我使用的批大小,并且在我洗牌数据后它们会发生变化。这毫无意义。我已经能够在一个最小的工作示例中重现这一点。在我的完整程序中(在3D中使用更大的数据集),变化更为显著。我不知道这是否取决于批处理规范化。。。但是当我预测的时候,我希望它是固定的!我的完整程序是进行多类分割,我的最小示例

在colab中使用默认的tensorflow和keras版本(打印tensorflow 2.2.0-rc2、keras 2.3.0-tf)

我犯了一个非常奇怪的错误。基本上,model.evaluate()的结果取决于我使用的批大小,并且在我洗牌数据后它们会发生变化。这毫无意义。我已经能够在一个最小的工作示例中重现这一点。在我的完整程序中(在3D中使用更大的数据集),变化更为显著。我不知道这是否取决于批处理规范化。。。但是当我预测的时候,我希望它是固定的!我的完整程序是进行多类分割,我的最小示例是在随机位置上有一个白色正方形的黑色图像,带有一些小噪声,并尝试从中分割出相同的白色正方形。 我使用keras序列作为生成器向模型提供数据,我想这可能是相关的,因为我没有看到直接评估数据时的行为。 下面是代码及其输出:

#environment setup
%tensorflow_version 2.x
from tensorflow.keras import backend as K
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input,Conv2D, Activation, BatchNormalization 
from tensorflow.keras import metrics

#set up a toy model
K.set_image_data_format("channels_last")
inputL = Input([64,64,1])
l1 = Conv2D(4,[3,3],padding='same')(inputL)
l1N = BatchNormalization(axis=-1,momentum=0.9) (l1)
l2 = Activation('relu') (l1N)
l3 = Conv2D(32,[3,3],padding='same')(l2)
l3N = BatchNormalization(axis=-1,momentum=0.9) (l3)
l4 = Activation('relu') (l3N)
l5 = Conv2D(1,[1,1],padding='same',dtype='float32')(l4)
l6 = Activation('sigmoid') (l5)
model = Model(inputs=inputL,outputs=l6)
model.compile(optimizer='sgd',loss='mse',metrics='accuracy' )

#Create random images
import numpy as np
import random
X_train = np.zeros([96,64,64,1])
for imIdx in range(96):
  centPoin = random.randrange(7,50)
  X_train[imIdx,centPoin-5:centPoin+5,centPoin-5:centPoin+5,0]=1

X_val = X_train[:32,:,:,:]
X_train = X_train[32:,:,:,:]
Y_train = X_train.copy()
X_train = np.random.normal(0.,0.1,size=X_train.shape)+X_train
for imIdx in range(64):
  X_train[imIdx,:,:,:] = X_train[imIdx,:,:,:]+np.random.normal(0,0.2,size=1)

from tensorflow.keras.utils import Sequence
import random
import tensorflow as tf

#setup the data generator
class dataGen (Sequence):
  def __init__ (self,x_set,y_set,batch_size):
    self.x, self.y = x_set, y_set
    self.batch_size = batch_size
    nSamples = self.x.shape[0]
    patList = np.array(range(nSamples),dtype='int16')
    patList = patList.reshape(nSamples,1)
    np.random.shuffle(patList)
    self.patList = patList

  def __len__ (self):
    return round(self.patList.shape[0] / self.batch_size)

  def __getitem__ (self, idx):
    patStart = idx
    batchS = self.batch_size
    listLen = self.patList.shape[0]
    Xout = np.zeros((batchS,64,64,1))
    Yout = np.zeros((batchS,64,64,1))    
    for patIdx in range(batchS):
       curPat = (patStart+patIdx) % listLen
       patInd = self.patList[curPat]
       Xout[patIdx,:,:] = self.x[patInd,:,:,:]
       Yout[patIdx,:,:] = self.y[patInd,:,:,:]
    return Xout, Yout

  def on_epoch_end(self):
    np.random.shuffle(self.patList)

  def setBatchSize(self,batchS):
    self.batch_size = batchS

#load the data in the generator
trainGen = dataGen(X_train,Y_train,16)
valGen = dataGen(X_val,X_val,16)

# train the model for two epochs, so that the loss is bad 
trainSteps = len(trainGen)
model.fit(trainGen,steps_per_epoch=trainSteps,epochs=32,validation_data=valGen,validation_steps=len(valGen))

trainGen.setBatchSize(4)
model.evaluate(trainGen)
[0.16259156167507172, 0.9870567321777344]

trainGen.setBatchSize(16)
model.evaluate(trainGen)
[0.17035068571567535, 0.9617958068847656]

trainGen.on_epoch_end()
trainGen.setBatchSize(16)
model.evaluate(trainGen)
[0.16663715243339539, 0.9710426330566406]
如果我做了
model.evaluate(Xtrain,Ytrain,batch_size=16)
,结果与批次大小无关。 如果我训练模型直到收敛,损失达到0.05,同样的事情仍然会发生。精度从一个评估到另一个评估在0.95到0.99之间波动。 为什么会发生这种情况?
我希望预测非常简单,我错了吗?

您在
\uuu getitem\uuu
函数中犯了一个小错误

curPat = (patStart+patIdx)
应改为

curPat = (patStart*batchS+patIdx)
patStart
等于当前批号
idx
。如果数据集包含64个样本,且批大小设置为16,则
idx
的可能值将为0、1、2和3


另一方面,
curPat
指的是样本编号无序列表中当前样本编号的索引<因此,code>curPat应该能够接受0到63之间的所有值。在代码中,情况并非如此。通过进行上述更改,此问题已得到修复。

您在
\uu getitem\uu
函数中犯了一个小错误

curPat = (patStart+patIdx)
应改为

curPat = (patStart*batchS+patIdx)
patStart
等于当前批号
idx
。如果数据集包含64个样本,且批大小设置为16,则
idx
的可能值将为0、1、2和3


另一方面,
curPat
指的是样本编号无序列表中当前样本编号的索引<因此,code>curPat应该能够接受0到63之间的所有值。在代码中,情况并非如此。通过进行上述更改,此问题已得到解决。

Gosh darn it。在这上面浪费了3天,包括从头开始写一个最低限度的工作示例。。。我没有注意到这样一个小错误。如果你原谅我,我要躲起来。该死的。在这上面浪费了3天,包括从头开始写一个最低限度的工作示例。。。我没有注意到这样一个小错误。对不起,我要躲起来了。