戰(zhàn):從貝葉斯推斷到不確定性量化)
不確定性這個(gè)東西做數(shù)據(jù)建模的人天天掛在嘴邊但真正把它當(dāng)回事、拿進(jìn)模型里去算的其實(shí)少得可憐。大多數(shù)業(yè)務(wù)分析還是停留在“點(diǎn)估計(jì)”的層面——均值多少、預(yù)測(cè)值多少、系數(shù)多大頂多再給個(gè)置信區(qū)間應(yīng)付一下。問(wèn)題是現(xiàn)實(shí)世界從來(lái)不給你一個(gè)確定答案。渠道投放效果會(huì)有波動(dòng)用戶對(duì)價(jià)格的反應(yīng)有隨機(jī)性供應(yīng)鏈缺貨風(fēng)險(xiǎn)更是層層疊加。你要是只用單一數(shù)值去描述這些現(xiàn)象等于把所有風(fēng)險(xiǎn)都?jí)涸谝粋€(gè)毫無(wú)彈性的點(diǎn)上一旦真實(shí)值偏離整個(gè)決策鏈跟著遭殃。概率編程解決的就是這件事。它讓你把不確定性當(dāng)成模型的一等公民不是事后貼上去的誤差條而是從數(shù)據(jù)生成機(jī)制里就帶出來(lái)的東西。用概率編程做不確定性推理本質(zhì)上是在回答一個(gè)更誠(chéng)實(shí)的問(wèn)題基于當(dāng)前掌握的數(shù)據(jù)和先驗(yàn)知識(shí)某個(gè)未知量到底可能落在哪些值上各自的可能性有多大。這個(gè)思路在營(yíng)銷歸因、推薦系統(tǒng)、風(fēng)控建模、實(shí)驗(yàn)分析這些場(chǎng)景里都適用而且一旦跑通你會(huì)發(fā)現(xiàn)它能帶來(lái)的信息量遠(yuǎn)不止一個(gè)預(yù)測(cè)數(shù)字那么簡(jiǎn)單。這篇文章寫給正在用傳統(tǒng)統(tǒng)計(jì)或機(jī)器學(xué)習(xí)方法做建模、想嘗試概率編程但不知道從哪下手的人。我會(huì)從概念拆解講到工具選型再用一個(gè)完整的實(shí)操案例帶你走一遍從數(shù)據(jù)到后驗(yàn)分布的建模流程最后把我踩過(guò)的坑和排查經(jīng)驗(yàn)一并整理出來(lái)。適合作為你入坑概率編程的第一篇參考。1. 為什么常規(guī)建模方法在不確定性面前不夠用1.1 你平時(shí)用的模型其實(shí)是在“假裝確定”先做一個(gè)思想實(shí)驗(yàn)。你在做某產(chǎn)品的付費(fèi)轉(zhuǎn)化率預(yù)估用歷史數(shù)據(jù)訓(xùn)練出一個(gè)模型算出轉(zhuǎn)化率是 2.3%。這個(gè)數(shù)字看起來(lái)精確但它背后省略了太多信息。當(dāng)你說(shuō)轉(zhuǎn)化率是 2.3% 的時(shí)候你其實(shí)是在說(shuō)“我手頭所有數(shù)據(jù)指向的最佳猜測(cè)是 2.3%。”可問(wèn)題是這個(gè)猜測(cè)的置信度有多高如果樣本量只有 500 個(gè)用戶這個(gè) 2.3% 的可信區(qū)間可能橫跨 1.5% 到 3.5%如果樣本量是 50 萬(wàn)區(qū)間可能收窄到 2.25% 到 2.35%。同樣的點(diǎn)估計(jì)背后代表的不確定性完全不同。傳統(tǒng)頻率派方法不是不能描述這種差異它用置信區(qū)間、標(biāo)準(zhǔn)誤來(lái)刻畫。但這里有幾個(gè)脫節(jié)的地方。第一置信區(qū)間的解釋方式極其繞它的嚴(yán)格含義是“重復(fù)抽樣下區(qū)間包含真實(shí)值的頻率”而不是“真實(shí)值落在這個(gè)區(qū)間的概率是 95%”——后者才是業(yè)務(wù)決策者真正想問(wèn)的問(wèn)題。第二當(dāng)你引入先驗(yàn)信息的時(shí)候頻率派沒(méi)有一套自然的框架來(lái)做這件事。第三復(fù)雜的層級(jí)結(jié)構(gòu)、缺失數(shù)據(jù)、測(cè)量誤差這些實(shí)際建模中常見的問(wèn)題用傳統(tǒng)方法處理起來(lái)要么得做大量近似假設(shè)要么計(jì)算復(fù)雜到難以下手。概率編程換了一個(gè)問(wèn)法。它直接問(wèn)你你對(duì)某個(gè)未知量先驗(yàn)上有什么判斷觀察到的數(shù)據(jù)長(zhǎng)什么樣然后通過(guò)貝葉斯公式把這兩部分融合起來(lái)輸出一個(gè)完整的后驗(yàn)分布。這個(gè)分布就是你要的答案全貌——不光告訴你最大概率的值在哪還告訴你其他值有多大可能。1.2 偶然不確定性和認(rèn)知不確定性兩個(gè)必須分清的概念進(jìn)入概率編程之前先要把兩種性質(zhì)完全不同的不確定性區(qū)分清楚因?yàn)樗鼈儗?duì)模型設(shè)計(jì)的影響是決定性的。偶然不確定性Aleatoric Uncertainty這類不確定性來(lái)自系統(tǒng)本身的隨機(jī)性是數(shù)據(jù)生成機(jī)制自帶的。比如拋硬幣的結(jié)果、用戶訪問(wèn)時(shí)長(zhǎng)的波動(dòng)、傳感器讀數(shù)的噪聲。就算你收集一億個(gè)樣本這類不確定性不會(huì)消失它只是被刻畫得更精確而已。在概率編程里它通常體現(xiàn)在似然函數(shù)的噪聲項(xiàng)上。比如你假設(shè)觀測(cè)值服從正態(tài)分布那個(gè)方差參數(shù)就是在描述偶然不確定性的大小。認(rèn)知不確定性Epistemic Uncertainty這類不確定性來(lái)自你認(rèn)知的局限是“不知道”造成的。比如你只有 100 個(gè)樣本對(duì)模型參數(shù)的真實(shí)值心里沒(méi)底再比如你完全不確定某個(gè)渠道的投放效果究竟是正還是負(fù)。認(rèn)知不確定性的特點(diǎn)是可以隨著數(shù)據(jù)積累而減小——樣本越多你對(duì)參數(shù)的認(rèn)識(shí)越清晰后驗(yàn)分布越集中。這兩種不確定性在業(yè)務(wù)上有著完全不同的含義。拿庫(kù)存預(yù)測(cè)來(lái)說(shuō)偶然不確定性對(duì)應(yīng)的是需求本身的隨機(jī)波動(dòng)你再怎么優(yōu)化模型也改變不了消費(fèi)者行為的隨機(jī)性能做的是在安全庫(kù)存里給它留出緩沖認(rèn)知不確定性對(duì)應(yīng)的是你對(duì)“需求均值到底是多少”這個(gè)參數(shù)的認(rèn)識(shí)不清這個(gè)可以通過(guò)增加數(shù)據(jù)量、做更細(xì)致的分層來(lái)縮小。如果你在建模時(shí)把這兩種不確定性混為一談模型給出的風(fēng)險(xiǎn)評(píng)估就是失真的。概率編程天然適合區(qū)分這兩者。通過(guò)為模型參數(shù)設(shè)置先驗(yàn)分布并做后驗(yàn)推斷你可以直觀地看到哪些不確定性在數(shù)據(jù)進(jìn)來(lái)后大幅縮小了——那是認(rèn)知不確定性在消退哪些沒(méi)有縮小——那些就是系統(tǒng)本身的偶然不確定性需要你接受并適應(yīng)它。這個(gè)視角上的轉(zhuǎn)變對(duì)實(shí)際決策的影響非常大。2. 概率編程里的核心概念從先驗(yàn)到后驗(yàn)的完整鏈路2.1 貝葉斯公式概率編程的地基概率編程的本質(zhì)是貝葉斯推斷在計(jì)算層面的工程化實(shí)現(xiàn)。貝葉斯公式表達(dá)起來(lái)非常簡(jiǎn)單$$P(\theta | D) \frac{P(D | \theta) P(\theta)}{P(D)}$$翻譯成大白話$P(\theta)$ 是先驗(yàn)分布在看到數(shù)據(jù)之前你對(duì)參數(shù) $\theta$ 的認(rèn)知。它可以是信息性的比如“根據(jù)過(guò)往經(jīng)驗(yàn)?zāi)城赖霓D(zhuǎn)化率大概率在 1%-3% 之間”也可以是弱信息性的比如“轉(zhuǎn)化率不會(huì)小于 0 也不會(huì)大于 100%”就行。$P(D | \theta)$ 是似然函數(shù)在給定參數(shù) $\theta$ 的情況下觀測(cè)到當(dāng)前數(shù)據(jù)集 $D$ 的概率。它是模型與數(shù)據(jù)的連接點(diǎn)描述了數(shù)據(jù)生成機(jī)制。$P(\theta | D)$ 是后驗(yàn)分布在看到數(shù)據(jù)之后你更新過(guò)的、對(duì) $\theta$ 的最終認(rèn)知。這是你做推斷、做預(yù)測(cè)、做決策的依據(jù)。理解貝葉斯公式的關(guān)鍵在于“更新”這個(gè)詞。先驗(yàn)是你最初的判斷數(shù)據(jù)像秤砣一樣改變著判斷的天平后驗(yàn)是了這個(gè)過(guò)程之后的結(jié)果。數(shù)據(jù)量越大、數(shù)據(jù)越“硬”后驗(yàn)就越被數(shù)據(jù)主導(dǎo)先驗(yàn)的影響越弱數(shù)據(jù)量小的時(shí)候后驗(yàn)則主要是先驗(yàn)在支撐——這也是貝葉斯方法在小樣本場(chǎng)景下依然能給出合理推斷的原因。概率編程做的事情用一句話概括就是你定義先驗(yàn)和似然它幫你算后驗(yàn)。這里的核心難點(diǎn)是$P(D)$ 這個(gè)歸一化常數(shù)在絕大多數(shù)現(xiàn)實(shí)模型里是沒(méi)有解析解的你需要用數(shù)值方法來(lái)逼近后驗(yàn)分布。這就是 MCMC 和變分推斷登場(chǎng)的時(shí)刻。2.2 后驗(yàn)推斷的兩條路MCMC 與變分推斷**MCMC馬爾可夫鏈蒙特卡洛**是目前概率編程中最主流的后驗(yàn)推斷方法。它的思路很巧妙既然無(wú)法直接算出后驗(yàn)的解析形式那就構(gòu)造一種采樣策略讓采樣出來(lái)的樣本服從的分布逐漸逼近后驗(yàn)分布。具體實(shí)現(xiàn)的算法很多比較常用的是 No-U-Turn SamplerNUTS它是 Hamilton Monte CarloHMC的一種自適應(yīng)變體。NUTS 能在參數(shù)空間里利用梯度信息高效地探索后驗(yàn)分布在參數(shù)維度不高、后驗(yàn)形態(tài)不算太扭曲的情況下收斂速度相當(dāng)理想。使用 MCMC 得到的是一堆從后驗(yàn)分布中抽取的樣本。通過(guò)對(duì)這些樣本做統(tǒng)計(jì)你可以得到后驗(yàn)均值、分位數(shù)、可信區(qū)間等一切你關(guān)心的量。樣本量越大對(duì)后驗(yàn)的刻畫就越精細(xì)。它的優(yōu)點(diǎn)是準(zhǔn)確——沒(méi)有系統(tǒng)性偏差只要鏈長(zhǎng)足夠、診斷通過(guò)樣本分布就趨近于真實(shí)后驗(yàn)。缺點(diǎn)是需要大量計(jì)算尤其在高維參數(shù)空間或大數(shù)據(jù)集場(chǎng)景下采樣成本可能高到不可接受。**變分推斷Variational Inference**是另一個(gè)方向。它不是采樣而是把后驗(yàn)推斷轉(zhuǎn)化成一個(gè)最優(yōu)化問(wèn)題設(shè)定一個(gè)參數(shù)化的分布族 $q(\theta; \phi)$然后調(diào)整變分參數(shù) $\phi$讓 $q$ 盡可能接近真實(shí)后驗(yàn) $P(\theta | D)$通常用 KL 散度來(lái)衡量?jī)蓚€(gè)分布的距離。優(yōu)化完成后$q$ 就作為后驗(yàn)的近似輸出。變分推斷的優(yōu)勢(shì)是速度快能處理大規(guī)模數(shù)據(jù)和復(fù)雜模型。缺點(diǎn)是有偏——$q$ 的分布族選擇如果表達(dá)能力不夠近似結(jié)果就和真實(shí)后驗(yàn)有偏差?,F(xiàn)實(shí)使用中一般是這樣取舍的先期探索模型結(jié)構(gòu)、需要快速迭代的時(shí)候用變分推斷最終關(guān)鍵結(jié)論的確定、當(dāng)模型維度不算太高的時(shí)候用 MCMC。我在實(shí)際項(xiàng)目里經(jīng)常兩個(gè)一起跑變分推斷給出的結(jié)果當(dāng)“預(yù)報(bào)員”MCMC 的結(jié)果當(dāng)“裁判員”。2.3 概率編程語(yǔ)言到底“編程”了什么很多人第一次看到 PyMC 或 Stan 的代碼時(shí)會(huì)有個(gè)困惑這不就是寫個(gè)模型公式然后調(diào) sample() 嗎這算什么編程這個(gè)問(wèn)題的答案在于概率編程語(yǔ)言提供的是一種生成式模型的表達(dá)框架。你寫的模型代碼實(shí)際上是在描述“數(shù)據(jù)是如何被生成出來(lái)的”——先定義參數(shù)的先驗(yàn)分布再定義中間變量之間的確定性關(guān)系最后定義觀測(cè)變量的似然分布??蚣茇?fù)責(zé)在后臺(tái)把這個(gè)描述轉(zhuǎn)換成對(duì)應(yīng)的計(jì)算圖然后自動(dòng)推導(dǎo)采樣所需的梯度信息、自動(dòng)完成推斷過(guò)程。這種“聲明式建模”的方式帶來(lái)兩個(gè)巨大的好處。第一模型和推斷解耦你可以把精力全部放在模型結(jié)構(gòu)的合理性上而不需要自己實(shí)現(xiàn)采樣算法、推導(dǎo)梯度公式。第二模型靈活度極高你可以輕松地構(gòu)建多層級(jí)的回歸模型、帶有測(cè)量誤差的模型、混合分布模型等復(fù)雜結(jié)構(gòu)這些用傳統(tǒng)統(tǒng)計(jì)軟件做會(huì)非常痛苦但在概率編程框架里只是多寫幾行代碼的事。當(dāng)然這并不意味著你可以完全不懂推斷原理。實(shí)際上對(duì) MCMC 的收斂性、采樣器行為、先驗(yàn)敏感性有基本了解是排查模型問(wèn)題的前提——這個(gè)問(wèn)題我在后面的章節(jié)會(huì)詳細(xì)展開。3. 工具選型主流概率編程框架怎么選3.1 PyMC、Stan、NumPyro 三足鼎立概率編程的生態(tài)經(jīng)過(guò)這么多年的發(fā)展基本形成了三足鼎立的局面老牌的 PyMC前身是 PyMC2/PyMC3、工業(yè)級(jí)口碑的 Stan尤其它的 R 接口、以及 JAX 生態(tài)下的新銳 NumPyro。選型沒(méi)有絕對(duì)的正確答案關(guān)鍵看你的技術(shù)棧和需求。PyMCPython 生態(tài)中最成熟、社區(qū)最活躍的選擇。API 設(shè)計(jì)對(duì)新手非常友好內(nèi)置的pm.Model()上下文管理器讓模型定義像寫公式一樣自然。它封裝了多種采樣器和變分推斷方法pm.sample()一把梭就能完成推斷。配合arviz庫(kù)做后驗(yàn)診斷和可視化體驗(yàn)非常順滑。如果你的主力語(yǔ)言是 Python想快速上手、快速驗(yàn)證想法PyMC 基本是首選。Stan以 C 為基礎(chǔ)實(shí)現(xiàn)通過(guò)cmdstanpy或 R 的rstan接口調(diào)用。Stan 的建模語(yǔ)言是獨(dú)立的 Stan Language需要在.stan文件中寫模型然后編譯運(yùn)行。它的 HMC/NUTS 實(shí)現(xiàn)非常扎實(shí)在某些復(fù)雜模型和后驗(yàn)幾何相對(duì)崎嶇的情況下采樣效率往往優(yōu)于其他框架。代價(jià)是建模和調(diào)試的迭代周期比 Python 原生環(huán)境要長(zhǎng)一些。適合需要極致采樣性能、模型復(fù)雜度高的場(chǎng)景。NumPyro基于 JAX 構(gòu)建用函數(shù)式編程風(fēng)格來(lái)描述模型。它的最大優(yōu)勢(shì)是支持 JIT 編譯和 GPU 加速在對(duì)性能極度敏感的大規(guī)模任務(wù)上有明顯優(yōu)勢(shì)。如果你是 JAX 生態(tài)的玩家或者模型大到采樣不得不靠 GPU 加速NumPyro 非常值得嘗試。學(xué)習(xí)曲線相對(duì)陡——函數(shù)式風(fēng)格、numpyro.sample的調(diào)用方式和plate與numpyro.handlers的交互邏輯都需要一些適應(yīng)時(shí)間。3.2 我的選型建議按場(chǎng)景對(duì)號(hào)入座這里給你一個(gè)比較實(shí)際的參考場(chǎng)景推薦工具理由Python 新手 / 快速原型PyMC上手快、社區(qū)資料多、可視化配套完善復(fù)雜模型 / 需要最高采樣質(zhì)量StanNUTS 實(shí)現(xiàn)成熟對(duì)崎嶇后驗(yàn)的適應(yīng)性強(qiáng)大數(shù)據(jù)量 / GPU 加速 / JAX 生態(tài)NumPyroJIT 向量化性能天花板最高R 用戶做統(tǒng)計(jì)建模rstan與 R 的統(tǒng)計(jì)生態(tài)銜接自然生產(chǎn)環(huán)境部署NumPyro / PyMC 序列化Python 棧部署路徑最順還有兩個(gè)輕量級(jí)選手值得一提。sampyl和emcee都是輕量級(jí) MCMC 庫(kù)體量小、依賴少適合教學(xué)和小型項(xiàng)目。但它們功能覆蓋面有限生產(chǎn)級(jí)建模我還是建議回到三大件上來(lái)。我的個(gè)人經(jīng)驗(yàn)是不要貪多主攻一個(gè)熟悉另一個(gè)。比如你日常用 PyMC 建模遇到采樣性能瓶頸時(shí)能切到 Stan 去對(duì)比驗(yàn)證或者你在 PyMC 里原型驗(yàn)證沒(méi)問(wèn)題之后用 NumPyro 在生產(chǎn)環(huán)境里重寫一遍上 GPU??蚣苤g模型表達(dá)的思路是相通的重點(diǎn)是把核心概念吃透。4. 完整實(shí)操用概率編程做一個(gè)轉(zhuǎn)化率不確定性分析4.1 業(yè)務(wù)場(chǎng)景與數(shù)據(jù)構(gòu)造我們來(lái)走一個(gè)完整的建模流程。場(chǎng)景設(shè)定某產(chǎn)品線做了一個(gè)新版本的功能產(chǎn)品經(jīng)理想知道新版上線后購(gòu)買轉(zhuǎn)化率到底比舊版高多少。收集到的數(shù)據(jù)形式很簡(jiǎn)單舊版觀察了 2000 個(gè)用戶其中 96 人完成購(gòu)買新版觀察了 2140 個(gè)用戶其中 132 人完成購(gòu)買。傳統(tǒng)做法是算轉(zhuǎn)換率、做個(gè)卡方檢驗(yàn)或者兩比例 z 檢驗(yàn)給個(gè) p 值。但概率編程能給出的信息豐富得多你能直接得到轉(zhuǎn)化率差異的完整后驗(yàn)分布并基于概率做出決策比如“新版轉(zhuǎn)化率比舊版高的概率是 93.7%”。為了演示我們用 Python 的 PyMC 框架。核心代碼如下import pymc as pm import arviz as az import numpy as np # 觀測(cè)數(shù)據(jù) n_old, x_old 2000, 96 n_new, x_new 2140, 132 with pm.Model() as conversion_model: # 先驗(yàn)舊版本轉(zhuǎn)化率 ~ Beta(1, 1) 等價(jià)于 Uniform(0, 1) p_old pm.Beta(p_old, alpha1, beta1) p_new pm.Beta(p_new, alpha1, beta1) # 似然觀測(cè)到的購(gòu)買人數(shù)服從二項(xiàng)分布 obs_old pm.Binomial(obs_old, nn_old, pp_old, observedx_old) obs_new pm.Binomial(obs_new, nn_new, pp_new, observedx_new) # 派生量轉(zhuǎn)化率差異 diff pm.Deterministic(diff, p_new - p_old) # 執(zhí)行 MCMC 采樣 trace pm.sample(draws2000, tune2000, chains4, random_seed42)這段代碼一共做了四件事定義轉(zhuǎn)化率的先驗(yàn)Beta(1,1) 表示完全沒(méi)有任何先驗(yàn)傾向、定義觀測(cè)數(shù)據(jù)的似然二項(xiàng)分布、定義關(guān)心的派生量轉(zhuǎn)化率差異、執(zhí)行 NUTS 采樣。跑完之后trace里存的就是所有參數(shù)的 MCMC 樣本。4.2 后驗(yàn)分析從樣本到業(yè)務(wù)決策采樣完成后我們把后驗(yàn)樣本展示出來(lái)az.summary(trace, var_names[p_old, p_new, diff])你會(huì)得到一張表格每一行一個(gè)變量包含均值、標(biāo)準(zhǔn)差、HDI最高密度區(qū)間、有效樣本量等指標(biāo)。假設(shè)輸出結(jié)果是mean sd hdi_3% hdi_97% ess_bulk r_hat p_old 0.048 0.003 0.042 0.054 6234 1.00 p_new 0.062 0.004 0.055 0.069 6157 1.00 diff 0.013 0.005 0.004 0.022 6128 1.00這份結(jié)果信息的密度遠(yuǎn)超一個(gè) p 值。首先p_old和p_new的 HDI 區(qū)間都是兩側(cè)遠(yuǎn)離 0 的表明數(shù)據(jù)充分支持這兩個(gè)轉(zhuǎn)化率確實(shí)在 4%-7% 這個(gè)量級(jí)。其次diff的 94% HDI 區(qū)間是 [0.004, 0.022]不包含 0——這說(shuō)明新版轉(zhuǎn)化率顯著高于舊版等等先別急著說(shuō)“顯著”在貝葉斯框架里我們換一種更直白的表述。更直觀的做法是直接計(jì)算概率prob_improvement (trace.posterior[diff] 0).mean().item() print(f新版轉(zhuǎn)化率高于舊版的概率: {prob_improvement:.1%})假設(shè)輸出為 98.3%那這個(gè)信息可以直接扔給業(yè)務(wù)方我們不需要糾結(jié)“顯著性水平 0.05”或“p0.03”這種解釋易產(chǎn)生歧義的表述而是可以說(shuō)“根據(jù)數(shù)據(jù)表現(xiàn)新版轉(zhuǎn)化率高于舊版的概率為 98.3%”。這個(gè)概率表述業(yè)務(wù)方一聽就懂決策者可以直接基于它來(lái)評(píng)估是否全量上線。4.3 先驗(yàn)敏感性分析給結(jié)論上雙保險(xiǎn)有經(jīng)驗(yàn)的建模者不會(huì)只看一組先驗(yàn)跑出來(lái)的結(jié)果就下結(jié)論。貝葉斯方法的先驗(yàn)選擇在一定程度上是主觀判斷所以要做敏感性分析換不同的先驗(yàn)設(shè)置看結(jié)論會(huì)不會(huì)被顯著改變。我們把代碼改成用信息性強(qiáng)的先驗(yàn)再跑一遍with pm.Model() as conversion_model_prior: # 更強(qiáng)的先驗(yàn)根據(jù)歷史經(jīng)驗(yàn)轉(zhuǎn)化率集中在 3%-7% 之間 p_old pm.Beta(p_old, alpha10, beta170) p_new pm.Beta(p_new, alpha10, beta170) obs_old pm.Binomial(obs_old, nn_old, pp_old, observedx_old) obs_new pm.Binomial(obs_new, nn_new, pp_new, observedx_new) diff pm.Deterministic(diff, p_new - p_old) trace_prior pm.sample(draws2000, tune2000, chains4, random_seed42)對(duì)比兩次運(yùn)行的diff后驗(yàn)均值如果結(jié)論基本一致說(shuō)明你的結(jié)論受先驗(yàn)影響不大、是數(shù)據(jù)本身在驅(qū)動(dòng)如果差異明顯說(shuō)明當(dāng)前數(shù)據(jù)量還不足以克服先驗(yàn)的影響。這時(shí)候就要慎重對(duì)結(jié)論的表述要加上“基于當(dāng)前先驗(yàn)判斷”的限定條件。這是貝葉斯建模的好習(xí)慣也是規(guī)避“先驗(yàn)挑數(shù)據(jù)”這個(gè)爭(zhēng)議的有效方式。4.4 層級(jí)模型的擴(kuò)展不滿足于單一體轉(zhuǎn)化率上述模型是極其簡(jiǎn)化的——它假設(shè)所有用戶同質(zhì)忽略用戶來(lái)源渠道、訪問(wèn)設(shè)備、時(shí)間區(qū)間等因素。一個(gè)更接近真實(shí)業(yè)務(wù)的建模方式是把這些變量吸納進(jìn)模型。設(shè)想數(shù)據(jù)升級(jí)為用戶來(lái)源渠道有 A、B、C 三個(gè)每個(gè)渠道既有舊版也有新版訪問(wèn)。你可以在模型里加入渠道作為分組變量構(gòu)建一個(gè)層級(jí)模型# 假設(shè) channels 是 0,1,2 編碼的渠道標(biāo)簽 import pandas as pd # 這里簡(jiǎn)化為構(gòu)造一個(gè)示例 DataFrame np.random.seed(42) df pd.DataFrame({ channel: np.repeat([0, 1, 2], [800, 700, 640]), version: 0 # 占位 }) # 實(shí)際使用時(shí)數(shù)據(jù)是每個(gè) [channel, version] 組合的訪問(wèn)量和購(gòu)買量 with pm.Model() as hierarchical_model: # 渠道間共享的超先驗(yàn) mu pm.Normal(mu, mu0, sigma1) sigma pm.HalfCauchy(sigma, beta0.5) # 渠道效應(yīng)收縮到共同均值 z_channel pm.Normal(z_channel, mu0, sigma1, shape3) beta_channel pm.Deterministic(beta_channel, mu sigma * z_channel) # 版本效應(yīng)按渠道建模時(shí)也能分層處理 p_logit mu beta_channel * ... # 根據(jù)具體編碼設(shè)計(jì) # 似然觀測(cè)銷售數(shù)據(jù)服從伯努利/二項(xiàng)分布 ...層級(jí)模型的好處在于部分池化樣本量少的渠道會(huì)參考整體分布的信息避免因樣本稀疏產(chǎn)生極端的估計(jì)樣本量大的渠道則保持自己的特性。這在轉(zhuǎn)化率、客單價(jià)等業(yè)務(wù)指標(biāo)的多維度分析中非常實(shí)用也是傳統(tǒng)頻率派方法處理起來(lái)最棘手、而概率編程最舒展的場(chǎng)景之一。5. 建模實(shí)操中的常見問(wèn)題與排查經(jīng)驗(yàn)5.1 采樣不收斂最讓人抓狂的 MCMC 問(wèn)題跑pm.sample()之后第一件事永遠(yuǎn)是看r_hat和有效樣本量ess_bulk。r_hat大于 1.01 基本就意味著采樣鏈沒(méi)有收斂。常見原因和排查手段我整理成了一張速查表癥狀可能原因排查方向r_hat 恒大于 1.01鏈初始值差異太大burn-in 不夠增加 tune 長(zhǎng)度提供更合理的初始值鏈呈“毛蟲”狀、游走緩慢后驗(yàn)分布相關(guān)性高 / 參數(shù)化不佳嘗試標(biāo)準(zhǔn)化參數(shù)、非中心化參數(shù)化高維模型頻繁發(fā)散后驗(yàn)幾何太崎嶇調(diào)整步長(zhǎng)、換用pm.Metropolis對(duì)比、簡(jiǎn)化模型結(jié)構(gòu)eff 樣本量極低500自相關(guān)性過(guò)高增加chains或draws檢查模型是否有冗余參數(shù)警告出現(xiàn)很多divergencesNUTS 步長(zhǎng)自適應(yīng)遇到問(wèn)題通常因先驗(yàn)與數(shù)據(jù)沖突檢查先驗(yàn)是否有不合理取值并調(diào)整或降低target_accept到 0.9有參數(shù)后驗(yàn)分布“掛在邊界”先驗(yàn)約束不夠或者數(shù)據(jù)對(duì)參數(shù)的識(shí)別力弱考慮更換先驗(yàn)分布族例如在比例參數(shù)上使用 Beta 而不是 Uniform我遇到最多的還是參數(shù)化不當(dāng)導(dǎo)致的無(wú)效采樣。一個(gè)典型的例子是方差參數(shù)用了HalfNormal(sigma10)這樣過(guò)寬的弱先驗(yàn)。表面上看起來(lái)“不預(yù)設(shè)立場(chǎng)很客觀”但實(shí)際上過(guò)寬的先驗(yàn)會(huì)在高參數(shù)空間分配大量概率質(zhì)量導(dǎo)致采樣器在極端值之間掙扎表現(xiàn)為大量發(fā)散。改用HalfCauchy或適度窄的先驗(yàn)分布之后采樣立刻恢復(fù)順暢。這里想多說(shuō)一句弱先驗(yàn)不等于打死不設(shè)先驗(yàn)。在你對(duì)參數(shù)的合理范圍多少有數(shù)的情況下給一個(gè)符合業(yè)務(wù)常識(shí)的約束或弱信息先驗(yàn)往往既不會(huì)扭曲后驗(yàn)結(jié)論又能顯著改善采樣性能。這也是貝葉斯工程師的經(jīng)驗(yàn)之談。5.2 模型結(jié)果怪得離譜先檢查似然和數(shù)據(jù)的對(duì)應(yīng)關(guān)系有次我做一個(gè)客單價(jià)預(yù)估模型后驗(yàn)均值明顯偏離業(yè)務(wù)直覺(jué)。排查了半天最后發(fā)現(xiàn)是“數(shù)據(jù)對(duì)齊”的問(wèn)題訓(xùn)練數(shù)據(jù)里的一列應(yīng)是當(dāng)日訂單量結(jié)果因?yàn)槟炒翁幚砟_本的 bug混入了前一天的訂單數(shù)據(jù)。這類“模型以外”的錯(cuò)誤在統(tǒng)計(jì)推斷里比模型本身的錯(cuò)誤更隱蔽。排查順序建議為檢查數(shù)據(jù)描述性統(tǒng)計(jì)——均值、分位數(shù)、缺失率是否合理。畫出關(guān)鍵變量在模型下的生成模擬數(shù)據(jù)posterior_predictive對(duì)比真實(shí)數(shù)據(jù)的分布。如果模擬數(shù)據(jù)和觀測(cè)數(shù)據(jù)差別巨大說(shuō)明模型的一個(gè)或多個(gè)核心假設(shè)有問(wèn)題。逐一簡(jiǎn)化模型先跑一個(gè)只含主要效應(yīng)、不含交互項(xiàng)和層級(jí)結(jié)構(gòu)的模型看看能否得到大概合理的后驗(yàn)再逐步增加復(fù)雜度。對(duì)連續(xù)型變量檢查數(shù)據(jù)縮放是否均勻。數(shù)值范圍跨度過(guò)大的特征會(huì)讓 NUTS 的步長(zhǎng)自適應(yīng)失靈建議對(duì)輸入數(shù)據(jù)做標(biāo)準(zhǔn)化同時(shí)可以在模型的確定性關(guān)系中使用相應(yīng)的偏差。曾有很長(zhǎng)一段時(shí)間我在做這類分析時(shí)從不做第 2 步——posterior_predictive檢查——后來(lái)一個(gè)實(shí)際案例教育了我模型對(duì)某個(gè)分組變量的層級(jí)方差估得很小把分組間差異全都“解釋”成了偶然噪聲完全不符合業(yè)務(wù)預(yù)期。當(dāng)時(shí)的模型層設(shè)計(jì)沒(méi)有包含組內(nèi)樣本量對(duì)先前方差收縮作用的權(quán)衡簡(jiǎn)單來(lái)說(shuō)就是層級(jí)結(jié)構(gòu)設(shè)置不當(dāng)。如果沒(méi)有事后預(yù)測(cè)檢查這個(gè)問(wèn)題會(huì)一直潛伏到部署階段才爆發(fā)。5.3 變分推斷的結(jié)果不靠譜別急著怪算法使用變分推斷時(shí)最容易掉進(jìn)的坑是q的分布族表達(dá)能力不足導(dǎo)致求出的近似后驗(yàn)和真實(shí)后驗(yàn)差異巨大但收斂過(guò)程看起來(lái)完全正常。一種驗(yàn)證方法是把變分推斷結(jié)果和 MCMC 結(jié)果做對(duì)比。在數(shù)據(jù)量不大、模型不太復(fù)雜的時(shí)候這個(gè)對(duì)比成本有限卻能一針見血地發(fā)現(xiàn)問(wèn)題。如果兩者差異在可接受范圍內(nèi)就用變分推斷如果差異巨大檢查是不是原始模型的非光滑環(huán)節(jié)比如離散變量、硬截?cái)鄬?dǎo)致變分分布族嚴(yán)重失配。另外變分推斷對(duì)初始化也比較敏感多試幾組隨機(jī)種子如果結(jié)果的不確定性變動(dòng)異常說(shuō)明優(yōu)化過(guò)程本身可能有問(wèn)題。從工程實(shí)踐角度看我的策略是變分推斷用在探索階段MCMC 用在發(fā)布階段。每次項(xiàng)目保質(zhì)期內(nèi)的最終結(jié)論都以 MCMC 驗(yàn)證過(guò)的版本為準(zhǔn)。5.4 業(yè)務(wù)落地時(shí)的不確定性傳播最后分享一個(gè)經(jīng)常被忽略但實(shí)際影響很大的問(wèn)題——概率編程的輸出如何進(jìn)入下游系統(tǒng)。一種常見做法是只把后驗(yàn)均值傳到下游這等于又把不確定性丟掉了。較好的做法是把后驗(yàn)分布的完整樣本傳遞過(guò)去。具體而言如果你在做庫(kù)存優(yōu)化不是傳一個(gè)“預(yù)測(cè)需求 1300 件”而是傳一組后驗(yàn)樣本“需求可能分布在 [1180, 1420]”下游的庫(kù)存優(yōu)化代碼可以對(duì)每一個(gè)樣本跑一遍最優(yōu)庫(kù)存決策得到一整套決策的分布最終選擇覆蓋某個(gè)分位的庫(kù)存量。如果你在做 A/B 實(shí)驗(yàn)的后續(xù)部署判斷不是簡(jiǎn)單用“贏了的版本全量上”而是用后驗(yàn)差異的分布來(lái)確定上線節(jié)奏和回滾閾值。在做預(yù)算分配時(shí)可以把每個(gè)渠道的收益參數(shù)后驗(yàn)樣本輸入優(yōu)化器這樣得到的分配方案本身就是帶不確定性的決策者可以看到最壞情況和最好情況之間的差異。很多業(yè)務(wù)場(chǎng)景本身并不缺少優(yōu)化方法缺少的是給優(yōu)化方法提供不確定性輸入的意識(shí)。概率編程給出了這個(gè)輸入它是從純粹統(tǒng)計(jì)推斷到實(shí)際業(yè)務(wù)決策的橋梁。6. 實(shí)戰(zhàn)進(jìn)階從簡(jiǎn)單模型走向生產(chǎn)級(jí)應(yīng)用6.1 從單模型到貝葉斯工作流把概率編程真正用起來(lái)之后你會(huì)發(fā)現(xiàn)自己需要的不只是一個(gè)采樣函數(shù)而是一整套工作流?;谖业膶?shí)戰(zhàn)經(jīng)驗(yàn)一個(gè)可復(fù)用的貝葉斯建模流程應(yīng)該是這樣的問(wèn)題形式化把業(yè)務(wù)問(wèn)題翻譯成一個(gè)可以計(jì)算概率的可量化問(wèn)題。明確哪些是觀測(cè)變量、哪些是待推斷參數(shù)、哪些是派生業(yè)務(wù)指標(biāo)。數(shù)據(jù)審查做描述性統(tǒng)計(jì)和可視化識(shí)別數(shù)據(jù)缺失、異常值和量綱問(wèn)題。先驗(yàn)設(shè)立基于業(yè)務(wù)常識(shí)設(shè)定合理的弱信息先驗(yàn)。向業(yè)務(wù)方了解“過(guò)去的歷史水平”讓它成為先驗(yàn)均值的重要參考。模型搭建與迭代從簡(jiǎn)單模型開始驗(yàn)證核心結(jié)構(gòu)正確后再加復(fù)雜度。每次增加結(jié)構(gòu)后都用后驗(yàn)預(yù)測(cè)檢查來(lái)檢驗(yàn)改進(jìn)效果。推斷與診斷執(zhí)行 MCMC或變分推斷檢查r_hat、ess_bulk、發(fā)散情況。必要時(shí)做參數(shù)化調(diào)整。敏感性分析改變先驗(yàn)設(shè)定、修改數(shù)據(jù)子集做重跑確認(rèn)結(jié)論的穩(wěn)定性。產(chǎn)出與溝通把后驗(yàn)分布翻譯成業(yè)務(wù)語(yǔ)言輸出“差異為 X 的概率是多少”、“落在哪個(gè)區(qū)間的把握有多大”這類可操作的結(jié)論。部署與監(jiān)控把模型固化為可重復(fù)執(zhí)行的推斷管道對(duì)新數(shù)據(jù)周期性地更新后驗(yàn)并監(jiān)控分布漂移。這八步聽起來(lái)多但每一步的時(shí)間消耗并不大。熟練之后一個(gè)中等復(fù)雜度的模型從開始到出結(jié)論基本在一兩天內(nèi)就能完成。6.2 讓模型可復(fù)現(xiàn)的幾個(gè)工程習(xí)慣概率編程的模型天然具有隨機(jī)性因此可復(fù)現(xiàn)性是工程化繞不開的問(wèn)題。沒(méi)有固定隨機(jī)種子的模型運(yùn)行每次結(jié)果都有細(xì)微差異這在調(diào)試時(shí)會(huì)讓人分不清改動(dòng)模型前的效果差異是真實(shí)的還是隨機(jī)波動(dòng)造成的。我現(xiàn)在在項(xiàng)目里嚴(yán)格遵守以下三條規(guī)則固定隨機(jī)種子所有采樣、變分推斷調(diào)用都顯式傳入random_seed并使它在每次運(yùn)行中保持一致。記錄模型版本和依賴環(huán)境把模型代碼、依賴庫(kù)版本號(hào)、數(shù)據(jù)文件哈希寫進(jìn)自動(dòng)生成的模型元數(shù)據(jù)文件。后期排查問(wèn)題這一條能救命。用緩存機(jī)制保存推斷結(jié)果同一個(gè)模型、同一份數(shù)據(jù)跑過(guò)一次后把 trace 序列化為文件。后續(xù)所有人的分析都基于這份緩存結(jié)果保證溝通討論的前后一致。這些習(xí)慣沒(méi)有任何高深之處但它們直接決定了概率編程能否從小項(xiàng)目走向正式工作流。7. 一些關(guān)于概率編程的實(shí)話做概率編程這些年最大的感受是這項(xiàng)技術(shù)真正改變?nèi)说牡胤讲辉诓蓸铀惴ǖ男识谒季S方式的轉(zhuǎn)換。當(dāng)你開始用分布去思考所有問(wèn)題你會(huì)意識(shí)到以前那些點(diǎn)估計(jì)的結(jié)論有多少是被“平均”掩埋掉的。但也要說(shuō)句公道話概率編程不是萬(wàn)能的。它的計(jì)算成本遠(yuǎn)高于傳統(tǒng)統(tǒng)計(jì)方法在超大樣本場(chǎng)景下需要投入相當(dāng)?shù)臅r(shí)間去做工程優(yōu)化。如果業(yè)務(wù)場(chǎng)景的數(shù)據(jù)量足夠大、模型相對(duì)簡(jiǎn)單、只需要一個(gè)穩(wěn)定輸出傳統(tǒng)方法可能更實(shí)惠。概率編程適合的是對(duì)不確定性本身有決策價(jià)值、數(shù)據(jù)規(guī)??煽?、模型復(fù)雜度中上的場(chǎng)景——用一句直白的話說(shuō)當(dāng)你發(fā)現(xiàn)“上線后預(yù)測(cè)總偏差大、又不知道偏差從哪來(lái)”的時(shí)候概率編程大概能給你答案。另外概率編程也不該被你當(dāng)成一個(gè)“萬(wàn)能答案機(jī)”。它給出的后驗(yàn)分布依然建立在模型假設(shè)之上。假設(shè)錯(cuò)了后驗(yàn)再漂亮也不可靠。這也是為什么做概率編程的人一半時(shí)間在建模另一半時(shí)間在驗(yàn)證假設(shè)、檢查模型與數(shù)據(jù)的匹配度。這些約束和代價(jià)不是說(shuō)概率編程不好而是說(shuō)你有必要想清楚我到底需要的是不確定性信息還是僅僅需要結(jié)論的確定性去完成眼前這一步。想明白了再上手這個(gè)工具給你的回報(bào)會(huì)遠(yuǎn)高于你的預(yù)期。