Установите нелинейную функцию для данных/наблюдений с помощью pyMCMC/pyMC
Я пытаюсь поместить некоторые данные с помощью гауссовой (и более сложной) функции. Я создал небольшой пример ниже.
Мой первый вопрос: правильно ли я это делаю?
Мой второй вопрос: как добавить ошибку в направлении 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]()
Ответы
Ответ 1
Мой первый вопрос: правильно ли я делаю это?
Да! Вам нужно указать период ожога, который вы знаете. Мне нравится выкидывать первую половину моих образцов. Вам не нужно прореживаться, но иногда это заставит ваш пост-MCMC работать быстрее, чтобы обрабатывать и меньше хранить.
Единственное, что я советую, это установить случайное семя, чтобы ваши результаты были "воспроизводимыми": np.random.seed(12345)
выполнит трюк.
О, и если бы я действительно давал слишком много советов, я бы сказал import seaborn
, чтобы сделать matplotlib
результаты немного более красивыми.
Мой второй вопрос: как добавить ошибку в направлении x, т.е. в x-положении наблюдений/данных?
Один из способов - включить скрытую переменную для каждой ошибки. Это работает в вашем примере, но это будет невозможно, если у вас будет много других наблюдений. Я приведу небольшой пример, чтобы вы начали по этой дороге:
# 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
Похоже, может быть сложно получить хорошие ответы, если у вас есть шум в x
и y
:
![model fit with noise in x and y]()
Вот блокнот, собирающий все это.
Ответ 2
Я обратил Авраама Флаксмана в ответ на PyMC3, если кому-то это понадобится. Некоторые очень незначительные изменения, но все же могут запутать.
Во-первых, детерминированный декоратор @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).random()
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
.