How do I optimize the hyperparameters of LightFM?
Asked Answered
V

1

7

I am using the LightFM recommender library on my dataset, which gives me the results in the image below.

Poor results after some epochs

NUM_THREADS = 4
NUM_COMPONENTS = 30
NUM_EPOCHS = 5
ITEM_ALPHA = 1e-6
LEARNING_RATE = 0.005
LEARNING_SCHEDULE = 'adagrad'
RANDOM_SEED = 29031994    

warp_model = LightFM(loss='warp',
                    learning_rate=LEARNING_RATE,
                    learning_schedule=LEARNING_SCHEDULE,
                    item_alpha=ITEM_ALPHA,
                    no_components=NUM_COMPONENTS,
                    random_state=RANDOM_SEED)

bpr_model = LightFM(loss='bpr',
                    learning_rate=LEARNING_RATE,
                    learning_schedule=LEARNING_SCHEDULE,
                    item_alpha=ITEM_ALPHA,
                    no_components=NUM_COMPONENTS,
                    random_state=RANDOM_SEED)

The shapes of my features are as follows:

Item and user features shapes

How can I optimize my hyperparameters in order to improve Area Under Curve (AUC) scores?

Valente answered 18/4, 2018 at 10:2 Comment(0)
Y
15

You can find a good general guide to hyperparameter optimization in the sklearn docs.

One simple but effective technique which you can apply to optimizing a LightFM model is random search. Roughly, it consists of the following steps:

  1. Split your data into a training set, a validation set, and a test set.
  2. Define a distribution for each hyperparameter you would like to optimize. For example, if you are optimizing your learning rate, you could use an exponential distribution with a mean of 0.05; if you are optimizing the loss function, you could sample uniformly from ['warp', 'bpr', 'warp-kos'].
  3. In each iteration of the optimization, sample all your hyperparameters and use them to fit the model on the training data. Evaluate the model's performance on the validation set.
  4. After performing a number of optimization steps, select the one with the best validation performance.

To gauge the performance of the final model you should use the test set: simply evaluate the best validation model on the test set.

The following script illustrates this:

import itertools

import numpy as np

from lightfm import LightFM
from lightfm.evaluation import auc_score


def sample_hyperparameters():
    """
    Yield possible hyperparameter choices.
    """

    while True:
        yield {
            "no_components": np.random.randint(16, 64),
            "learning_schedule": np.random.choice(["adagrad", "adadelta"]),
            "loss": np.random.choice(["bpr", "warp", "warp-kos"]),
            "learning_rate": np.random.exponential(0.05),
            "item_alpha": np.random.exponential(1e-8),
            "user_alpha": np.random.exponential(1e-8),
            "max_sampled": np.random.randint(5, 15),
            "num_epochs": np.random.randint(5, 50),
        }


def random_search(train, test, num_samples=10, num_threads=1):
    """
    Sample random hyperparameters, fit a LightFM model, and evaluate it
    on the test set.

    Parameters
    ----------

    train: np.float32 coo_matrix of shape [n_users, n_items]
        Training data.
    test: np.float32 coo_matrix of shape [n_users, n_items]
        Test data.
    num_samples: int, optional
        Number of hyperparameter choices to evaluate.


    Returns
    -------

    generator of (auc_score, hyperparameter dict, fitted model)

    """

    for hyperparams in itertools.islice(sample_hyperparameters(), num_samples):
        num_epochs = hyperparams.pop("num_epochs")

        model = LightFM(**hyperparams)
        model.fit(train, epochs=num_epochs, num_threads=num_threads)

        score = auc_score(model, test, train_interactions=train, num_threads=num_threads).mean()

        hyperparams["num_epochs"] = num_epochs

        yield (score, hyperparams, model)


if __name__ == "__main__":
    from lightfm.datasets import fetch_movielens

    data = fetch_movielens()
    train = data["train"]
    test = data["test"]

    (score, hyperparams, model) = max(random_search(train, test, num_threads=2), key=lambda x: x[0])

    print("Best score {} at {}".format(score, hyperparams))
Yakut answered 23/4, 2018 at 14:31 Comment(5)
I've noticed that when not passing train_interactions=train in a metric (auc / precision_at_k / recall_at_k), the respective values get quite low. Is the right way for computing these to always pass train interactions?Philippopolis
@Philippopolis I also want to know the same.Pneumatic
Before applying your optimization technique I was getting Precision: train 0.36, test 0.13. Recall: train 0.49, test 0.39. AUC: train 0.97, test 0.93. after Precision: train 0.02, test 0.01. Recall: train 0.02, test 0.02. AUC: train 0.60, test 0.60.Pneumatic
@AshokRayal yes, it is confusing to need to pass train into computing metrics on test. That's not how one would do it by the book.Philippopolis
@Maciej Kula how can we use bayesian optimization hyper parameter tuning here ?Lathery

© 2022 - 2024 — McMap. All rights reserved.