import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.stats import binom
4 Prior Distributions 之选择
一枚硬币,随机抛掷 250 次,观测的结果是 140 次正面朝上,110 次背面朝上。将这枚硬币随机抛掷一次,是正面朝上的概率最有可能是多少?
上图来自搜狐
我们可以用 Chapter 3 那部分的例子来思考。
可以把这个问题转化为:
我们目前有 \(n+1\) 枚硬币,其各有如下特征:
- 随机抛硬币 0,正面朝上的概率是 0/n
- 随机抛硬币 1,正面朝上的概率是 1/n
- 随机抛硬币 2,正面朝上的概率是 2/n
… - 随机抛硬币 n,正面朝上的概率是 n/n
我们从这些硬币中随机挑了一枚,随机抛掷 250 次,观测的结果是 140 次正面朝上,110 次背面朝上。这枚硬币最有可能是哪枚硬币?
我们先用 uniform prior,也就是说,每一枚硬币是该硬币的概率相同。
def normalize_array(arr):
return np.array([i/sum(arr) for i in arr])
# 稍微修改一下之前的 update_bowls_pmf
def update_coins_pmf(n, h, t):
"""
n: 总共几枚硬币
h: 正面朝上
t: 背面朝上
"""
= np.array([1]*n)
prior = normalize_array(prior)
prior = np.array([i/(n-1) for i in range(n)])
likelihood_head = np.array([1- i for i in likelihood_head])
likelihood_tail = {
likelihood "head": likelihood_head,
"tail": likelihood_tail
}= ["head"]*h + ["tail"]*t
dataset = prior.copy()
posterior for data in dataset:
*= likelihood[data]
posterior return normalize_array(posterior)
= update_coins_pmf(1001, 140, 110) posterior
Code
= pd.DataFrame(posterior, columns=['probs'])
df = df.reset_index(names = "Coin #")
df = "Coin #", y="probs",
df.plot.scatter(x = 4,
s = "140 heads out of 250")
label "Probability mass function")
plt.ylabel(
plt.legend()"Posterior distribution of x")
plt.title( plt.show()
4.1 不同的 Prior Distributions
上面我们用到的是 uniform prior:
Code
= 1001
n = np.array([1]*n)
prior = normalize_array(prior)
uniform = range(n)
x_axis = x_axis, y = uniform,
plt.scatter(x ="prior", color="orange", s = 2)
label"Coin #")
plt.xlabel("Probability mass function")
plt.ylabel("Uniform prior")
plt.title( plt.show()
但是该 prior 并不一定是 uniform 的,也可能是这样子:
Code
= np.arange(500)
ramp_up = np.arange(500, -1, -1)
ramp_down = np.append(ramp_up, ramp_down)
prior = normalize_array(prior)
triangle = x_axis, y = triangle,
plt.scatter(x ="prior", color="orange", s = 2)
label"Coin #")
plt.xlabel("Probability mass function")
plt.ylabel("Triangle prior")
plt.title( plt.show()
如果我们用如此的 prior,结果是什么呢:
# 稍微修改一下之前的 update_coins_pmf 以便可以修改 prior
def update_coins_pmf(n, h, t, prior):
"""
n: 总共几枚硬币
h: 正面朝上
t: 背面朝上
prior: a normalized array
"""
= np.array([i/(n-1) for i in range(n)])
likelihood_head = np.array([1- i for i in likelihood_head])
likelihood_tail = {
likelihood "head": likelihood_head,
"tail": likelihood_tail
}= ["head"]*h + ["tail"]*t
dataset = prior.copy()
posterior for data in dataset:
*= likelihood[data]
posterior return normalize_array(posterior)
= 1001
n = 140
h = 110
t = np.arange(500)
ramp_up = np.arange(500, -1, -1)
ramp_down = np.append(ramp_up, ramp_down)
prior = normalize_array(prior)
prior
= update_coins_pmf(n, h, t, prior) posterior
Code
= x_axis, y = posterior,
plt.scatter(x ="140 heads out of 250", color="orange", s = 2)
label"Coin #")
plt.xlabel("Probability mass function")
plt.ylabel("Posterior distribution of triangle prior")
plt.title(
plt.legend() plt.show()
= np.argmax(posterior)
max_index print("最高点出现在 Coin #", max_index)
最高点出现在 Coin # 558
我们看到和用 uniform prior 结果差不多。这说明什么?这说明数据够多的话,prior 对 posterior 的影响没那么大。
但我们来个更极端的:随机 prior。
= np.random.rand(n)
random_prior_values = normalize_array(random_prior_values)
random_prior
= update_coins_pmf(n, h, t, prior=random_prior) posterior
Code
= x_axis, y = posterior,
plt.scatter(x ="140 heads out of 250", color="orange", s = 2)
label"Coin #")
plt.xlabel("Probability mass function")
plt.ylabel("Posterior distribution of random prior")
plt.title(
plt.legend() plt.show()
= np.argmax(posterior)
max_index print("最高点出现在 Coin #", max_index)
最高点出现在 Coin # 565
我们看到,其实结果依然没有太大的影响。这进一步说明「这说明数据够多的话,prior 对 posterior 的影响没那么大」。
4.2 Batch updating
上面我们是一个数据点一个数据点地在更新 posterior:
for data in dataset:
*= likelihood[data] posterior
我们之前证明过了,顺序不重要,那我们就把「随机抛掷 250 次,观测的结果是 140 次正面朝上,110 次背面朝上」当成一个单独的事件就好了。这个事件也就是 Data。
我们来分析:
- hypothesis: 哪枚硬币
- data: 随机抛掷 250 次,观测的结果是 140 次正面朝上,110 次背面朝上
- prior: \(p(h)\)
- likelihood: \(p(d|h)\)
- posterior: \(p(h|d)\)
我们现在要用 prior 和 likelihood 求得 posterior。Prior 我们有了,不管是最开始的 uniform prior 还是 triangle prior,其本质就是一个数组 (array) 而已。那我们如何得到 Likelihood 这一数组?需要用到 binomial distribution:
\[P(X = k) = \binom{n}{k} p^k (1-p)^{n-k}\]
我们用 scipy.stats.binom
。
scipy.stats.binom.pmf(k, n, p)
其中 k 是正面朝上的次数,n 是总攻抛掷的次数, p 是正面朝上的概率。需要注意的是,p 可以是一个数,也可以是一组数。当 p 是一个数时,其结果是一个数。当 p 是一组数时,结果是一组数。
其实,这里的 \(p\) 就是 prior 这一数组
def update_binom(n, heads, tosses, prior):
"""
heads: number of heads
tosses: total tosses
prior: prior distribution; should be a empiricaldist.pmf object (a Series)
"""
# 0/n, 1/n, 2/n ...
= np.array([i/(n-1) for i in range(n)])
likelihood_head = likelihood_head
coin_head_probabilities = binom.pmf(k = heads, n = tosses, p = coin_head_probabilities)
likelihood = prior.copy()
posterior *= likelihood
posterior return normalize_array(posterior)
# n: number of coins
= 1001
n = 250
tosses # number of heads out of 250 tosses
= 140
heads = np.array([1]*n)
prior = normalize_array(prior)
uniform = update_binom(n, heads, tosses, uniform) posterior
Code
= x_axis, y = posterior,
plt.scatter(x ="140 heads out of 250", color="orange", s = 2)
label"Coin #")
plt.xlabel("Probability mass function")
plt.ylabel("Posterior distribution of a uniform prior")
plt.title(
plt.legend() plt.show()