---
# System prepended metadata

title: 'Module 4 (Time Series): LSTM, GRU, and Deep Learning'

---

# Module 4 (Time Series): LSTM, GRU, and Deep Learning

### 1. From ARIMA to Deep Learning
Traditional models like **ARIMA** work well for linear, stationary time series.  
However, real-world data often shows **nonlinear** and **long-term** dependencies.

Deep learning models, such as **RNN**, **LSTM**, and **GRU**, are designed to handle these complex sequential patterns.

> “Unlike ARIMA, which looks only at the mathematical relationship between past and future values, LSTM learns temporal patterns directly from data.”
> 

---

### 2. Sequence Problems

In **sequence problems**, the **order of datapoints matters**.

Example:  
To predict next month’s sales, we must consider the previous months, not treat each as independent.

A standard neural network (MLP) cannot do this because:
- It assumes independence between inputs.
- It cannot retain information from past timesteps.

Hence, we need networks that **remember context** → RNNs and LSTMs.

---

### 3. Recurrent Neural Network (RNN)

#### Mechanism
RNNs pass information from one time step to the next:

$$
h_t = f(W_x x_t + W_h h_{t-1} + b)
$$
Where:
- $(x_t)$: input at time step *t*  
- $(h_{t-1})$: previous hidden state (memory)  
- $(h_t)$: current hidden state  

This allows RNNs to process **sequential data** like time series, text, or speech.

---

### 4. Long Short-Term Memory (LSTM)

#### Motivation

LSTM (Long Short-Term Memory) networks were designed to **solve the vanishing gradient problem.** They introduce a special unit called the cell state that can store information across many time steps.

This makes LSTMs ideal for long sequences where context from far back still matters.

> Imagine you’re baking a cake:  
> you remember *“add eggs”* but forget *“what shirt you wore.”*  
>  
> LSTM works the same way — it decides what to **remember**, **forget**, and **output** based on context.

#### The Three Gates of LSTM

Each LSTM cell contains three “gates” that control information flow:

| Gate | Function | Formula |
|------|-----------|----------|
| **Forget Gate** | Decides what old information to discard | $$ f_t = \sigma(W_f[h_{t-1}, x_t] + b_f) $$ |
| **Input Gate** | Adds new relevant information | $$ i_t = \sigma(W_i[h_{t-1}, x_t] + b_i) $$ |
| **Candidate State** | Generates potential new memory | $$ \tilde{C}_t = \tanh(W_c[h_{t-1}, x_t] + b_c) $$ |
| **Cell State Update** | Combines past and new information | $$ C_t = f_t * C_{t-1} + i_t * \tilde{C}_t $$ |
| **Output Gate** | Determines what to output next | $$ h_t = o_t * \tanh(C_t) $$ |

#### Explanation of Symbols

| Symbol | Meaning |
|--------|----------|
| $x_t$ | Input at time step *t* |
| $h_{t-1}$ | Hidden state from previous step |
| $C_{t-1}$ | Previous cell state |
| $\sigma$ | Sigmoid activation (values between 0–1) |
| $\tanh$ | Hyperbolic tangent activation |
| $f_t, i_t, o_t$ | Forget, Input, and Output gate activations |
| $W$ | Weight matrices for each gate |
| $b$ | Bias terms |

---

#### Information Flow Summary

1. **Forget Gate:** Decides which information from the previous state should be forgotten.  
2. **Input Gate:** Determines which new information to store in the cell state.  
3. **Cell State Update:** Combines the remembered and new information.  
4. **Output Gate:** Produces the output (hidden state) for the next step.

---

####  Example: LSTM in TensorFlow

```python
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense

# Build a simple LSTM model
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(timesteps, features)))
model.add(Dense(1))

# Compile and train
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(X_train, y_train, epochs=50, batch_size=16, verbose=1)
```

---

### 5. Gated Recurrent Unit (GRU)

The **Gated Recurrent Unit (GRU)** is a simplified version of the **LSTM** architecture.  
It was introduced by Cho et al. (2014) to **reduce the computational cost** of LSTMs while keeping similar performance.

Unlike LSTM, GRU merges the **cell state** and **hidden state** into a single vector and uses only **two gates**:
- **Update Gate**
- **Reset Gate**

This makes GRU:
- Faster to train  
- Less prone to overfitting  
- Easier to implement for smaller datasets

#### GRU Structure Overview

| Gate | Function | Formula |
|------|-----------|----------|
| **Update Gate** | Controls how much of the past information to keep | $$ z_t = \sigma(W_z \cdot [h_{t-1}, x_t]) $$ |
| **Reset Gate** | Decides how much past information to forget | $$ r_t = \sigma(W_r \cdot [h_{t-1}, x_t]) $$ |
| **Candidate Activation** | Generates a new memory content | $$ \tilde{h}_t = \tanh(W_h \cdot [r_t * h_{t-1}, x_t]) $$ |
| **Final Hidden State** | Blends old memory and new information | $$ h_t = (1 - z_t) * h_{t-1} + z_t * \tilde{h}_t $$ |



#### Explanation of Terms

| Symbol | Meaning |
|--------|----------|
| $x_t$ | Input at time step *t* |
| $h_{t-1}$ | Previous hidden state |
| $z_t$ | Update gate value |
| $r_t$ | Reset gate value |
| $\tilde{h}_t$ | Candidate hidden state |
| $\sigma$ | Sigmoid activation function |
| $\tanh$ | Hyperbolic tangent activation |
| $W_z, W_r, W_h$ | Weight matrices for each gate |



#### How GRU Works Step-by-Step

1. **Reset Gate ($r_t$):**  
   Controls how much of the previous memory to forget.  
   - If $r_t$ → 0 → the network forgets old information.  
   - If $r_t$ → 1 → the network remembers it.

2. **Update Gate ($z_t$):**  
   Determines how much of the new candidate $\tilde{h}_t$ replaces the old memory $h_{t-1}$.  
   - If $z_t$ → 0 → mostly old memory is kept.  
   - If $z_t$ → 1 → the new information dominates.

3. **Candidate State ($\tilde{h}_t$):**  
   Generates new information based on the current input and reset gate.

4. **Final Output ($h_t$):**  
   Blends the previous hidden state and the new candidate state.

---

#### Visual Comparison: LSTM vs GRU

| Feature | **LSTM** | **GRU** |
|----------|-----------|----------|
| Number of Gates | 3 (Forget, Input, Output) | 2 (Reset, Update) |
| Cell State | Yes (separate memory) | No (merged with hidden state) |
| Computation | Heavier | Lighter |
| Training Time | Slower | Faster |
| Memory Retention | Better for long sequences | Good for short to medium sequences |

#### Example: GRU in TensorFlow
```python
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import GRU, Dense

# Build a simple GRU model
model = Sequential()

# Add GRU layer
model.add(GRU(50, activation='tanh', input_shape=(timesteps, features)))

# Add output layer
model.add(Dense(1))

# Compile the model
model.compile(optimizer='adam', loss='mean_squared_error')

# Train the model
history = model.fit(
    X_train,
    y_train,
    epochs=50,
    batch_size=16,
    validation_data=(X_val, y_val),
    verbose=1
)
```

---

### 6. Attention Mechanism

Even with powerful models like **LSTM** or **GRU**, there’s a limitation —  
they must compress **all past information into a single hidden state vector**.

This makes it difficult for the model to:
- Remember long-term dependencies  
- Focus on relevant parts of the input sequence  
- Interpret which parts of the past data influence the prediction  

The **Attention Mechanism** solves this by allowing the model to **“focus” on specific time steps or features** dynamically — much like how humans pay attention to certain words in a sentence or key points in a graph.

> Imagine you’re reading a paragraph to answer a question.  
> You don’t reread every word — you focus only on **the relevant sentences**.  
>  
> Similarly, the attention mechanism lets a neural network assign **weights** to important time steps and ignore the rest.

#### Core Idea

At each prediction step, the model computes a **weighted average** of all previous hidden states —  
the more relevant a hidden state is, the **higher its weight (attention score)**.


#### Mathematical Formulation

1. **Score Function** — measures how relevant each previous hidden state is to the current step:

$$
e_{t,i} = \text{score}(h_t, h_i)
$$

2. **Softmax Normalization** — converts scores into probabilities (attention weights):

$$
\alpha_{t,i} = \frac{\exp(e_{t,i})}{\sum_j \exp(e_{t,j})}
$$

3. **Context Vector** — combines all hidden states using the attention weights:

$$
c_t = \sum_i \alpha_{t,i} h_i
$$

4. **Final Output** — uses both context and current state to make prediction:

$$
\tilde{h}_t = \tanh(W_c [c_t ; h_t])
$$


#### Explanation of Symbols

| Symbol | Meaning |
|--------|----------|
| $h_t$ | Hidden state at time *t* |
| $e_{t,i}$ | Alignment score between current and past state |
| $\alpha_{t,i}$ | Attention weight (importance of each past step) |
| $c_t$ | Context vector (weighted sum of past hidden states) |
| $W_c$ | Weight matrix for combining context and hidden state |

---

#### Common Attention Types

| Type | Description | Example Formula |
|------|--------------|----------------|
| **Dot Product (Luong)** | Computes similarity via dot product | $$ e_{t,i} = h_t^\top h_i $$ |
| **Additive (Bahdanau)** | Uses a small neural network to learn attention weights | $$ e_{t,i} = v_a^\top \tanh(W_1 h_t + W_2 h_i) $$ |
| **Scaled Dot Product** | Normalizes dot product by vector dimension | $$ e_{t,i} = \frac{h_t^\top h_i}{\sqrt{d_k}} $$ |


### Attention in Time Series Forecasting

For time series, attention allows the model to **focus on important time periods**  
for example, recent spikes or seasonal peaks rather than treating every point equally.

The model can learn:
- Which past observations are most influential  
- Which features (variables) are relevant  
- How to dynamically shift focus across time

---

#### Example: Add Attention Layer (Keras)

```python
import tensorflow as tf
from tensorflow.keras.layers import Layer

# Custom Attention Layer
class Attention(Layer):
    def __init__(self):
        super(Attention, self).__init__()

    def call(self, query, values):
        # Compute attention scores
        score = tf.matmul(query, values, transpose_b=True)
        weights = tf.nn.softmax(score, axis=-1)
        # Weighted sum of values
        context = tf.matmul(weights, values)
        return context, weights

# Example usage with LSTM
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, LSTM, Dense

timesteps = 10
features = 16

inputs = Input(shape=(timesteps, features))
lstm_out = LSTM(64, return_sequences=True)(inputs)
context, attention_weights = Attention()(lstm_out, lstm_out)
output = Dense(1)(context[:, -1, :])

model = Model(inputs, output)
model.compile(optimizer='adam', loss='mse')
model.summary()
```

---
### 7. Combining Attention with LSTM / GRU for Time Series Forecasting

While **LSTM** and **GRU** can capture temporal dependencies,  
they treat all past time steps *equally* once the information is passed through the hidden state.

However, in real-world **time series**, not every past event has the same importance.  
Some periods or spikes (like holidays, sudden market shifts, or recent trends)  
are far more relevant than others.

The solution?
**Combine LSTM/GRU with an Attention Mechanism**  
to create a model that can both **learn long-term dependencies** and **focus on important time steps**.


> Imagine you’re predicting next month’s sales.  
> You remember trends from past years (long-term memory, LSTM)  
> but focus more on recent promotional months or holidays (attention).  
>  
> The hybrid Attention–LSTM model does exactly this.

---

#### Architecture Overview

The Attention–LSTM model works in three main steps:

1. **Sequence Encoding:**  
   LSTM (or GRU) encodes temporal dependencies from input sequences.

2. **Attention Layer:**  
   Assigns *importance weights* to each hidden state output of the LSTM.

3. **Weighted Context Vector:**  
   Combines the most relevant hidden states for the final prediction.


#### Mathematical Representation

Let’s formalize this process.

1. **LSTM Output Sequence**
   $$
   H = [h_1, h_2, ..., h_T]
   $$

2. **Attention Weights**
   $$
   \alpha_t = \frac{\exp(\text{score}(h_t))}{\sum_{i=1}^{T} \exp(\text{score}(h_i))}
   $$

3. **Context Vector (Weighted Average)**
   $$
   c = \sum_{t=1}^{T} \alpha_t h_t
   $$

4. **Final Prediction**
   $$
   \hat{y} = \sigma(W_c c + b)
   $$

The result $\hat{y}$ is your model’s time-series forecast (e.g., next month’s value).

---

#### Advantages of Attention–LSTM

| Feature | Description |
|----------|-------------|
| **Selective Focus** | The model learns which time steps are most important. |
| **Interpretability** | You can visualize attention weights to understand model reasoning. |
| **Improved Accuracy** | Helps capture both short- and long-term dependencies. |
| **Robustness** | Performs better on irregular or noisy time series. |


#### Implementation Example: Attention LSTM in TensorFlow

```python
import tensorflow as tf
from tensorflow.keras.layers import Input, LSTM, Dense, Layer
from tensorflow.keras.models import Model

# Custom Attention Layer
class Attention(Layer):
    def __init__(self):
        super(Attention, self).__init__()

    def call(self, inputs):
        query = inputs
        score = tf.matmul(query, query, transpose_b=True)
        weights = tf.nn.softmax(score, axis=-1)
        context = tf.matmul(weights, query)
        return context, weights

# Input and LSTM encoding
timesteps = 10
features = 8

inputs = Input(shape=(timesteps, features))
lstm_out = LSTM(64, return_sequences=True)(inputs)

# Attention mechanism applied on LSTM output
context, attention_weights = Attention()(lstm_out)

# Final dense layer for regression output
output = Dense(1)(context[:, -1, :])

model = Model(inputs=inputs, outputs=output)
model.compile(optimizer='adam', loss='mse')

model.summary()
```
---
### 8. Hyperparameter Tuning & Regularization in Time Series Models

Deep learning models such as **LSTM**, **GRU**, and **Attention-LSTM** are powerful —  
but also **sensitive to hyperparameters** like learning rate, number of layers, and batch size.

Poor tuning can cause:
- Overfitting: model memorizes training data but fails on unseen data  
- Underfitting: model too simple to capture patterns  
- Slow convergence: model trains too long or oscillates

To achieve reliable forecasting performance, we need **systematic tuning and regularization**.

---

#### What Are Hyperparameters?

Hyperparameters are **configuration variables** set before training begins.

| Category | Examples |
|-----------|-----------|
| **Model Architecture** | Number of layers, units per layer |
| **Training Process** | Learning rate, batch size, optimizer |
| **Regularization** | Dropout rate, early stopping, L2 penalty |
| **Sequence Settings** | Time steps, look-back window |

---

#### Key Hyperparameters in LSTM/GRU

| Hyperparameter | Description | Typical Range |
|----------------|-------------|----------------|
| **Learning Rate (η)** | Controls step size of updates | 1e-5 → 1e-2 |
| **Batch Size** | Number of samples per gradient update | 16 → 128 |
| **Hidden Units** | Neurons per LSTM/GRU layer | 32 → 256 |
| **Epochs** | Training cycles over dataset | 50 → 300 |
| **Dropout Rate** | Fraction of neurons to drop | 0.1 → 0.5 |
| **Optimizer** | Controls weight update method | Adam, RMSprop, SGD |
| **Lookback Window** | Number of time steps considered | 7 → 60 |


#### Tuning Strategy

A common process to tune models efficiently:

1. **Start Simple** — Train baseline LSTM with default parameters.  
2. **Adjust Learning Rate** — Use learning rate scheduling or find best η via experiments.  
3. **Tune Units & Layers** — Increase gradually until validation loss stops improving.  
4. **Regularize** — Add dropout, L2 penalties, or early stopping.  
5. **Optimize Batch Size** — Balance speed and stability.  
6. **Monitor Validation Metrics** — Plot training vs validation loss over time.


#### Example: Learning Rate Update Formula

The parameter update for gradient descent:

$$
\theta_{t+1} = \theta_t - \eta \cdot \nabla_\theta J(\theta_t)
$$

where:
- $\theta_t$ = model parameters  
- $\eta$ = learning rate  
- $\nabla_\theta J(\theta_t)$ = gradient of loss function

If $\eta$ is too large → overshoot minimum  
If $\eta$ is too small → slow convergence

---

#### Example: Implementing Dropout & Early Stopping

```python
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping

# Build model with Dropout
model = Sequential()
model.add(LSTM(128, activation='tanh', return_sequences=True, input_shape=(timesteps, features)))
model.add(Dropout(0.3))
model.add(LSTM(64, activation='tanh'))
model.add(Dropout(0.3))
model.add(Dense(1))

# Compile model
model.compile(optimizer='adam', loss='mse')

# Early stopping callback
early_stop = EarlyStopping(
    monitor='val_loss',
    patience=10,          # stop after 10 epochs of no improvement
    restore_best_weights=True
)

# Train model
history = model.fit(
    X_train, y_train,
    epochs=200,
    batch_size=32,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    verbose=1
)
```
---
### 9. Summary
In this module, we explored how **deep learning models** can be applied to **time series forecasting**, moving from simple recurrent structures to advanced attention-based architectures.

We covered:
1. The fundamentals of **sequence modeling**  
2. Classic recurrent models — **RNN**, **LSTM**, **GRU**  
3. Modern improvements — **Attention Mechanisms**  
4. **Hybrid models** that combine memory and focus  
5. Techniques for **tuning, regularization, and interpretation**  
6. A **real-world case study** using Attention-LSTM on airline data  

---

#### Evolution of Time Series Models

| Stage | Model | Key Capability | Limitation |
|--------|--------|----------------|-------------|
| **1️⃣** | RNN | Sequential modeling of data | Vanishing gradients, short-term memory |
| **2️⃣** | LSTM | Long-term dependency capture via cell states | Computationally expensive |
| **3️⃣** | GRU | Simplified memory gating (faster) | Slightly less expressive |
| **4️⃣** | Attention | Focus on relevant time steps dynamically | Heavier computation |
| **5️⃣** | Hybrid (Attention-LSTM/GRU) | Combines memory + interpretability | Requires careful tuning |

> “Each step in evolution wasn’t a replacement, it was an improvement.”

#### Comparative Summary

| Model | Memory | Interpretability | Computation | Use Case |
|--------|----------|------------------|--------------|-----------|
| **RNN** | Short-term | Low | Fast | Simple sequential data |
| **LSTM** | Long-term | Medium | 🕓 Slow | Seasonal/long dependencies |
| **GRU** | Medium-long | Medium | Faster | Faster training with less data |
| **Attention** | Variable focus | High | Heavy | Long/complex dependencies |
| **Attention-LSTM** | Long + Focused | High | Moderate | Accurate, explainable forecasting |



#### Practical Guidelines for Future Projects

1. **Start simple, scale up**: always benchmark against a basic LSTM or ARIMA.  
2. **Watch for overfitting**: use dropout, early stopping, and regularization.  
3. **Visualize your model’s reasoning**: plot attention weights or feature importances.  
4. **Tune systematically**: adjust learning rate, lookback window, and units gradually.  
5. **Document experiments**: reproducibility is key in forecasting research.  
6. **Interpret results**: always link predictions back to domain insights.

#### The Future of Time Series Forecasting

Modern forecasting is shifting towards:
- **Transformers for Time Series** (e.g., *Informer*, *Autoformer*)  
- **Hybrid Neural–Statistical Models** (combining ARIMA + DL)  
- **Multimodal Forecasting** (mixing images, text, and sensors)  
- **Explainable AI (XAI)** for better decision trust  
---
### Recommended Reading

- https://medium.com/data-science-collective/future-forecasting-of-time-series-using-lstm-a-quick-guide-for-business-leaders-370661c574c9
- https://medium.com/@myskill.id/time-series-prediction-lstm-759763728c48

## Notebook Example

This notebook provides a practical implementation of an Attention-based LSTM model for time series forecasting, demonstrating how attention mechanisms enhance interpretability and accuracy in sequential data prediction.

https://colab.research.google.com/drive/1mW44w-OBS_720puYvpx4oZ0i6ADrKbil?usp=sharing