RUMnet model Usage
Introduction to modelling with RUMnet
We reproduce in this notebook the results of the paper Reprensenting Random Uility Models with Neural Networks on the SwissMetro dataset.
import os
# Remove/Add GPU use
os.environ["CUDA_VISIBLE_DEVICES"] = "1"
import sys
sys.path.append("../../")
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 RUMnet
from choice_learn.datasets import load_swissmetro
Note that there are two implementation of RUMnet: One more CPU-oriented and one more GPU-oriented. The import of the right model is automatically done. You can also import the model directly with:
First, we download the SwissMetro dataset:
We follow the same data preparation as in the original paper in order to get the exact same results.
Now, we can create our ChoiceDataset from the dataframe.
Let's Cross-Validate ! We keep a scikit-learn-like structure. To avoid creating dependancies, we use a different train/test split code, but the following would totally work:
from sklearn.model_selection import ShuffleSplit
rs = ShuffleSplit(n_splits=5, test_size=.2, random_state=0)
for i, (train_index, test_index) in enumerate(rs.split(dataset.choices)):
train_dataset = dataset[train_index]
test_dataset = dataset[test_index]
model = RUMnet(**args)
model.instantiate()
model.fit(train_dataset)
model.evaluate(test_dataset)
We just use a numpy based split, but the core code is the same!
model_args = {
"num_products_features": 6,
"num_customer_features": 83,
"width_eps_x": 20,
"depth_eps_x": 5,
"heterogeneity_x": 10,
"width_eps_z": 20,
"depth_eps_z": 5,
"heterogeneity_z": 10,
"width_u": 20,
"depth_u": 5,
"optimizer": "Adam",
"lr": 0.0002,
"logmin": 1e-10,
"label_smoothing": 0.02,
"callbacks": [],
"epochs": 140,
"batch_size": 32,
"tol": 0,
}
indexes = np.random.permutation(list(range(len(dataset))))
fit_losses = []
test_eval = []
for i in range(5):
test_indexes = indexes[int(len(indexes) * 0.2 * i):int(len(indexes) * 0.2 * (i + 1))]
train_indexes = np.concatenate([indexes[:int(len(indexes) * 0.2 * i)],
indexes[int(len(indexes) * 0.2 * (i + 1)):]],
axis=0)
train_dataset = dataset[train_indexes]
test_dataset = dataset[test_indexes]
model = RUMnet(**model_args)
model.instantiate()
losses = model.fit(train_dataset, val_dataset=test_dataset)
probas = model.predict_probas(test_dataset)
eval = tf.keras.losses.CategoricalCrossentropy(from_logits=False)(y_pred=model.predict_probas(test_dataset), y_true=tf.one_hot(test_dataset.choices, 3))
test_eval.append(eval)
print(test_eval)
fit_losses.append(losses)
cmap = plt.cm.coolwarm
colors = [cmap(j / 4) for j in range(5)]
for i in range(len(fit_losses)):
plt.plot(fit_losses[i]["train_loss"], c=colors[i], linestyle="--")
plt.plot(fit_losses[i]["test_loss"], label=f"fold {i}", c=colors[i])
plt.legend()
A larger and more complex dataset: Expedia ICDM 2013
The RUMnet paper benchmarks the model on a second dataset. If you want to use it you need to download the file from Kaggle and place the train.csv file in the folder choice_learn/datasets/data with the name expedia.csv.
from choice_learn.datasets import load_expedia
# It takes some time...
expedia_dataset = load_expedia(preprocessing="rumnet")
test_dataset = expedia_dataset[int(len(expedia_dataset)*0.8):]
train_dataset = expedia_dataset[:int(len(expedia_dataset)*0.8)]
model_args = {
"num_products_features": 46,
"num_customer_features": 84,
"width_eps_x": 10,
"depth_eps_x": 3,
"heterogeneity_x": 5,
"width_eps_z": 10,
"depth_eps_z": 3,
"heterogeneity_z": 5,
"width_u": 10,
"depth_u": 3,
"tol": 0,
"optimizer": "Adam",
"lr": 0.001,
"logmin": 1e-10,
"label_smoothing": 0.02,
"callbacks": [],
"epochs": 15,
"batch_size": 128,
"tol": 1e-5,
}
model = RUMnet(**model_args)
model.instantiate()
losses = model.fit(train_dataset, val_dataset=test_dataset)
probas = model.predict_probas(test_dataset)
test_loss = tf.keras.losses.CategoricalCrossentropy(from_logits=False)(y_pred=model.predict_probas(test_dataset), y_true=tf.one_hot(test_dataset.choices, 39))
print(test_loss)