對新手友好的PyTorch深度概率推斷工具Brancher,掌握ML和Python基礎即可上手
機器之心報道
參與:一鳴、張倩
近日,來自荷蘭拉德堡德大學(Radboud University)團隊的開發者在 reddit 上釋出了一個 PyTorch 深度概率推斷工具——Brancher,旨在使貝葉斯統計和深度學習之間的整合變得簡單而直觀。與其他概率推斷工具相比,Brancher 對新手更加友好,只具備機器學習和 Python 基礎的人也可以上手使用。
專案地址:https://brancher.org/
特點
Brancher 官網顯示,這一工具具有靈活(flexible)、整合(integrated)、直觀(intuitive)的特點。
與其他概率建模工具有什麼區別?
專案的主要開發者 LucaAmbrogioni 表示,與 Brancher 緊密相關的兩個模組是 Pyro 和 PyMC3。Brancher 的目標受眾比 Pyro 更廣泛,包括那些只接受過機器學習和 Python 程式設計基本培訓的人。介面設計得儘可能接近數學。缺點是 Brancher 不如 Pyro 靈活。
Brancher 的前端與 PyMC3 非常相似。與 PyMC 的主要區別在於,Brancher 構建在深度學習庫 PyTorch 的頂部。每一個在 PyTorch 中實現的深度學習工具都可以用來在 Brancher 中構建深度概率模型。此外,PyMC 主要利用取樣,而 Brancher 則基於變分推理。
安裝
使用者需要首先安裝 PyTorch,然後使用 pip 命令列:
pip install *brancher*
或從 GitHub 地址克隆程式碼,Github 地址:https://github.com/AI-DI/Brancher
教程
Google Colab 上有相關教程,包括
Brancher 入門
Brancher 是一個以使用者為中心的概率微分程式包。Brancher 希望能夠為初學者提供友好的服務,在保證計算執行效率和靈活性的前提下減少多餘的程式碼。Brancher 以 PyTorch 為核心構建。
安裝 Brancher 成功後,首先需要使用者匯入相關包:
importtorch
importmatplotlib.pyplot asplt
frombrancher.variables importProbabilisticModel
frombrancher.standard_variables importNormalVariable, LogNormalVariable
frombrancher importinference
importbrancher.functions asBF
Brancher 是一個物件導向的工具包。因此內部的所有物件都是一個類,可以用來抽象化為概率計算程式。建立所有 Brancher 程式的基礎元件是 RandomVariable 類。通過微分方程連線隨機變數,可以建立概率模型。
例如,可以建立這樣一個模型,其中一個正則隨機變數的均值是由另一個正則隨機變數的正弦函式值決定的。Brancher 可以讓你像在學術論文裡那樣使用符號定義模型。
建立變數:
nu = LogNormalVariable(loc= 0., scale= 1., name= "nu")
mu = NormalVariable(loc= 0., scale= 10., name= "mu")
x = NormalVariable(loc=BF.sin(mu), scale=nu, name= "x")
model = ProbabilisticModel([x, mu, nu])
列印模型的內部組成:
model.model_summary
正如我們所預計的那樣,變數 x 是 mu 和 nu 的計算結果。但是,列表中也出現了 mu_mu 或 mu_sigma 這樣沒有提前明確定義的變數。這些確定變數(Deterministic Variables)代表的是概率分佈引數的固定值。確定變數是 Brancher 中的特例,和隨機變數相似,但值是確定的。我們不需要定義他們,只需要在計算時輸入數字即可。
由於現在沒有輸入資料,因此 Observed 一欄為 False,現在我們輸入一些樣本資料,看看概率模型如何工作。
sample = model.get_sample( 10)
sample
如果只需要單個變數的結果:
x_sample = x.get_sample( 10)
x_sample
我們還可以做到通過輸入某些變數的值後進行取樣,如設定 mu 變數為 100 時,檢視樣本結果:
in_sample = model.get_sample( 10, input_values={mu: 100.})
in_sample
為了對某些已知的值進行上取樣,我們需要定義一些觀測值,並使用變分推斷的方法獲得分佈。我們可以首先對 mu 和 nu 變數定義一些真實值,並生產一些觀測結果:
nu_real = 0.5
mu_real = -1.
data = x.get_sample(number_samples= 100, input_values={mu: mu_real, nu: nu_real})
現在我們可以告訴 Brancher 變數 x 是從生成資料的值中觀察到的。
x.observe(data)
model.model_summary
這時可以看到變數 x 變為 observed。
如果你想取樣下游 x 的變數 mu 和 nu,你需要執行近似貝葉斯推理。在 Brancher 中,可以通過為所有想要取樣的變數定義一個變分分佈來實現這一點。變分模型本身是一個概率模型,其構造方法與原概率模型完全相同。
指定此分佈的最簡單方法是使用與原始模型中相同的分佈:
Qnu = LogNormalVariable( 0., 1., "nu", learnable= True)
Qmu = NormalVariable( 0., 1., "mu", learnable= True)
model.set_posterior_model(ProbabilisticModel([Qmu, Qnu]))
現在我們需要使用一些隨機優化來學習變分近似的引數。這種技術被稱為隨機變分推理,該技術非常強大,因為它可以將貝葉斯推理很好地融入到深度學習框架中(實際上 brancher 的目的是與深度神經網路一起作為構建複雜概率模型的模組)。
現在讓 Brancher 知道,變數分佈的引數可以使用「learnable」flag 學習。接下來學習這些引數:
inference.perform_inference(model,
number_iterations= 500,
number_samples= 50,
optimizer= "Adam",
lr= 0.01)
loss_list = model.diagnostics[ "loss curve"]
現在把損失函式畫出來,以確保一切順利。
plt.plot(loss_list)
現在從後驗取一些樣本:
post_sample = model.get_posterior_sample( 1000)
post_sample.describe
g = plt.hist(post_sample[ "mu"], 50)
plt.axvline(x=mu_real, color= "k", lw= 2)
[Image: image.png]
可以用 Brancher 繪製的函式視覺化後驗分佈。這個函式依賴於 Seaborn,Seaborn 是一個非常方便的視覺化庫,與 panda 結合使用非常好。
frombrancher.visualizations importplot_posterior
plot_posterior(model, variables=[ "mu", "nu", "x"])
更多教程請參考:
案例
作者提供了許多使用 Brancher 的案例,包括:
importmatplotlib.pyplot asplt
importnumpy asnp
frombrancher.variables importRootVariable, RandomVariable, ProbabilisticModel
frombrancher.standard_variables importNormalVariable, LogNormalVariable, BetaVariable
frombrancher importinference
importbrancher.functions asBF
# Probabilistic model #
T = 200
nu = LogNormalVariable( 0.3, 1., 'nu')
x0 = NormalVariable( 0., 1., 'x0')
b = BetaVariable( 0.5, 1.5, 'b')
x = [x0]
names = [ "x0"]
fort inrange( 1, T):
names.append( "x{}".format(t))
x.append(NormalVariable(b*x[t -1], nu, names[t]))
AR_model = ProbabilisticModel(x)
# Generate data #
data = AR_model._get_sample(number_samples= 1)
time_series = [float(data[xt].cpu.detach.numpy) forxt inx]
true_b = data[b].cpu.detach.numpy
true_nu = data[nu].cpu.detach.numpy
print( "The true coefficient is: {}".format(float(true_b)))
# Observe data #
[xt.observe(data[xt][:, 0, :]) forxt inx]
# Variational distribution #
Qnu = LogNormalVariable( 0.5, 1., "nu", learnable= True)
Qb = BetaVariable( 0.5, 0.5, "b", learnable= True)
variational_posterior = ProbabilisticModel([Qb, Qnu])
AR_model.set_posterior_model(variational_posterior)
# Inference #
inference.perform_inference(AR_model,
number_iterations= 200,
number_samples= 300,
optimizer= 'Adam',
lr= 0.05)
loss_list = AR_model.diagnostics[ "loss curve"]
# Statistics
posterior_samples = AR_model._get_posterior_sample( 2000)
nu_posterior_samples = posterior_samples[nu].cpu.detach.numpy.flatten
b_posterior_samples = posterior_samples[b].cpu.detach.numpy.flatten
b_mean = np.mean(b_posterior_samples)
b_sd = np.sqrt(np.var(b_posterior_samples))
print( "The estimated coefficient is: {} +- {}".format(b_mean, b_sd))
# Two subplots, unpack the axes array immediately
f, (ax1, ax2, ax3, ax4) = plt.subplots( 1, 4)
ax1.plot(time_series)
ax1.set_title( "Time series")
ax2.plot(np.array(loss_list))
ax2.set_title( "Convergence")
ax2.set_xlabel( "Iteration")
ax3.hist(b_posterior_samples, 25)
ax3.axvline(x=true_b, lw= 2, c= "r")
ax3.set_title( "Posterior samples (b)")
ax3.set_xlim( 0, 1)
ax4.hist(nu_posterior_samples, 25)
ax4.axvline(x=true_nu, lw= 2, c= "r")
ax4.set_title( "Posterior samples (nu)")
plt.show