111贝叶斯方法数据分析实战--先验的选取策略

先验的选取策略

主观先验和客观先验

贝叶斯先验可分为客观先验和主管先验两大类。
我们之前常用的扁平先验其实就是一种客观先验。扁平先验的意思就是用均匀分布来表示某个参数的先验分布。也就是说,客观先验点的意思是,我们对参数到底取什么值,没有一个特定趋向,认为在没有任何数据集的情况下,参数取任何值的概率相同。当然就目前来看,只有很少的客观先验能够真正意义上做到客观。
当我们认为先验的值在某个特定区域内的概率应该大一些,在其他地方的概率应该小一些时,其实我们就在使用主观先验。也就是说,参数取值的概率随着我们的主观推断进行了偏倚,有高有低。
当然,也并不是说只要先验分布是均匀分布就是客观先验,不是均匀分布就是主观先验,如下图所示:

image.png

如上图所示,虽然第一个均匀分布时客观先验,但是第二个均匀分布(绿色部分)却是主观先验。这是因为绿色的均匀分布的范围被人为的限制在了 0.5~1 之间,也就是说尽管该参数采用均匀分布作为先验,但是它的范围确实被人主观的定义的。因此绿色分布也应该是主观分布。
那么是客观先验好呢?还是主观先验好呢?其实它们并没有谁好谁坏之分,更多的应该还是取决于问题本身。
其实先验的选择也是建模的过程之一,我们不能随意给每个参数定义先验,也没有什么固定的套路来确定先验。但是,如果一个模型参数的先验选择不到位,那么他的后验就会失去意义。因此,应用格尔曼的话,在模型拟合以后,应该坚持模型的后验分布,看看它是否有意义。如果后验分布没有意义,那么就可能是因为我们的真实数据和定义的先验分布假设大不相同,这是,我们就可以回来改变先验分布并重新训练。
也就是说,如果后验分布看起来没有意义(即你觉得你算出来的后验分布和你想象中差别太大),则表明我们之前定义的先验分布没有包含完所有的初始信息。这时,我们就应该放弃当前的先验分布,换一个更加能反应初始信息的先验。
实验轮盘赌法
指定主观先验,代表着从业者将问题的领域知识结合到数学框架中思维方式。也就是说,针对于某个行业中的实际问题,我们对某个参数的先验分布最好是由相关行业的专家给出。但是,直接向专家询问某某参数的先验分布是什么,其实是不科学的。这有可能会吓跑很多专家(因为不是所有行业所有专家都懂概率分布,就算他们懂,他们也很难直接说出某种分布)。为此,我们引入了一种简单的做法,叫做实验轮盘赌法。
通过在专家认为的,可能的结果上放置计数器(可以理解为赌博的筹码),来建立一个先验分布的方法叫做实验轮盘赌法。
首先我们给专家 N 个计数器(即筹码),假设这里 N=20,因此,一个筹码代表的就是 0.05 的概率。然后,我们将参数的肯能取值分成几个区域,专家只需要将手中的筹码放置在他认为合适的区域即可,如下图所示:
image.png

上图显示了一个已完成1的探寻主观概率分布的网格。网格的横轴表示该参数可能的取值,然后一个方块代表一个筹码。专家只需根据自己的经验,在不同区间内添加筹码即可。他们无需告诉我们连他自己都不搞不清楚的分布函数的数学形式,只需按照自己的想法,往每个区间中放筹码即可,最后我们再利用计算机对该参数的先验进行拟合即可。(注意:上图所有的筹码之后必须为 1)。
实验轮盘赌法可以很容易的为我们提供某个参数的先验,并且在创建先验分布的过程中,专家还可以根据自己的情况对最初放置的筹码位置进行调整。
当然如果想要较为准确的确定一个参数的先验分布,那么我们就需要了解有哪些先验分布。除了之前我们介绍的一些先验分布外,接下来,我们学习一些其他的分布函数。
Gamma 分布
Gamma 分布的随机变量是一个正实数变量,记作X∼Gamma(α,β)。它其实是指数随机变量的扩展。该分布的密度函数如下:
image.png

其中Γ(α) 是 Gamma 函数,α 和β是函数中的常量。
我们可以直接利用 Scipy.stats 函数库调用该分布。接下来让我们对该函数进行可视化,观察不同α和β下的函数变化:

import numpy as np
import scipy.stats as stats
from IPython.core.pylabtools import figsize
import matplotlib.pyplot as plt
%matplotlib inline

figsize(12.5, 5)
gamma = stats.gamma

# 设置里多组函数的常数量
parameters = [(1, 0.5), (9, 2), (3, 0.5), (7, 0.5)]
x = np.linspace(0.001, 20, 150)
for alpha, beta in parameters:
    y = gamma.pdf(x, alpha, scale=1./beta)
    lines = plt.plot(x, y, label="(%.1f,%.1f)" % (alpha, beta), lw=3)
    plt.fill_between(x, 0, y, alpha=0.2, color=lines[0].get_color())
    plt.autoscale(tight=True)

plt.legend(title=r"$\alpha, \beta$ - parameters")

如上图,展示了不同α和β下的 Gamma 分布。其实,我们在学习一个分布函数时,无需记住该函数的具体表达式(当然,如果你对公式很敏感的话,也可以将它们记下啦),我们只需记住这些分函数的图像即可。
威沙特分布
威沙特分布和我们之前学的所有分布函数都不一样。我们之前学的都是分布都是产生一个标量的随机变量,而这次我们学的分布函数会产生一个随机矩阵。具体的说,威沙特分布其实就是所有半正定矩阵的分布。
由于一个合适的协方差矩阵是正定的,因此,该威沙特分布其实是一个协方差矩阵的适当先验。当然,我们无法真正去可视化一个矩阵分布。但是我们可以显示一下,它随机产生的随机变量,如下图所示:

image.png

如上图,第一行为威沙特分布产生的 4\times44×4 的随机矩阵。第二行是由该分布产生的15×15 的随机矩阵。
Beta 分布
Beta 分布是贝叶斯统计学中最常用的分布函数之一。随机变量 XX 的密度函数如下:
image.png

其中, B 是 beta 函数。α和β是函数中的两个常量。由于 Beta 分布的随机变量的范围在 0 到 1 之间,因此,如果模型中有一个参数代表概率或者比例,那么Beta分布是它的首选。
并且,常数α和β为分布的形状提供了很大的灵活性。如下图所示,我们绘制了不同α和β的 Beta 分布:

figsize(12.5, 5)
# 设置了多组参数
params = [(2, 5), (1, 1), (0.5, 0.5), (5, 5), (20, 4), (5, 1)]

x = np.linspace(0.01, .99, 100)
beta = stats.beta
for a, b in params:
    y = beta.pdf(x, a, b)
    lines = plt.plot(x, y, label="(%.1f,%.1f)" % (a, b), lw=3)
    plt.fill_between(x, 0, y, alpha=0.2, color=lines[0].get_color())
    plt.autoscale(tight=True)
plt.ylim(0)
plt.legend(loc='upper left', title="(a,b)-parameters")

仔细观察上图,我们可以发现其实由扁平分布的存在,即参数为(1,1)情况下,Beta 分布即为均匀分布。因此,我们可以说 Beta 分布是均匀分布的一般形式,均匀分布是 Beta 分布的特殊形式。
不仅是均匀分布,Beta 分布还和二项分布存在着微妙的关系。假设模型中有一个参数 p,然后设置它的先验它的先验概率为Beta分布。假设我们利用二项分布X∼Binomial(N,p) 去随机生成观测样本,并把这些样本放入模型中进行学习。那么,当我们得到 p 的后验分布时,会惊讶的发现后验分布也是 Beta 分布。换句话说,一个 Beta 先验分布连同有二项式分布生成的观察数据会形成一个 Beta 的后验分布,该分布为,p∣X∼Beta(α+X,β+N−X),其中 N 表示样本数。
这是一个非常有用的例子,无论是计算角度还是启发性角度。接下来,我们将利用上面的分布函数,来解决一个实际的问题。

例子:贝叶斯多臂老虎机

假设现在你面对 10 台老虎机(即为多臂的意思),每台老虎机会以某种概率发奖金。假设每台老虎机的奖金份额相同,只是概率不同。当然,我们不知道这些概率,并且每次我们只能选择一个老虎机,那么我们应该制定一个怎样的策略去赢取更多的奖金呢?
其实很免明显的事情,就是我们只要知道哪一台老虎机发放奖金的概率最大,那我们就总是用这台老虎机,这样我们就能拿到更多的奖金了。因此,其实,我们的任务可以简述为“最快速度找到发放奖金概率最高的老虎机”。
应用
乍看之下,仿佛这个多臂老虎机只存在于数学家的脑子里,没有人会去算老虎机的出钱概率。当时仔细想想,其实我们日常生活中很多的策略方式,其实都是想问题中描述的一样,寻找概率最高的老虎机。比如下面这些例子:
互联网的广告展示:公司有一系列广告可以想潜在客户展示,但是公司不知道应该采用何种展示策略,来最大限度提高销售额度。
生态学:动物冬眠是不摄取能量,在此期间每一种行为都会消耗不同的能力,那么应该进行哪些行为,才能让它挺过冬天呢?
金融学:在随时间变化的回报量中,哪些股票期权能够带来最高的回报?

事实上,无论上面的哪一个例子,寻找它的最佳解决方案都是非常困难的,这可能会花上几十年的时间。当然,也有一个近似最优的方案。该方案结果较好(可能不是最好),扩展性强还特别容易修改。这里,我们姑且把它称之为贝叶斯老虎机方案。
贝叶斯老虎机方案
假设我们对老虎机的发钱概率完全不知,因此,我们可以很自然的采用 0 到 1 的均匀先验分布。

image.png

其实,这就是一个不断迭代,对每种老虎机建立自信的过程。至于计算方面,由于该算法涉及到 N 个分布采样。而最初的先验是Beta(α=1,β=1)(一个均匀分布),且观察到的样本结果 XX 是二项分布(即盈利或者不盈利),因此该变量的后验也应该是一个 Beta 分布,即为:Beta(α=1+X,β=1+1−X).
下一步,让我们来实现这个算法。为此,我们定义了两个类:Bandits 类,用于定义老虎机。BayesianStrategy 类,用于实现上面的学习策略。

rand = np.random.rand


class Bandits(object):
    """
     该类表示 N 个老虎机

    parameters:
        p_array: a (n 

    methods:
        pull( i ): 返回 1,0.
            1:代表此次发了钱 0:代表此次没发钱
    """

    def __init__(self, p_array):
        self.p = p_array
        self.optimal = np.argmax(p_array)

    def pull(self, i):
        # i is which arm to pull
        return np.random.rand() < self.p[i]

    def __len__(self):
        return len(self.p)


# 测试
Bandits([1])

接下来,让我们定义 BayesianStrategy 类:

class BayesianStrategy(object):
    """
    实现多臂老虎机的学习算法 
    parameters:
        bandits: 上面的老虎机类

    methods:
        sample_bandits(n): 对老虎机进行训练和取样

    attributes:
        N: 累计的样本数
        choices:大小:(N,),记录历史结果 
        bb_score:大小: (N,) ,记录历史分数
    """

    def __init__(self, bandits):

        self.bandits = bandits
        n_bandits = len(self.bandits)
        self.wins = np.zeros(n_bandits)
        self.trials = np.zeros(n_bandits)
        self.N = 0
        self.choices = []
        self.bb_score = []

    def sample_bandits(self, n=1):

        bb_score = np.zeros(n)
        choices = np.zeros(n)

        for k in range(n):
            # sample from the bandits's priors, and select the largest sample
            choice = np.argmax(np.random.beta(
                1 + self.wins, 1 + self.trials - self.wins))

            # sample the chosen bandit
            result = self.bandits.pull(choice)

            # update priors and score
            self.wins[choice] += result
            self.trials[choice] += 1
            bb_score[k] = result
            self.N += 1
            choices[k] = choice

        self.bb_score = np.r_[self.bb_score, bb_score]
        self.choices = np.r_[self.choices, choices]
        return

接下来,定义一个可视化函数,该函数用于可视化上列所写的算法策略:

figsize(11.0, 10)

beta = stats.beta
x = np.linspace(0.001, .999, 200)


def plot_priors(bayesian_strategy, prob, lw=3, alpha=0.2, plt_vlines=True):
    # plotting function
    wins = bayesian_strategy.wins
    trials = bayesian_strategy.trials
    for i in range(prob.shape[0]):
        y = beta(1+wins[i], 1 + trials[i] - wins[i])
        p = plt.plot(x, y.pdf(x), lw=lw)
        c = p[0].get_markeredgecolor()
        plt.fill_between(x, y.pdf(x), 0, color=c, alpha=alpha,
                         label="underlying probability: %.2f" % prob[i])
        if plt_vlines:
            plt.vlines(prob[i], 0, y.pdf(prob[i]),
                       colors=c, linestyles="--", lw=2)
        plt.autoscale(tight="True")
        plt.title("Posteriors After %d pull" % bayesian_strategy.N +
                  "s"*(bayesian_strategy.N > 1))
        plt.autoscale(tight=True)
    return

最后,让我们初始化三台老虎机和它的发钱概率,并传入老虎机类中(模拟现实的老虎机)。然后利用老虎机的解决方案,寻找这三台老虎机的发钱概率分布,如下:

hidden_prob = np.array([0.85, 0.60, 0.75])
bandits = Bandits(hidden_prob)
bayesian_strat = BayesianStrategy(bandits)

draw_samples = [1, 1, 3, 10, 10, 25, 50, 100, 200, 600]

for j, i in enumerate(draw_samples):
    plt.subplot(5, 2, j+1)
    bayesian_strat.sample_bandits(i)
    plot_priors(bayesian_strat, hidden_prob)
    # plt.legend()
    plt.autoscale(tight=True)
plt.tight_layout()

上图分别表示了 1、2、5、15、25、50、100、200、400、1000 局以后的,老虎机的后验分布。我们可以很清楚的看到,在1000 局以后,老虎机的后验分布逐渐接近于真实值。
算法的评价
我们需要一个指标来衡量算法来衡量我们的算法的优越性。因此,这里我们引入总遗憾的概念。即 T 轮最优策略(即理想情况下,每一局,都在那台最好的老虎机上)和上述策略之间的收益差距。
简单的说, 如果每次都能选中最好的老虎机的总利润和实际操作时的利润之差即为总遗憾。计算遗憾的代码如下:

# 返回前N 天的遗憾总和
# 第一个参数为每台老虎机的真实发钱率,第二个表示实验中,每次选择的是第几胎老虎机


def regret(probabilities, choices):
    w_opt = probabilities.max()
    # 返回前 1 的数据总和,前 2的数据总和....前 N 的数据总和,
    return (w_opt - probabilities[choices.astype(int)]).cumsum()


bayesian_strat.sample_bandits(10000)
regrets = regret(hidden_prob, bayesian_strat.choices)
regrets[3]  # 表示前三次实验的遗憾总和

我们可以将前 N 次试验的总遗憾绘制出来:

figsize(12.5, 5)
plt.plot(regrets, lw=3)

从上图中可以看出,总遗憾逐渐趋于稳定,表示这我们的算法确实在不断寻找最佳的老虎机,并且最终也找到了这台老虎机。
算法的扩展
由于贝叶斯老虎机算法是很简单的,因此,该算法极易进行拓展。比如:

image.png

我们还可以通过加入一个学习速率来促进算法的自我更新,如下:

self.wins[choice] = rate*self.wins[choice] + result
self.trials[choice] = rate*self.trials[choice] + 1

如果rate<1,则该算法将更快的忘记其先前有过获胜的经历。相反,若 rate>1 则表示你的算法会以一种风险较高的方式行事,即更容易的将赌注压在早期赢过的老虎机上。
至此,老虎机的整个实验已经完成了。细心的你或许已经发现,本次实验,我们并没有使用耗时的 MCMC 算法,这是因为我们已知封闭形式的后验。也就是说,我们知道, Beta 先验和二项式数据结合能够得到 Beta 后验分布。如下图所示:

image.png

值得注意的是,该方程两边都有 Beta ,但是不能消除。因为这不是真正的方程,这其实是一个模型。上面的模型,是一个非常有用的特性,我们将这种特性称之为共轭。
共轭先验
image.png

正如之前所说,共轭先验在计算上非常有用,可以使我们避免使用 MCMC 算法来做近似推断,而直接得到后验。

尽管如此,共轭先验也存在一些问题,如下:
共轭先验是不客观的。因此,它只有当需要主观先验时才能使用。
对于简单的一维问题,通常存在共轭先验。但是对于更大的问题,因为涉及到更加复杂的结构,所以很难找到他的共轭先验。关于共轭分布的公式推导可以参考 这篇文章

样本数对先验的印象

在实验 1 中,我们说过这样一句话,当我们拥有足够多的观测数据时,参数的先验就显得那么重要了。这其实是符合现实的,因为我们的先验也是基于以前的信息,而足够多的新信息完全可以代替以前的信息对模型的价值。并且足够多的数据对先验的修正是有帮助的。因此,即使我们的先验明显错误,但是数据的自我修正性质也可以为我们呈现出较为合理的后验。


image.png

换句话说,随着样本量的增加,所选择的先验的影响会变小。因此,只要非零概率点的区域是相同的,那么推断的收敛和先验无关。接下来,让我们更加形象的展示这个过程。
接下来,让我们定义两个先验,一个是扁平先验,一个是 朝着 0 偏移的 beta 先验。

# 扁平先验
beta1_params = np.array([1., 1.])
# 朝着0偏移的beta先验
beta2_params = np.array([2, 10])
beta = stats.beta

# 定义真实数据的产生,服从二项分布
p = 0.6
data = stats.bernoulli.rvs(p, size=500)
data

接下来,让我们不断的观察一下,随着样本的不断增加,两个模型的后验分布的变化情况:

figsize(12.5, 15)
# 定义很多表
x = np.linspace(0.00, 1, 125)
plt.figure()
# 观察具有0,4,8, 32,64, 128, 500个样本下的两个模型的后验分布
for i, N in enumerate([0, 4, 8, 32, 64, 128, 500]):
    s = data[:N].sum()
    plt.subplot(8, 1, i+1)
    params1 = beta1_params + np.array([s, N-s])
    params2 = beta2_params + np.array([s, N-s])
    y1, y2 = beta.pdf(x, *params1), beta.pdf(x, *params2)
    plt.plot(x, y1, label=r"flat prior", lw=3)
    plt.plot(x, y2, label="biased prior", lw=3)
    plt.fill_between(x, 0, y1, color="#348ABD", alpha=0.15)
    plt.fill_between(x, 0, y2, color="#A60628", alpha=0.15)
    plt.legend(title="N=%d" % N)
    plt.vlines(p, 0.0, 7.5, linestyles="--", linewidth=1)

其中蓝色代表扁平先验模型的后验分布,黄色曲线代表有偏先验的后验分布。
从上图中,我们可以清楚的看到后验分布在随着样本量的逐渐增多的情况下,忘记他的先验,数据越多,先验忘得越多。因此,在数据量足够的情况下,贝叶斯推断和频率论推断是收敛在一起的。

你可能感兴趣的:(111贝叶斯方法数据分析实战--先验的选取策略)