Saving a model defined by model subclassing can not be saved
@PowerToThePeople111 Please refer to the TensorFlow documentation for the latest recommendations on saving subclassed models: https://www.tensorflow.org/tutorials/keras/save_and_load and kindly provide the access to your notebook.
Thank you!  Hi sushreebarsa,

thank you for your reply. You should now be able to access the notebook. Also I put the whole code neccessary below the link to it.

I read the documentation again and was lead by the following parts:


> You can switch to the SavedModel format by:
> 
> Passing save_format='tf' to save()
> Passing a filename without an extension
> 
> ...
> 
> *Custom objects (for example, subclassed models or layers) require special attention when saving and loading. Refer to the Saving custom objects section below.
> 
> ...
> 

So for me it seemed that the above is basically everything i need to do since later on in the "Custom objects"-section you can read at the very top:

> If you are using the SavedModel format, you can skip this section. 

So actually I should be able to store the model by using `save()` with `save_format 'tf'` and providing a filename without ending, which is what I did:
Neither `dp2.save("delteme", save_format="tf") ` nor `dp2.save("delteme") ` did the trick for me and result in the error message i posted in my initial statement.

Did I miss something here?


 Hi @PowerToThePeople111 ,

I don't think the problem in saving is actually coming from model subclassing. Most probably, it comes from passing `df` as argument to the `call` method.

By slightly changing your code to pass a list instead of a Pandas dataframe, the model can be saved.

```@tf.keras.utils.register_keras_serializable("DataPrep2")
class DataPrep2(tf.keras.Model):
    def __init__(self):
        super().__init__()
        self.layer1 = tf.keras.layers.StringLookup(output_mode='int', vocabulary=["a", "b"])
        self.layer2 = tf.keras.layers.Discretization(bin_boundaries=[0,1,2,3,4], output_mode='int')


    def call(self, x):
        return {
            "conv1": self.layer1(x[0]),
            "conv2": self.layer2(x[1])
        }
dp2 = DataPrep2()
dp2([df["var1"], df["var2"]])
dp2.save("delteme")
``` Thank you @karimsr4,

that solution works. :)  Are you satisfied with the resolution of your issue?
<a href="https://docs.google.com/forms/d/e/1FAIpQLSfaP12TRhd9xSxjXZjcZFNXPGk4kc1-qMdv3gc6bEP90vY1ew/viewform?entry.85265664=Yes&entry.2137816233=https://github.com/tensorflow/tensorflow/issues/64415">Yes</a>
<a href="https://docs.google.com/forms/d/e/1FAIpQLSfaP12TRhd9xSxjXZjcZFNXPGk4kc1-qMdv3gc6bEP90vY1ew/viewform?entry.85265664=No&entry.2137816233=https://github.com/tensorflow/tensorflow/issues/64415">No</a>

### Issue type

Bug

### Have you reproduced the bug with TensorFlow Nightly?

Yes

### Source

binary

### TensorFlow version

2.15.1

### Custom code

No

### OS platform and distribution

Mac 14.2.1 

### Mobile device

_No response_

### Python version

3.10.13

### Bazel version

_No response_

### GCC/compiler version

_No response_

### CUDA/cuDNN version

_No response_

### GPU model and memory

_No response_

### Current behavior?

Hey all,

_Remark:_ 
I am using simple CPU computations and installed tf using pip (so i did not build it myself). Therefor, I did not provide any Cuda version.

_The problem I am facing:_ 
I am currently trying to save a model which was created by subclassing which throws me an error. (see below)

This is the code for the definition of the model ...

1. first using function API
2. secondly using model subclassing

The first one runs fine, the second throws an error during the last step where I try to save the model. As you can see, I called the model with the data before saving it (just as the error message suggests), but that still does not help. It seems to me that this is a bug, but maybe I have also missed something important. 


```
df = pd.DataFrame({"var1": ["a", "b", "c"], "var2": [1, 2, 3]})

inp_var1 = tf.keras.layers.Input(shape=(1,))
inp_var2 = tf.keras.layers.Input(shape=(1,))
output1 = tf.keras.layers.StringLookup(output_mode='int', vocabulary=["a", "b"])(inp_var1)
output2 = tf.keras.layers.Discretization(bin_boundaries=[0,1,2,3,4], output_mode='int')(inp_var2)
dp = tf.keras.Model([inp_var1, inp_var2],[output1, output2])
converted = dp([df["var1"],df["var2"]])
for row in converted:
    print(row)
dp.save("delteme1") #this works


class DataPrep2(tf.keras.Model):
    def __init__(self):
        super().__init__()
        self.layer1 = tf.keras.layers.StringLookup(output_mode='int', vocabulary=["a", "b"])
        self.layer2 = tf.keras.layers.Discretization(bin_boundaries=[0,1,2,3,4], output_mode='int')
     

    def call(self, x):
        return {
            "conv1": self.layer1(x["var1"]),
            "conv2": self.layer2(x["var2"])
        }

dp2 = DataPrep2()
dp2(df)
dp2.save("delteme") #this does not
# same result with this line btw: dp2.save("delteme", save_format="tf") 
```

Thanks for looking into this in advance.

best,


### Standalone code to reproduce the issue
The code is in a notebook here: https://colab.research.google.com/drive/1oaGNP902-MsN8o5OvY_FeH7pedeev8M_?usp=sharing

The code inline here:
```
import tensorflow as tf
import pandas as pd

df = pd.DataFrame({"var1": ["a", "b", "c"], "var2": [1, 2, 3]})

inp_var1 = tf.keras.layers.Input(shape=(1,))
inp_var2 = tf.keras.layers.Input(shape=(1,))
output1 = tf.keras.layers.StringLookup(output_mode='int', vocabulary=["a", "b"])(inp_var1)
output2 = tf.keras.layers.Discretization(bin_boundaries=[0,1,2,3,4], output_mode='int')(inp_var2)
dp = tf.keras.Model([inp_var1, inp_var2],[output1, output2])
converted = dp([df["var1"],df["var2"]])
for row in converted:
    print(row)
dp.save("delteme1") #this works

@tf.keras.utils.register_keras_serializable("DataPrep2")
class DataPrep2(tf.keras.Model):
    def __init__(self):
        super().__init__()
        self.layer1 = tf.keras.layers.StringLookup(output_mode='int', vocabulary=["a", "b"])
        self.layer2 = tf.keras.layers.Discretization(bin_boundaries=[0,1,2,3,4], output_mode='int')


    def call(self, x):
        return {
            "conv1": self.layer1(x["var1"]),
            "conv2": self.layer2(x["var2"])
        }

dp2 = DataPrep2()
dp2(df)
dp2.save("delteme") #this does not
# same result with this line btw: dp2.save("delteme", save_format="tf") 
```




### Relevant log output

```shell
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-3-ece1b8e07810> in <cell line: 32>()
     30 dp2 = DataPrep2()
     31 dp2(df)
---> 32 dp2.save("delteme") #this does not

1 frames
/usr/local/lib/python3.10/dist-packages/keras/src/saving/legacy/saving_utils.py in raise_model_input_error(model)
     95     # If the model is not a `Sequential`, it is intended to be a subclassed
     96     # model.
---> 97     raise ValueError(
     98         f"Model {model} cannot be saved either because the input shape is not "
     99         "available or because the forward pass of the model is not defined."

ValueError: Model <__main__.DataPrep2 object at 0x7cfd49e6f8b0> cannot be saved either because the input shape is not available or because the forward pass of the model is not defined.To define a forward pass, please override `Model.call()`. To specify an input shape, either call `build(input_shape)` directly, or call the model on actual data using `Model()`, `Model.fit()`, or `Model.predict()`. If you have a custom training step, please make sure to invoke the forward pass in train step through `Model.__call__`, i.e. `model(inputs)`, as opposed to `model.call()`.
```
