Info:
- Issue type: **Bug**
- Have you reproduced the bug with TensorFlow Nigh…tly? No
- Source: binary
- TensorFlow version: 2.14.0
- Custom code: No
- OS platform and distribution: macOS 13.6
- Python version: 3.11
- CUDA/cuDNN version: none
### Current behavior?
When trying to serialize/deserialize a `tf.keras.Model` nested input shapes cause an error.
Note that this has been observed many years back in #37061, which was closed because their MVP included a `tf.keras.Sequential` which was deemed as not supported. However, the issue has nothing to do with `tf.keras.Sequential` at all, and instead lies purely in the deserialisation code of keras.
### Standalone code to reproduce the issue
```shell
import tensorflow as tf
@tf.keras.saving.register_keras_serializable(package="MyPackage")
class DummyModel(tf.keras.Model):
def __init__(self, name=None):
super().__init__(name=name)
self.sublayer = tf.keras.layers.Dense(16)
def call(self, x, **kw):
a = x["a"]
nested = x["nested"]
b = nested["b"]
c = nested["c"]
return self.sublayer(tf.concat([a,b,c], axis=-1))
model = DummyModel()
out = model(dict(
a = tf.keras.Input(3,dtype=tf.float32),
nested = dict(
b = tf.keras.Input(4,dtype=tf.float32),
c = tf.keras.Input(5,dtype=tf.float32),
),
))
model.summary()
model.save("temp.keras")
tf.keras.saving.load_model("temp.keras")
```
### Relevant log output
```shell
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/tensorflow/python/framework/tensor_shape.py:851, in TensorShape.__init__(self, dims)
850 try:
--> 851 self._dims.append(as_dimension(d).value)
852 except TypeError as e:
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/tensorflow/python/framework/tensor_shape.py:741, in as_dimension(value)
740 else:
--> 741 return Dimension(value)
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/tensorflow/python/framework/tensor_shape.py:217, in Dimension.__init__(self, value)
216 except AttributeError:
--> 217 raise TypeError(
218 "Dimension value must be integer or None or have "
219 "an __index__ method, got value '{0!r}' with type '{1!r}'".format(
220 value, type(value))) from None
221 if self._value < 0:
TypeError: Dimension value must be integer or None or have an __index__ method, got value ''b'' with type '<class 'str'>'
The above exception was the direct cause of the following exception:
TypeError Traceback (most recent call last)
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/tensorflow/python/eager/execute.py:204, in make_shape(v, arg_name)
203 try:
--> 204 shape = tensor_shape.as_shape(v)
205 except TypeError as e:
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/tensorflow/python/framework/tensor_shape.py:1526, in as_shape(shape)
1525 else:
-> 1526 return TensorShape(shape)
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/tensorflow/python/framework/tensor_shape.py:853, in TensorShape.__init__(self, dims)
852 except TypeError as e:
--> 853 raise TypeError(
854 "Failed to convert '{0!r}' to a shape: '{1!r}'"
855 "could not be converted to a dimension. A shape should "
856 "either be single dimension (e.g. 10), or an iterable of "
857 "dimensions (e.g. [1, 10, None]).".format(dims, d)) from e
858 self._dims = tuple(self._dims)
TypeError: Failed to convert '{'b': [None, 4], 'c': [None, 5]}' to a shape: ''b''could not be converted to a dimension. A shape should either be single dimension (e.g. 10), or an iterable of dimensions (e.g. [1, 10, None]).
During handling of the above exception, another exception occurred:
TypeError Traceback (most recent call last)
Cell In[6], line 1
----> 1 tf.keras.saving.load_model("temp.keras")
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/keras/src/saving/saving_api.py:254, in load_model(filepath, custom_objects, compile, safe_mode, **kwargs)
249 if kwargs:
250 raise ValueError(
251 "The following argument(s) are not supported "
252 f"with the native Keras format: {list(kwargs.keys())}"
253 )
--> 254 return saving_lib.load_model(
255 filepath,
256 custom_objects=custom_objects,
257 compile=compile,
258 safe_mode=safe_mode,
259 )
261 # Legacy case.
262 return legacy_sm_saving_lib.load_model(
263 filepath, custom_objects=custom_objects, compile=compile, **kwargs
264 )
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/keras/src/saving/saving_lib.py:281, in load_model(filepath, custom_objects, compile, safe_mode)
278 asset_store.close()
280 except Exception as e:
--> 281 raise e
282 else:
283 return model
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/keras/src/saving/saving_lib.py:246, in load_model(filepath, custom_objects, compile, safe_mode)
244 # Construct the model from the configuration file in the archive.
245 with ObjectSharingScope():
--> 246 model = deserialize_keras_object(
247 config_dict, custom_objects, safe_mode=safe_mode
248 )
250 all_filenames = zf.namelist()
251 if _VARS_FNAME + ".h5" in all_filenames:
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/keras/src/saving/serialization_lib.py:731, in deserialize_keras_object(config, custom_objects, safe_mode, **kwargs)
729 build_config = config.get("build_config", None)
730 if build_config:
--> 731 instance.build_from_config(build_config)
732 compile_config = config.get("compile_config", None)
733 if compile_config:
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/keras/src/engine/base_layer.py:2331, in Layer.build_from_config(self, config)
2329 input_shape = config["input_shape"]
2330 if input_shape is not None:
-> 2331 self.build(input_shape)
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/keras/src/engine/training.py:494, in Model.build(self, input_shape)
489 x = [
490 base_layer_utils.generate_placeholders_from_shape(shape)
491 for shape in input_shape
492 ]
493 elif isinstance(input_shape, dict):
--> 494 x = {
495 k: base_layer_utils.generate_placeholders_from_shape(
496 shape
497 )
498 for k, shape in input_shape.items()
499 }
500 else:
501 x = base_layer_utils.generate_placeholders_from_shape(
502 input_shape
503 )
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/keras/src/engine/training.py:495, in <dictcomp>(.0)
489 x = [
490 base_layer_utils.generate_placeholders_from_shape(shape)
491 for shape in input_shape
492 ]
493 elif isinstance(input_shape, dict):
494 x = {
--> 495 k: base_layer_utils.generate_placeholders_from_shape(
496 shape
497 )
498 for k, shape in input_shape.items()
499 }
500 else:
501 x = base_layer_utils.generate_placeholders_from_shape(
502 input_shape
503 )
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/keras/src/engine/base_layer_utils.py:189, in generate_placeholders_from_shape(shape)
188 def generate_placeholders_from_shape(shape):
--> 189 return tf1.placeholder(shape=shape, dtype=backend.floatx())
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/tensorflow/python/ops/array_ops.py:3283, in placeholder(dtype, shape, name)
3279 if context.executing_eagerly():
3280 raise RuntimeError("tf.placeholder() is not compatible with "
3281 "eager execution.")
-> 3283 return gen_array_ops.placeholder(dtype=dtype, shape=shape, name=name)
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/tensorflow/python/ops/gen_array_ops.py:7071, in placeholder(dtype, shape, name)
7069 if shape is None:
7070 shape = None
-> 7071 shape = _execute.make_shape(shape, "shape")
7072 _, _, _op, _outputs = _op_def_library._apply_op_helper(
7073 "Placeholder", dtype=dtype, shape=shape, name=name)
7074 _result = _outputs[:]
File ~/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/tensorflow/python/eager/execute.py:206, in make_shape(v, arg_name)
204 shape = tensor_shape.as_shape(v)
205 except TypeError as e:
--> 206 raise TypeError("Error converting %s to a TensorShape: %s." % (arg_name, e))
207 except ValueError as e:
208 raise ValueError("Error converting %s to a TensorShape: %s." %
209 (arg_name, e))
TypeError: Error converting shape to a TensorShape: Failed to convert '{'b': [None, 4], 'c': [None, 5]}' to a shape: ''b''could not be converted to a dimension. A shape should either be single dimension (e.g. 10), or an iterable of dimensions (e.g. [1, 10, None])..
```