1
$\begingroup$

The following time series exercise is about writing the best possible model, minimizing the MAE. Helper functions normalize_series, windowed_dataset are given and not to be changed as well as BATCH_SIZE, N_PAST, N_FUTURE or SHIFT. We use a window of the past 10 observations of 1 feature, and train the model to predict the next 10 observations of that feature.

import pandas as pd
import tensorflow as tf

def normalize_series(data, min, max):
    data = data - min
    data = data / max
    return data

def windowed_dataset(series, batch_size, n_past=10, n_future=10, shift=1):
    ds = tf.data.Dataset.from_tensor_slices(series)
    ds = ds.window(size=n_past + n_future, shift=shift, drop_remainder=True)
    ds = ds.flat_map(lambda w: w.batch(n_past + n_future))
    ds = ds.map(lambda w: (w[:n_past], w[n_past:]))
    return ds.batch(batch_size).prefetch(1)
    
def solution_model():

    # Reads the dataset.
    df = pd.read_csv('Weekly_U.S.Diesel_Retail_Prices.csv',
                     infer_datetime_format=True, index_col='Week of', header=0)

    # Number of features in the dataset. We use all features as predictors to
    # predict all features of future time steps.
    N_FEATURES = len(df.columns) # =1

    # Normalizes the data
    data = df.values
    data = normalize_series(data, data.min(axis=0), data.max(axis=0))

    # Splits the data into training and validation sets.
    SPLIT_TIME = int(len(data) * 0.8)
    x_train = data[:SPLIT_TIME]
    x_valid = data[SPLIT_TIME:]

    tf.keras.backend.clear_session()
    tf.random.set_seed(42)

    BATCH_SIZE = 32  

    # Number of past time steps based on which future observations should be
    # predicted
    N_PAST = 10   

    # Number of future time steps which are to be predicted.
    N_FUTURE = 10   

    # By how many positions the window slides to create a new window
    # of observations.
    SHIFT = 1  

    # Code to create windowed train and validation datasets.
    train_set = windowed_dataset(series=x_train, batch_size=BATCH_SIZE,
                                 n_past=N_PAST, n_future=N_FUTURE,
                                 shift=SHIFT)
    valid_set = windowed_dataset(series=x_valid, batch_size=BATCH_SIZE,
                                 n_past=N_PAST, n_future=N_FUTURE,
                                 shift=SHIFT)

    model = tf.keras.models.Sequential([

        tf.keras.layers.Conv1D(filters=1,
                               kernel_size=5,
                               strides=1,
                               padding="causal",
                               activation="relu",
                               input_shape=[None, 1]
                               ),

        tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(60, return_sequences=True)), 
        tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(60, return_sequences=True)), 
        #tf.keras.layers.Dense(30, activation="relu"),
        #tf.keras.layers.Dense(10, activation="relu"),
        tf.keras.layers.Dense(N_FEATURES)
    ])

    # Code to train and compile the model
    lr_schedule = tf.keras.callbacks.LearningRateScheduler(
        lambda epoch: 1e-8 * 10 ** (epoch / 20)
    )

    optimizer = tf.keras.optimizers.SGD(learning_rate=1e-5, momentum=0.9)

    model.compile(
        loss=tf.keras.losses.Huber(),
        optimizer=optimizer,
        metrics=["mae"]
    )

    model.fit(
        train_set, validation_data=valid_set, epochs=100, callbacks=[lr_schedule]
    )

    model.summary()

    return model

With main

if __name__ == '__main__':
    model = solution_model()
    model.save("model.h5")

I tried adding several dense layers, replacing the Bidirectional LSTM layers with LSTM/GRU/Bidirectional GRU layers. MAE obtained stays around 0.2 at best.

Is there any obvious (ex. input or output size) mistake in the approach, and how to improve to lower mae?

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.