from scipy.stats import poisson
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
def normalize_array(arr):
return np.array([i/np.sum(arr) for i in arr])
= 5
lam = poisson(lam)
dist = [dist.pmf(i) for i in range(11)] pmfs
7 泊松过程
7.1 Gamma 分布
首先,我建议大家把我在博客讲过的 离散分布先理解透,特别是泊松分布那一部分。
在泊松分布中我们讲到这样一个问题:
中国男足队平均每场比赛进 5 个球 (请允许我在平行宇宙做一次梦),请问下一场比赛中国进 2 个球的概率是多少
我们用泊松分布模拟了中国队进 0-10 个球的概率:
Code
= range(11)
x_axis ='steelblue', label=r'Poisson distribution with `$\lambda=5$`')
plt.bar(x_axis, pmfs, color"Number of Goals (k)")
plt.xlabel("Probability Mass")
plt.ylabel(f"Poisson Distribution of Goals ($\lambda=5$)") plt.title(
Text(0.5, 1.0, 'Poisson Distribution of Goals ($\\lambda=5$)')
使用泊松分布,我们可以由
中国队在一场球赛中进了 2 个球 (
),请问中国队平常一般进多少个球 ( )?
根据贝叶斯定理:
这里
是数据 是假设 是先验概率 是似然
我们可以先忽略
为什么我们可以忽略
我们最后让 posteior 和为一的时候,也是要对所有的结果乘上一个常量。那我们最后再做就好,没有必要这时候乘。
我们用 FIFA 的数据 (世界杯场均进分数据):
= pd.read_csv('public/data/fifa.csv')
df df.head()
year | lam | |
---|---|---|
0 | 1930 | 3.89 |
1 | 1934 | 4.12 |
2 | 1938 | 4.67 |
3 | 1950 | 4.00 |
4 | 1954 | 5.38 |
Code
=2.5)
plt.bar(df.year, df.lam, width"Year")
plt.xlabel("Average Goals per Match")
plt.ylabel("Average Goals per Match at FIFA World Cups")
plt.title(='y', linestyle='--', alpha=0.5)
plt.grid(axis plt.show()
np.mean(df.lam)
3.065909090909091
我们看到一场球平均进 3 个球。那我们大概就有数了。但是 Prior 不能只是一个数,需要是一个分布,我们用 Gamma 分布来模拟。该分布有两个变量:
其中
其中
如果是非整数,比如
为什么用 Gamma 分布?
- 因为它的自变量是从 0 开始的。
- 如果我们把
预设为 1,那么就只剩下一个变量 ,这个就是上面算出来的 3 个球。 - 结果符合现实情况。
所以以下就是 Prior:
from scipy.stats import gamma
= 3
alpha_prior
# assume it is almost impossible to score more than 10
= np.linspace(0, 10, 101)
lambdas = lambdas[1] - lambdas[0]
delta_lambda
# probability mass function
= gamma(alpha_prior).pdf(lambdas)*delta_lambda
prior_pmf
# normalize the prior for visualization purposes
# note that the posterior is the same whether we normalize prior or not
= normalize_array(prior_pmf) prior_pmf
注意,gamma(alpha_prior).pdf(lambdas)
算的是 PDF,要乘以间隔才是 PMF (probability mass function)。
sum(prior_pmf) np.
0.9999999999999997
Likelihood,也就是已知
= 2
k = poisson(lambdas).pmf(k)
likelihood_pmf 0:5] likelihood_pmf[
array([0. , 0.00452419, 0.01637462, 0.03333682, 0.0536256 ])
那求 Posterior 就是 Prior 和 Likelihood 这两个数列逐元素相乘,然后让最后的结果之和为1就可以:
= normalize_array(prior_pmf * likelihood_pmf) posterior
Code
= lambdas,
plt.scatter(x = prior_pmf,
y = 2,
s ="grey",
color="Prior")
label= lambdas,
plt.scatter(x = posterior,
y = 2,
s = "orange",
color ="Posterior")
labelr"$\lambda$")
plt.xlabel("Probability Mass")
plt.ylabel(
plt.legend()r"Prior and Posterior Distribution of $\lambda$") plt.title(
Text(0.5, 1.0, 'Prior and Posterior Distribution of $\\lambda$')
7.2 Probability of Superiority
德国男足最近的一场比赛射进 4 个球,巴西队最近一场比赛射进 2 个球。我们有多大信心说德国男足更强?
我们首先要把各自队的 posterior 求出来:
def get_posterior_pmf(lambdas, k, prior_pmf):
= poisson(lambdas).pmf(k)
likelihood_pmf = normalize_array(
posterior * likelihood_pmf)
prior_pmf return posterior
= get_posterior_pmf(lambdas, 4, prior_pmf)
ge_posterior_pmf = get_posterior_pmf(lambdas, 2, prior_pmf) br_posterior_pmf
Code
= lambdas,
plt.scatter(x = ge_posterior_pmf,
y = 2,
s ="steelblue",
color="Germany")
label= lambdas,
plt.scatter(x = br_posterior_pmf,
y = 2,
s = "orange",
color ="Brazil")
labelr"$\lambda$")
plt.xlabel("Probability Mass")
plt.ylabel(
plt.legend()r"Posterior Distribution") plt.title(
Text(0.5, 1.0, 'Posterior Distribution')
这里
总体思路是这样:德国队随机一个
算法如下:
其中
= 0
total for i, lam_ge in enumerate(lambdas):
for j, lam_br in enumerate(lambdas):
if lam_ge > lam_br:
+= ge_posterior_pmf[i] * br_posterior_pmf[j]
total total
0.7152072603914539
想一下下面这个想法为什么是错的:
sum = 0
for i, lam in enumerate(lambdas):
if ge_posterior_pmf[i] > br_posterior_pmf[i]:
sum += 1
7.3 Conjugate Priors
我们来讲一个很重要的概念:Conjugate priors,中文翻译成「共轭先验」。其所表达的意思是:Posterior Distribution 和 Prior Distribution 同属于一个 Distribution。因为我们的 prior 是 gamma distribution,那 posterior 也是,只是 shape (alpha) 和 rate (beta) 不同。
我们具体来看一下。
在公式 Equation 7.2 中,求先验概率
那
相乘的结果是:
有这样一个知识:对于一个 Gamma 函数
Code
= 3
alpha_0 = 2
beta_0 = np.linspace(0, 10, 500)
x = gamma.pdf(x, alpha_0, scale = 1/beta_0)
gamma_pdf = 2 * gamma_pdf
scaled_gamma_pdf
=(10, 6))
plt.figure(figsize='Gamma($\\alpha=3, \\beta=2$)', color='blue')
plt.plot(x, gamma_pdf, label
plt.plot(x, scaled_gamma_pdf, ='2 * Gamma($\\alpha=3, \\beta=2$)',
label='red',
color='--')
linestyle0, color='gray', linestyle=':', linewidth=1)
plt.axhline('$\\lambda$')
plt.xlabel('Probability Density')
plt.ylabel('Original vs. Scaled Gamma Distribution')
plt.title(
plt.legend() plt.show()
那我们就可以理解,在 Gamma 分布中,
为了得到 Posterior distribution, 我们要把所有的
因此我们就可以这么说:Posterior Distribution 也是一个 Gamma Distribution,其参数现在变成了
代码:
def get_posterior_pmf_conjugate_prior(alpha_prior, beta_prior, k, lambdas):
"""Get posterior pmf through conjugate prior
"""
= lambdas[1] - lambdas[0]
delta_lambda
= alpha_prior + k
alpha_post = beta_prior + 1
beta_post
= gamma(alpha_post, scale=1/beta_post).pdf(lambdas)
posterior_pdf = posterior_pdf*delta_lambda
posterior_pmf = normalize_array(posterior_pmf)
posterior_pmf
return posterior_pmf
= get_posterior_pmf_conjugate_prior(
ge_posterior_pmf 1, 4, lambdas)
alpha_prior, = get_posterior_pmf_conjugate_prior(
br_posterior_pmf 1, 2, lambdas) alpha_prior,
Code
= lambdas,
plt.scatter(x = ge_posterior_pmf,
y = 2,
s ="steelblue",
color="Germany")
label= lambdas,
plt.scatter(x = br_posterior_pmf,
y = 2,
s = "orange",
color ="Brazil")
labelr"$\lambda$")
plt.xlabel("Probablity Mass")
plt.ylabel(
plt.legend()r"Posterior Distribution") plt.title(
Text(0.5, 1.0, 'Posterior Distribution')
我们看到这个结果和我们通过常规的 Prior 与 Likelihood 相乘所得到的结果是一样的。
7.4 后验预测分布
我们来讲一下 Posterior predictive distribution (后验预测分布)。
我们看到 Posterior distribution 中,X 轴是
回到刚才的德国和巴西男足的叙事,我们现在如果问:
两队现在马上要进行一场比赛,请问德国对获胜的概率是多少?
请注意,我们现在已经不再是问
还记得我们上面提到的吗?
这里
def post_predictive_dist(lambdas, posterior_pmfs):
"""compute posterior predictive distribution
Inputs:
- lambdas: an numpy array
- posterior_pmfs: a numpy array
Output:
- res: a dic where key is k and value is P(k)
"""
= range(int(max(lambdas)))
goals = {}
res for goal in goals:
= 0
total for i, lam in enumerate(lambdas):
= poisson(lam).pmf(goal)
poisson_pmf = posterior_pmfs[i]
posterior_pmf += poisson_pmf * posterior_pmf
total = total
res[goal]
= normalize_array(np.array(list(res.values())))
normalized_values for i, pair in enumerate(res):
= normalized_values[i]
res[i] return res
= post_predictive_dist(lambdas, ge_posterior_pmf)
ge_res = post_predictive_dist(lambdas, br_posterior_pmf) br_res
sum(ge_res.values())
1.0
Code
def plot_post_pred_dist(res, cntry_name):
= list(res.keys())
gaols = list(res.values())
probs =(8,5))
plt.figure(figsize="skyblue", edgecolor='black')
plt.bar(gaols, probs, color"Number of Goals (k)")
plt.xlabel("Probability Mass")
plt.ylabel(f"Posterior Predictive Distribution of Goals for {cntry_name}")
plt.title( plt.show()
'Germany') plot_post_pred_dist(ge_res,
'Brazil') plot_post_pred_dist(br_res,
下面我们计算一下在下一场比赛中德国队获胜的概率。
总体思路是这样:德国队随机一个
算法如下:
def prob_win(res1, res2):
= 0
win_prob = list(res1.keys())
goals for goal1 in goals:
for goal2 in goals:
if goal1 > goal2:
+= res1[goal1] * res2[goal2]
win_prob return win_prob
prob_win(ge_res, br_res)
0.5582596630665152