Skip to content

TypeError with single-output dictionary targets - "Expected float32, but got my_output_0 of type 'str'" #21513

@ebnertom

Description

@ebnertom

Hi,
I've stumbled across a bug that prevents me to use dictionaries for specifying targets when having single outputs. For multiple outputs it seems to work.

Summary

Keras 3 fails when using dictionary format for single-output models, throwing TypeError: Expected float32, but got my_output_0 of type 'str'. Multi-output dictionaries work correctly.

Environment

Keras 3.10.0
TensorFlow backend (version 2.19.0)
Python 3.12

Expected Behavior

Dictionary format should work consistently for both single and multiple outputs, as documented.
Actual Behavior
Single-output dictionary fails with string/tensor type confusion error during loss computation.

Actual Behavior

Single-output dictionary fails with string/tensor type confusion error during loss computation.

Minimal Reproduction Code

import tensorflow as tf
import keras
import numpy as np


# Generate dummy data with dictionary inputs
def create_dataset(n_outputs, dict_output):
    def generator():
        for _ in range(100):  # 100 samples
            x = {
                'image': np.random.random((28, 28, 1)).astype(np.float32),
                'features': np.random.random((5,)).astype(np.float32)
            }
            if dict_output:
                y = {f'my_output_{i}': np.random.randint(0, 10, size=(10,)).astype(np.float32)  # One-hot encoded label
                    for i in range(n_outputs)}
            else:
                y = tuple([np.random.randint(0, 10, size=(10,)).astype(np.float32)  # One-hot encoded label
                     for i in range(n_outputs)])
            yield x, y


    if dict_output:
        output_signature=(
            {
                'image': tf.TensorSpec(shape=(28, 28, 1), dtype=tf.float32),
                'features': tf.TensorSpec(shape=(5,), dtype=tf.float32)
            },
            {f'my_output_{i}': tf.TensorSpec(shape=(10,), dtype=tf.float32) for i in range(n_outputs)}

        )
    else:
        output_signature = (
            {
                'image': tf.TensorSpec(shape=(28, 28, 1), dtype=tf.float32),
                'features': tf.TensorSpec(shape=(5,), dtype=tf.float32)
            },
            tuple((tf.TensorSpec(shape=(10,), dtype=tf.float32) for i in range(n_outputs)))

        )
    dataset = tf.data.Dataset.from_generator(
        generator,
        output_signature=output_signature
    )
    return dataset.batch(8)  # Batch size of 8


# Create model with dictionary inputs
def create_model(n_outputs):
    # Define multiple inputs
    image_input = keras.Input(shape=(28, 28, 1), name='image')
    features_input = keras.Input(shape=(5,), name='features')

    # Process image
    x1 = keras.layers.Flatten()(image_input)
    x1 = keras.layers.Dense(64, activation='relu')(x1)

    # Process features
    x2 = keras.layers.Dense(32, activation='relu')(features_input)

    # Combine both inputs
    combined = keras.layers.Concatenate()([x1, x2])
    outputs = [keras.layers.Dense(10, activation='softmax', name=f'my_output_{i}')(combined)
               for i in range(n_outputs)]

    model = keras.Model(inputs={'image': image_input, 'features': features_input}, outputs=outputs)
    return model


# Main execution
if __name__ == "__main__":
    keras.config.disable_traceback_filtering()

    # the following configuration fails:
    n_outputs = 1
    dict_output = True

    # # the following configurations work:
    # n_outputs = 2
    # dict_output = True
    #
    # n_outputs = 1
    # dict_output = False

    # Create dataset
    train_dataset = create_dataset(n_outputs, dict_output)

    # Create and compile model
    model = create_model(n_outputs)
    model.compile(
        optimizer='adam',
        loss={f'my_output_{i}': 'categorical_crossentropy'
              for i in range(n_outputs)},
        metrics={f'my_output_{i}': ['accuracy']
              for i in range(n_outputs)},
    )

    # Train
    model.fit(train_dataset, epochs=3, verbose=1)

Error Stack Trace

Traceback (most recent call last):
  File "/home/tom/kml/repos/pypotato/pypotato/pypotato/applications/examples/keras_tf_data/keras_training.py", line 99, in <module>
    model.fit(train_dataset, epochs=3, verbose=1)
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py", line 113, in error_handler
    return fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py", line 377, in fit
    logs = self.train_function(iterator)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py", line 220, in function
    opt_outputs = multi_step_on_iterator(iterator)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/tensorflow/python/util/traceback_utils.py", line 153, in error_handler
    raise e.with_traceback(filtered_tb) from None
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py", line 133, in multi_step_on_iterator
    one_step_on_data(iterator.get_next())
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py", line 114, in one_step_on_data
    outputs = self.distribute_strategy.run(step_function, args=(data,))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py", line 61, in train_step
    loss = self._compute_loss(
           ^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/trainers/trainer.py", line 383, in _compute_loss
    return self.compute_loss(
           ^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/trainers/trainer.py", line 351, in compute_loss
    loss = self._compile_loss(y, y_pred, sample_weight)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/trainers/compile_utils.py", line 690, in __call__
    return self.call(y_true, y_pred, sample_weight)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/trainers/compile_utils.py", line 797, in call
    loss_fn(y_t, y_p, _sample_weight), dtype=self.dtype
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/losses/loss.py", line 63, in __call__
    y_true = tree.map_structure(
             ^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/tree/tree_api.py", line 192, in map_structure
    return tree_impl.map_structure(func, *structures)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/tree/optree_impl.py", line 111, in map_structure
    return optree.tree_map(
           ^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/optree/ops.py", line 766, in tree_map
    return treespec.unflatten(map(func, *flat_args))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/losses/loss.py", line 64, in <lambda>
    lambda x: ops.convert_to_tensor(x, dtype=self.dtype), y_true
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/ops/core.py", line 958, in convert_to_tensor
    return backend.core.convert_to_tensor(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tom/.virtualenvs/pypotato-py312/lib/python3.12/site-packages/keras/src/backend/tensorflow/core.py", line 153, in convert_to_tensor
    return tf.convert_to_tensor(x, dtype=dtype)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: Expected float32, but got my_output_0 of type 'str'.

Workarounds

Use list/tuple format for single outputs
Use dictionary format only for multiple outputs (≥2)

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions