Univariate time series forecasting
A time series is a set of observations ordered in time and dependednt of each other. Due to this order in time, it is likely that the value of a variable \(y\) at moment \(t\) reflects the past history of the series, that is, the observations of a time series are likely to be correlated. 
The term "univariate time series" refers to a time series that consists of single (scalar) observations recorded sequentially over equal time increments.
These are data sets in which only one variable is observed each time, for example the temperature at each hour, in the case of the notebook that was made.
1. Data preparation
1.1. Data extraction
First, we trained a model using only a single feature (temperature of the node 28), and use it to make predictions for that value in the future.
We select two months of data that do not contain any missing data during these two months on node 28.
The dataset contains \(1441\) rows, the first \(1200\) rows of the data will be the training dataset (\(TRAIN\_SPLIT = 1200\)), and there remaining will be the validation dataset. This amounts to \(~50\) days worth of training data and \(10\) days of validation data. (more information on the operating process of machine learning methods is presented in this link).
1.2. Data scaling
It is important to scale features before training a neural network. Standardization is a common way of doing this scaling by subtracting the mean and dividing by the standard deviation of each feature of the training data.
uni_train_mean = uni_data[:TRAIN_SPLIT].mean()
uni_train_std = uni_data[:TRAIN_SPLIT].std()
uni_data = (uni_datauni_train_mean)/uni_train_std
1.3. Data modelling
The function below returns the windows of time for the model to train on. The parameter history_size is the size of the past window of information. The target_size is how far in the future does the model need to learn to predict. The target_size is the label that needs to be predicted:
def univariate_data(dataset, start_index, end_index, history_size, target_size):
data = []
labels = []
start_index = start_index + history_size
if end_index is None:
end_index = len(dataset)  target_size
for i in range(start_index, end_index):
indices = range(ihistory_size, i)
# Reshape data from (history_size,) to (history_size, 1)
data.append(np.reshape(dataset[indices], (history_size, 1)))
labels.append(dataset[i+target_size])
return np.array(data), np.array(labels)
1.4. Data separation
Then comes the creation of the data. The model will be given the last 20 recorded temperature observations, and needs to learn to predict the temperature at the next time step:
univariate_past_history = 20
univariate_future_target = 0
x_train_uni, y_train_uni = univariate_data(uni_data, 0, TRAIN_SPLIT,
univariate_past_history,
univariate_future_target)
x_val_uni, y_val_uni = univariate_data(uni_data, TRAIN_SPLIT, None,
univariate_past_history,
univariate_future_target)
2. Baseline
Before proceeding to train a model, a simple baseline was first established. Given an input point, the baseline method looks at all the history (the information given to the network is given in pink, and it must predict the value at the red cross) and predicts the next point to be the average of the last 20 observations :
3. Recurrent neural network(RNN)
In order to beat this baseline, a recurrent neural network was used to predict the temperature at time \(t\) based on the last 20 observations.
3.1. Shuffling , batching and caching data
We used tf.data to shuffle, batch, and cache the dataset:
BATCH_SIZE = 32
BUFFER_SIZE = 1000
train_univariate = tf.data.Dataset.from_tensor_slices((x_train_uni, y_train_uni))
train_univariate = train_univariate.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat()
val_univariate = tf.data.Dataset.from_tensor_slices((x_val_uni, y_val_uni))
val_univariate = val_univariate.batch(BATCH_SIZE).repeat()

3.2. Defintion of the model
Then comes the definition of the model. For this, we used an artificial recurrent neural network (RNN) architecture named Long shortterm memory (LSTM), used in the field of deep learning. Unlike standard feedforward neural networks, LSTM has feedback connections.
simple_lstm_model = tf.keras.models.Sequential([
tf.keras.layers.LSTM(8, input_shape=x_train_uni.shape[2:]),
tf.keras.layers.Dense(1)])
The model is a sequential model, which consists of two layers: an LSTM input layer composed of 8 neurons, in which we passed the argument input_shape, and a dense output layer.

Before training the model, the learning process was configured by calling the \(compile\) method, taking as optimizer the stochastic gradient descent (SGD), and as loss the root mean square error (RMSE) :
simple_lstm_model.compile(optimizer='SGD', loss=root_mean_squared_error)
3.3. Training the model
For that, we choosed an epoch of \(20\). An epoch is an arbitrary cutoff, generally defined as "one pass over the entire dataset", used to separate training into distinct phases, which is useful for logging and periodic evaluation.
EPOCHS = 20
history=simple_lstm_model.fit(train_univariate, epochs=EPOCHS, steps_per_epoch=TRAIN_SPLIT, validation_data=val_univariate, validation_steps=50)
We then obtain the following plot:
We notice that the two graphs have the same behavior:

a strong decrease in the value of the loss (which represents the root mean square error RMSE) from the 1st epoch with a value of \(0.2657\) for the training space and a value of \(0.2057\) for the validation space, to the 4th epoch with a value of \(0.0769\) for the training space and a value of \(0.0868\) for the validation space.

a slight decrease in the value of the loss over the course of the epochs from the 5th epoch onwards, converging to a value of \(0.06\).
Having obtained a low loss value, we then conclude that the model created is well and will be able to efficiently predict the value of the temperature at time \(t\) based on the last \(20\) observations.
After inputting our simple LSTM model, we confirm the last observation by making some predictions :