-
Notifications
You must be signed in to change notification settings - Fork 19.6k
Description
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)