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)