Issue with Identical Input Shapes Not Equalling

Hello!

I have been using the CNN single-shot model on TensorFlow’s Time series forecasting tutorial to make predictions about future animal movements. I managed to get the model compiled, running, and saved; however, I am having difficulty loading the model from a .keras file.

When I try to load the model, I get a ValueError saying :
Sequential model 'sequential' has already been configured to use input shape (None, 10, 4). You cannot build it with input_shape [None, 10, 4].

This is probably due to tuples and lists being considered separate despite having the same contents, yet it is odd that equivalent input shapes would cause an error.

Would anyone be able to help me resolve the issue?

Here is the code I used:

import datetime
import numpy as np
import pandas as pd
import tensorflow as tf

OUT_STEPS = 10
MAX_EPOCS = 20

CONV_WIDTH = 3


class WindowGenerator():
    def __init__(self, input_width, label_width, shift,
                 cv_name='CleanCSV\\Moose\\yl1.csv',
                 label_columns = None):
        
        # Derive Original Data
        self.url_name = cv_name
        self.orig_df = pd.read_csv(f'{cv_name}')
        self.orig_df['timestamp'] = pd.DatetimeIndex(self.orig_df['timestamp'])

        self.date_time = pd.to_datetime(self.orig_df.pop('timestamp'))

        # Split Data
        n = len(self.orig_df)
        self.train_df = self.orig_df[0:int(n*0.8)]
        self.val_df = self.orig_df[int(n*0.8):int(n*0.9)]
        self.test_df = self.orig_df[int(n*0.9):]
    
        # Normalize Data
        self.train_mean = self.train_df.mean(numeric_only=True)
        self.train_std = self.train_df.std(numeric_only=True)
        
        self.train_df = (self.train_df - self.train_mean) / self.train_std
        self.val_df = (self.val_df - self.train_mean) / self.train_std
        self.test_df = (self.test_df - self.train_mean) / self.train_std

        # Work out label column indices
        self.label_columns = label_columns
        if label_columns is not None:
            self.label_columns_indices = {name: i for i, name in enumerate(label_columns)}
        
        self.column_indices = {name: i for i, name in enumerate(self.train_df.columns)}

        # Work out the window parameters.
        self.input_width = input_width
        self.label_width = label_width
        self.shift = shift

        self.total_window_size = input_width + shift

        # Slicing Input and label widths
        self.input_slice = slice(0, input_width)
        self.input_indices = np.arange(self.total_window_size)[self.input_slice]

        self.label_start = self.total_window_size - self.label_width
        self.label_slice = slice(self.label_start, None)
        self.label_indices = np.arange(self.total_window_size)[self.label_slice]

    def __repr__(self):
        return '\n'.join([
            f'Total window size: {self.total_window_size}',
            f'Input indices: {self.input_indices}',
            f'Label indices: {self.label_indices}',
            f'Label column name(s): {self.label_columns}'])

    def split_window(self, features):
        inputs = features[:, self.input_slice, :]
        labels = features[:, self.label_slice, :]

        if self.label_columns is not None:
            labels = tf.stack(
                [labels[:,:, self.column_indices[name]] for name in self.label_columns],
                axis = -1)
        
        inputs.set_shape([None, self.input_width, None])
        labels.set_shape([None, self.label_width, None])

        return inputs, labels
    
    def make_dataset(self, data):
        data = np.array(data, dtype=np.float32)
        ds = tf.keras.utils.timeseries_dataset_from_array(
            data=data,
            targets=None,
            sequence_length = self.total_window_size,
            sequence_stride=1,
            shuffle=False,
            batch_size=32,)
        ds = ds.map(self.split_window)

        return ds
    
    @property
    def train(self):
        return self.make_dataset(self.train_df)
    
    @property
    def val(self):
        return self.make_dataset(self.val_df)
    
    @property
    def test(self):
        return self.make_dataset(self.test_df)
    
    @property
    def example(self):
        result = getattr(self, '_example', None)
        if result is None:
            result = next(iter(self.train))

            self._example = result
        return result

def compile_and_fit(model, window, patience=2):
    early_stopping = tf.keras.callbacks.EarlyStopping(monitor='val_loss',
                                                      patience=patience,
                                                      mode='min')
    
    model.compile(loss=tf.keras.losses.MeanSquaredError(),
                  optimizer=tf.keras.optimizers.Adam(),
                  metrics=[tf.keras.metrics.MeanAbsoluteError()])
    
    history = model.fit(window.train, epochs=MAX_EPOCS,
                        validation_data=window.val,
                        callbacks=[early_stopping])

    return history

test_window = WindowGenerator(input_width=OUT_STEPS,
                               label_width=OUT_STEPS,
                               shift=OUT_STEPS,
                               cv_name='CleanCSV\\Moose\\yl1.csv')

multi_conv_model = tf.keras.Sequential([
    # Shape [batch, time, features] => [batch, CONV_WIDTH, features]
    tf.keras.layers.Lambda(lambda x: x[:, -CONV_WIDTH:, :]),
    # Shape => [batch, 1, conv_units]
    tf.keras.layers.Conv1D(256, activation='relu', kernel_size=(CONV_WIDTH)),
    # Shape => [batch, 1,  out_steps*features]
    tf.keras.layers.Dense(OUT_STEPS*len(test_window.column_indices),
                          kernel_initializer=tf.initializers.zeros()),
    # Shape => [batch, out_steps, features]
    tf.keras.layers.Reshape([OUT_STEPS, len(test_window.column_indices)])
])

history = compile_and_fit(multi_conv_model, test_window)

multi_conv_model.save(r'ModelCreation\SavedModels\single_shot.keras')

imported_single_shot = tf.keras.models.load_model(r'ModelCreation\SavedModels\single_shot.keras', safe_mode=False)
1 Like

isn’t the code missing the input layer ?

Actually I just noticed that the tutorial itself says it’s optional.

Yeah. I tried adding an input layer and got the same error.

hi @Raul_Castillo
it is difficult to help without all the pieces (your data, details on your setup…). a colab would help!
i see maybe 2 directions to explore to resolve you problem:

  • TF/keras version
  • datatypes of the pandas object as you read from csv

BTW you made this comment : This is probably due to tuples and lists being considered separate despite having the same contents . Did you try to cast these object so that they are of the same type?

1 Like

Thank you for the suggestion to use Google Colab!

I tried the code on this Colab Notebook. The code worked fine initially when TensorFlow was set to version 2.15.0; however, when I updated Tensorflow to the latest version of 2.16.2 (which is what I had), the code reproduced the same error.

I believe this may be a bug with the latest version of TensorFlow.

Also, in regards to my tuple comment. This was speculation on my end based on how the ValueError call was constructed in the Sequential(Model).build method.

I know that lists and tuples can’t equal each other, so I thought that it may be a reason for the error.

The tf version i am on is 2.16.2. I ran the code using tensorflow docker image latest-gpu

i am having the exact same issue.

My model looks like this

def create_model_convlstm2d(input_shape: Tuple[int, int, int], batch_size: int) -> models.Model:
    model = models.Sequential()

    model.add(layers.Input(batch_shape=(
        batch_size,) + input_shape))

    # Spatial feature extraction
    model.add(layers.Conv2D(16, (3, 3)))
    model.add(layers.ReLU(max_value=6))
    model.add(layers.MaxPooling2D((2, 2)))

    model.add(layers.Conv2D(32, (3, 3)))
    model.add(layers.ReLU(max_value=6))
    model.add(layers.MaxPooling2D((2, 2)))

    model.add(layers.Lambda(lambda x: expand_dims(x, axis=1)))
    model.add(layers.ConvLSTM2D(
        64, (3, 3), stateful=True))
    model.add(layers.ReLU(max_value=6))
    model.add(layers.MaxPooling2D((2, 2)))

    model.add(layers.Lambda(lambda x: expand_dims(x, axis=1)))
    model.add(layers.ConvLSTM2D(
        64, (3, 3), stateful=True))
    model.add(layers.ReLU(max_value=6))
    model.add(layers.MaxPooling2D((2, 2)))

    model.add(layers.Flatten())
    model.add(layers.Dense(128, activation='relu',
              kernel_regularizer=regularizers.l2(0.01)))
    model.add(layers.Dropout(0.3))
    model.add(layers.Dense(64, activation='relu',
              kernel_regularizer=regularizers.l2(0.01)))
    model.add(layers.Dense(MAX_VEHICLES * 3))
    model.add(layers.Reshape((MAX_VEHICLES, 3)))
    model.add(layers.Lambda(lambda x: tf.concat([
        x[:, :, :2],
        tf.sigmoid(x[:, :, 2:3])
    ], axis=-1)))

    return model

it works fine with model.fit, predict, save etc.
Until i tries to load the model, then it fails, and gives me this error:

ValueError: Sequential model 'sequential' has already been configured to use input shape (32, 64, 128, 1). You cannot build it with input_shape [32, 64, 128, 1]

I just posted my problem as an issue on the Tensorflow github. If you are interested, the issue is under the title Issue with Loading Sequential Models.

2 Likes

The issue was finally solved after it got sent to the Keras github.

Here is a link to the fix: link to fix

1 Like