Keras Predict/predict_on_batch 给出的答案与 Predict_step/__call__() 不同

问题描述 投票:0回答:1

根据我的理解,这四种方法:

predict
predict_on_batch
predict_step
,以及直接向前穿过模型(例如
model(x, training=False)
__call__()
) 都应该给出相同的结果,有些只是在处理批量数据与单个样本的方式上比其他更有效。

但是我实际上在我正在从事的图像超分辨率(升级)任务中得到了不同的结果:

for lowres, _ in val.take(1):
    # Get a randomly cropped region of the lowres image for upscaling
    lowres = tf.image.random_crop(lowres, (150, 150, 3))  # uint8
    
    # Need to add a dummy batch dimension for the predict step    
    model_inputs = tf.expand_dims(lowres, axis=0)  # (1, 150, 150, 3), uint8
    
    # And convert the uint8 image values to float32 for input to the model
    model_inputs = tf.cast(model_inputs, tf.float32)  # float32
    
    preds = model.predict_on_batch(model_inputs)
    min_val = tf.reduce_min(preds).numpy()
    max_val = tf.reduce_max(preds).numpy()
    print("Min value: ", min_val)
    print("Max value: ", max_val)
    
    preds = model.predict(model_inputs)
    min_val = tf.reduce_min(preds).numpy()
    max_val = tf.reduce_max(preds).numpy()
    print("Min value: ", min_val)
    print("Max value: ", max_val)
    
    preds = model.predict_step(model_inputs)
    min_val = tf.reduce_min(preds).numpy()
    max_val = tf.reduce_max(preds).numpy()
    print("Min value: ", min_val)
    print("Max value: ", max_val)
    
    preds = model(model_inputs, training=False)  # __call__()
    min_val = tf.reduce_min(preds).numpy()
    max_val = tf.reduce_max(preds).numpy()
    print("Min value: ", min_val)
    print("Max value: ", max_val)
    

打印:

Min value:  -6003.622
Max value:  5802.6826

Min value:  -6003.622
Max value:  5802.6826

Min value:  -53.7696
Max value:  315.1499

Min value:  -53.7696
Max value:  315.1499

predict_step
__call__()
都给出了由放大图像定义的“正确”答案,看起来是正确的。

如果有帮助的话,我很乐意分享有关模型的更多详细信息,但现在我想我应该就此保留,以免问题变得过于复杂。起初我想知道这些方法是否根据训练/推理模式有不同的结果,但我的模型不使用任何

BatchNorm
Dropout
层,所以这在这里应该不会产生影响。它完全由:
Conv2D
Add
tf.nn.depth_to_space
(像素随机播放)和
Rescaling
层组成。就是这样。它也不使用任何子类化或重写任何方法,仅使用
keras.Model(inputs, outputs)

你知道为什么这些预测方法会给出不同的答案吗?

编辑: 我已经能够创建一个最低限度可重现的示例,您可以在其中看到问题。请参阅:https://www.kaggle.com/code/quackaddict7/really-minimum-reproducible-example

我最初无法在最小的示例中重现该问题。我最终添加回数据集、批处理、数据增强、训练、模型文件保存/恢复,并最终发现问题是 GPU 与 CPU!所以我将所有这些都拿回来作为我的最小示例。如果您运行附带的笔记本,您将看到在 CPU 上,所有四种方法都给出相同的答案,并具有随机初始化的权重。但如果换成 P100 GPU,

predict
/
predict_on_batch
predict_step
/前向传递 (
__call__
) 不同。

所以我想此时我的问题是,为什么 CPU 与 GPU 结果不同?

tensorflow keras tensorflow2.0 keras-2
1个回答
0
投票

我已经测试了

tf.keras==2.12.0
中给定的示例代码,发现 API 中可能存在错误,并且仅在 GPU 上失败。在您的示例代码中,由于
relu
激活而发生不匹配。如果我们设置其他任何内容,即
selu
elu
甚至
leaky_relu
,它们将按预期工作。

def ResBlock(inputs):
    x = layers.Conv2D(64, 3, padding="same")(inputs)
    x = layers.Conv2D(64, 3, padding="same")(x)
    x = layers.Add()([inputs, x])
    return x

为了继续使用

relu
方法,暂时可以采用以下修复。

def relu(x):
    return keras.backend.maximum(x, 0)

def ResBlock(inputs):
    x = layers.Conv2D(64, 3, padding="same")(inputs)
    x = keras.layers.Lambda(relu, output_shape=lambda shape: shape)(x)
    x = layers.Conv2D(64, 3, padding="same")(x)
    x = layers.Add()([inputs, x])
    return x

这里是完整的代码供参考。

型号

def relu(x):
    return keras.backend.maximum(x, 0)

def ResBlock(inputs):
    x = layers.Conv2D(64, 3, padding="same")(inputs)
    x = keras.layers.Lambda(
        relu, output_shape=lambda shape: shape
    )(x)
    x = layers.Conv2D(64, 3, padding="same")(x)
    x = layers.Add()([inputs, x])
    return x

def Upsampling(inputs, factor=2, **kwargs):
    x = layers.Conv2D(
        64 * (factor ** 2), 3, padding="same", **kwargs
    )(inputs)
    x = tf.nn.depth_to_space(x, block_size=factor)
    x = layers.Conv2D(
        64 * (factor ** 2), 3, padding="same", **kwargs
    )(x)
    x = tf.nn.depth_to_space(x, block_size=factor)
    return x
def make_model(num_filters, num_of_residual_blocks):
    input_layer = layers.Input(shape=(None, None, 3))
    x = layers.Rescaling(scale=1.0 / 255)(input_layer)
    x = x_new = layers.Conv2D(num_filters, 3, padding="same")(x)

    for i in range(num_of_residual_blocks):
        x_new = ResBlock(x_new)

    x_new = layers.Conv2D(num_filters, 3, padding="same")(x_new)
    x = layers.Add()([x, x_new])
    x = Upsampling(x)
    x = layers.Conv2D(3, 3, padding="same")(x)
    
    output_layer = layers.Rescaling(scale=255)(x)
    return keras.Model(input_layer, output_layer)

推理

lowres = tf.random.uniform(
    shape=(150, 150, 3), 
    minval=0, 
    maxval=256, 
    dtype='float32'
)
model_inputs = tf.expand_dims(lowres, axis=0)

predict_out = model.predict(model_inputs)
predict_on_batch_out = model.predict_on_batch(model_inputs)
predict_call_out = model(model_inputs, training=False).numpy()
predict_step_out = model.predict_step(model_inputs).numpy()
print(
    predict_out.shape, 
    predict_on_batch_out.shape, 
    predict_call_out.shape, 
    predict_step_out.shape
)
1/1 [==============================] - 1s 1s/step
(1, 600, 600, 3) (1, 600, 600, 3) (1, 600, 600, 3) (1, 600, 600, 3)

日志检查

# OK
np.testing.assert_allclose(
    predict_out,
    predict_on_batch_out,
    1e-5, 1e-5
)

# OK
np.testing.assert_allclose(
    predict_on_batch_out,
    predict_call_out,
    1e-5, 1e-5
)

# OK
np.testing.assert_allclose(
    predict_call_out,
    predict_step_out,
    1e-5, 1e-5
)
© www.soinside.com 2019 - 2024. All rights reserved.