人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

 

 

貝葉斯統計是個神鳥呢?

數學家貝葉斯,在200多年前寫的《機會學說中一個問題的解》這本書中提過個觀點,他說,支持某項屬性的事件發生得愈多,則該屬性成立的可能性就愈大。簡言之,如果你看到一個人總是做一些好事,那個人多半會是個好人。

很好理解對吧?

下面就從一個簡單的例子入手,來進一步理論結合Python學習如何進行貝葉斯統計。

 

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

 

拋硬幣問題

拋硬幣是統計學中的一個經典問題,其描述如下:我們隨機拋一枚硬幣,重複一定次數,記錄正面朝上和反面朝上的次數,根據這些數據,我們需要回答諸如這枚硬幣是否公平,以及更進一步這枚硬幣有多不公平等問題。拋硬幣是一個學習貝葉斯統計非常好的例子,一方面是因爲幾乎人人都熟悉拋硬幣這一過程,另一方面是因爲這個模型很簡單,我們可以很容易計算並解決這個問題。此外,許多真實問題都包含兩個互斥的結果,例如0或者1、正或者負、奇數或者偶數、垃圾郵件或者正常郵件、安全或者不安全、健康或者不健康等。因此,即便我們討論的是硬幣,該模型也同樣適用於前面這些問題。

 

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

 

爲了估計硬幣的偏差,或者更廣泛地說,想要用貝葉斯學派理論解決問題,我們需要數據和一個概率模型。對於拋硬幣這個問題,假設我們已試驗了一定次數並且記錄了正面朝上的次數,也就是說數據部分已經準備好了,剩下的就是模型部分了。考慮到這是第一個模型,我們會列出所有必要的數學公式,並且一步一步推導。下一章中,我們會重新回顧這個問題,並借用PyMC3從數值上解決它(也就是說那部分不需要手動推導,而是利用PyMC3和計算機來完成)。

通用模型

首先,我們要抽象出偏差的概念。我們稱,如果一枚硬幣總是正面朝上,那麼它的偏差就是1,反之,如果總是反面朝上,那麼它的偏差就是0,如果正面朝上和反面朝上的次數各佔一半,那麼它的偏差就是0.5。這裏用參數 θ 來表示偏差,用 y 表示 N 次拋硬幣實驗中正面朝上的次數。根據貝葉斯定理,我們有如下公式:

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

這裏需要指定我們將要使用的先驗

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

和似然

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

分別是什麼。讓我們首先從似然開始。

選擇似然

假設多次拋硬幣的結果相互之間沒有影響,也就是說每次拋硬幣都是相互獨立的,同時還假設結果只有兩種可能:正面朝上或者反面朝上。但願你能認同我們對這個問題做出的合理假設。基於這些假設,一個不錯的似然候選是二項分佈:

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

這是一個離散分佈,表示 N 次拋硬幣實驗中 y 次正面朝上的概率(或者更通俗地描述是, N 次實驗中, y 次成功的概率)。下面的代碼生成了9個二項分佈,每個子圖中的標籤顯示了對應的參數:

n_params = [1, 2, 4]
p_params = [0.25, 0.5, 0.75]
x = np.arange(0, max(n_params)+1)
f, ax = plt.subplots(len(n_params), len(p_params), sharex=True, sharey=True)
for i in range(3):
 for j in range(3):
 n = n_params[i]
 p = p_params[j]
 y = stats.binom(n=n, p=p).pmf(x)
 ax[i,j].vlines(x, 0, y, colors='b', lw=5)
 ax[i,j].set_ylim(0, 1)
 ax[i,j].plot(0, 0, label="n = {:3.2f}\np = 
 {:3.2f}".format(n, p), alpha=0)
 ax[i,j].legend(fontsize=12)
ax[2,1].set_xlabel('$\\theta$', fontsize=14)
ax[1,0].set_ylabel('$p(y|\\theta)$', fontsize=14)
ax[0,0].set_xticks(x)

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

二項分佈是似然的一個合理選擇,直觀上講, θ 可以看作拋一次硬幣時正面朝上的可能性,並且該過程發生了 y 次。類似地,我們可以把“1− θ ”看作拋一次硬幣時反面朝上的概率,並且該過程發生了“ N − y ”次。

假如我們知道了 θ ,那麼就可以從二項分佈得出硬幣正面朝上的分佈。如果我們不知道 θ ,也別灰心,在貝葉斯統計中,當我們不知道某個參數的時候,就對其賦予一個先驗。接下來繼續選擇先驗。

選擇先驗

這裏我們選用貝葉斯統計中最常見的 beta分佈 ,作爲先驗,其數學形式如下:

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

仔細觀察上面的式子可以看出,除了 Γ 部分之外,beta分佈和二項分佈看起來很像。 Γ 是希臘字母中大寫的伽馬,用來表示伽馬函數。現在我們只需要知道,用分數表示的第一項是一個正則化常量,用來保證該分佈的積分爲1,此外,

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

兩個參數用來控制具體的分佈形態。beta分佈是我們到目前爲止見到的第3個分佈,利用下面的代碼,我們可以深入瞭解其形態:

params = [0.5, 1, 2, 3]
x = np.linspace(0, 1, 100)
f, ax = plt.subplots(len(params), len(params), sharex=True, sharey=True)
for i in range(4):
 for j in range(4):
 a = params[i]
 b = params[j]
 y = stats.beta(a, b).pdf(x)
 ax[i,j].plot(x, y)
 ax[i,j].plot(0, 0, label="$\\alpha$ = {:3.2f}\n$\\beta$ = {:3.2f}".format(a, b), alpha=0)
 ax[i,j].legend(fontsize=12)
ax[3,0].set_xlabel('$\\theta$', fontsize=14)
ax[0,0].set_ylabel('$p(\\theta)$', fontsize=14)
plt.savefig('B04958_01_04.png', dpi=300, figsize=(5.5, 5.5))

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

爲什麼要在模型中使用beta分佈呢?在拋硬幣以及一些其他問題中使用beta分佈的原因之一是:beta分佈的範圍限制在0到1之間,這跟我們的參數

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

一樣;另一個原因是其通用性,從前面的圖可以看出,該分佈可以有多種形狀,包括均勻分佈、類高斯分佈、U型分佈等。第3個原因是:beta分佈是二項分佈(前面我們使用了該分佈描述似然)的共軛先驗。似然的共軛先驗是指,將該先驗分佈與似然組合在一起之後,得到的後驗分佈與先驗分佈的表達式形式仍然是一樣的。簡單說,就是每次用beta分佈作爲先驗、二項分佈作爲似然時,我們會得到一個beta分佈的後驗。除beta分佈之外還有許多其他共軛先驗,例如高斯分佈,其共軛先驗就是自己。關於共軛先驗更詳細的內容可以查看https://en.wikipedia.org/wiki/Conjugate_prior。許多年來,貝葉斯分析都限制在共軛先驗範圍內,這主要是因爲共軛能讓後驗在數學上變得更容易處理,要知道貝葉斯統計中一個常見問題的後驗都很難從分析的角度去解決。在建立合適的計算方法來解決任意後驗之前,這只是個折中的辦法。從下一章開始,我們將學習如何使用現代的計算方法來解決貝葉斯問題而不必考慮是否使用共軛先驗。

計算後驗

首先回憶一下貝葉斯定理:後驗正比於似然乘以先驗。

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

對於我們的問題而言,需要將二項分佈乘以beta分佈:

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

現在,對上式進行簡化。針對我們的實際問題,可以先把與 θ 不相關的項去掉而不影響結果,於是得到下式:

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

重新整理之後得到:

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

可以看出,上式和beta分佈的形式很像(除了歸一化部分),其對應的參數分別爲

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

。也就是說,在拋硬幣這個問題中,後驗分佈是如下beta分佈:

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

計算後驗並畫圖 現在已經有了後驗的表達式,我們可以用Python對其計算並畫出結果。下面的代碼中,其實只有一行是用來計算後驗結果的,其餘的代碼都是用來畫圖的:

theta_real = 0.35
trials = [0, 1, 2, 3, 4, 8, 16, 32, 50, 150]
data = [0, 1, 1, 1, 1, 4, 6, 9, 13, 48]

beta_params = [(1, 1), (0.5, 0.5), (20, 20)]
dist = stats.beta
x = np.linspace(0, 1, 100)

for idx, N in enumerate(trials):
 if idx == 0:
 plt.subplot(4,3, 2)
 else:
 plt.subplot(4,3, idx+3)
 y = data[idx]
 for (a_prior, b_prior), c in zip(beta_params, ('b', 'r', 'g')):
 p_theta_given_y = dist.pdf(x, a_prior + y, b_prior + N - y)
 plt.plot(x, p_theta_given_y, c)
 plt.fill_between(x, 0, p_theta_given_y, color=c, alpha=0.6)

 plt.axvline(theta_real, ymax=0.3, color='k')
 plt.plot(0, 0, label="{:d} experiments\n{:d} heads".format(N, y), alpha=0)
 plt.xlim(0,1)
 plt.ylim(0,12)
 plt.xlabel(r"$\theta$") 
 plt.legend()
 plt.gca().axes.get_yaxis().set_visible(False)
plt.tight_layout()
plt.savefig('B04958_01_05.png', dpi=300, figsize=(5.5, 5.5))

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

在上圖的第一行中,實驗的次數爲0,因此第一個圖中的曲線描繪的是先驗分佈,其中有3條曲線,每條曲線分別表示一種先驗。

  • 藍色的線是一個均勻分佈先驗,其含義是:偏差的所有可能取值都是等概率的。
  • 紅色的線與均勻分佈有點類似,對拋硬幣這個例子而言可以理解爲:偏差等於0或者1的概率要比其他值更大一些。
  • 最後一條綠色的線集中在中間值0.5附近,該分佈反映了通常硬幣正面朝上和反面朝上的概率大致是差不多的。我們也可以稱,該先驗與大多數硬幣都是公平的這一信念是兼容的。“兼容”這個詞在貝葉斯相關的討論中會經常用到,特別是在提及受到數據啓發的模型時。

剩餘的子圖描繪了後續實驗的後驗分佈

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

,回想一下,後驗可以看做是在給定數據之後更新了的先驗。實驗(拋硬幣)的次數和正面朝上的次數分別標註在每個子圖中。此外每個子圖中在橫軸0.35附近還有一個黑色的豎線,表示的是真實的 θ ,顯然,在真實情況下,我們並不知道該值,在這裏標識出來只是爲了方便理解。從這幅圖中可以學到很多貝葉斯分析方面的知識。

  • 貝葉斯分析的結果是後驗分佈而不是某個值,該分佈描述了根據給定數據和模型得到的不同數值的可能性。
  • 後驗最可能的值是根據後驗分佈的形態決定的(也就是後驗分佈的峯值)。
  • 後驗分佈的離散程度與我們對參數的不確定性相關;分佈越離散,不確定性越大。
  • 儘管1/2=4/8=0.5,但從圖中可以看出,前者的不確定性要比後者大。這是因爲我們有了更多的數據來支撐我們的推斷,該直覺也同時反映在了後驗分佈上。
  • 在給定足夠多的數據時,兩個或多個不同先驗的貝葉斯模型會趨近於收斂到相同的結果。在極限情況下,如果有無限多的數據,不論我們使用的是怎樣的先驗,最終都會得到相同的後驗。注意這裏說的無限多是指某種程度而非某個具體的數量,也就是說,從實際的角度來講,某些情況下無限多的數據可以通過比較少量的數據近似。
  • 不同後驗收斂到相同分佈的速度取決於數據和模型。從前面的圖中可以看出,藍色和紅色的後驗在經過8次實驗之後就很難看出區別了,而紅色的曲線則一直到150次實驗之後才與另外兩個後驗看起來比較接近。
  • 有一點從前面的圖中不太容易看出來,如果我們一步一步地更新後驗,最後得到的結果跟一次性計算得到的結果是一樣的。換句話說,我們可以對後驗分150次計算,每次增加一個新的觀測數據並將得到的後驗作爲下一次計算的先驗,也可以在得到150次拋硬幣的結果之後一次性計算出後驗,而這兩種計算方式得到的結果是完全一樣的。這個特點非常有意義,在許多數據分析的問題中,每當我們得到新的數據時可以更新估計值。

先驗的影響以及如何選擇合適的先驗

從前面的例子可以看出,先驗對分析的結果會有影響。一些貝葉斯分析的新手(以及一些詆譭該方法的人)會對如何選擇先驗感到茫然,因爲他們不希望先驗起到決定性作用,而是更希望數據本身替自己說話!有這樣的想法很正常,不過我們得牢記,數據並不會真的“說話”,只有在模型中纔會有意義,包括數學上的和腦海中的模型。面對同一主題下的同一份數據,不同人會有不同的看法,這類例子在科學史上有許多,查看以下鏈接可以瞭解《紐約時報》最近一次實驗的例子: http://www.nytimes.com/interactive/2016/09/20/upshot/the-error-the-polling-world-rarely-talks-about.html?_r=0 。

有些人青睞於使用沒有信息量的先驗(也稱作均勻的、含糊的或者發散的先驗),這類先驗對分析過程的影響最小。本書將遵循Gelman、McElreath和Kruschke 3人的建議

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

,更傾向於使用帶有較弱信息量的先驗。在許多問題中,我們對參數可以取的值一般都會有些瞭解,比如,參數只能是正數,或者知道參數近似的取值範圍,又或者是希望該值接近0或大於/小於某值。這種情況下,我們可以給模型加入一些微弱的先驗信息而不必擔心該先驗會掩蓋數據本身的信息。由於這類先驗會讓後驗近似位於某一合理的邊界內,因此也被稱作正則化先驗。

當然,使用帶有較多信息量的強先驗也是可行的。視具體的問題不同,有可能很容易或者很難找到這類先驗,例如在我工作的領域(結構生物信息學),人們會儘可能地利用先驗信息,通過貝葉斯或者非貝葉斯的方式來了解和預測蛋白質的結構。這樣做是合理的,原因是我們在數十年間已經從上千次精心設計的實驗中收集了數據,因而有大量可信的先驗信息可供使用。如果你有可信的先驗信息,完全沒有理由不去使用。試想一下,如果一個汽車工程師每次設計新車的時候,他都要重新發明內燃機、輪子乃至整個汽車,這顯然不是正確的方式。

現在我們知道了先驗有許多種,不過這並不能緩解我們選擇先驗時的焦慮。或許,最好是沒有先驗,這樣事情就簡單了。不過,不論是否基於貝葉斯,模型都在某種程度上擁有先驗,即使這裏的先驗並沒有明確表示出來。事實上,許多頻率統計學方面的結果可以看做是貝葉斯模型在一定條件下的特例,比如均勻先驗。讓我們再仔細看看前面那幅圖,可以看到藍色後驗分佈的峯值與頻率學分析中 θ 的期望值是一致的:

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

注意,這裏

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

是點估計而不是後驗分佈(或者其他類型的分佈)。由此看出,你沒辦法完全避免先驗,不過如果你在分析中引入先驗,得到的會是概率分佈分佈而不只是最可能的一個值。明確引入先驗的另一個好處是,我們會得到更透明的模型,這意味着更容易評判、(廣義上的)調試以及優化。構建模型是一個循序漸進的過程,有時候可能只需要幾分鐘,有時候則可能需要數年;有時候整個過程可能只有你自己,有時候則可能包含你不認識的人。而且,模型復現很重要,而模型中透明的假設能有助於復現。

在特定分析任務中,如果我們對某個先驗或者似然不確定,可以自由使用多個先驗或者似然進行嘗試。模型構建過程中的一個環節就是質疑假設,而先驗就是質疑的對象之一。不同的假設會得到不同的模型,根據數據和與問題相關的領域知識,我們可以對這些模型進行比較,本書第6章模型比較部分會深入討論該內容。

由於先驗是貝葉斯統計中的一個核心內容,在接下來遇到新的問題時我們還會反覆討論它,因此,如果你對前面的討論內容感到有些疑惑,別太擔心,要知道人們在這個問題上已經困惑了數十年並且相關的討論一直在繼續。

報告貝葉斯分析結果

現在我們已經有了後驗,相關的分析也就結束了。下面我們可能還需要對分析結果進行總結,將分析結果與別人分享,或者記錄下來以備日後使用。

模型註釋和可視化

根據受衆不同,你可能在交流分析結果的同時還需要交流模型。以下是一種簡單表示概率模型的常見方式:

  •  
  •  

這是我們拋硬幣例子中用到的模型。符號~表示左邊隨機變量的分佈服從右邊的分佈形式,也就是說,這裏 θ 服從於參數爲 α 和 β 的Beta分佈,而 y 服從於參數爲 n = 1和 p = θ 的二項分佈。該模型還可以用類似Kruschke書中的圖表示成如下形式:

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

在第一層,根據先驗生成了 θ ,然後通過似然生成最下面的數據。圖中的箭頭表示變量之間的依賴關係,符號~表示變量的隨機性。

本書中用到的類似Kruschke中的圖都是由Rasmus Bååth( http://www.sumsar.net/blog/2013/10/diy-kruschke-style-diagrams/ )提供的模板生成的。

總結後驗

貝葉斯分析的結果是後驗分佈,該分佈包含了有關參數在給定數據和模型下的所有信息。如果可能的話,我們只需要將後驗分佈展示給觀衆即可。通常,一個不錯的做法是:同時給出後驗分佈的均值(或者衆數、中位數),這樣能讓我們瞭解該分佈的中心,此外還可以給出一些描述該分佈的衡量指標,如標準差,這樣人們能對我們估計的離散程度和不確定性有一個大致的瞭解。拿標準差衡量類似正態分佈的後驗分佈很合適,不過對於一些其他類型的分佈(如偏態分佈)卻可能得出誤導性結論,因此,我們還可以採用下面的方式衡量。

最大後驗密度

一個經常用來描述後驗分佈分散程度的概念是 最大後驗密度 ( Highest Posterior Density,HPD)區間。一個HPD區間是指包含一定比例概率密度的最小區間,最常見的比例是95%HPD或98%HPD,通常還伴隨着一個50%HPD。如果我們說某個分析的HPD區間是[2, 5],其含義是指:根據我們的模型和數據,參數位於2~5的概率是0.95。這是一個非常直觀的解釋,以至於人們經常會將頻率學中的置信區間與貝葉斯方法中的可信區間弄混淆。如果你對頻率學的範式比較熟悉,請注意這兩種區間的區別。貝葉斯學派的分析告訴我們的是參數取值的概率,這在頻率學的框架中是不可能的,因爲頻率學中的參數是固定值,頻率學中的置信區間只能包含或不包含參數的真實值。在繼續深入之前,有一點需要注意:選擇95%還是50%或者其他什麼值作爲HPD區間的概率密度比例並沒有什麼特殊的地方,這些不過是經常使用的值罷了。比如,我們完全可以選用比例爲91.37%的HPD區間。如果你選的是95%,這完全沒問題,只是要記住這只是個默認值,究竟選擇多大比例仍然需要具體問題具體分析。

對單峯分佈計算95%HPD很簡單,只需要計算出2.5%和97.5%處的值即可:

def naive_hpd(post):
 sns.kdeplot(post)
 HPD = np.percentile(post, [2.5, 97.5])
 plt.plot(HPD, [0, 0], label='HPD {:.2f} {:.2f}'.format(*HPD), 
 linewidth=8, color='k')
 plt.legend(fontsize=16);
 plt.xlabel(r'$\theta$', fontsize=14)
 plt.gca().axes.get_yaxis().set_ticks([])


np.random.seed(1)
post = stats.beta.rvs(5, 11, size=1000)
naive_hpd(post)
plt.xlim(0, 1)

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

對於多峯分佈而言,計算HPD要稍微複雜些。如果把對HPD的原始定義應用到混合高斯分佈上,我們可以得到:

np.random.seed(1)
gauss_a = stats.norm.rvs(loc=4, scale=0.9, size=3000)
gauss_b = stats.norm.rvs(loc=-2, scale=1, size=2000)
mix_norm = np.concatenate((gauss_a, gauss_b))

naive_hpd(mix_norm)
plt.savefig('B04958_01_08.png', dpi=300, figsize=(5.5, 5.5))

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

從上圖可以看出,通過原始HPD定義計算出的可信區間包含了一部分概率較低的區間,位於[0,2]。爲了正確計算出HPD,這裏我們使用了plot_post函數,你可以從本書附帶的代碼中下載對應的源碼:

from plot_post import plot_post
plot_post(mix_norm, roundto=2, alpha=0.05)
plt.legend(loc=0, fontsize=16)
plt.xlabel(r"$\theta$", fontsize=14)

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

從上圖可以看出,95%HPD包含兩個區間,同時plot_post函數也返回了兩個衆數。

 

人人都可以當賭神的祕密:用Python學習神奇的貝葉斯統計

 

今天到這裏 ,喜歡的給小編一個關注 謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章