I have been following the "Time series forecasting" tutorial and I have some troubles making an own multi-step baseline model that can average the input data in the time axis, and repeat it as the target values in the model’s prediction.
In the link above, the authors of the tutorial use simple baselines such as the code below, to take the last time element of the input and tile (or repeat) it OUT_STEPS
times.
class MultiStepLastBaseline(tf.keras.Model):
def call(self, inputs):
return tf.tile(inputs[:, -1:, :], [1, OUT_STEPS, 1])
Or a baseline that just repeats the same values of the data in the past, according to the number of steps stated in the window
of data (input_width
) of the model.
class RepeatBaseline(tf.keras.Model):
def call(self, inputs):
return inputs
I would like to take all the elements of the inputs
and apply a simple average to them before tf.tile
them. Basically apply an average to RepeatBaselineand repeat the average as in
MultiStepLastBaseline`.
The minimal code to produce a tf.Dataset that can be replicated with the code in the tutorial (https://www.tensorflow.org/tutorials/structured_data/time_series) is the following:
import numpy as np
import pandas as pd
import tensorflow as tf
n = 8
rng = np.random.default_rng(seed=0)
df = pd.DataFrame(np.around(rng.random((n, 4)), 1), columns=['a','b','c','y'])
In the tutorial, the data is managed by a window
of consecutive samples from the data, which can be emulated with:
# Settings of what a window object would have
OUT_STEPS = 2
input_width = 2 # Take two rows (or time steps) of all columns as input
label_width = OUT_STEPS # Size of the prediction (output)
shift = OUT_STEPS # Time (or rows) offset between input and output
total_window_size = input_width + shift
batch_size = 1
label_index = None #In the future will be the index of 'y'
# Just a conversion of the df to an tf._.EagerTensor
data = np.array(df.values, dtype=np.float32)
def stack_data(data, total_window_size):
batches = []
start = 0
end = total_window_size
for start in range(data.shape[0]-1):
batch = data[start:end]
start = start + total_window_size + 1
end = start
if batch.shape[0] == total_window_size:
batches.append(batch)
return tf.stack(batches)
stacked_data = stack_data(data, total_window_size)
Further, the data is manipulated and transformed into a dataset
. The minimal code is
input_slice = slice(0, input_width)
label_slice = slice(total_window_size-label_width, None)
def split_stacked_data(stacked_data):
"""
Split dataset into inputs and labels (or targets)
https://www.tensorflow.org/tutorials/structured_data/time_series#2_split
"""
inputs = stacked_data[:,input_slice, :]
labels = stacked_data[:,label_slice,label_index:]
inputs.set_shape([None, input_width, None])
labels.set_shape([None, label_width, None])
return inputs, labels
# inputs, labels = split_stacked_data(stacked_data)
input_dataset = tf.keras.utils.timeseries_dataset_from_array(
data=data, targets=None, sequence_length=total_window_size,
sequence_stride=1, shuffle=False, batch_size=batch_size)
input_dataset = input_dataset.map(split_stacked_data)
So far, in the MyAverageBaseline
model, I have succeeded to pass the inputs
through a Dense
model and make an Average
of the data. Since the df
has four features, a similar number of units are declared in de Dense
. However, the results are not what I expect to get.
class MyAverageBaseline(tf.keras.Model):
def __init__(self, out_steps, label_index=None):
super().__init__()
self.label_index = label_index
self.out_steps = out_steps
self.a_model = tf.keras.layers.Dense(4, activation=tf.nn.relu, trainable=False)
def call(self, inputs):
# type(inputs): <class 'tensorflow.python.framework.ops.Tensor'>
if self.label_index is None:
# How can I grab each input & average the values along the time?
# Working but not delivering the results
x = self.a_model(inputs)
result = tf.keras.layers.Average()([x])
# The **pseudocode** where each inputs is averaged in time dimension
# average_time_dim = inputs[:, np.mean(:, axis=0), :] # SyntaxError
# possible shape of average_time_dim: (4,)
# average_reshaped = average_time_dim[tf.newaxis, tf.newaxis, :]
# return tf.tile(average_reshaped, [1, self.out_steps, 1])
return result
# TODO. I would just have to pick a part of the resul
# result = inputs[:, :, self.label_index]
# return result[:, :, tf.newaxis]
In the pseudocode, def call():
would calculate the average of the inputs
along the time axis and then tf.tile
the average OUT_STEPS
times.
I also have tried with tf.mean_reduce()
and tf.mean()
but I guess I am new in Tensorflow to make a calculation working inside an iterator.
The desired results should be such that once I compile and evaluate the baseline model:
baseline_model = MyAverageBaseline()
baseline_model.compile(loss=tf.keras.losses.MeanSquaredError(),
metrics=[tf.keras.metrics.MeanAbsoluteError()])
evaluation = baseline_model.evaluate(input_dataset, verbose=2)
predictions = baseline_model.predict(input_dataset)
The predicted values would be the same as the results below, taking into account that seed=0
to generate the random df
.
>>> print(predictions)
>>> array([[[0.7, 0.6, 0.3, 0.35],
[0.7, 0.6, 0.3, 0.35]],
[[0.65, 0.9, 0.7, 0.35],
[0.65, 0.9, 0.7, 0.35]],
... More data here ]])
Could somebody help me to figure out the code that the def call(self, inputs):
method should include in the class MyAverageBaseline(tf.keras.Model):
?
After a couple of days, I could manage to solve the issue with the
call ()
method. Usingtf.reduce_mean
was the good way, but I had to fix the dimensionality of the results. The extra code added to the baseline model was.