I’m running into what I think might be a bug with PyMC3’s Binomial likelihood. Here’s the example.
- Simulate data
itrs = 100
p = 0.4
W = []
L = []
N = []
for i in range(itrs):
n = int(np.random.normal(loc=100, scale=20))
wins = np.random.binomial(n=n,p=p)
losses = n - wins
N.append(n)
W.append(wins)
L.append(losses)
- Define a model
with pm.Model() as m1:
alpha = pm.HalfNormal('alpha',sigma=1)
beta = pm.HalfNormal('beta',sigma=1)
theta = pm.Beta('theta',alpha, beta)
lik = pm.Binomial('likelihood',n=N,p=theta,observed=W)
trace1 = pm.sample(chains=4)
- Examine parameter estimates
pm.traceplot(trace1, compact=False)
Note: Look at the distribution of Theta.
4. Use mean inferred alpha and beta parameters to visualize P(\theta|X).
def sample_post_plot(samples=100, trace_obj=trace1):
X = np.linspace(0,1,1000)
for i in range(samples):
idx = random.randint(0,len(trace_obj.get_values('alpha')))
a = trace_obj.get_values('alpha')[idx]
b = trace_obj.get_values('beta')[idx]
Y = stats.beta(a=a,b=b).pdf(X)
plt.plot(X,Y)
sample_post_plot(trace_obj=trace1)
Note: This is a completely different distribution than the theta posterior plot from step 3.
4. Reference expectation E[P(\theta|E[\alpha], E[\beta])]
np.average(np.random.beta(a=az.summary(trace1).to_dict()['mean']['alpha'],
b=az.summary(trace1).to_dict()['mean']['beta'], size=1000))
>>>
0.4701658349492111
Note: So both the shape and expectation of P(\theta|\alpha, \beta) differ from PyMC3’s posterior plots. What’s perplexing to me is that E[P(\theta)]=0.403 is extremely close to the theta value we provided in step 1 (0.4). So posterior inference on theta is correct; it’s just that posterior inference on alpha and beta, is completely off.
What’s going on here? I’m hesitant to think this is truly a PyMC3 bug; probably more likely operator error.
Edit: I’ve discovered that this {alpha, beta, theta}
disagreement, for lack of a better term, goes away when I use a Beta-Binomial likelihood instead. This post os already quite long, so for the sake of brevity, I’ve linked a Google Colab with all relevant code.
Nonetheless, I’m still really curious-- what is going on here?