我发现在以下两个功能之间存在明显的不一致性:
keras.preprocessing
中的图像调整功能,这些功能是 PIL 函数的包装器。- TensorFlow 的
tf.image
中的图像调整功能。
我正在使用 Keras(实际上是 tf.keras
)训练计算机视觉任务的深度学习模型。然后,我使用 TF Serving 提供服务,它要求我以编码字节字符串的形式发送图像到模型,这些图像会在通过模型图之前使用 tf.image.decode_png
进行解码。
问题出现在我调整图像大小时。使用双线性插值(或其他任何方法)与使用 tf.image
相比,使用 PIL 给出的结果不同,甚至因此导致模型的分类结果发生变化。
下面的代码提供了一个可重现的示例。
import numpy as np
from PIL import Image
from keras.preprocessing.image import load_img, img_to_array
import tensorflow as tf
# Generate an 'image' with numpy, save as png
np.random.seed(42)
image = np.random.randint(0, 255, size=(256, 256, 3)).astype(np.uint8)
Image.fromarray(image).convert("RGB").save('my_image.png')
现在,我们将以两种方式加载图像。首先使用Keras中的PIL包装器,就像在模型训练期间一样,然后将其编码为二进制字符串,并使用TensorFlow函数进行解码,就像在我的模型服务器中一样。
# Using Keras PIL wrappers
keras_image = img_to_array(load_img('./my_image.png'))
# Using TF functionalities
with tf.Session() as sess:
with open('./my_image.png', 'rb') as f:
tf_image_ = tf.image.decode_png(f.read())
tf_image = sess.run(tf_image_)
目前为止还不错,因为这两个图像完全相同(除了 dtype 外,因为 Keras 已经将图像转换为 float32):
# Assert equality
np.array_equal(keras_image, tf_image)
> True
然而,使用调整大小重复此代码会产生不同的结果:
# Using Keras PIL wrappers, with resizing
keras_image_rs = img_to_array(load_img('./my_image.png',
target_size=(224, 224),
interpolation='bilinear'))
# Using TF functionalities, with resizing
with tf.Session() as sess:
with open('./my_image.png', 'rb') as f:
tf_image_ = tf.image.decode_png(f.read())
# Add and remove dimension
# As tf.image.resize_* requires a batch dimension
tf_image_ = tf.expand_dims(tf_image_, 0)
tf_image_ = tf.image.resize_bilinear(tf_image_,
[224, 224],
align_corners=True)
tf_image_ = tf.squeeze(tf_image_, axis=[0])
tf_image_rs = sess.run(tf_image_)
# Assert equality
np.array_equal(keras_image_rs, tf_image_rs)
> False
这两幅图像之间的平均绝对差不可忽略:
np.mean(np.abs(keras_image_rs - tf_image_rs))
7.982703
我尝试了align_corners
参数,并尝试了其他可用的插值方法。然而,没有一种方法能给出与PIL调整图像大小时相同的输出结果。这很让人困扰,因为它导致了训练结果和测试结果之间的偏差。是否有人知道是什么原因导致了这种行为,或者如何解决这个问题呢?