Approach: maintain an ensemble of K online regressors (e.g., sklearn SGDRegressor or simple linear models). For each interaction, pick one model uniformly at random, use it to predict rewards for each arm given context, choose the arm with highest predicted reward, observe reward, and update all models using bootstrap — either by Poisson(1) weights or by Bernoulli resampling per-model so each model gets a noisy view of the data online.python
import numpy as np
from sklearn.linear_model import SGDRegressor
class BootstrappedThompson:
def __init__(self, n_arms, context_dim, K=10, lr=0.01, use_poisson=True):
self.n_arms = n_arms
self.K = K
self.use_poisson = use_poisson
# one regressor per model; predict expected reward given (arm, context)
self.models = [SGDRegressor(learning_rate='constant', eta0=lr, max_iter=1, tol=None)
for _ in range(K)]
# initialize with a single dummy fit to set dimensions
X0 = np.zeros((1, context_dim + n_arms))
y0 = np.zeros(1)
for m in self.models:
m.partial_fit(X0, y0)
def _featurize(self, context, arm):
# one-hot arms concatenated with raw context
arm_onehot = np.zeros(self.n_arms)
arm_onehot[arm] = 1.0
return np.concatenate([arm_onehot, context])
def select_arm(self, context):
model_idx = np.random.randint(self.K)
m = self.models[model_idx]
preds = []
for a in range(self.n_arms):
x = self._featurize(context, a).reshape(1, -1)
preds.append(m.predict(x)[0])
return int(np.argmax(preds)), model_idx
def update(self, context, arm, reward):
x = self._featurize(context, arm).reshape(1, -1)
for m in self.models:
# decide multiplicity via Poisson(1) or Bernoulli(0.5)
if self.use_poisson:
k = np.random.poisson(1)
else:
k = np.random.binomial(1, 0.5)
for _ in range(k):
m.partial_fit(x, np.array([reward]))
Key concepts:- Each model sees a stochastic subset/weighted copy of data → ensemble approximates posterior uncertainty.- Sampling one model per decision ≈ sampling from approximate posterior (hence "Thompson-like").Complexity:- Per step: O(K * n_arms * D) prediction if evaluating each arm per sampled model, but we sample one model then evaluate n_arms with that single model → O(n_arms * D). Update cost O(K * D * expected multiplicity).Edge cases & practical tips:- Use more expressive base learners or add regularization/learning-rate scheduling.- For large n_arms, cache or parallelize predictions.- If rewards are sparse/noisy, increase K or use larger Poisson mean to increase diversity.Trade-offs vs Bayesian Thompson Sampling:- Pros: Simple, scalable, works with arbitrary online learners, easy to implement; nonparametric uncertainty from resampling.- Cons: Not a true posterior — can under/overestimate uncertainty depending on model and bootstrap scheme; may require many ensemble members; no closed-form credible intervals; Bayesian methods can be more sample-efficient when good priors/models exist.- Use bootstrap when Bayesian posterior is hard to compute or when using complex models (trees, neural nets) where approximate Bayesian is expensive.