Search code examples
pythontensorflowdeep-learningtypeerrorvalueerror

TensorFlow - ValueError: Argument `output` must have rank (ndim) `target.ndim - 1`


I'm trying to train a sequential model on the TinyImageNet dataset, using TensorFlow.

TinyImageNet has 200 classes, 100k rows for training and 10k for validating. It has two columns, image and label.

The structure of an instance:

{
  'image': <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=64x64 at 0x1A800E8E190,
  'label': 15
}

I'm preprocessing the data to standardize the RGB channel values between 0 and 1 as seen here.

However, when fitting the data to the model, I'm getting this error :

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[47], line 1
----> 1 model.fit(train_dataset_tf, epochs=10)

File .venv\Lib\site-packages\keras\src\utils\traceback_utils.py:122, in filter_traceback.<locals>.error_handler(*args, **kwargs)
    119     filtered_tb = _process_traceback_frames(e.__traceback__)
    120     # To get the full stack trace, call:
    121     # `keras.config.disable_traceback_filtering()`
--> 122     raise e.with_traceback(filtered_tb) from None
    123 finally:
    124     del filtered_tb

File .venv\Lib\site-packages\keras\src\backend\tensorflow\nn.py:619, in sparse_categorical_crossentropy(target, output, from_logits, axis)
    613     raise ValueError(
    614         "Argument `output` must be at least rank 1. "
    615         "Received: "
    616         f"output.shape={output.shape}"
    617     )
    618 if len(target.shape) != len(output.shape[:-1]):
--> 619     raise ValueError(
    620         "Argument `output` must have rank (ndim) `target.ndim - 1`. "
    621         "Received: "
    622         f"target.shape={target.shape}, output.shape={output.shape}"
    623     )
    624 for e1, e2 in zip(target.shape, output.shape[:-1]):
    625     if e1 is not None and e2 is not None and e1 != e2:

ValueError: Argument `output` must have rank (ndim) `target.ndim - 1`. Received: target.shape=(None, 64, 64, 3), output.shape=(None, 200)

Complete code :

import tensorflow as tf
from tensorflow.keras import layers
from datasets import load_dataset
import numpy as np

train_dataset = load_dataset("Maysee/tiny-imagenet", split="train")
test_dataset = load_dataset("Maysee/tiny-imagenet", split="valid")

def preprocess_image(image, label):
    image = tf.io.decode_jpeg(image, channels=3)
    image = tf.image.resize(image, [64, 64])
    image = image / 255.0
    return image, label

def generator(dataset):
    for example in dataset:
        yield example['image'], example['label']

train_dataset_tf = tf.data.Dataset.from_generator(
    lambda: generator(train_dataset),
    output_signature=(
        tf.TensorSpec(shape=(), dtype=tf.string),
        tf.TensorSpec(shape=(64, 64, 3), dtype=tf.int64),
    ),
)

test_dataset_tf = tf.data.Dataset.from_generator(
    lambda: generator(test_dataset),
    output_signature=(
        tf.TensorSpec(shape=(), dtype=tf.string),
        tf.TensorSpec(shape=(64, 64, 3), dtype=tf.int64),
    ),
)

train_dataset_tf = (
    train_dataset_tf.map(preprocess_image).batch(32).prefetch(tf.data.AUTOTUNE)
)
test_dataset_tf = (
    test_dataset_tf.map(preprocess_image).batch(32).prefetch(tf.data.AUTOTUNE)
)

model = tf.keras.Sequential(
    [
        layers.Conv2D(32, (3, 3), activation="relu", input_shape=(64, 64, 3)),
        layers.MaxPooling2D((2, 2)),
        layers.Flatten(),
        layers.Dense(128, activation="relu"),
        layers.Dense(200, activation="softmax"),
    ]
)

model.compile(
    optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"]
)

model.fit(train_dataset_tf, epochs=10) # Error is thrown here

I tried changing the input_shape parameter of the layers, as well as changing the shape parameters of the TensorSpec output_signature to None or just empty as I saw on some StackOverflow answers, but that gave a TypeError: 'generator' yielded an element of shape (64, 64, 3) where an element of shape () was expected.

I also tried using ImageDataGenerator(rescale=1./255) with generators such as :

train_df = pd.DataFrame(train_dataset, dtype=str)
train_generator = train_datagen.flow_from_dataframe(
    train_df,
    directory=None,
    x_col="image",
    y_col="label",
    target_size=(64, 64),
    batch_size=32,
    class_mode="categorical"
)

but this gave me a TypeError: 'generator' yielded an element of shape (0, 64, 64, 3) where an element of shape (64, 64, 3) was expected. because of the generator x shape which is (0, 64, 64, 3) for some reason :

for x, y in train_generator:
    print(x.shape, y.shape) # outputs (0, 64, 64, 3) (0, 0)

I'm using TensorFlow 2.16.1 and Python 3.11.9 and tried downgrading to TF 2.14 but nothing changed.

Any help would be greatly appreciated.


Solution

  • That error is due to some images of the dataset being in only 2 dimensions and not 3 (grayscale for example).

    You need to modify your generator to check for such a case. You can handle it like this:

    for element in dataset:
       image = np.array(element["img"])
       label = element["label"]
    
       # duplicate on 3 dims if image grayscale
       if image.ndim == 2:
           image = np.stack([image] * 3, axis=-1)
    
       yield image, label