标签:
这个notebook出自Pythonic Perambulations的博文。The content is BSD licensed.
我之前花了一堆时间来分享这两种思想。
这一篇文章,我打算撇开哪些争论,谈谈Python实现贝叶斯研究的工具。 现代贝叶斯主义的核心是马尔可夫链蒙特卡罗算法,MCMC,样本后验分布(sample posterior distributions)的高效算法。
下面我就用Python的三个包来演示MCMC:
我不会太关心它们的性能,也不会比较各自的API。这篇文章也不是教程;这三个包都有很完整的文档和教程。我要做的是比较一下三者的用法。用一个相对简单的例子,演示三个包的解法,希望对你有所帮助。
为了解决问题,我们做一个三参数模型来找数据的线性拟合解。参数是斜率,截距和相关性(scatter about the line);这个相关性可以当作冗余参数
先来定义一些数据
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
theta_true = (25, 0.5)
xdata = 100 * np.random.random(20)
ydata = theta_true[0] + theta_true[1] * xdata
# add scatter to points
xdata = np.random.normal(xdata, 10)
ydata = np.random.normal(ydata, 10)
plt.plot(xdata, ydata, ‘ok‘)
plt.xlabel(‘x‘)
plt.ylabel(‘y‘);
数据明显具有相关性,我们假设我们不知道误差。让我们构建一条直线来拟合。
由贝叶斯定义可得
其中,D表示观察数据,θ表示模型
假设线性模型有斜率β,y轴截距α:
假设y轴坐标具有正态分布误差, 那么模型下的任何数据点的概率为:
其中,σ是未知测量误差,为冗余参数。
所以i的似然估计的乘积为:
这里我们比之前的更仔细地选择先验条件。我们可以简单的认为α,β,和σ都是flat priors(扁平先验),但是我们必须记住扁平先验并非都无信息先验(uninformative priors)!Jeffreys先验可能是更好的选择,用对称的最大熵(symmetry and/or maximum entropy)来选择最大化的无信息先验(maximally noninformative priors)。虽然这种做法被频率论认为太主观,但是这么做是可以从信息理论中找到理论依据的。
为什么flat prior可能产生坏的选择?看看斜率就明白了。让我们来演示一下斜率从0-10的直线变化情况;
fig, ax = plt.subplots(subplot_kw=dict(aspect=‘equal‘))
x = np.linspace(-1, 1)
for slope in np.arange(0, 10, 0.1):
plt.plot(x, slope * x, ‘-k‘)
ax.axis([-1, 1, -1, 1], aspect=‘equal‘);
这些线按0.1的斜率间隔进行分布,高斜率区域的线十分密集。因为是扁平先验,你肯定会认为这些斜率彼此无差异。由上面的密集区域显然可以看出,斜率的扁平先验满足那些很陡的斜率。斜率的扁平先验不是一个最小化的无信息先验(minimally informative prior),可能是最终使你的结果有偏差(尽管有足够多的数据,结果可能几乎是0)。
你可能想动手做出一个更好的方案(可能在直线与x轴的夹角θ上用扁平先验),但是我们有更严谨的方案。这个问题在贝叶斯文献中已经有了答案;我找到的最佳方案来自Jaynes的论文直线拟合:贝叶斯方法(Straight Line Fitting: A Bayesian Solution) (pdf)
假设模型为
那么构建参数空间(parameter-space),其概率元素为P(α,β) dα dβ
因为x和y是对称的,我们就可以进行参数交互
其概率元素为Q(α′,β′)dα′dβ′,由此可得
通过雅可比变换(the Jacobian of the transformation),可得
为了保持对称性,需要保证变量的改变不会影响先验概率,因此有:
此函数满足:
即α服从均匀分布,β服从参数为sinθ的均匀分布,其中,θ=tan−1β。
你可能会奇怪,斜率的分布服从参数为sinθ的均匀分布,而非θ。sinθ可以被认为是源自截距。如果把变量α改为α⊥=αcosθ,那么均匀分布就变为(α⊥, θ)。在用PyStan求解时我们会用这个。
同理,我们希望σ的先验与问题的规模变化无关(如,改变点的数据)。因此其概率必须满足
方程等价于
这就是Harold Jeffreys提出的Jeffreys先验。
把它们放到一起,我们用这些对称性参数推导出模型的最小无信息先验:
综上所述,你可能用扁平先验做参数变换(α,β,σ)→(α⊥,θ,logσ),来解决这个问题, 但我认为对称/最大熵(symmetry/maximum entropy)方法更简洁明了——而且让我们能看到三个Python包演示这个先验的内涵。
现在有了数据,似然估计和先验,让我们用Python的emcee,PyMC和PyStan来演示。首先,让我们做一些辅助工作来可视化数据:
# Create some convenience routines for plotting
def compute_sigma_level(trace1, trace2, nbins=20):
"""From a set of traces, bin by number of standard deviations"""
L, xbins, ybins = np.histogram2d(trace1, trace2, nbins)
L[L == 0] = 1E-16
logL = np.log(L)
shape = L.shape
L = L.ravel()
# obtain the indices to sort and unsort the flattened array
i_sort = np.argsort(L)[::-1]
i_unsort = np.argsort(i_sort)
L_cumsum = L[i_sort].cumsum()
L_cumsum /= L_cumsum[-1]
xbins = 0.5 * (xbins[1:] + xbins[:-1])
ybins = 0.5 * (ybins[1:] + ybins[:-1])
return xbins, ybins, L_cumsum[i_unsort].reshape(shape)
def plot_MCMC_trace(ax, xdata, ydata, trace, scatter=False, **kwargs):
"""Plot traces and contours"""
xbins, ybins, sigma = compute_sigma_level(trace[0], trace[1])
ax.contour(xbins, ybins, sigma.T, levels=[0.683, 0.955], **kwargs)
if scatter:
ax.plot(trace[0], trace[1], ‘,k‘, alpha=0.1)
ax.set_xlabel(r‘$\alpha$‘)
ax.set_ylabel(r‘$\beta$‘)
def plot_MCMC_model(ax, xdata, ydata, trace):
"""Plot the linear model and 2sigma contours"""
ax.plot