使用pymc通过MCMC拟合两个正态分布(直方图)?
我正在尝试用光谱仪在CCD上拟合线型轮廓。为了方便说明,我提供了一个示例,如果解决了这个问题,就和我真正想解决的问题非常相似。
我查看了这个链接: https://stats.stackexchange.com/questions/46626/fitting-model-for-two-normal-distributions-in-pymc 以及其他各种问题和答案,但他们做的事情和我想做的根本不同。
import pymc as mc
import numpy as np
import pylab as pl
def GaussFunc(x, amplitude, centroid, sigma):
return amplitude * np.exp(-0.5 * ((x - centroid) / sigma)**2)
wavelength = np.arange(5000, 5050, 0.02)
# Profile 1
centroid_one = 5025.0
sigma_one = 2.2
height_one = 0.8
profile1 = GaussFunc(wavelength, height_one, centroid_one, sigma_one, )
# Profile 2
centroid_two = 5027.0
sigma_two = 1.2
height_two = 0.5
profile2 = GaussFunc(wavelength, height_two, centroid_two, sigma_two, )
# Measured values
noise = np.random.normal(0.0, 0.02, len(wavelength))
combined = profile1 + profile2 + noise
# If you want to plot what this looks like
pl.plot(wavelength, combined, label="Measured")
pl.plot(wavelength, profile1, color='red', linestyle='dashed', label="1")
pl.plot(wavelength, profile2, color='green', linestyle='dashed', label="2")
pl.title("Feature One and Two")
pl.legend()
我的问题:PyMC(或其他变种)能给我上面两个部分的均值、幅度和标准差吗?
请注意,我在实际问题中拟合的函数并不是高斯函数——所以请用一个通用函数(就像我示例中的GaussFunc),而不是“内置的”pymc.Normal()类型的函数。
另外,我明白模型选择是另一个问题:所以在当前的噪声下,1个部分(轮廓)可能是唯一在统计上合理的选择。但我想看看1、2、3个部分等的最佳解决方案是什么。
我也并不执着于使用PyMC——如果scikit-learn、astroML或其他某个包看起来更合适,请告诉我!
编辑:
我尝试了很多方法,但我认为有一个方向是对的,具体如下:
sigma_mc_one = mc.Uniform('sig', 0.01, 6.5)
height_mc_one = mc.Uniform('height', 0.1, 2.5)
centroid_mc_one = mc.Uniform('cen', 5015., 5040.)
但我无法构建一个有效的mc.model。
1 个回答
16
这段PyMC代码可能不是最简洁的,但我这样写是为了帮助读者理解。这个代码应该可以运行,并且会给出(非常)准确的结果。
我选择使用均匀的先验分布,范围比较宽泛,因为我对我们要建模的内容并不太确定。不过,可能对于中心位置有一些想法,可以在这方面使用更好的先验分布。
### Suggested one runs the above code first.
### Unknowns we are interested in
est_centroid_one = mc.Uniform("est_centroid_one", 5000, 5050 )
est_centroid_two = mc.Uniform("est_centroid_two", 5000, 5050 )
est_sigma_one = mc.Uniform( "est_sigma_one", 0, 5 )
est_sigma_two = mc.Uniform( "est_sigma_two", 0, 5 )
est_height_one = mc.Uniform( "est_height_one", 0, 5 )
est_height_two = mc.Uniform( "est_height_two", 0, 5 )
#std deviation of the noise, converted to precision by tau = 1/sigma**2
precision= 1./mc.Uniform("std", 0, 1)**2
#Set up the model's relationships.
@mc.deterministic( trace = False)
def est_profile_1(x = wavelength, centroid = est_centroid_one, sigma = est_sigma_one, height= est_height_one):
return GaussFunc( x, height, centroid, sigma )
@mc.deterministic( trace = False)
def est_profile_2(x = wavelength, centroid = est_centroid_two, sigma = est_sigma_two, height= est_height_two):
return GaussFunc( x, height, centroid, sigma )
@mc.deterministic( trace = False )
def mean( profile_1 = est_profile_1, profile_2 = est_profile_2 ):
return profile_1 + profile_2
observations = mc.Normal("obs", mean, precision, value = combined, observed = True)
model = mc.Model([est_centroid_one,
est_centroid_two,
est_height_one,
est_height_two,
est_sigma_one,
est_sigma_two,
precision])
#always a good idea to MAP it prior to MCMC, so as to start with good initial values
map_ = mc.MAP( model )
map_.fit()
mcmc = mc.MCMC( model )
mcmc.sample( 50000,40000 ) #try running for longer if not happy with convergence.
重要提示
请记住,这个算法对标签是无所谓的,所以结果可能会把profile1
的所有特征都显示为profile2
的特征,反之亦然。