python - model.evaluate() 根据批量大小改变结果,当由生成器提供时
问题描述
在 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。为什么会发生这种情况?我希望预测非常容易,我错了吗?
解决方案
__getitem__
你在函数内部犯了一个小错误。
curPat = (patStart+patIdx)
应该改为
curPat = (patStart*batchS+patIdx)
patStart
等于idx
,当前批号。如果您的数据集包含 64 个样本并且批量大小设置为 16,则可能的值为idx
0、1、2 和 3。
curPat
另一方面是指当前样本编号在样本编号打乱列表中的索引。curPat
因此应该能够采用从 0 到 63 的所有值。在您的代码中,情况并非如此。通过进行上述更改,此问题已得到解决。
推荐阅读
- javascript - 第一个输入/文本区域的动态焦点在 Firefox 中不起作用
- git - 如果没有要提交的内容,Git 提交和推送将失败
- reflection - 如何系统地填充沙盒程序的白名单?
- reactjs - 在 Draft-js NPM 中突出显示单词
- python - 没有 Numpy 的 Python 中的矩阵乘法
- sql - 相似查询的不同输出
- hadoop - 纱线容量调度程序中的内部队列抢占
- angular - 在angular7中将HTML页面转换为PDF
- android - 如何在同步 Gradle 时修复 android studio 中的“无法调用方法 buildTypes()”错误?
- c# - 在 ListView 中显示嵌套的 JSON 数据(Xamarin.Forms,C#)