david-cortes / contextualbandits

Python implementations of contextual bandits algorithms
http://contextual-bandits.readthedocs.io
BSD 2-Clause "Simplified" License
750 stars 148 forks source link

contextualbandits.online._predict_from_beta_prior_and_smoothing nameError: name '_apply_smoothing' is not defined #72

Closed nwilder closed 2 months ago

nwilder commented 2 months ago

I'm calling contextualbandits.online.decision_function in my application where my instantiation shares the same betas for each arm. As a result, the __predict_from_beta_prior_and_smoothing function is called which then calls _apply_smoothing after generating samples from the beta distribution. I cannot find a line in contextualbandits.online.py where _apply_smoothing is defined or imported -- which, I believe explains the error I'm receiving.

P.S. This is my first time creating an issue, so I hope to learn proper etiquette in addition to raising this issue.

david-cortes commented 2 months ago

Thanks for the bug report.

Are you able to provide a reproducible example? The following works without errors for me:

import numpy as np
from sklearn.linear_model import LogisticRegression
from contextualbandits.online import EpsilonGreedy

rng = np.random.default_rng(seed=123)
nchoices = 3
X = rng.standard_normal(size=(100,10))
a = rng.integers(nchoices, size=X.shape[0])
r = rng.integers(2, size=X.shape[0])

policy = EpsilonGreedy(
    LogisticRegression(),
    nchoices = nchoices,
    beta_prior = ((2,3), 10),
    random_state = 123
)
policy.fit(X, a, r)
policy.predict(X)
nwilder commented 2 months ago

Thank you for your quick reply!

Here is a quick example I was able to reproduce the error with:

from sklearn.linear_model import SGDClassifier
from contextualbandits.online import BootstrappedUCB
from copy import deepcopy
import numpy as np

nchoices = 3

rng = np.random.default_rng(seed=123)
X = rng.standard_normal(size=(100,10))
a = rng.integers(nchoices, size=X.shape[0])
r = rng.integers(2, size=X.shape[0])

# Distinct betas passed in spite of being the same for flexibility later in adding new arms with different betas
bp = ((3,2), 2)
beta_prior = [bp] * 3

base_algorithm = SGDClassifier(loss='log_loss')
model = BootstrappedUCB(deepcopy(base_algorithm), 
                        nchoices = nchoices,
                        beta_prior = beta_prior,
                        batch_train = True,
                        )

# Calling decision_function method directly to get probability scores
model.decision_function(X)

What's distinctly different is:

  1. Instantiating the object with a list of beta priors for flexibility to add an arm later on with a different beta_prior
  2. Calling the decision_function method to get probabilities
  3. This would be an first set of recommendations generated before a round of updating the model with feedback in training
david-cortes commented 2 months ago

Thanks, should be fixed now:

pip install -U contextualbandits
nwilder commented 2 months ago

Thank you @david-cortes!