Python model.evaluate()由生成器馈送时,根据批次大小更改结果
在colab中使用默认的tensorflow和keras版本(打印tensorflow 2.2.0-rc2、keras 2.3.0-tf) 我犯了一个非常奇怪的错误。基本上,model.evaluate()的结果取决于我使用的批大小,并且在我洗牌数据后它们会发生变化。这毫无意义。我已经能够在一个最小的工作示例中重现这一点。在我的完整程序中(在3D中使用更大的数据集),变化更为显著。我不知道这是否取决于批处理规范化。。。但是当我预测的时候,我希望它是固定的!我的完整程序是进行多类分割,我的最小示例是在随机位置上有一个白色正方形的黑色图像,带有一些小噪声,并尝试从中分割出相同的白色正方形。 我使用keras序列作为生成器向模型提供数据,我想这可能是相关的,因为我没有看到直接评估数据时的行为。 下面是代码及其输出: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中使用更大的数据集),变化更为显著。我不知道这是否取决于批处理规范化。。。但是当我预测的时候,我希望它是固定的!我的完整程序是进行多类分割,我的最小示例
#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天,包括从头开始写一个最低限度的工作示例。。。我没有注意到这样一个小错误。对不起,我要躲起来了。