| Title: | Random Walk-Informed LSTM for Time Series Forecasting |
|---|---|
| Description: | Implements a Random Walk (RW)-informed Long Short-Term Memory (LSTM) network for univariate time series forecasting. The LSTM is trained on the full series with a composite loss that blends the usual mean squared error (MSE) between predictions and observations with the MSE between predictions and the fitted Random Walk, weighted by a parameter lambda (0 gives a standard LSTM, 1 gives a fully RW-guided LSTM). The Random Walk, either simple or with drift, is selected automatically by its in-sample root mean squared error (RMSE), and the lambda value with the lowest training RMSE is retained. The LSTM hyperparameters can be supplied manually or tuned by grid search, and functions for out-of-sample forecasting, accuracy measures and plots are provided. The methodology is motivated by hybrid forecasting framework proposed by Yeasin and Paul (2024) <doi:10.1007/s11227-023-05542-3>. |
| Authors: | Md Yeasin [aut], Ranjit Kumar Paul [aut, cre], Pushkar Bora [aut] |
| Maintainer: | Ranjit Kumar Paul <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-10-29 17:12:48 UTC |
| Source: | https://github.com/cran/RWInfoLSTM |
Out-of-sample accuracy for RWiL forecasts vs actual values
AccuracyRWiL(fit, actual, lambda = NULL, verbose = TRUE)AccuracyRWiL(fit, actual, lambda = NULL, verbose = TRUE)
fit |
A RWiL object returned by RWiL(). |
actual |
Numeric vector of actual values after training. |
lambda |
Lambda to use. NULL = best. |
verbose |
Logical. If |
Data frame with RMSE, MAE, SMAPE, MAPE.
# Runs only when the libtorch backend is installed (torch::install_torch()) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(40, 0.5, 1)) fit <- RWiL(y, mode = "manual", lag = 3, hidden_size = 4, lr = 0.01, epochs = 5, patience = 5, lambda_list = 0.5, verbose = FALSE, seed = 1) future <- tail(y, 1) + cumsum(rnorm(6, 0.5, 1)) AccuracyRWiL(fit, actual = future) }# Runs only when the libtorch backend is installed (torch::install_torch()) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(40, 0.5, 1)) fit <- RWiL(y, mode = "manual", lag = 3, hidden_size = 4, lr = 0.01, epochs = 5, patience = 5, lambda_list = 0.5, verbose = FALSE, seed = 1) future <- tail(y, 1) + cumsum(rnorm(6, 0.5, 1)) AccuracyRWiL(fit, actual = future) }
Plot Training RMSE Across All Lambda Values (RWiL)
plotlambda_rwil(fit)plotlambda_rwil(fit)
fit |
RWiL object. |
ggplot2 object (invisibly).
# Runs only when the libtorch backend is installed (torch::install_torch()) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(40, 0.5, 1)) fit <- RWiL(y, mode = "manual", lag = 3, hidden_size = 4, lr = 0.01, epochs = 5, patience = 5, lambda_list = 0.5, verbose = FALSE, seed = 1) plotlambda_rwil(fit) }# Runs only when the libtorch backend is installed (torch::install_torch()) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(40, 0.5, 1)) fit <- RWiL(y, mode = "manual", lag = 3, hidden_size = 4, lr = 0.01, epochs = 5, patience = 5, lambda_list = 0.5, verbose = FALSE, seed = 1) plotlambda_rwil(fit) }
Plot RWiL: Actual vs Predicted (in-sample) + Out-of-sample Forecast
plotRWiL( fit, h = 12, lambda = NULL, actual = NULL, newdata = NULL, n_hist = NULL )plotRWiL( fit, h = 12, lambda = NULL, actual = NULL, newdata = NULL, n_hist = NULL )
fit |
RWiL object from RWiL(). |
h |
Forecast horizon. Default 12. |
lambda |
Lambda to use. NULL = best. |
actual |
Optional numeric vector of actual future values. |
newdata |
Optional new observations to update starting sequence. |
n_hist |
Number of historical points to show. NULL = all. |
ggplot2 object (invisibly).
# Runs only when the libtorch backend is installed (torch::install_torch()) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(40, 0.5, 1)) fit <- RWiL(y, mode = "manual", lag = 3, hidden_size = 4, lr = 0.01, epochs = 5, patience = 5, lambda_list = 0.5, verbose = FALSE, seed = 1) plotRWiL(fit, h = 6) }# Runs only when the libtorch backend is installed (torch::install_torch()) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(40, 0.5, 1)) fit <- RWiL(y, mode = "manual", lag = 3, hidden_size = 4, lr = 0.01, epochs = 5, patience = 5, lambda_list = 0.5, verbose = FALSE, seed = 1) plotRWiL(fit, h = 6) }
Forecast h steps ahead from a fitted RWiL model
predictRWiL(fit, h = 1, lambda = NULL, newdata = NULL)predictRWiL(fit, h = 1, lambda = NULL, newdata = NULL)
fit |
A RWiL object returned by RWiL(). |
h |
Number of steps to forecast. Default 1. |
lambda |
Lambda value to use. NULL = best (lowest Train RMSE). |
newdata |
Optional numeric vector of new observations after training. |
Data frame with columns: h, time_idx, forecast.
# Runs only when the libtorch backend is installed (torch::install_torch()) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(40, 0.5, 1)) fit <- RWiL(y, mode = "manual", lag = 3, hidden_size = 4, lr = 0.01, epochs = 5, patience = 5, lambda_list = 0.5, verbose = FALSE, seed = 1) predictRWiL(fit, h = 6) }# Runs only when the libtorch backend is installed (torch::install_torch()) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(40, 0.5, 1)) fit <- RWiL(y, mode = "manual", lag = 3, hidden_size = 4, lr = 0.01, epochs = 5, patience = 5, lambda_list = 0.5, verbose = FALSE, seed = 1) predictRWiL(fit, h = 6) }
Trains an LSTM on the FULL dataset using the Random Walk-informed composite loss:
Loss = (1-lambda) x MSE(y_hat, y) + lambda x MSE(y_hat, y_RW)
Trains on ALL data – no train/test split. Use predictRWiL() for out-of-sample forecasting. An internal 20 percent validation split handles early stopping.
The Random Walk model is auto-selected between:
Simple RW: y_hat[t] = y[t-1]
Drift RW: y_hat[t] = y[t-1] + d, where d = mean(diff(y))
Selection is by lowest in-sample RMSE.
RWiL( data, mode = c("auto", "manual"), lag, hidden_size = 32, num_layers = 1, lr = 0.001, dropout = 0.0, epochs = 500, patience = 30, tune_epochs = 50, batch_size = 16, lambda_list = seq(0.1, 0.9, 0.1), hidden_grid = c(8, 16, 24, 32, 40), layers_grid = c(1L, 2L), lr_grid = c(0.001, 0.003, 0.005), dropout_grid = c(0.0, 0.1, 0.2), verbose = TRUE, seed = NULL )RWiL( data, mode = c("auto", "manual"), lag, hidden_size = 32, num_layers = 1, lr = 0.001, dropout = 0.0, epochs = 500, patience = 30, tune_epochs = 50, batch_size = 16, lambda_list = seq(0.1, 0.9, 0.1), hidden_grid = c(8, 16, 24, 32, 40), layers_grid = c(1L, 2L), lr_grid = c(0.001, 0.003, 0.005), dropout_grid = c(0.0, 0.1, 0.2), verbose = TRUE, seed = NULL )
data |
Numeric vector. Full time series – ALL observations used for training. |
mode |
|
lag |
Look-back window length. COMPULSORY – no default. Example: lag = 3 means model uses last 3 values to predict next. |
|
LSTM hidden units (manual mode). Default 32. |
|
num_layers |
Stacked LSTM layers (manual mode). Default 1. |
lr |
Learning rate (manual mode). Default 0.001. |
dropout |
Dropout rate (manual mode). Default 0.0. |
epochs |
Maximum training epochs. Default 500. |
patience |
Early-stopping patience. Default 30. |
tune_epochs |
Epochs per grid-search combo (auto). Default 50. |
batch_size |
Mini-batch size. Default 16. |
lambda_list |
Lambda values to test. Default seq(0.1, 0.9, 0.1). lambda = 0.0: Standard LSTM (no RW guidance). lambda = 1.0: Fully RW-guided LSTM. |
|
Hidden-unit candidates (auto mode). |
|
layers_grid |
Layer-count candidates (auto mode). |
lr_grid |
Learning-rate candidates (auto mode). |
dropout_grid |
Dropout candidates (auto mode). |
verbose |
Print progress. Default TRUE. |
seed |
Optional integer random seed for reproducible results. Default
|
S3 object of class "RWiL" with elements:
Named list per lambda. Each contains: tr_rmse, tr_mae, tr_mape, tr_smape, tr_preds, tr_actuals, val_rmse, loss_hist, time, lambda, model.
Complete best lambda summary: lambda, hyperparams, rw (type, drift, rmse_simple, rmse_drift), metrics, predictions, loss_history, model object, train_time.
Random Walk results: rw_type, drift, rmse_simple, rmse_drift.
Best LSTM hyperparameters.
Grid-search log (auto mode only).
Settings: lag, lambda_list, mode, best_lam_nm, selection = "Train RMSE".
data, N, scaler, last_seq_scaled, lag.
# Small toy example (a few seconds) # Runs only when the libtorch backend is installed (torch::install_torch()) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(40, 0.5, 1)) fit <- RWiL(y, mode = "manual", lag = 3, hidden_size = 4, lr = 0.01, epochs = 5, patience = 5, lambda_list = 0.5, verbose = FALSE, seed = 1) print(fit) summary(fit) predictRWiL(fit, h = 6) fit$rw$rw_type # "simple" or "drift" } # Auto mode: grid search over the LSTM hyperparameters (takes longer) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(60, 0.5, 2)) fit_auto <- RWiL(y, mode = "auto", lag = 3, hidden_grid = c(4, 8), layers_grid = 1L, lr_grid = 0.01, dropout_grid = 0, tune_epochs = 5, epochs = 20, patience = 5, lambda_list = c(0.3, 0.7), verbose = FALSE, seed = 1) summary(fit_auto) plotRWiL(fit_auto, h = 12) plotlambda_rwil(fit_auto) }# Small toy example (a few seconds) # Runs only when the libtorch backend is installed (torch::install_torch()) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(40, 0.5, 1)) fit <- RWiL(y, mode = "manual", lag = 3, hidden_size = 4, lr = 0.01, epochs = 5, patience = 5, lambda_list = 0.5, verbose = FALSE, seed = 1) print(fit) summary(fit) predictRWiL(fit, h = 6) fit$rw$rw_type # "simple" or "drift" } # Auto mode: grid search over the LSTM hyperparameters (takes longer) if (torch::torch_is_installed()) { set.seed(1) y <- cumsum(rnorm(60, 0.5, 2)) fit_auto <- RWiL(y, mode = "auto", lag = 3, hidden_grid = c(4, 8), layers_grid = 1L, lr_grid = 0.01, dropout_grid = 0, tune_epochs = 5, epochs = 20, patience = 5, lambda_list = c(0.3, 0.7), verbose = FALSE, seed = 1) summary(fit_auto) plotRWiL(fit_auto, h = 12) plotlambda_rwil(fit_auto) }