Section 1: Introduction¶
The primary objective of this analysis is to predict the stock market price of Microsoft using deep learning methods. Leveraging historical stock market data from March 13, 1986, to December 29, 2023, the aim is to develop a robust predictive model that assists in forecasting Microsoft's stock prices.
# Importing necessary library
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
import warnings
warnings.filterwarnings('ignore')
# Reading the dataset 'MSFT.csv' into a DataFrame
df = pd.read_csv('MSFT.csv', index_col=False) # 'MSFT.csv' file contains the historical data of Microsoft stock prices.
df
| Date | Open | High | Low | Close | Volume | |
|---|---|---|---|---|---|---|
| 0 | 1986-03-13 00:00:00-05:00 | 0.054485 | 0.062498 | 0.054485 | 0.059827 | 1031788800 |
| 1 | 1986-03-14 00:00:00-05:00 | 0.059827 | 0.063032 | 0.059827 | 0.061963 | 308160000 |
| 2 | 1986-03-17 00:00:00-05:00 | 0.061963 | 0.063566 | 0.061963 | 0.063032 | 133171200 |
| 3 | 1986-03-18 00:00:00-05:00 | 0.063032 | 0.063566 | 0.060895 | 0.061429 | 67766400 |
| 4 | 1986-03-19 00:00:00-05:00 | 0.061429 | 0.061963 | 0.059827 | 0.060361 | 47894400 |
| ... | ... | ... | ... | ... | ... | ... |
| 9753 | 2024-11-22 00:00:00-05:00 | 411.369995 | 417.399994 | 411.059998 | 417.000000 | 24814600 |
| 9754 | 2024-11-25 00:00:00-05:00 | 418.380005 | 421.079987 | 414.850006 | 418.790008 | 27691100 |
| 9755 | 2024-11-26 00:00:00-05:00 | 419.589996 | 429.040008 | 418.850006 | 427.989990 | 23458900 |
| 9756 | 2024-11-27 00:00:00-05:00 | 425.109985 | 427.230011 | 422.019989 | 422.989990 | 18332400 |
| 9757 | 2024-11-29 00:00:00-05:00 | 420.089996 | 424.880005 | 417.799988 | 423.459992 | 16271900 |
9758 rows × 6 columns
Section 2: Data Exploration¶
Data Overview¶
The initial exploration involved loading the dataset, containing attributes such as 'Date,' 'Open,' 'High,' 'Low,' 'Close,' 'Adjusted Close,' and 'Volume.' Subsequently, the DataFrame was refined to retain only the 'Date' and 'Close' columns, essential for modeling purposes.
# Subsetting the DataFrame to retain only the 'Date' and 'Close' columns
df = df[['Date','Close']]
df
| Date | Close | |
|---|---|---|
| 0 | 1986-03-13 00:00:00-05:00 | 0.059827 |
| 1 | 1986-03-14 00:00:00-05:00 | 0.061963 |
| 2 | 1986-03-17 00:00:00-05:00 | 0.063032 |
| 3 | 1986-03-18 00:00:00-05:00 | 0.061429 |
| 4 | 1986-03-19 00:00:00-05:00 | 0.060361 |
| ... | ... | ... |
| 9753 | 2024-11-22 00:00:00-05:00 | 417.000000 |
| 9754 | 2024-11-25 00:00:00-05:00 | 418.790008 |
| 9755 | 2024-11-26 00:00:00-05:00 | 427.989990 |
| 9756 | 2024-11-27 00:00:00-05:00 | 422.989990 |
| 9757 | 2024-11-29 00:00:00-05:00 | 423.459992 |
9758 rows × 2 columns
Date Type Conversion¶
Data Type Conversion for 'Date' Column , The provided code segment aims to convert the 'Date' column, initially of type 'object', into a datetime type for enhanced temporal analysis.
- The code initializes a function str_to_datetime that accepts a date string in 'YYYY-MM-DD' format and converts it into a datetime object.
- str_to_datetime uses the datetime.datetime constructor to create a datetime object from the split year, month, and day values of the input string.
- An example conversion is demonstrated for the date '1986-03-13' to showcase the conversion process.
# Displaying the 'Date' column from the DataFrame
df['Date']
0 1986-03-13 00:00:00-05:00
1 1986-03-14 00:00:00-05:00
2 1986-03-17 00:00:00-05:00
3 1986-03-18 00:00:00-05:00
4 1986-03-19 00:00:00-05:00
...
9753 2024-11-22 00:00:00-05:00
9754 2024-11-25 00:00:00-05:00
9755 2024-11-26 00:00:00-05:00
9756 2024-11-27 00:00:00-05:00
9757 2024-11-29 00:00:00-05:00
Name: Date, Length: 9758, dtype: object
#show exactly what column names are available
print("Column names:")
print(df.columns.tolist())
Column names: ['Date', 'Close']
print("Current columns:", df.columns.tolist())
print("Index name:", df.index.name)
print("DataFrame shape:", df.shape)
print("\nFirst few rows:")
print(df.head())
Current columns: ['Date', 'Close']
Index name: None
DataFrame shape: (9758, 2)
First few rows:
Date Close
0 1986-03-13 00:00:00-05:00 0.059827
1 1986-03-14 00:00:00-05:00 0.061963
2 1986-03-17 00:00:00-05:00 0.063032
3 1986-03-18 00:00:00-05:00 0.061429
4 1986-03-19 00:00:00-05:00 0.060361
# Reset index to turn 'Date' back into a column
df = df.reset_index()
# Now convert the 'Date' column
df['Date'] = pd.to_datetime(df['Date'], errors='coerce', utc=True)
# Set it back as index
df.set_index('Date', inplace=True)
# Optional: Check for failed conversions
if df.index.isna().sum() > 0:
print("\n⚠️ Warning: Some dates could not be parsed:")
print(df[df.index.isna()])
# this confirm the index is datetime
print("Index dtype:", df.index.dtype)
print("Is index tz-aware?", df.index.tz is not None)
Index dtype: datetime64[ns, UTC] Is index tz-aware? True
print("Current columns:", df.columns.tolist())
print("Index name:", df.index.name)
print("Index type:", type(df.index))
print("First few index values:")
print(df.index[:5])
Current columns: ['index', 'Close']
Index name: Date
Index type: <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
First few index values:
DatetimeIndex(['1986-03-13 05:00:00+00:00', '1986-03-14 05:00:00+00:00',
'1986-03-17 05:00:00+00:00', '1986-03-18 05:00:00+00:00',
'1986-03-19 05:00:00+00:00'],
dtype='datetime64[ns, UTC]', name='Date', freq=None)
# Clean It Up
# Drop the 'index' column if it's not needed
if 'index' in df.columns:
df = df.drop(columns=['index'])
# Optional: Verify
print("Final columns:", df.columns.tolist())
print("Index name:", df.index.name)
Final columns: ['Close'] Index name: Date
# This to confirm everything is perfect
print("✅ DataFrame Info:")
print(df.info())
print("\n✅ First few rows:")
print(df.head())
print("\n✅ Index type:")
print(type(df.index))
print("Index dtype:", df.index.dtype)
print("Is index tz-aware?", df.index.tz is not None)
✅ DataFrame Info:
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 9758 entries, 1986-03-13 05:00:00+00:00 to 2024-11-29 05:00:00+00:00
Data columns (total 1 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Close 9758 non-null float64
dtypes: float64(1)
memory usage: 152.5 KB
None
✅ First few rows:
Close
Date
1986-03-13 05:00:00+00:00 0.059827
1986-03-14 05:00:00+00:00 0.061963
1986-03-17 05:00:00+00:00 0.063032
1986-03-18 05:00:00+00:00 0.061429
1986-03-19 05:00:00+00:00 0.060361
✅ Index type:
<class 'pandas.core.indexes.datetimes.DatetimeIndex'>
Index dtype: datetime64[ns, UTC]
Is index tz-aware? True
import matplotlib.pyplot as plt
# Plotting the 'Close' values against the DataFrame index
plt.plot(df.index, df['Close'])
[<matplotlib.lines.Line2D at 0x1e32097afc0>]
Section 3: Data Preparation for Modeling¶
Windowed DataFrame Generation¶
This section involves the creation of a windowed DataFrame, leveraging the function df_to_windowed_df, to structure the existing dataset for analysis. The generated windowed DataFrame aims to organize data into specific windows, setting the groundwork for subsequent time-based analysis or modeling tasks.
Purpose:¶
- Windowed DataFrame Creation: Focuses on segmenting the original dataset into distinct windows, allowing for structured representation suitable for time series or sequential analysis.
- Data Transformation for Analysis: Following the windowed DataFrame creation, on transforming this structured data into organized components—dates, input features (X), and output values (y)—essential for predictive modeling tasks or time-based analysis.
Code Complexity and Development Effort:¶
Code Complexity: Moderate-to-high due to date handling, DataFrame manipulation, and iterative processes involved.
Development Effort: Significant time and effort invested in comprehending, developing, and refining code functionalities, requiring thorough investigation and testing.
Overview:¶
The combined sections involve the creation of a windowed DataFrame and its conversion into structured data components—dates, input features (X), and output values (y). This process significantly contributes to the preparation of data suitable for predictive modeling, emphasizing the importance of organized data for time series analysis and forecasting tasks.
# Define helper function to convert string dates to datetime objects
def str_to_datetime(s):
"""
Convert string in format 'YYYY-MM-DD' to datetime object
Args:
s (str): Date string in format 'YYYY-MM-DD'
Returns:
datetime: Converted datetime object
"""
return datetime.strptime(s, '%Y-%m-%d')
print("✅ str_to_datetime function defined")
print("Example usage:", str_to_datetime('2020-01-01'))
✅ str_to_datetime function defined Example usage: 2020-01-01 00:00:00
def df_to_windowed_df(dataframe, first_date_str, last_date_str, n=3):
"""
Create a windowed DataFrame for supervised learning.
This function transforms sequential time series data into a format where:
- Input (X): Previous n days' closing prices
- Output (y): Next day's closing price
Args:
dataframe (pd.DataFrame): DataFrame with DatetimeIndex and 'Close' column
first_date_str (str): Start date as 'YYYY-MM-DD'
last_date_str (str): End date as 'YYYY-MM-DD'
n (int): Number of previous days to use for prediction (default: 3)
Returns:
pd.DataFrame: Windowed DataFrame with columns:
['Target Date', 'Target-3', 'Target-2', 'Target-1', 'Target']
"""
# Convert string dates to datetime objects
start_date = str_to_datetime(first_date_str)
end_date = str_to_datetime(last_date_str)
# Filter data within the specified date range
filtered_df = dataframe[(dataframe.index >= start_date) &
(dataframe.index <= end_date)]
# Check if we have any data in the specified range
if filtered_df.empty:
print(f"⚠️ Warning: No data found between {first_date_str} and {last_date_str}")
return pd.DataFrame()
# Initialize lists to store the results
dates = []
X, Y = [], []
# Loop through the data to create sliding windows
for i in range(n, len(filtered_df)):
# Get the target date (current row)
target_date = filtered_df.index[i]
# Get the n+1 previous values (for input and target)
window_values = filtered_df['Close'].iloc[i-n:i+1].values
# Split into input (X) and output (y)
x = window_values[:-1] # First n values (previous days)
y = window_values[-1] # Last value (target day)
# Store the results
dates.append(target_date)
X.append(x)
Y.append(y)
# Create result DataFrame if we have data
if not dates:
print(f"⚠️ Warning: Not enough data for window size n={n}")
return pd.DataFrame()
# Create the result DataFrame
result_df = pd.DataFrame({'Target Date': dates})
# Add input features (X values)
X_array = np.array(X)
for i in range(n):
result_df[f'Target-{n-i}'] = X_array[:, i]
# Add target values (y)
result_df['Target'] = Y
return result_df
print("✅ df_to_windowed_df function defined")
print("Function docstring:", df_to_windowed_df.__doc__[:200] + "...")
✅ df_to_windowed_df function defined
Function docstring:
Create a windowed DataFrame for supervised learning.
This function transforms sequential time series data into a format where:
- Input (X): Previous n days' closing prices
- Outp...
# After setting the index, ensure it's a proper DatetimeIndex
df.index = pd.to_datetime(df.index, errors='coerce', utc=True)
# Verify
print("Index type after fix:", type(df.index))
print("Index dtype after fix:", df.index.dtype)
Index type after fix: <class 'pandas.core.indexes.datetimes.DatetimeIndex'> Index dtype after fix: datetime64[ns, UTC]
# Define helper function
def str_to_datetime(s):
"""Convert string to timezone-aware datetime (UTC)."""
return pd.to_datetime(s, utc=True)
# Create windowed data
print("🔄 Creating windowed DataFrame...")
windowed_df = df_to_windowed_df(
dataframe=df,
first_date_str='2018-12-29',
last_date_str='2023-12-29',
n=3
)
if not windowed_df.empty:
print(f"✅ Windowed data created: {windowed_df.shape[0]} samples")
print("\nFirst few rows:")
print(windowed_df.head())
else:
print("❌ Failed to create windowed data")
🔄 Creating windowed DataFrame... ⚠️ Warning: No data found between 2018-12-29 and 2023-12-29 ❌ Failed to create windowed data
print("📊 Actual Date Range in Your Data:")
print(f"Min date: {df.index.min()}")
print(f"Max date: {df.index.max()}")
# Show first few and last few dates
print("\n📅 First 5 dates:")
print(df.index[:5])
print("\n📅 Last 5 dates:")
print(df.index[-5:])
📊 Actual Date Range in Your Data:
Min date: 1970-01-01 00:00:00+00:00
Max date: 1970-01-01 00:00:00.000009757+00:00
📅 First 5 dates:
DatetimeIndex([ '1970-01-01 00:00:00+00:00',
'1970-01-01 00:00:00.000000001+00:00',
'1970-01-01 00:00:00.000000002+00:00',
'1970-01-01 00:00:00.000000003+00:00',
'1970-01-01 00:00:00.000000004+00:00'],
dtype='datetime64[ns, UTC]', freq=None)
📅 Last 5 dates:
DatetimeIndex(['1970-01-01 00:00:00.000009753+00:00',
'1970-01-01 00:00:00.000009754+00:00',
'1970-01-01 00:00:00.000009755+00:00',
'1970-01-01 00:00:00.000009756+00:00',
'1970-01-01 00:00:00.000009757+00:00'],
dtype='datetime64[ns, UTC]', freq=None)
actual_start = df.index.min().strftime('%Y-%m-%d')
actual_end = df.index.max().strftime('%Y-%m-%d')
print(f"✅ Using actual date range: {actual_start} to {actual_end}")
windowed_df = df_to_windowed_df(
dataframe=df,
first_date_str=actual_start,
last_date_str=actual_end,
n=3
)
✅ Using actual date range: 1970-01-01 to 1970-01-01 ⚠️ Warning: Not enough data for window size n=3
print("🔍 First 20 raw Date values:")
print(df['Date'].head(20).tolist())
print("\n📊 Unique Date Values (Top 10):")
print(df['Date'].value_counts().head(10))
print("\n❌ How many NaT (Not a Time) values?")
print(df['Date'].isna().sum())
🔍 First 20 raw Date values: ['1986-03-13 00:00:00-05:00', '1986-03-14 00:00:00-05:00', '1986-03-17 00:00:00-05:00', '1986-03-18 00:00:00-05:00', '1986-03-19 00:00:00-05:00', '1986-03-20 00:00:00-05:00', '1986-03-21 00:00:00-05:00', '1986-03-24 00:00:00-05:00', '1986-03-25 00:00:00-05:00', '1986-03-26 00:00:00-05:00', '1986-03-27 00:00:00-05:00', '1986-03-31 00:00:00-05:00', '1986-04-01 00:00:00-05:00', '1986-04-02 00:00:00-05:00', '1986-04-03 00:00:00-05:00', '1986-04-04 00:00:00-05:00', '1986-04-07 00:00:00-05:00', '1986-04-08 00:00:00-05:00', '1986-04-09 00:00:00-05:00', '1986-04-10 00:00:00-05:00'] 📊 Unique Date Values (Top 10): Date 1986-03-13 00:00:00-05:00 1 2011-12-30 00:00:00-05:00 1 2011-12-20 00:00:00-05:00 1 2011-12-21 00:00:00-05:00 1 2011-12-22 00:00:00-05:00 1 2011-12-23 00:00:00-05:00 1 2011-12-27 00:00:00-05:00 1 2011-12-28 00:00:00-05:00 1 2011-12-29 00:00:00-05:00 1 2012-01-03 00:00:00-05:00 1 Name: count, dtype: int64 ❌ How many NaT (Not a Time) values? 0
# Try common formats
df['Date'] = pd.to_datetime(df['Date'], errors='coerce', utc=True, format='%b %d, %Y')
# If that doesn't work, try without format (let pandas guess)
df['Date'] = pd.to_datetime(df['Date'], errors='coerce', utc=True)
# Or try day-first format (common outside US)
df['Date'] = pd.to_datetime(df['Date'], errors='coerce', utc=True, dayfirst=True)
# Convert to string first
df['Date'] = df['Date'].astype(str)
# Now strip whitespace
df['Date'] = df['Date'].str.strip()
# Then convert back to datetime
df['Date'] = pd.to_datetime(df['Date'], errors='coerce', utc=True)
# Keep only Date and Close columns
df = df[['Date', 'Close']].copy()
# Step 1: Convert to string (to handle any type)
df['Date'] = df['Date'].astype(str)
# Step 2: Clean whitespace
df['Date'] = df['Date'].str.strip()
# Step 3: Optional - Remove extra characters if needed
# df['Date'] = df['Date'].str.replace(r'[^a-zA-Z0-9\-/:\s]', '', regex=True)
# Step 4: Try parsing with multiple formats
formats = [
'%Y-%m-%d',
'%b %d, %Y',
'%B %d, %Y',
'%d/%m/%Y',
'%m/%d/%Y',
'%Y/%m/%d'
]
for fmt in formats:
print(f"Trying format: {fmt}")
temp = pd.to_datetime(df['Date'], errors='coerce', utc=True, format=fmt)
if temp.notna().sum() > 0:
print(f"✅ Found {temp.notna().sum()} valid dates using {fmt}")
df['Date'] = temp
break
# If no format worked, try without format (pandas will guess)
if df['Date'].isna().sum() == len(df):
print("⚠️ No format worked. Trying pandas auto-parse...")
df['Date'] = pd.to_datetime(df['Date'], errors='coerce', utc=True)
# Set index
df.set_index('Date', inplace=True)
df.sort_index(inplace=True)
df.dropna(inplace=True)
# Verify
print(f"\n✅ Final Data Range: {df.index.min()} to {df.index.max()}")
print(f"Number of rows: {len(df)}")
# Show first few dates
print("\n📅 First 5 dates:")
print(df.index[:5])
Trying format: %Y-%m-%d Trying format: %b %d, %Y Trying format: %B %d, %Y Trying format: %d/%m/%Y Trying format: %m/%d/%Y Trying format: %Y/%m/%d ✅ Final Data Range: NaT to NaT Number of rows: 9758 📅 First 5 dates: Index(['NaT', 'NaT', 'NaT', 'NaT', 'NaT'], dtype='object', name='Date')
# Keep only Date and Close columns
df = df[['Date', 'Close']].copy()
# Step 1: Convert to string (to handle any type)
df['Date'] = df['Date'].astype(str)
# Step 2: Clean whitespace
df['Date'] = df['Date'].str.strip()
# Step 3: Optional - Remove extra characters if needed
# df['Date'] = df['Date'].str.replace(r'[^a-zA-Z0-9\-/:\s]', '', regex=True)
# Step 4: Try parsing with multiple formats
formats = [
'%Y-%m-%d',
'%b %d, %Y',
'%B %d, %Y',
'%d/%m/%Y',
'%m/%d/%Y',
'%Y/%m/%d'
]
for fmt in formats:
print(f"Trying format: {fmt}")
temp = pd.to_datetime(df['Date'], errors='coerce', utc=True, format=fmt)
if temp.notna().sum() > 0:
print(f"✅ Found {temp.notna().sum()} valid dates using {fmt}")
df['Date'] = temp
break
# If no format worked, try without format (pandas will guess)
if df['Date'].isna().sum() == len(df):
print("⚠️ No format worked. Trying pandas auto-parse...")
df['Date'] = pd.to_datetime(df['Date'], errors='coerce', utc=True)
# Set index
df.set_index('Date', inplace=True)
df.sort_index(inplace=True)
df.dropna(inplace=True)
# Verify
print(f"\n✅ Final Data Range: {df.index.min()} to {df.index.max()}")
print(f"Number of rows: {len(df)}")
# Show first few dates
print("\n📅 First 5 dates:")
print(df.index[:5])
Trying format: %Y-%m-%d Trying format: %b %d, %Y Trying format: %B %d, %Y Trying format: %d/%m/%Y Trying format: %m/%d/%Y Trying format: %Y/%m/%d ✅ Final Data Range: NaT to NaT Number of rows: 9758 📅 First 5 dates: Index(['NaT', 'NaT', 'NaT', 'NaT', 'NaT'], dtype='object', name='Date')
In this final part of this section, the code finalizes the dataset preparation by dividing it into distinct subsets: training, validation, and test sets. The dataset is segmented into these subsets based on defined proportions: 80% for training, 10% for validation, and 10% for testing. This partitioning is vital for evaluating the model's performance on unseen data after training and validation. The code then uses Matplotlib to create a visual representation, plotting each set—training, validation, and test—across time, enabling a clear comparison between these partitions. This step ensures that the model is trained on a substantial portion of the data, validated on a separate portion, and ultimately tested on completely unseen data to gauge its real-world predictive capacity.
# Splitting dataset into training, validation, and test sets
q_80 = int(len(dates) * .8) # Index for 80% of the dataset
q_90 = int(len(dates) * .9) # Index for 90% of the dataset
dates_train, X_train, y_train = dates[:q_80], X[:q_80], y[:q_80] # Training set
dates_val, X_val, y_val = dates[q_80:q_90], X[q_80:q_90], y[q_80:q_90] # Validation set
dates_test, X_test, y_test = dates[q_90:], X[q_90:], y[q_90:] # Test set
# Plotting the subsets
plt.figure(figsize=(10,6))
plt.plot(dates_train, y_train)
plt.plot(dates_val, y_val)
plt.plot(dates_test, y_test)
plt.legend(['Train', 'Validation', 'Test'])
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[43], line 2 1 # Splitting dataset into training, validation, and test sets ----> 2 q_80 = int(len(dates) * .8) # Index for 80% of the dataset 3 q_90 = int(len(dates) * .9) # Index for 90% of the dataset 5 dates_train, X_train, y_train = dates[:q_80], X[:q_80], y[:q_80] # Training set NameError: name 'dates' is not defined
Section 4 : Section 4: LSTM Model Building¶
Overview:¶
This section focuses on the construction and evaluation of a Long Short-Term Memory (LSTM) model using TensorFlow and Keras. The LSTM architecture is a type of recurrent neural network (RNN) known for its ability to process sequences of data and capture long-term dependencies within the data.
Purpose:¶
- Model Construction: The code segment initiates the construction of the LSTM model using TensorFlow and Keras. The model consists of an input layer, LSTM layer, and several densely connected layers.
- Model Compilation: It compiles the model with mean squared error (MSE) as the loss function and the Adam optimizer with a specific learning rate.
- Model Training: The model is trained on the training set (X_train, y_train) for a specified number of epochs, and its performance is evaluated on the validation set (X_val, y_val).
- Prediction Visualization: Subsequent code segments generate visualizations showcasing the model's predictions against the observed values for the validation and test datasets, providing insights into the model's performance.
- Recursive Predictions: Another part of the code generates recursive predictions where the model forecasts the next value based on its previous prediction, iterating over the validation and test sets.
Methodology:¶
- Model Architecture: Sequential model with an LSTM layer followed by densely connected layers with ReLU activation.
- Model Training: Trained using the training data with a defined optimizer and loss function.
- Prediction Evaluation: Compares model predictions with observed values for validation and test datasets, assessing model performance.
- Recursive Forecasting: Generates predictions recursively, where each prediction feeds into the model for the next prediction iteration. This methodology aims to showcase the LSTM model's ability to forecast time series data and assess its performance against different subsets of the dataset.
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adam
from tensorflow.keras import layers
# Create a sequential model
model = Sequential([layers.Input((3, 1)), # Input layer with shape (3, 1)
layers.LSTM(64), # LSTM layer with 64 units
layers.Dense(32, activation='relu'), # Dense layer with 32 units and ReLU activation
layers.Dense(32, activation='relu'), # Another dense layer with 32 units and ReLU activation
layers.Dense(1)]) # Output layer with single unit
# Compile the model with mean squared error loss, Adam optimizer with a specific learning rate,
# and mean absolute error as a metric
model.compile(loss='mse',
optimizer=Adam(learning_rate=0.001),
metrics=['mean_absolute_error'])
# Train the model on the training set (X_train, y_train) using validation data (X_val, y_val) for 100 epochs
model.fit(X_train, y_train, validation_data=(X_val, y_val), epochs=100)
Epoch 1/100 32/32 [==============================] - 5s 31ms/step - loss: 50726.1445 - mean_absolute_error: 215.5414 - val_loss: 79808.7656 - val_mean_absolute_error: 280.3969 Epoch 2/100 32/32 [==============================] - 0s 8ms/step - loss: 48372.3711 - mean_absolute_error: 210.0076 - val_loss: 74484.0781 - val_mean_absolute_error: 270.7208 Epoch 3/100 32/32 [==============================] - 0s 7ms/step - loss: 39159.3711 - mean_absolute_error: 186.0914 - val_loss: 52218.4219 - val_mean_absolute_error: 225.8048 Epoch 4/100 32/32 [==============================] - 0s 8ms/step - loss: 17394.5254 - mean_absolute_error: 113.5933 - val_loss: 16910.2520 - val_mean_absolute_error: 125.3984 Epoch 5/100 32/32 [==============================] - 0s 7ms/step - loss: 5030.3726 - mean_absolute_error: 60.2486 - val_loss: 5049.3706 - val_mean_absolute_error: 62.1609 Epoch 6/100 32/32 [==============================] - 0s 11ms/step - loss: 3496.0933 - mean_absolute_error: 50.2873 - val_loss: 4686.9941 - val_mean_absolute_error: 60.4328 Epoch 7/100 32/32 [==============================] - 0s 8ms/step - loss: 1377.8937 - mean_absolute_error: 27.9828 - val_loss: 1587.2222 - val_mean_absolute_error: 30.0090 Epoch 8/100 32/32 [==============================] - 0s 9ms/step - loss: 286.8610 - mean_absolute_error: 9.7137 - val_loss: 501.8519 - val_mean_absolute_error: 16.0639 Epoch 9/100 32/32 [==============================] - 0s 8ms/step - loss: 103.9734 - mean_absolute_error: 6.0067 - val_loss: 239.8957 - val_mean_absolute_error: 10.9306 Epoch 10/100 32/32 [==============================] - 0s 13ms/step - loss: 60.3380 - mean_absolute_error: 4.7646 - val_loss: 152.8372 - val_mean_absolute_error: 8.9447 Epoch 11/100 32/32 [==============================] - 0s 12ms/step - loss: 43.9838 - mean_absolute_error: 4.1641 - val_loss: 105.4178 - val_mean_absolute_error: 7.5323 Epoch 12/100 32/32 [==============================] - 0s 12ms/step - loss: 35.2579 - mean_absolute_error: 3.8796 - val_loss: 87.3697 - val_mean_absolute_error: 6.9575 Epoch 13/100 32/32 [==============================] - 0s 11ms/step - loss: 36.0701 - mean_absolute_error: 4.2106 - val_loss: 79.3274 - val_mean_absolute_error: 6.6942 Epoch 14/100 32/32 [==============================] - 0s 12ms/step - loss: 32.7236 - mean_absolute_error: 4.1058 - val_loss: 59.1139 - val_mean_absolute_error: 6.2949 Epoch 15/100 32/32 [==============================] - 0s 7ms/step - loss: 29.1068 - mean_absolute_error: 3.8779 - val_loss: 56.1612 - val_mean_absolute_error: 5.6483 Epoch 16/100 32/32 [==============================] - 0s 8ms/step - loss: 26.7665 - mean_absolute_error: 3.7200 - val_loss: 50.4490 - val_mean_absolute_error: 5.3375 Epoch 17/100 32/32 [==============================] - 0s 8ms/step - loss: 24.5873 - mean_absolute_error: 3.5160 - val_loss: 60.8637 - val_mean_absolute_error: 6.1548 Epoch 18/100 32/32 [==============================] - 0s 8ms/step - loss: 31.4589 - mean_absolute_error: 4.1101 - val_loss: 36.7707 - val_mean_absolute_error: 4.9632 Epoch 19/100 32/32 [==============================] - 0s 8ms/step - loss: 26.8563 - mean_absolute_error: 3.7649 - val_loss: 34.9859 - val_mean_absolute_error: 4.6048 Epoch 20/100 32/32 [==============================] - 0s 11ms/step - loss: 24.2428 - mean_absolute_error: 3.5081 - val_loss: 34.8649 - val_mean_absolute_error: 4.5156 Epoch 21/100 32/32 [==============================] - 0s 8ms/step - loss: 24.1515 - mean_absolute_error: 3.6006 - val_loss: 30.3779 - val_mean_absolute_error: 4.5209 Epoch 22/100 32/32 [==============================] - 0s 7ms/step - loss: 23.9884 - mean_absolute_error: 3.6063 - val_loss: 41.7536 - val_mean_absolute_error: 5.3572 Epoch 23/100 32/32 [==============================] - 0s 8ms/step - loss: 28.9343 - mean_absolute_error: 4.0460 - val_loss: 35.1988 - val_mean_absolute_error: 4.5060 Epoch 24/100 32/32 [==============================] - 0s 9ms/step - loss: 22.1466 - mean_absolute_error: 3.4944 - val_loss: 36.5249 - val_mean_absolute_error: 4.6173 Epoch 25/100 32/32 [==============================] - 0s 8ms/step - loss: 21.0343 - mean_absolute_error: 3.3313 - val_loss: 28.9334 - val_mean_absolute_error: 4.4262 Epoch 26/100 32/32 [==============================] - 0s 11ms/step - loss: 21.9365 - mean_absolute_error: 3.4103 - val_loss: 60.4685 - val_mean_absolute_error: 6.3462 Epoch 27/100 32/32 [==============================] - 0s 7ms/step - loss: 25.0985 - mean_absolute_error: 3.7659 - val_loss: 34.3633 - val_mean_absolute_error: 4.4097 Epoch 28/100 32/32 [==============================] - 0s 8ms/step - loss: 22.4752 - mean_absolute_error: 3.5568 - val_loss: 33.2390 - val_mean_absolute_error: 4.4080 Epoch 29/100 32/32 [==============================] - 0s 7ms/step - loss: 21.5835 - mean_absolute_error: 3.3964 - val_loss: 26.8974 - val_mean_absolute_error: 4.1572 Epoch 30/100 32/32 [==============================] - 0s 10ms/step - loss: 25.6173 - mean_absolute_error: 3.7936 - val_loss: 26.5916 - val_mean_absolute_error: 3.9785 Epoch 31/100 32/32 [==============================] - 0s 8ms/step - loss: 25.5473 - mean_absolute_error: 3.7232 - val_loss: 81.9715 - val_mean_absolute_error: 7.6601 Epoch 32/100 32/32 [==============================] - 0s 7ms/step - loss: 25.8138 - mean_absolute_error: 3.7710 - val_loss: 30.1247 - val_mean_absolute_error: 4.5052 Epoch 33/100 32/32 [==============================] - 0s 7ms/step - loss: 20.3365 - mean_absolute_error: 3.3070 - val_loss: 36.0484 - val_mean_absolute_error: 4.6115 Epoch 34/100 32/32 [==============================] - 0s 7ms/step - loss: 21.2953 - mean_absolute_error: 3.3291 - val_loss: 33.9067 - val_mean_absolute_error: 4.4085 Epoch 35/100 32/32 [==============================] - 0s 11ms/step - loss: 22.6468 - mean_absolute_error: 3.5000 - val_loss: 25.2795 - val_mean_absolute_error: 4.0287 Epoch 36/100 32/32 [==============================] - 0s 9ms/step - loss: 22.0695 - mean_absolute_error: 3.4399 - val_loss: 25.1860 - val_mean_absolute_error: 3.9477 Epoch 37/100 32/32 [==============================] - 0s 7ms/step - loss: 22.0614 - mean_absolute_error: 3.4256 - val_loss: 31.8564 - val_mean_absolute_error: 4.2846 Epoch 38/100 32/32 [==============================] - 0s 7ms/step - loss: 22.7449 - mean_absolute_error: 3.4957 - val_loss: 25.2479 - val_mean_absolute_error: 3.8817 Epoch 39/100 32/32 [==============================] - 0s 8ms/step - loss: 25.0833 - mean_absolute_error: 3.6898 - val_loss: 38.5298 - val_mean_absolute_error: 4.7794 Epoch 40/100 32/32 [==============================] - 0s 11ms/step - loss: 21.0166 - mean_absolute_error: 3.3372 - val_loss: 24.5818 - val_mean_absolute_error: 3.8404 Epoch 41/100 32/32 [==============================] - 0s 8ms/step - loss: 19.3731 - mean_absolute_error: 3.1792 - val_loss: 27.0325 - val_mean_absolute_error: 4.0314 Epoch 42/100 32/32 [==============================] - 0s 7ms/step - loss: 26.0746 - mean_absolute_error: 3.7549 - val_loss: 27.3762 - val_mean_absolute_error: 3.8948 Epoch 43/100 32/32 [==============================] - 0s 9ms/step - loss: 20.2978 - mean_absolute_error: 3.2653 - val_loss: 26.6908 - val_mean_absolute_error: 3.8598 Epoch 44/100 32/32 [==============================] - 0s 10ms/step - loss: 21.7916 - mean_absolute_error: 3.4047 - val_loss: 43.4781 - val_mean_absolute_error: 5.2178 Epoch 45/100 32/32 [==============================] - 0s 7ms/step - loss: 22.8816 - mean_absolute_error: 3.5740 - val_loss: 46.8919 - val_mean_absolute_error: 5.4479 Epoch 46/100 32/32 [==============================] - 0s 7ms/step - loss: 20.1738 - mean_absolute_error: 3.2714 - val_loss: 25.8548 - val_mean_absolute_error: 4.0512 Epoch 47/100 32/32 [==============================] - 0s 7ms/step - loss: 22.9297 - mean_absolute_error: 3.4697 - val_loss: 36.2870 - val_mean_absolute_error: 4.9797 Epoch 48/100 32/32 [==============================] - 0s 9ms/step - loss: 23.3037 - mean_absolute_error: 3.5083 - val_loss: 25.6105 - val_mean_absolute_error: 3.9776 Epoch 49/100 32/32 [==============================] - 0s 10ms/step - loss: 22.2615 - mean_absolute_error: 3.4498 - val_loss: 24.8393 - val_mean_absolute_error: 3.8651 Epoch 50/100 32/32 [==============================] - 0s 7ms/step - loss: 21.8947 - mean_absolute_error: 3.4305 - val_loss: 27.8521 - val_mean_absolute_error: 3.9416 Epoch 51/100 32/32 [==============================] - 0s 7ms/step - loss: 20.6147 - mean_absolute_error: 3.3327 - val_loss: 37.7302 - val_mean_absolute_error: 4.7183 Epoch 52/100 32/32 [==============================] - 0s 7ms/step - loss: 31.5291 - mean_absolute_error: 4.2654 - val_loss: 32.8833 - val_mean_absolute_error: 4.6925 Epoch 53/100 32/32 [==============================] - 0s 7ms/step - loss: 22.3771 - mean_absolute_error: 3.4268 - val_loss: 39.3379 - val_mean_absolute_error: 4.8987 Epoch 54/100 32/32 [==============================] - 0s 7ms/step - loss: 21.0260 - mean_absolute_error: 3.3026 - val_loss: 29.5800 - val_mean_absolute_error: 4.4387 Epoch 55/100 32/32 [==============================] - 0s 12ms/step - loss: 30.9052 - mean_absolute_error: 4.1463 - val_loss: 35.7195 - val_mean_absolute_error: 4.8670 Epoch 56/100 32/32 [==============================] - 0s 7ms/step - loss: 26.6116 - mean_absolute_error: 3.8209 - val_loss: 32.5536 - val_mean_absolute_error: 4.3137 Epoch 57/100 32/32 [==============================] - 0s 8ms/step - loss: 20.6008 - mean_absolute_error: 3.2420 - val_loss: 27.0348 - val_mean_absolute_error: 4.2155 Epoch 58/100 32/32 [==============================] - 0s 7ms/step - loss: 23.4402 - mean_absolute_error: 3.5081 - val_loss: 24.6843 - val_mean_absolute_error: 3.8621 Epoch 59/100 32/32 [==============================] - 0s 7ms/step - loss: 21.6499 - mean_absolute_error: 3.3737 - val_loss: 41.0533 - val_mean_absolute_error: 4.9862 Epoch 60/100 32/32 [==============================] - 0s 12ms/step - loss: 23.2434 - mean_absolute_error: 3.5644 - val_loss: 30.1804 - val_mean_absolute_error: 4.1528 Epoch 61/100 32/32 [==============================] - 0s 7ms/step - loss: 21.8650 - mean_absolute_error: 3.3928 - val_loss: 64.9241 - val_mean_absolute_error: 6.5724 Epoch 62/100 32/32 [==============================] - 0s 9ms/step - loss: 25.0806 - mean_absolute_error: 3.7527 - val_loss: 24.5863 - val_mean_absolute_error: 3.9597 Epoch 63/100 32/32 [==============================] - 0s 8ms/step - loss: 23.7072 - mean_absolute_error: 3.5963 - val_loss: 29.9752 - val_mean_absolute_error: 4.4513 Epoch 64/100 32/32 [==============================] - 0s 7ms/step - loss: 20.7408 - mean_absolute_error: 3.3504 - val_loss: 30.3059 - val_mean_absolute_error: 4.1228 Epoch 65/100 32/32 [==============================] - 0s 11ms/step - loss: 21.0484 - mean_absolute_error: 3.3621 - val_loss: 24.0271 - val_mean_absolute_error: 3.8282 Epoch 66/100 32/32 [==============================] - 0s 7ms/step - loss: 23.6899 - mean_absolute_error: 3.6150 - val_loss: 26.0410 - val_mean_absolute_error: 4.0932 Epoch 67/100 32/32 [==============================] - 0s 8ms/step - loss: 20.7963 - mean_absolute_error: 3.3253 - val_loss: 38.3111 - val_mean_absolute_error: 4.8907 Epoch 68/100 32/32 [==============================] - 0s 8ms/step - loss: 20.9105 - mean_absolute_error: 3.3548 - val_loss: 27.9666 - val_mean_absolute_error: 4.1168 Epoch 69/100 32/32 [==============================] - 0s 8ms/step - loss: 20.1888 - mean_absolute_error: 3.2586 - val_loss: 25.7007 - val_mean_absolute_error: 3.7996 Epoch 70/100 32/32 [==============================] - 0s 12ms/step - loss: 21.9967 - mean_absolute_error: 3.3751 - val_loss: 24.3632 - val_mean_absolute_error: 3.9003 Epoch 71/100 32/32 [==============================] - 0s 7ms/step - loss: 20.7171 - mean_absolute_error: 3.2270 - val_loss: 34.3956 - val_mean_absolute_error: 4.7486 Epoch 72/100 32/32 [==============================] - 0s 7ms/step - loss: 22.5949 - mean_absolute_error: 3.4522 - val_loss: 24.0795 - val_mean_absolute_error: 3.8568 Epoch 73/100 32/32 [==============================] - 0s 7ms/step - loss: 19.2761 - mean_absolute_error: 3.1225 - val_loss: 24.2182 - val_mean_absolute_error: 3.8945 Epoch 74/100 32/32 [==============================] - 0s 13ms/step - loss: 20.0854 - mean_absolute_error: 3.2272 - val_loss: 34.6308 - val_mean_absolute_error: 4.5167 Epoch 75/100 32/32 [==============================] - 0s 7ms/step - loss: 21.7760 - mean_absolute_error: 3.3865 - val_loss: 31.8813 - val_mean_absolute_error: 4.2365 Epoch 76/100 32/32 [==============================] - 0s 8ms/step - loss: 23.3229 - mean_absolute_error: 3.5881 - val_loss: 51.5505 - val_mean_absolute_error: 5.8026 Epoch 77/100 32/32 [==============================] - 0s 8ms/step - loss: 23.9316 - mean_absolute_error: 3.6062 - val_loss: 26.8458 - val_mean_absolute_error: 3.8551 Epoch 78/100 32/32 [==============================] - 0s 8ms/step - loss: 21.9814 - mean_absolute_error: 3.4719 - val_loss: 25.3911 - val_mean_absolute_error: 3.8101 Epoch 79/100 32/32 [==============================] - 0s 11ms/step - loss: 20.9901 - mean_absolute_error: 3.3594 - val_loss: 32.8351 - val_mean_absolute_error: 4.7251 Epoch 80/100 32/32 [==============================] - 0s 7ms/step - loss: 24.1758 - mean_absolute_error: 3.7161 - val_loss: 27.6141 - val_mean_absolute_error: 3.9292 Epoch 81/100 32/32 [==============================] - 0s 9ms/step - loss: 21.5106 - mean_absolute_error: 3.3727 - val_loss: 30.2790 - val_mean_absolute_error: 4.1270 Epoch 82/100 32/32 [==============================] - 0s 7ms/step - loss: 20.1135 - mean_absolute_error: 3.2504 - val_loss: 25.3960 - val_mean_absolute_error: 3.7765 Epoch 83/100 32/32 [==============================] - 0s 9ms/step - loss: 23.5658 - mean_absolute_error: 3.5773 - val_loss: 43.8989 - val_mean_absolute_error: 5.2368 Epoch 84/100 32/32 [==============================] - 0s 11ms/step - loss: 22.6244 - mean_absolute_error: 3.3774 - val_loss: 48.7853 - val_mean_absolute_error: 5.6219 Epoch 85/100 32/32 [==============================] - 0s 7ms/step - loss: 20.3692 - mean_absolute_error: 3.3059 - val_loss: 45.0758 - val_mean_absolute_error: 5.2920 Epoch 86/100 32/32 [==============================] - 0s 8ms/step - loss: 20.0955 - mean_absolute_error: 3.3121 - val_loss: 32.1239 - val_mean_absolute_error: 4.2728 Epoch 87/100 32/32 [==============================] - 0s 9ms/step - loss: 18.7950 - mean_absolute_error: 3.0712 - val_loss: 41.2694 - val_mean_absolute_error: 5.0852 Epoch 88/100 32/32 [==============================] - 0s 12ms/step - loss: 22.5343 - mean_absolute_error: 3.4513 - val_loss: 45.0232 - val_mean_absolute_error: 5.3158 Epoch 89/100 32/32 [==============================] - 0s 10ms/step - loss: 24.7313 - mean_absolute_error: 3.6302 - val_loss: 29.6013 - val_mean_absolute_error: 4.4197 Epoch 90/100 32/32 [==============================] - 0s 7ms/step - loss: 19.8725 - mean_absolute_error: 3.1976 - val_loss: 24.7524 - val_mean_absolute_error: 3.7740 Epoch 91/100 32/32 [==============================] - 0s 8ms/step - loss: 22.2237 - mean_absolute_error: 3.4613 - val_loss: 31.3193 - val_mean_absolute_error: 4.2246 Epoch 92/100 32/32 [==============================] - 0s 8ms/step - loss: 21.2648 - mean_absolute_error: 3.3238 - val_loss: 25.9929 - val_mean_absolute_error: 3.9420 Epoch 93/100 32/32 [==============================] - 0s 11ms/step - loss: 19.6476 - mean_absolute_error: 3.1882 - val_loss: 25.7189 - val_mean_absolute_error: 3.8255 Epoch 94/100 32/32 [==============================] - 0s 10ms/step - loss: 20.5151 - mean_absolute_error: 3.3408 - val_loss: 23.9001 - val_mean_absolute_error: 3.8074 Epoch 95/100 32/32 [==============================] - 0s 6ms/step - loss: 18.9152 - mean_absolute_error: 3.0737 - val_loss: 34.7337 - val_mean_absolute_error: 4.8874 Epoch 96/100 32/32 [==============================] - 0s 9ms/step - loss: 26.1909 - mean_absolute_error: 3.7697 - val_loss: 40.1694 - val_mean_absolute_error: 4.9520 Epoch 97/100 32/32 [==============================] - 0s 8ms/step - loss: 20.3085 - mean_absolute_error: 3.2980 - val_loss: 45.7694 - val_mean_absolute_error: 5.3313 Epoch 98/100 32/32 [==============================] - 0s 7ms/step - loss: 20.9214 - mean_absolute_error: 3.4237 - val_loss: 27.2224 - val_mean_absolute_error: 3.9263 Epoch 99/100 32/32 [==============================] - 0s 6ms/step - loss: 19.9604 - mean_absolute_error: 3.2042 - val_loss: 30.6290 - val_mean_absolute_error: 4.1616 Epoch 100/100 32/32 [==============================] - 0s 6ms/step - loss: 22.9296 - mean_absolute_error: 3.5538 - val_loss: 25.9603 - val_mean_absolute_error: 4.0136
<keras.src.callbacks.History at 0x2db1bbdc250>
The Output :¶
The output represents the training progress of an LSTM model over 100 epochs, displaying both training and validation metrics after each epoch:
- Epochs 1-100: For each epoch, the model's performance metrics are shown, including loss (mean squared error) and mean absolute error, both for the training dataset and the validation dataset.
- Loss: The loss values represent the error between the predicted values and the actual values in the respective datasets (training and validation). Lower values indicate better performance.
- Mean Absolute Error (MAE): This metric measures the average absolute difference between predicted and actual values. Like loss, lower values are indicative of better performance.
Observations:¶
- The loss and MAE metrics for both training and validation datasets are reported after each epoch.
- Initially, both training and validation loss and MAE are quite high but gradually decrease over epochs, indicating that the model is learning and improving its performance.
- There seems to be a gap between the training and validation metrics, suggesting a potential overfitting issue if the validation loss starts increasing while the training loss continues to decrease significantly.
Visualization of Training Data Predictions¶
The provided code below segment is responsible for generating a visual comparison between the predictions made by a machine learning model and the actual observations within the training dataset. This visualization aims to offer insights into the model's performance on the training data.
- Training Predictions Line: This line represents the values predicted by the machine learning model for the training dataset. Each point on this line indicates the model's prediction for a specific instance within the training data.
- Training Observations Line: This line illustrates the actual observed values from the training dataset. It represents the real data points or ground truth values.
ideally, the two lines should align closely or overlap each other. A close fit between the 'Training Predictions' and 'Training Observations' lines signifies that the model has learned well from the training data. If there's a significant deviation or gap between these lines, it might indicate:
- Model Accuracy: A close alignment between the lines indicates that the model has learned to predict the training data accurately.
- Overfitting or Underfitting:
- Overfitting: If the 'Training Predictions' line closely follows the training data but deviates significantly from the 'Training Observations' line, it could indicate the model has overfit the training data, capturing noise or specific patterns that don't generalize well.
- Underfitting: A large gap between both lines suggests the model hasn't captured the underlying patterns in the training data adequately.
- Prediction Errors: Differences between the lines could also indicate areas where the model struggles to make accurate predictions, pointing to instances where the model requires improvement or where the data might be inherently noisy or complex.
train_predictions = model.predict(X_train).flatten()
plt.figure(figsize=(10, 6))
plt.plot(dates_train, train_predictions)
plt.plot(dates_train, y_train)
plt.legend(['Training Predictions', 'Training Observations'])
32/32 [==============================] - 1s 3ms/step
<matplotlib.legend.Legend at 0x2db16fc9a50>
val_predictions = model.predict(X_val).flatten()
plt.figure(figsize=(10,4))
plt.plot(dates_val, val_predictions)
plt.plot(dates_val, y_val)
plt.legend(['Validation Predictions', 'Validation Observations'])
4/4 [==============================] - 0s 4ms/step
<matplotlib.legend.Legend at 0x2db14fffa50>
test_predictions = model.predict(X_test).flatten()
plt.figure(figsize=(10,4))
plt.plot(dates_test, test_predictions)
plt.plot(dates_test, y_test)
plt.legend(['Testing Predictions', 'Testing Observations'])
4/4 [==============================] - 0s 4ms/step
<matplotlib.legend.Legend at 0x2db17c9fa50>
By plotting these six lines together, this visualization allows for a holistic view of how well the model performs across different datasets:¶
- Model Performance Assessment: Comparing predictions with actual observations across all datasets.
- Generalization Check: Analyzing if the model's performance remains consistent across unseen data (validation and testing datasets).
- Overfitting/Underfitting Evaluation: Observing potential disparities between predictions and observations across datasets. The legend provided corresponds to each line in the graph, enabling clear identification of which line represents which dataset's predictions and observations.
- Model Consistency Across Datasets: all six lines closely align and follow a similar pattern across the training, validation, and testing datasets, it indicates that the model is consistent in its predictions across different datasets. This consistency suggests that the model has learned patterns that generalize well to unseen data.
- Generalization and Robustness: Consistent alignment between the prediction and observation lines across the three datasets (training, validation, testing) suggests that the model generalizes well. It indicates that the model has not overfit to the training data and is performing similarly on unseen validation and testing datasets.
- Performance Evaluation: A close fit between the prediction and observation lines on all datasets implies that the model is making accurate predictions, capturing the underlying patterns in the data across different subsets.
- Prediction Accuracy Assessment: The closer the lines fit together, the higher the accuracy of the model's predictions across the datasets, indicating a strong performance and reliability in forecasting or modeling the observed data.
plt.plot(dates_train, train_predictions)
plt.plot(dates_train, y_train)
plt.plot(dates_val, val_predictions)
plt.plot(dates_val, y_val)
plt.plot(dates_test, test_predictions)
plt.plot(dates_test, y_test)
plt.legend(['Training Predictions',
'Training Observations',
'Validation Predictions',
'Validation Observations',
'Testing Predictions',
'Testing Observations'])
<matplotlib.legend.Legend at 0x2db1ab6ac50>
Using a trained model to generate recursive predictions. This process use a recursive approach where the model generates predictions iteratively, using its own predictions as inputs for subsequent predictions. This can be useful for forecasting multiple steps ahead or simulating real-time prediction scenarios.
However, it's important to handle the recursive predictions with care, as errors might accumulate over each step, potentially affecting the accuracy of long-term predictions. Evaluation and validation on such recursive predictions are crucial to assess the model's performance in this scenario.
from copy import deepcopy
recursive_predictions = [] # Initialize an empty list to store the predicted values
recursive_dates = np.concatenate([dates_val, dates_test]) # Concatenate validation and test dates to create a combined date sequence
# Iterate through each date in the combined validation and test set
for target_date in recursive_dates:
last_window = deepcopy(X_train[-1]) # Create a copy of the last window in the training data
next_prediction = model.predict(np.array([last_window])).flatten() # Predict the next value based on the last window and flatten the result
recursive_predictions.append(next_prediction) # Append the predicted value to the list of recursive predictions
last_window[-1] = next_prediction # Update the last element in the window with the predicted value for the next iteration
1/1 [==============================] - 1s 862ms/step 1/1 [==============================] - 0s 27ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 16ms/step 1/1 [==============================] - 0s 16ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 50ms/step 1/1 [==============================] - 0s 19ms/step 1/1 [==============================] - 0s 22ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 17ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 24ms/step 1/1 [==============================] - 0s 41ms/step 1/1 [==============================] - 0s 40ms/step 1/1 [==============================] - 0s 42ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 42ms/step 1/1 [==============================] - 0s 53ms/step 1/1 [==============================] - 0s 49ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 45ms/step 1/1 [==============================] - 0s 48ms/step 1/1 [==============================] - 0s 43ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 39ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 37ms/step 1/1 [==============================] - 0s 21ms/step 1/1 [==============================] - 0s 21ms/step 1/1 [==============================] - 0s 16ms/step 1/1 [==============================] - 0s 16ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 38ms/step 1/1 [==============================] - 0s 22ms/step 1/1 [==============================] - 0s 103ms/step 1/1 [==============================] - 0s 51ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 48ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 37ms/step 1/1 [==============================] - 0s 23ms/step 1/1 [==============================] - 0s 37ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 48ms/step 1/1 [==============================] - 0s 46ms/step 1/1 [==============================] - 0s 165ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 30ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 51ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 38ms/step 1/1 [==============================] - 0s 65ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 62ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 38ms/step 1/1 [==============================] - 0s 29ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 37ms/step 1/1 [==============================] - 0s 38ms/step 1/1 [==============================] - 0s 38ms/step 1/1 [==============================] - 0s 18ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 51ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 97ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 50ms/step 1/1 [==============================] - 0s 49ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 51ms/step 1/1 [==============================] - 0s 46ms/step 1/1 [==============================] - 0s 37ms/step 1/1 [==============================] - 0s 37ms/step 1/1 [==============================] - 0s 154ms/step 1/1 [==============================] - 0s 49ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 46ms/step 1/1 [==============================] - 0s 55ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 52ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 69ms/step 1/1 [==============================] - 0s 53ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 55ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 38ms/step 1/1 [==============================] - 0s 49ms/step 1/1 [==============================] - 0s 48ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 106ms/step 1/1 [==============================] - 0s 16ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 16ms/step 1/1 [==============================] - 0s 56ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 57ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 16ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 16ms/step 1/1 [==============================] - 0s 16ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 123ms/step 1/1 [==============================] - 0s 102ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 78ms/step 1/1 [==============================] - 0s 41ms/step 1/1 [==============================] - 0s 38ms/step 1/1 [==============================] - 0s 29ms/step 1/1 [==============================] - 0s 48ms/step 1/1 [==============================] - 0s 92ms/step 1/1 [==============================] - 0s 65ms/step 1/1 [==============================] - 0s 164ms/step 1/1 [==============================] - 0s 56ms/step 1/1 [==============================] - 0s 51ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 39ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 40ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 60ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 49ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 48ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 51ms/step 1/1 [==============================] - 0s 46ms/step 1/1 [==============================] - 0s 36ms/step 1/1 [==============================] - 0s 54ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 61ms/step 1/1 [==============================] - 0s 40ms/step 1/1 [==============================] - 0s 45ms/step 1/1 [==============================] - 0s 45ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 40ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 52ms/step 1/1 [==============================] - 0s 41ms/step 1/1 [==============================] - 0s 42ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 138ms/step 1/1 [==============================] - 0s 46ms/step 1/1 [==============================] - 0s 32ms/step 1/1 [==============================] - 0s 47ms/step 1/1 [==============================] - 0s 35ms/step
plt.figure(figsize=(10,6))
plt.plot(dates_train, train_predictions)
plt.plot(dates_train, y_train)
plt.plot(dates_val, val_predictions)
plt.plot(dates_val, y_val)
plt.plot(dates_test, test_predictions)
plt.plot(dates_test, y_test)
plt.plot(recursive_dates, recursive_predictions)
plt.legend(['Training Predictions',
'Training Observations',
'Validation Predictions',
'Validation Observations',
'Testing Predictions',
'Testing Observations',
'Recursive Predictions'])
<matplotlib.legend.Legend at 0x2db19b7e2d0>