從平坦的均勻數到任何形狀
上一篇指南留給我們一個偽隨機數產生器:給它一個種子,它就吐出一串確定性的、平均散布在區間 (0,1) 上的數字。大多數產生器能給你的就只有這個:平坦、毫無特徵的均勻抽樣,每一個落在單位區間任何位置的機會都相等。但大自然並不平坦。身高聚集在平均值附近、在極端處變稀疏;你等下一班公車的時間服從指數衰減;一個物理模擬也許需要一個有兩個駝峰、凹凸不平的自訂密度。所以這篇指南的核心問題講起來很簡單、答起來卻出奇地深:只給你一個均勻數字的水龍頭,我們要怎麼產生出服從某個指定分布的抽樣?
為什麼這件事這麼重要?因為下游的每一個蒙地卡羅想法都建立在它之上。要做蒙地卡羅積分,你撒下隨機點、再把一個函數在這些點上取平均——但這些點必須服從正確的分布,否則平均出來的是錯的東西。要執行重要性抽樣,你刻意從一個精心挑選的密度抽樣,而不是從自然的那個。要用 Metropolis-Hastings 演算法去探索一個困難的高維目標,你仍然需要抽樣出簡單的提議步伐。抽樣是地基;這個階梯其餘的部分都蓋在它上面。
反轉換法:把累積分布函數倒著走
最乾淨的招式是 反轉換抽樣,它背後的圖像值得牢牢記在腦中。每個分布都有一個累積分布函數,寫成 F(x):它是一個抽樣落在 x 或以下的機率,所以它從最左邊的 0 一路爬升到最右邊的 1,永不下降。現在反過來看。不要問「給定 x,機率 F(x) 是多少?」,反著問:「給定一個機率高度 u,哪個 x 坐落在那個高度上?」那個答案就是反函數 F^{-1}(u)。整個方法只有一行:抽一個 (0,1) 上的均勻數 u,然後輸出 x = F^{-1}(u)。
把累積分布函數倒著走為什麼有效?把 F 的曲線想成一座你要爬的樓梯。在分布稠密的地方,F 陡升,所以 u 軸上一大塊會映射到 x 軸上一小塊——許多均勻抽樣被漏斗般灌進那個又窄又熱門的區域。在分布稀疏的地方,F 幾乎平坦,所以 u 上一小塊會攤開到 x 上一大片,落在那裡的抽樣就很少。平坦的均勻水流被擠壓與拉伸,直到它的密度恰好吻合 F。舉一個小小的算例,速率為 1 的指數分布有 F(x) = 1 - e^{-x};反解得 x = -ln(1 - u),於是每個均勻數 u 立刻變成一個指數分布的抽樣。
u ~ Uniform(0,1) # one flat draw
x = Finv(u) # x = F^{-1}(u), the inverse CDF
# exponential(rate=1): Finv(u) = -ln(1 - u)
# u = 0.5 -> x = 0.693
# u = 0.9 -> x = 2.303 (rarer, deeper into the tail)拒絕抽樣:擲飛鏢,留下好的那些
當你無法反轉 F——或者你只知道密度的形狀、甚至只知道它相差一個未知的縮放常數時——拒絕抽樣就來救場了。這個想法樂得很有畫面感。假設你的目標密度是某條坐落在一個區間上的曲線 p(x)。找一個你「能」抽樣的簡單形狀——常常就是一個均勻的方框,或者一個放大的、容易抽樣的密度,稱為提議分布——讓它像帳篷一樣完全罩住 p(x)。現在在帳篷頂底下均勻地擲飛鏢。有些飛鏢落在曲線 p(x) 之下;有些落在曲線與帳篷頂之間的空隙裡。留下落在 p(x) 之下的飛鏢;把其餘的丟掉(拒絕)。留下來那些飛鏢的 x 座標,就是從 p 抽出的精確抽樣。
- 選一個你能抽樣的提議分布 q,以及一個常數 M,使得放大後的提議 M*q(x) 處處完全蓋在目標 p(x) 之上——帳篷必須罩住曲線。
- 從提議分布 q 抽一個候選 x,並獨立地抽一個 (0,1) 上的均勻數 u——這個 u 在該 x 處挑出帳篷底下的一個隨機高度。
- 若 u <= p(x) / (M*q(x)) 則接受 x——飛鏢落在了真正的曲線底下;否則拒絕它,回到上一步重試。
- 被接受的那串 x 值的分布恰好就是 p,不管 p 多醜,只要帳篷真的罩住了它。
拒絕抽樣絕妙地通用——它對根本沒有 F 公式的密度也管用,而且容許你只知道 p 相差一個常數,因為那個未知的縮放會一起被吸收進 M 裡。但誠實要求我們指出代價:它的效率就是帳篷面積中落在曲線底下的那一比例。如果帳篷貼得很緊,你接受大多數飛鏢;如果它鬆垮垮,你幾乎拒絕一切、白白浪費巨大的力氣。而這裡有個殘酷的轉折,正是它推動了本階梯其餘的部分:在高維中,一個罩住目標的帳篷幾乎總是變得災難性地鬆垮,於是接受率崩潰趨近於零。這個詛咒正是為什麼困難的高維問題會放棄樸素的拒絕抽樣,轉而採用第 5 篇的馬可夫鏈方法。
誠實面對:「正確」的抽樣究竟是什麼意思
我們很容易想像抽樣器輸出的是「那個」正確的數字。它並沒有。這兩種方法產生的都是一個隨機序列,它的長期直方圖會收斂到目標密度——任何單一批次的抽樣都只是一次帶雜訊的實現,兩個不同的種子會給出兩個不同的(同樣有效的)批次。「正確」指的是抽樣的分布是對的,而不是某個特定抽樣有什麼特別。這正是貫穿整個計算數學的同一種謙遜:就像 A x = b 的數值解是一個有著向後穩定故事撐腰的近似答案一樣,一個樣本也是一個分布的近似替身,它的品質只有在你平均許多抽樣時才會顯現出來。
再提兩個誠實的告誡。第一,每個抽樣都經過浮點運算的過濾:-ln(1 - u) 不是精確的實數對數,而且當 u 極度接近 1 時,減法 1 - u 會喪失精度(這是災難性抵消的一個小小回音),所以深處的尾部抽樣得不如主體那麼忠實。第二,整條管線繼承了它源頭的確定性。你那些「隨機」抽樣是種子的一個固定函數,這對於可重現性與除錯是一份禮物,但若一個有瑕疵的產生器藏著隱蔽的相關性,那就是個陷阱——那時你那些形狀漂亮的樣本會以某種微妙的方式出錯,而任何單一座標的直方圖都看不出來。
如何選方法,以及它通往何處
那麼你該伸手拿哪個招式?如果累積分布函數及其反函數有乾淨的公式,就用反轉換法——它精確、每個抽樣都是確定性的、而且毫不浪費。如果你只有密度的形狀(也許未經正規化),又能搭出一個貼緊的覆蓋帳篷,就用拒絕抽樣。至於那匹主力的常態分布,實務上兩者的天真版本都不用:一個專門的轉換能把成對的均勻數直接變成成對的高斯數,這正是為什麼一個好的程式庫會有一個專屬的高斯常式,而不是手動去反轉 F。教訓是:「抽樣」不是單一演算法,而是一個工具箱,而對的工具取決於你能對目標算出些什麼。
注意把這個階梯串起來的那條線。這裡的兩種方法都能產生真正獨立的抽樣,這是最乾淨不過的情況。但兩者在高維中都會瓦解:反轉換法需要一個可用的反累積分布函數,而對複雜的聯合分布你很少有;拒絕抽樣的接受率則隨著覆蓋帳篷變得鬆垮而暴跌。當目標是一個糾纏的高維密度、兩種方法都拿它沒辦法時,你就放棄獨立性,轉而建一條朝目標漫遊過去的馬可夫鏈——也就是第 5 篇的 Metropolis-Hastings 想法。在那之前,第 3、4 篇會展示你現在會做的這些獨立抽樣能拿來「做」什麼:以著名的 O(1/sqrt(N)) 速率估計積分,並用重要性抽樣及其同伴來縮小那個誤差。