‹ Back to Tutorials

Forecasting: Airline passengers estimation

This example uses the previous twelve monthly observations to forecast the next value in the classic international airline passengers series.

The data set contains 144 monthly passenger totals, in thousands, from 1949 through 1960.

Contents:

  1. Identify the application type
  2. Prepare the time-series data
  3. Build the forecasting network
  4. Configure and train the model
  5. Evaluate the forecast
  6. Forecast the next month and export the model
  7. Complete example

1. Identify the application type

Observations are ordered in time and the objective is to predict a future value from previous values. This is a univariate forecasting problem.

A twelve-month input window allows the recurrent network to observe one full annual cycle before estimating the following month.

2. Prepare the time-series data

Download the airline_passengers.csv file used by the original OpenNN example. It has one header field, Passengers, followed by 144 monthly values.

TimeSeriesDataset dataset(
    "airline_passengers.csv",
    ",",
    true,
    false);

dataset.set_past_time_steps(12);
dataset.set_future_time_steps(1);

TimeSeriesDataset marks the single numeric series as both input and target. It creates chronological training, validation and testing windows and excludes incomplete windows at each boundary.

The resulting input shape is 12 time steps × 1 feature, and the target shape contains the next passenger value.

3. Build the forecasting network

ForecastingNetwork builds scaling, a recurrent stack, a dense identity output, unscaling and a clamping layer configured with no clamping:

const Index hidden_neurons = 8;

ForecastingNetwork forecasting_network(
    dataset.get_input_shape(),
    {hidden_neurons},
    dataset.get_target_shape());

For a larger problem, ForecastingLstmNetwork provides the same high-level constructor with long short-term memory layers.

4. Configure and train the model

Train the recurrent model with mean squared error, L2 regularization and Adam:

TrainingStrategy training_strategy(
    &forecasting_network,
    &dataset);

training_strategy.set_loss("MeanSquaredError");
training_strategy.get_loss()->set_regularization("L2");
training_strategy.get_loss()->set_regularization_weight(0.001f);

training_strategy.set_optimization_algorithm(
    "AdaptiveMomentEstimation");

Optimizer* optimizer =
    training_strategy.get_optimization_algorithm();
optimizer->set_maximum_epochs(1000);
optimizer->set_display_period(100);

TrainingResult result = training_strategy.train();

Sequential data must not be randomly shuffled between subsets. The time-series data set keeps later observations for validation and testing.

5. Evaluate the forecast

Goodness-of-fit analysis compares the one-step forecasts with the observed passenger totals in the testing period:

TestingAnalysis testing_analysis(
    &forecasting_network,
    &dataset);

testing_analysis.print_goodness_of_fit_analysis();

Inspect the determination coefficient and predicted-versus-observed values, but also review the residuals over time because a single aggregate score can hide seasonal bias.

6. Forecast the next month and export the model

The final twelve observations from 1960 form one inference window:

const std::array<float, 12> recent_values = {
    417.0f, 391.0f, 419.0f, 461.0f,
    472.0f, 535.0f, 622.0f, 606.0f,
    508.0f, 461.0f, 390.0f, 432.0f
};

Tensor3 inputs(1, 12, 1);
for(Index month = 0; month < 12; ++month)
    inputs(0, month, 0) = recent_values[size_t(month)];

const MatrixR outputs =
    forecasting_network.calculate_outputs(inputs);

cout << "Next-month forecast: "
     << outputs(0, 0) << " thousand passengers\n";
forecasting_network.save("airline_passengers_model.json");

ModelExpression expression(&forecasting_network);
expression.save(
    "airline_passengers_model.py",
    ModelExpression::ProgrammingLanguage::Python);

7. Complete example

#include <array>
#include <iostream>

#include "opennn/core/configuration.h"
#include "opennn/dataset/time_series_dataset.h"
#include "opennn/models/models.h"
#include "opennn/neural_network/model_expression.h"
#include "opennn/testing_analysis/testing_analysis.h"
#include "opennn/training_strategy/training_strategy.h"

using namespace opennn;

int main()
{
    try
    {
        Configuration::instance().set(Device::Auto, Type::FP32);

        TimeSeriesDataset dataset(
            "airline_passengers.csv",
            ",", true, false);

        dataset.set_past_time_steps(12);
        dataset.set_future_time_steps(1);

        ForecastingNetwork forecasting_network(
            dataset.get_input_shape(),
            {8},
            dataset.get_target_shape());

        TrainingStrategy training_strategy(
            &forecasting_network,
            &dataset);

        training_strategy.set_loss("MeanSquaredError");
        training_strategy.get_loss()->set_regularization("L2");
        training_strategy.get_loss()->set_regularization_weight(0.001f);
        training_strategy.set_optimization_algorithm(
            "AdaptiveMomentEstimation");

        Optimizer* optimizer =
            training_strategy.get_optimization_algorithm();
        optimizer->set_maximum_epochs(1000);
        optimizer->set_display_period(100);

        TrainingResult result = training_strategy.train();
        result.print();

        TestingAnalysis testing_analysis(
            &forecasting_network,
            &dataset);
        testing_analysis.print_goodness_of_fit_analysis();

        const std::array<float, 12> recent_values = {
            417.0f, 391.0f, 419.0f, 461.0f,
            472.0f, 535.0f, 622.0f, 606.0f,
            508.0f, 461.0f, 390.0f, 432.0f
        };

        Tensor3 inputs(1, 12, 1);
        for(Index month = 0; month < 12; ++month)
            inputs(0, month, 0) = recent_values[size_t(month)];

        const MatrixR outputs =
            forecasting_network.calculate_outputs(inputs);
        cout << "Next-month forecast: "
             << outputs(0, 0)
             << " thousand passengers\n";

        forecasting_network.save(
            "airline_passengers_model.json");
        ModelExpression expression(&forecasting_network);
        expression.save(
            "airline_passengers_model.py",
            ModelExpression::ProgrammingLanguage::Python);

        return 0;
    }
    catch(const exception& error)
    {
        cerr << error.what() << '\n';
        return 1;
    }
}

References