Skip to content

ResLogit model Usage

Introduction to modelling with ResLogit

Open In Colab

We use the Swissmetro dataset to demonstrate how to use the ResLogit model [1].

# Install necessary requirements

# If you run this notebook on Google Colab, or in standalone mode, you need to install the required packages.
# Uncomment the following lines:

# !pip install choice-learn

# If you run the notebook within the GitHub repository, you need to run the following lines, that can skipped otherwise:
import os
import sys

sys.path.append("../../")
import os
# Remove/Add GPU use
os.environ["CUDA_VISIBLE_DEVICES"] = "1"

import timeit
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf

from choice_learn.data import ChoiceDataset
from choice_learn.models import ResLogit
from choice_learn.datasets import load_swissmetro

First, we create a ChoiceDataset from the dataframe.

dataset = load_swissmetro(as_frame=False)
dataset.summary()
print(f"\n\n{type(dataset)=}")
print(f"\n{np.shape(dataset.items_features_by_choice)=}")
print(f"{np.shape(dataset.shared_features_by_choice)=}")
n_items = np.shape(dataset.items_features_by_choice)[2]
n_items_features = np.shape(dataset.items_features_by_choice)[3]
n_shared_features = np.shape(dataset.shared_features_by_choice)[2]
n_vars = n_items_features + n_shared_features
n_choices = len(np.unique(dataset.choices))

print(f"{n_items=}\n{n_items_features=}\n{n_shared_features=}\n{n_vars, n_choices=}\n\n")

We split the dataset into train and test subsets.

n_samples = len(dataset.choices)
# Slicing index for train and valid split
slice = np.floor(0.7 * n_samples).astype(int)
train_indexes = np.arange(0, slice)
test_indexes = np.arange(slice, n_samples)

train_dataset = dataset[train_indexes]
test_dataset = dataset[test_indexes]

Now we can fit several ResLogit models with different numbers of residual layers. We will use the same learning rate and number of epochs for all models. We add itemwise intercept to all the models.

model_args = {
    "intercept": "item",
    "optimizer": "SGD",
    "lr": 1e-6,
    "epochs": 100,
}
print(f"{model_args=}")
list_n_layers = [k for k in range(1, 17)]
metrics = pd.DataFrame(columns=["n_layers", "fit_losses", "train_loss", "test_loss", "initial_trainable_weights", "final_trainable_weights", "execution_time"])

for n_layers in list_n_layers:
    print("\n------------------------------------"
          "------------------------------------"
          f"\n{n_layers=}")

    start_time = timeit.default_timer()
    model = ResLogit(n_layers=n_layers, **model_args)
    model.instantiate(n_items=n_items, n_shared_features=n_shared_features, n_items_features=n_items_features)

    initial_trainable_weights = [model.trainable_weights[i].numpy() for i in range(len(model.trainable_weights))]

    fit_losses = model.fit(choice_dataset=train_dataset, val_dataset=test_dataset)

    end_time = timeit.default_timer()
    execution_time = end_time - start_time
    print(f"Execution time with {n_layers} residual layers: {execution_time} seconds")

    final_trainable_weights = [model.trainable_weights[i].numpy() for i in range(len(model.trainable_weights))]

    new_metric_row = pd.DataFrame({
        "n_layers": [n_layers],
        "fit_losses": [fit_losses],
        "train_loss": [model.evaluate(train_dataset)],
        "test_loss": [model.evaluate(test_dataset)],
        "initial_trainable_weights": [initial_trainable_weights],
        "final_trainable_weights": [final_trainable_weights],
        "execution_time": [execution_time]
    })
    metrics = pd.concat([metrics, new_metric_row], ignore_index=True)
metrics.head()
for index, row in metrics.iterrows():
    plt.plot(row["fit_losses"]["train_loss"], label=f"n_layers={row['n_layers']}")

plt.xlabel("Epochs")
plt.ylabel("Training loss through the epochs")
plt.title("ResLogit model with different number of residual layers")
plt.legend()
plt.show()
train_losses = [row["train_loss"] for index, row in metrics.iterrows()]
test_losses = [row["test_loss"] for index, row in metrics.iterrows()]

plt.plot(list_n_layers, train_losses, label="Train loss after the last epoch")
plt.plot(list_n_layers, test_losses, label="Test loss")

plt.xlabel("Number of residual layers")
plt.ylabel("Loss")
plt.title("ResLogit model with different number of residual layers")
plt.legend()
plt.show()
execution_times = [row["execution_time"] for index, row in metrics.iterrows()]

plt.plot(list_n_layers, execution_times)

plt.xlabel("Number of residual layers")
plt.ylabel("Execution time (s)")
plt.title("ResLogit model with different number of residual layers")
plt.legend()
plt.show()

References

[1] ResLogit: A residual neural network logit model for data-driven choice modelling, Wong, M.; Farooq, B. (2021), Transportation Research Part C: Emerging Technologies 126\ (URL: https://doi.org/10.1016/j.trc.2021.103050)