Keras调整图层大小不接受不同大小的图像作为输入

q3aa0525  于 2022-12-27  发布在  其他
关注(0)|答案(3)|浏览(232)

我的理解是Keras的Resizing图层可以接受不同大小的输入图像,但我一直无法让它工作。

import tensorflow as tf
import tensorflow_datasets as tfds
from tensorflow.keras import layers
dsTrain = tfds.load('CatsVsDogs', split='train[:10%]', as_supervised=True).batch(128).prefetch(tf.data.AUTOTUNE)

model = tf.keras.models.Sequential()
model.add(layers.InputLayer(input_shape=(None, None, 3)))
model.add(layers.Resizing(200, 200, crop_to_aspect_ratio=True)) # PROBLEM CAUSED HERE. See https://www.tensorflow.org/tutorials/images/data_augmentation
model.add(layers.Rescaling(1./255))
model.add(layers.Conv2D(8, (3, 3), activation='relu', kernel_initializer='random_normal', padding='same'))
model.add(layers.MaxPooling2D((3,3)))
model.add(layers.Dense(2))

model.compile(optimizer='rmsprop',loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),metrics=['accuracy'])
model.fit(dsTrain,epochs=10,batch_size=128) 

# Result:
# Cannot batch tensors with different shapes in component 0. First element had shape [262,350,3] and element 1 had shape [409,336,3].

有人知道这个层的设置吗?显然我可以在将图像传递到模型之前调整数据集中图像的大小(这是我以前一直在做的),但我很想了解这个层的正确用法。

编辑1

这是我的尝试(在我的回复中作为笔记本链接),以制作一个将预处理集成到模型中的版本

import tensorflow as tf
import tensorflow_datasets as tfds
from tensorflow import keras
from tensorflow.keras import layers
dsTrain = tfds.load('CatsVsDogs', split='train[:10%]', as_supervised=True).batch(128).prefetch(tf.data.AUTOTUNE)
IMAGE_SIZE = 100

# https://keras.io/guides/preprocessing_layers/
# https://blog.tensorflow.org/2021/11/an-introduction-to-keras-preprocessing.html 

preprocessing_model = tf.keras.models.Sequential()
preprocessing_model.add(layers.InputLayer(input_shape=(None, None, 3)))
preprocessing_model.add(layers.Resizing(IMAGE_SIZE, IMAGE_SIZE, crop_to_aspect_ratio=True))
preprocessing_model.add(layers.Rescaling(1./255))

training_model = tf.keras.models.Sequential()
training_model.add(layers.InputLayer(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3)))
training_model.add(layers.Conv2D(8, (3, 3), activation='relu', kernel_initializer='random_normal', padding='same'))
training_model.add(layers.MaxPooling2D((5,5)))
training_model.add(layers.Flatten())
training_model.add(layers.Dense(2))

inputs = keras.Input(shape=preprocessing_model.input_shape)
outputs = training_model(preprocessing_model(inputs))
model = tf.keras.Model(inputs, outputs)

model.summary()
model.compile(optimizer='rmsprop',loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),metrics=['accuracy'])
model.fit(dsTrain,epochs=3,batch_size=128)

这会产生以下错误:

WARNING:tensorflow:Model was constructed with shape (None, None, None, 3) for input KerasTensor(type_spec=TensorSpec(shape=(None, None, None, 3), dtype=tf.float32, name='input_117'), name='input_117', description="created by layer 'input_117'"), but it was called on an input with incompatible shape (None, None, None, None, 3).
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-82-6e99f023839a> in <module>
     22 
     23 inputs = keras.Input(shape=preprocessing_model.input_shape)
---> 24 outputs = training_model(preprocessing_model(inputs))
     25 model = tf.keras.Model(inputs, outputs)
     26 

1 frames
/usr/local/lib/python3.7/dist-packages/keras/preprocessing/image.py in smart_resize(x, size, interpolation)
    110     if img.shape.rank < 3 or img.shape.rank > 4:
    111       raise ValueError(
--> 112           'Expected an image array with shape `(height, width, channels)`, '
    113           'or `(batch_size, height, width, channels)`, but '
    114           f'got input with incorrect rank, of shape {img.shape}.')

ValueError: Exception encountered when calling layer "resizing_72" (type Resizing).

Expected an image array with shape `(height, width, channels)`, or `(batch_size, height, width, channels)`, but got input with incorrect rank, of shape (None, None, None, None, 3).

Call arguments received:
  • inputs=tf.Tensor(shape=(None, None, None, None, 3), dtype=float32)

编辑2

我现在已经把预处理层作为模型的一部分,但是它们仍然不接受不同大小的图像。下面的程序失败了,并指出了错误,但是如果我把所有的图像调整到统一的尺寸,它就可以工作。Notebook version

import tensorflow as tf
import tensorflow_datasets as tfds
from tensorflow import keras
from tensorflow.keras import layers
IMAGE_SIZE = 100
dsTrain = tfds.load('CatsVsDogs', split='train[:10%]', as_supervised=True).batch(128).prefetch(tf.data.AUTOTUNE)

preprocessing_model = tf.keras.models.Sequential()
preprocessing_model.add(layers.InputLayer(input_shape=(None, None, 3)))
preprocessing_model.add(layers.Resizing(IMAGE_SIZE, IMAGE_SIZE, crop_to_aspect_ratio=True))
preprocessing_model.add(layers.Rescaling(1./255))

training_model = tf.keras.models.Sequential()
training_model.add(layers.InputLayer(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3)))
training_model.add(layers.Conv2D(8, (3, 3), activation='relu', kernel_initializer='random_normal', padding='same'))
training_model.add(layers.MaxPooling2D((5,5)))
training_model.add(layers.Flatten())
training_model.add(layers.Dense(2))

inputs = keras.Input(shape=preprocessing_model.input_shape[1:]) # [1:] Adds a dimension for a batching
outputs = training_model(preprocessing_model(inputs))
model = tf.keras.Model(inputs, outputs)

model.summary()
model.compile(optimizer='rmsprop',loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),metrics=['accuracy'])
model.fit(dsTrain,epochs=3,batch_size=128) 

# error from model.fit: Cannot batch tensors with different shapes in component 0. First element had shape [262,350,3] and element 1 had shape [409,336,3]

最终编辑+解决方案

好吧,Souresh Mirzaei非常耐心地试图向我解释的信息最终在我离开的时候被我理解了,我得到了我的解决方案。对于任何关注这个问题的人来说,答案是,虽然训练数据不能有不同的大小(我猜是因为它破坏了批处理)一个在开始时有一个调整大小层的训练模型将接受不同大小的图像作为输入

import tensorflow as tf
import tensorflow_datasets as tfds
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
import matplotlib.pyplot as plt
import random

IMAGE_SIZE = 100
dsTrain = tfds.load('CatsVsDogs', split='train[:10%]', as_supervised=True)
dsPredict = dsTrain.take(77)
def preprocess(image, label):
    image = tf.image.resize(image, (100,100))
    return image, label
dsTrain = dsTrain.map(preprocess).shuffle(1024).batch(128).prefetch(tf.data.AUTOTUNE)

preprocessing_model = tf.keras.models.Sequential()
preprocessing_model.add(layers.Resizing(IMAGE_SIZE, IMAGE_SIZE, crop_to_aspect_ratio=True, input_shape=(None, None, 3))) # redundant
preprocessing_model.add(layers.Rescaling(1./255))

training_model = tf.keras.models.Sequential()
training_model.add(layers.Conv2D(8, (3, 3), activation='relu', kernel_initializer='random_normal', padding='same'))
training_model.add(layers.MaxPooling2D((5,5)))
training_model.add(layers.Flatten())
training_model.add(layers.Dense(2))

inputs = keras.Input(shape=(None, None, None, 3)) # [1:] Remove the dimension added by batching
inputs = keras.Input(shape=preprocessing_model.input_shape[1:]) # [1:] Remove the dimension added by batching
outputs = training_model(preprocessing_model(inputs))
model = tf.keras.Model(inputs, outputs)

model.summary()
model.compile(optimizer='rmsprop',loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),metrics=['accuracy'])
model.fit(dsTrain,epochs=1,batch_size=128) 

# Test an image
for image, labelId in dsPredict.skip(random.randint(0, 50)).take(1):
    dsImage = tf.keras.preprocessing.image.img_to_array(image)
    dsImage = np.expand_dims(dsImage, axis = 0)
    prediction = model.predict(dsImage)
    plt.imshow(image)
    plt.title(f"{prediction[0][0]}, {prediction[0][1]}")
    plt.show()

notebook of the above

yhived7q

yhived7q1#

预处理数据集(调整大小、重新缩放、增加等),然后再将其输入模型,而不是调整大小层,因为模型无法在未定义的要素上初始化,然后使用它来训练和预测各种大小的图像,尽管您已在模型中实现了调整大小层。
因此定义函数,然后Map到数据集,如下所示

dsTrain = tfds.load('CatsVsDogs', split='train[:10%]', as_supervised=True)

def preprocess(image, label):
    image = tf.image.resize(image, (200,200))
    return image, label

dsTrain = dsTrain.map(preprocess).shuffle(1024).batch(128).prefetch(tf.data.AUTOTUNE)

请注意,您没有在卷积层之后立即实现Flatten层或GlobalAvgPool层,因此请在Dense层之前实现Flatten层,除非它会引发错误。
注意,如果你想创建一系列的预处理操作,你应该先定义它,如下所示:

dsTrain = tfds.load('CatsVsDogs', split='train[:10%]', as_supervised=True)

resize_rescale = tf.keras.Sequential()
resize_rescale.add(layers.Resizing(200, 200, crop_to_aspect_ratio=True))
resize_rescale.add(layers.Rescaling(1./255))

dsTrain = dsTrain.map(lambda x, y : (resize_rescale(x), y)).batch(128).prefetch(tf.data.AUTOTUNE)

model = tf.keras.models.Sequential()
model.add(layers.Conv2D(8, (3, 3), activation='relu', kernel_initializer='random_normal', padding='same'))
model.add(layers.MaxPooling2D((3,3)))
model.add(layers.Flatten())
model.add(layers.Dense(2))
mnowg1ta

mnowg1ta2#

正确的编辑代码如下所示:

def prcs(imag, label):
    imag = tf.image.resize(imag, (100,100))
    imag = tf.reshape(imag, (100,100,3))
    return imag, label

dsTrain = dsTrain.map(prcs).batch(128).prefetch(tf.data.AUTOTUNE)

preprocessing_model = tf.keras.models.Sequential()
preprocessing_model.add(layers.Resizing(IMAGE_SIZE, IMAGE_SIZE))
preprocessing_model.add(layers.Rescaling(1./255))

training_model = tf.keras.models.Sequential()
training_model.add(layers.Conv2D(8, (3, 3), activation='relu', kernel_initializer='random_normal', padding='same'))
training_model.add(layers.MaxPooling2D((5,5)))
training_model.add(layers.Flatten())
training_model.add(layers.Dense(2))

inputs = keras.Input(shape=(100, 100, 3))
outputs = training_model(preprocessing_model(inputs))
model = tf.keras.Model(inputs, outputs)

注意,你已经实现了这么多的输入层,通过这种方式,我只在你定义了输入变量的地方使用了一次。
如果您注意到层的顺序,您为预处理层实现了两个输入层,其中一个是顺序层

preprocessing_model.add(layers.InputLayer(input_shape=(None, None, 3)))
preprocessing_model.add(layers.Resizing(IMAGE_SIZE, IMAGE_SIZE, crop_to_aspect_ratio=True))
preprocessing_model.add(layers.Rescaling(1./255))

另一个定义了inputs变量

inputs = keras.Input(shape=preprocessing_model.input_shape)

所以层的顺序应该是这样的,这是绝对错误的:

preprocessing_model.add(keras.Input(shape=preprocessing_model.input_shape))
preprocessing_model.add(layers.InputLayer(input_shape=(None, None, 3)))
preprocessing_model.add(layers.Resizing(IMAGE_SIZE, IMAGE_SIZE, crop_to_aspect_ratio=True))
preprocessing_model.add(layers.Rescaling(1./255))

并且甚至注意到,由于数据在输入层之后经过预处理层,因此不需要在训练模型序列中彼此定义输入层。

atmip9wb

atmip9wb3#

我在tf.keras.layers.Resizing层也遇到过同样的问题,我将提出一个可能对某些人有用的替代解决方案。
在训练网络时,我使用了tf.keras.utils.image_dataset_from_directory方法生成的tf.data.Dataset,正如Marquo在其最终编辑中解释的那样,一批图像中所有图像的形状必须相同。
tf.keras.utils.image_dataset_from_directory默认加载256 x256个图像。
当我们想使用model.predict(sample)测试一个简单的、孤立的任意形状的样本时,问题就来了,我遇到的情况是Resizing层显然不接受任何形状。
解决方案是将Resizing图层的input_shape值显式设置为(None, None, 3),以便在使用image_dataset_from_directory数据集中的256 x256个图像进行训练期间不会推断其输入大小。
TL;DR layer_resize = Resizing(64, 64, input_shape=(None, None, 3))而不是layer_resize = Resizing(64, 64),以便在训练时不推断层的输入大小。
看看我是怎么做到的

相关问题