Git Product home page Git Product logo

bayesianbandits's Introduction

bayesianbandits

Downloads codecov

Bayesian Multi-Armed Bandits for Python

Problem: Despite having a conceptually simple interface, putting together a multi-armed bandit in Python is a daunting task.

Solution: bayesianbandits is a Python package that provides a simple interface for creating and running Bayesian multi-armed bandits. It is built on top of scikit-learn and scipy, taking advantage of conjugate priors to provide fast and accurate inference.

While the API is still evolving, this library is already being used in production for marketing optimization, dynamic pricing, and other applications. Are you using bayesianbandits in your project? Let us know!

Features

  • Simple API: bayesianbandits provides a simple interface - most users will only need to call pull and update to get started.
  • Fast: bayesianbandits is built on top of already fast scientific Python libraries, but, if installed, will also use SuiteSparse to further speed up matrix operations on sparse matrices. Handling tens or even hundreds of thousands of features in a sparse model is no problem.
  • scikit-learn compatible: Use sklearn pipelines and transformers to preprocess data before feeding it into your bandit.
  • Flexible: Pick from a variety of policy algorithms, including Thompson sampling, upper confidence bound, and epsilon-greedy. Pick from a variety of prior distributions, including beta, gamma, normal, and normal-inverse-gamma.
  • Extensible: bayesianbandits provides simple interfaces for creating custom policies and priors.
  • Well-tested: bayesianbandits is well-tested, with nearly 100% test coverage.

Compatibility

bayesianbandits is tested with Python 3.9, 3.10, 3.11, and 3.12 with scikit-learn 1.2.2, 1.3.2, and 1.4.2.

Getting Started

Install this package from PyPI.

pip install -U bayesianbandits

Define a LinearUCB contextual bandit with a normal prior.

import numpy as np
from bayesianbandits import (
    Arm,
    NormalInverseGammaRegressor,
    ContextualAgent,
    UpperConfidenceBound,
)

arms = [
    Arm(1, learner=NormalInverseGammaRegressor()),
    Arm(2, learner=NormalInverseGammaRegressor()),
    Arm(3, learner=NormalInverseGammaRegressor()),
    Arm(4, learner=NormalInverseGammaRegressor()),
]

policy = UpperConfidenceBound(alpha=0.84)

Instantiate the agent and pull an arm with context.

agent = ContextualAgent(arms, policy)

context = np.array([[1, 0, 0, 0]])

# Can be constructed with sklearn, formulaic, patsy, etc...
# context = formulaic.Formula("1 + article_number").get_model_matrix(data)
# context = sklearn.preprocessing.OneHotEncoder().fit_transform(data)

agent.pull(context)

Update the bandit with the reward.

agent.update(context, np.array([15.0]))

That's it! Check out the documentation for more examples.

bayesianbandits's People

Contributors

dependabot[bot] avatar rishi-kulkarni avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

jaedukseo

bayesianbandits's Issues

Add sparse matrix support to linear estimators

Users should be able to specify if they want to store the covariance matrix as a dense or sparse matrix in the constructor, and it should be handled smoothly from there.

Potentially also emit warnings if the stored matrix is dense.

Better handling of prior setting for one-hot encoded data when using Bayesian linear regression

Currently, if the user chooses to set priors for each coefficient separately, they'd have to manually set a value for each coefficient generated by one-hot encoding. This is a bit inconvenient, as it requires the user to check how many categories are in each of their categorical columns to set up the prior.

Instead, we could assume the prior is the same for each category, allowing the user to just provide a prior vector that has the same length as the number of columns in their data, which seems more intuitive.

Support masking of arms when pulling

For continuum-arm bandits being treated with fixed discretization, it can be useful to "activate" or "deactivate" arms in certain scenarios. Additionally, there may be business reasons to want to never pull an arm in certain contexts. Providing an interface for masking arms for a given pull would give users a lot of flexibility to handle these situations.

Better error handling in `update`

  1. Raise custom exception if key not found in cache for delayed_reward bandits
  2. Raise warning if key not found in cache for batch updates

Potential bug with agent.decay() when not all arms are updated

Context: trying to update the arms of an agent using batches of data (weeks at the time) where it is possible that not all arms appear in every given week.
An agent.decay() is applied after each week update.

Nevertheless, that creates an issue after the first update and the first decay, potentially involving the covariance matrix.
If the Sparse = False option is used, the error returned is: LinAlgError: Matrix is singular.
If Sparse = True, the error returned is: ValueError: inconsistent shapes

Both errors seem to be triggered at the following step: # Update the inverse covariance matrix

Potential cause: for the arms that were not present in a given week, the covariance matrix was never initialized.
Potential fix: only decay an arm if check_if_fitted_ returns true

Bug in Sparse LU decomposition

When computing the LU decomposition of a sparse, square matrix, permutation of the columns will not guarantee that the LU decomposition is equal to the Cholesky decomposition.

Potential fix: set permc_spec="NATURAL" in splu, to keep the natural order of the columns

Allow users to `decay` bandit on their own

In delayed_reward situations, calling decay after each update doesn't make sense, as updates may not come in the same order as they were pulled. To account for this, the user should be able to decide when to decay the bandit, just as they decide when to pull and update.

Add integration tests

That the docs run properly is okay, but testing the entire user-facing API would be good

Batch updates for `delayed_reward` bandits to reduce calls to `arm.update`

arm.update calls can be very expensive, especially when the learner has a LAPACK call. Currently, a batch of updates for a delayed_reward looks up each arm associated with each unique_id, then calls arm.update. Grouping all unique_ids that resulted in pulling the same arm would bound the number of arm.update calls per bandit.update to the number of total arms, which is far far less than it currently is.

Deprecate `delayed_reward` requiring an additional `unique_id` argument to `pull` and `update`

delayed_reward makes the API hard to statically type check. In most real-world scenarios (including my own experiences putting these models into production), the calling code should be keeping track of arms pulled in a delayed-reward scenario. Perhaps the cleanest way to implement this is by having delayed_reward add a arm_to_update property with a setter that takes an action token. Then, users could do something like agent.arm_to_update(19.5).update(X, y).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.