Skip to content

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.

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

dataset.set_variable_role(
    "Passengers",
    VariableRole::InputTarget);

dataset.configure_forecasting(12, 1);

The InputTarget role marks the single numeric series as both input and target. configure_forecasting then creates chronological training, validation and testing windows and keeps only complete windows in each subset.

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:

Training training(
    &forecasting_network,
    &dataset);

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

training.set_optimization_algorithm("Adam");

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

TrainingResult result = training.train();

Sequential data must not be randomly shuffled between subsets. The forecasting configuration 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:

Evaluation evaluation(
    &forecasting_network,
    &dataset);

evaluation.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/tabular_dataset.h"
#include "opennn/evaluation/evaluation.h"
#include "opennn/models/models.h"
#include "opennn/network/model_expression.h"
#include "opennn/training/training.h"

using namespace opennn;

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

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

        dataset.set_variable_role(
            "Passengers",
            VariableRole::InputTarget);
        dataset.configure_forecasting(12, 1);

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

        Training training(
            &forecasting_network,
            &dataset);

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

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

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

        Evaluation evaluation(
            &forecasting_network,
            &dataset);
        evaluation.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