web-dev-qa-db-ja.com

pyMCMC / pyMCを使用してデータ/観測値に非線形関数を適合させます

いくつかのデータをガウス(およびより複雑な)関数に適合させようとしています。以下に小さな例を作成しました。

私の最初の質問は、私はそれを正しくやっていますか?

私の2番目の質問は、x方向、つまり観測値/データのx位置にエラーを追加するにはどうすればよいですか?

PyMCでこの種の回帰を行う方法に関する優れたガイドを見つけるのは非常に困難です。おそらく、いくつかの最小二乗法または同様のアプローチを使用する方が簡単であるため、最終的には多くのパラメーターがあり、それらをどの程度適切に制約してさまざまなモデルを比較できるかを確認する必要があるため、pyMCはそのための良い選択のように思われました。

import pymc
import numpy as np
import matplotlib.pyplot as plt; plt.ion()

x = np.arange(5,400,10)*1e3

# Parameters for gaussian
amp_true = 0.2
size_true = 1.8
ps_true = 0.1

# Gaussian function
gauss = lambda x,amp,size,ps: amp*np.exp(-1*(np.pi**2/(3600.*180.)*size*x)**2/(4.*np.log(2.)))+ps
f_true = gauss(x=x,amp=amp_true, size=size_true, ps=ps_true )

# add noise to the data points
noise = np.random.normal(size=len(x)) * .02 
f = f_true + noise 
f_error = np.ones_like(f_true)*0.05*f.max()

# define the model/function to be fitted.
def model(x, f): 
    amp = pymc.Uniform('amp', 0.05, 0.4, value= 0.15)
    size = pymc.Uniform('size', 0.5, 2.5, value= 1.0)
    ps = pymc.Normal('ps', 0.13, 40, value=0.15)

    @pymc.deterministic(plot=False)
    def gauss(x=x, amp=amp, size=size, ps=ps):
        e = -1*(np.pi**2*size*x/(3600.*180.))**2/(4.*np.log(2.))
        return amp*np.exp(e)+ps
    y = pymc.Normal('y', mu=gauss, tau=1.0/f_error**2, value=f, observed=True)
    return locals()

MDL = pymc.MCMC(model(x,f))
MDL.sample(1e4)

# extract and plot results
y_min = MDL.stats()['gauss']['quantiles'][2.5]
y_max = MDL.stats()['gauss']['quantiles'][97.5]
y_fit = MDL.stats()['gauss']['mean']
plt.plot(x,f_true,'b', marker='None', ls='-', lw=1, label='True')
plt.errorbar(x,f,yerr=f_error, color='r', marker='.', ls='None', label='Observed')
plt.plot(x,y_fit,'k', marker='+', ls='None', ms=5, mew=2, label='Fit')
plt.fill_between(x, y_min, y_max, color='0.5', alpha=0.5)
plt.legend()

私は、より多くの反復を実行し、最終的にバーンインとシンニングを使用する必要があるかもしれないことを認識しています。データと近似をプロットした図を以下に示します。

Resulting figure from the code.

Pymc.Matplot.plot(MDL)の数値は次のようになり、ピークのある分布を示しています。これはいいですよね?

enter image description here

28
Magnus Persson

私の最初の質問は、私はそれを正しくやっているかということです。

はい!あなたが知っているバーンイン期間を含める必要があります。サンプルの前半を捨てるのが好きです。間引きを行う必要はありませんが、MCMC後の処理が速くなり、保存が小さくなる場合があります。

私がアドバイスする他の唯一のことは、結果が「再現可能」になるようにランダムシードを設定することです。np.random.seed(12345)でうまくいきます。

ああ、もし私が本当にあまりにも多くのアドバイスをしているのなら、matplotlibの結果をもう少し美しくするためにimport seabornと言うでしょう。

私の2番目の質問は、x方向、つまり観測値/データのx位置にエラーを追加するにはどうすればよいですか?

1つの方法は、各エラーの潜在変数を含めることです。これはあなたの例では機能しますが、さらに多くの観測がある場合は実行できません。この道を歩み始めるための簡単な例を示します。

# add noise to observed x values
x_obs = pm.rnormal(mu=x, tau=(1e4)**-2)

# define the model/function to be fitted.
def model(x_obs, f): 
    amp = pm.Uniform('amp', 0.05, 0.4, value= 0.15)
    size = pm.Uniform('size', 0.5, 2.5, value= 1.0)
    ps = pm.Normal('ps', 0.13, 40, value=0.15)

    x_pred = pm.Normal('x', mu=x_obs, tau=(1e4)**-2) # this allows error in x_obs

    @pm.deterministic(plot=False)
    def gauss(x=x_pred, amp=amp, size=size, ps=ps):
        e = -1*(np.pi**2*size*x/(3600.*180.))**2/(4.*np.log(2.))
        return amp*np.exp(e)+ps
    y = pm.Normal('y', mu=gauss, tau=1.0/f_error**2, value=f, observed=True)
    return locals()

MDL = pm.MCMC(model(x_obs, f))
MDL.use_step_method(pm.AdaptiveMetropolis, MDL.x_pred) # use AdaptiveMetropolis to "learn" how to step
MDL.sample(200000, 100000, 10)  # run chain longer since there are more dimensions

xyにノイズがあると、良い答えを得るのが難しいようです。 model fit with noise in x and y

これが これをすべて集めたノートブック です。

15

編集:重要な注意これはしばらくの間私を悩ませてきました。ここで私とアブラハムによって与えられた答えは、xに変動性を追加するという意味で正しいです。ただし、できない単純にこの方法で不確実性を追加して、x値にあるエラーをキャンセルし、「true」に対して回帰することに注意してください。バツ"。この回答のメソッドは、真のxがある場合、xにエラーを追加することが回帰にどのように影響するかを示すことができます。 xの測定が間違っている場合、これらの回答は役に立ちません。 x値にエラーがあると、「減衰」と「変数内エラーの影響」が発生するため、解決するのが非常に難しい問題です。短いバージョンは次のとおりです。xに偏りのないランダムなエラーがあると、回帰推定にバイアスが発生します。この問題が発生した場合は、Carroll、R.J.、Ruppert、D.、Crainiceanu、C.M。をチェックしてください。 and Stefanski、L.A.、2006。非線形モデルの測定誤差:現代の視点。 Chapman and Hall/CRC。、またはベイジアンアプローチの場合、Gustafson、P.、2003。統計および疫学における測定誤差と誤分類:影響とベイジアン調整。 CRCプレス。キャロルらのSIMEXメソッドとPyMC3を使用して、特定の問題を解決することになりました。詳細は、Carstens、H.、Xia、X。and Yadavalli、S.、2017にあります。測定と検証のための低コストのエネルギーメーター校正方法。応用エネルギー、188、 pp.563-575。 ArXivでも利用できます


上記のAbrahamFlaxmanの回答を、誰かが必要とする場合に備えてPyMC3に変換しました。いくつかの非常に小さな変更ですが、それでも混乱する可能性があります。

1つ目は、決定論的デコレータ_@Deterministic_がディストリビューションのような呼び出し関数var=pymc3.Deterministic()に置き換えられることです。次に、正規分布の確率変数のベクトルを生成する場合、

_rvs = pymc2.rnormal(mu=mu, tau=tau)
_

に置き換えられます

_rvs = pymc3.Normal('var_name', mu=mu, tau=tau,shape=size(var)).random()
_

完全なコードは次のとおりです。

_import numpy as np
from pymc3 import *
import matplotlib.pyplot as plt

# set random seed for reproducibility
np.random.seed(12345)

x = np.arange(5,400,10)*1e3

# Parameters for gaussian
amp_true = 0.2
size_true = 1.8
ps_true = 0.1

#Gaussian function
gauss = lambda x,amp,size,ps: amp*np.exp(-1*(np.pi**2/(3600.*180.)*size*x)**2/(4.*np.log(2.)))+ps
f_true = gauss(x=x,amp=amp_true, size=size_true, ps=ps_true )

# add noise to the data points
noise = np.random.normal(size=len(x)) * .02 
f = f_true + noise 
f_error = np.ones_like(f_true)*0.05*f.max()

with Model() as model3:
    amp = Uniform('amp', 0.05, 0.4, testval= 0.15)
    size = Uniform('size', 0.5, 2.5, testval= 1.0)
    ps = Normal('ps', 0.13, 40, testval=0.15)

    gauss=Deterministic('gauss',amp*np.exp(-1*(np.pi**2*size*x/(3600.*180.))**2/(4.*np.log(2.)))+ps)

    y =Normal('y', mu=gauss, tau=1.0/f_error**2, observed=f)

    start=find_MAP()
    step=NUTS()
    trace=sample(2000,start=start)

# extract and plot results
y_min = np.percentile(trace.gauss,2.5,axis=0)
y_max = np.percentile(trace.gauss,97.5,axis=0)
y_fit = np.percentile(trace.gauss,50,axis=0)
plt.plot(x,f_true,'b', marker='None', ls='-', lw=1, label='True')
plt.errorbar(x,f,yerr=f_error, color='r', marker='.', ls='None', label='Observed')
plt.plot(x,y_fit,'k', marker='+', ls='None', ms=5, mew=1, label='Fit')
plt.fill_between(x, y_min, y_max, color='0.5', alpha=0.5)
plt.legend()
_

その結果

y_error

Xのエラーの場合(変数の「x」サフィックスに注意してください):

_# define the model/function to be fitted in PyMC3:
with Model() as modelx:

    x_obsx = pm3.Normal('x_obsx',mu=x, tau=(1e4)**-2, shape=40)

    ampx = Uniform('ampx', 0.05, 0.4, testval=0.15)
    sizex = Uniform('sizex', 0.5, 2.5, testval=1.0)
    psx = Normal('psx', 0.13, 40, testval=0.15)

    x_pred = Normal('x_pred', mu=x_obsx, tau=(1e4)**-2*np.ones_like(x_obsx),testval=5*np.ones_like(x_obsx),shape=40) # this allows error in x_obs

    gauss=Deterministic('gauss',ampx*np.exp(-1*(np.pi**2*sizex*x_pred/(3600.*180.))**2/(4.*np.log(2.)))+psx)

    y = Normal('y', mu=gauss, tau=1.0/f_error**2, observed=f)

    start=find_MAP()
    step=NUTS()
    tracex=sample(20000,start=start)
_

その結果:

x_error_graph

最後の観察は、

_traceplot(tracex[100:])
plt.tight_layout();
_

(結果は表示されていません)、sizexの測定誤差により、xが「減衰」または「回帰希釈」に苦しんでいるように見えることがわかります。

13
herman