微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

试图用pyhf组合一个教学示例

如何解决试图用pyhf组合一个教学示例

我试图学习有关pyhf的更多信息,而我对目标的理解可能会受到限制。我希望将我的HEP数据放在ROOT之外,但我可能对pyhf施加了期望,而这并不是作者打算使用的。

我想给自己写一个hello-world示例,但我可能只是不知道自己在做什么。我的误解也可能是我的统计知识方面的空白。

有了这个序言,让我解释一下我要探索的东西。

我有一些观察到的事件,可以为它们计算一些可观察的事件,并对该数据进行装箱的直方图。我假设存在两个贡献物理过程,我称之为信号和背景。我为这些过程生成了一些蒙特卡洛样本,从理论上讲,事件总数接近但不完全符合我的观察结果。

我想:

  • 使数据适合这两个过程的假设
  • 从适合中获得每个过程的事件数的最佳值
  • 获取这些拟合值的不确定性
  • 在适当的情况下,计算信号事件数的上限。

下面是我的入门代码,我所要做的只是适合ML,但我不确定要去哪里。我知道它并没有做我想做的事,但是我迷失了在RTD上找到的示例。我敢肯定是我,这不是对文档的批评。

import pyhf
import numpy as np
import matplotlib.pyplot as plt

nbins = 15

# Generate a background and signal MC sample`
MC_signal_events = np.random.normal(5,1.0,200)
MC_background_events = 10*np.random.random(1000)

signal_data = np.histogram(MC_signal_events,bins=nbins)[0]
bkg_data = np.histogram(MC_background_events,bins=nbins)[0]

# Generate an observed dataset with a slightly different
# number of events
signal_events = np.random.normal(5,180)
background_events = 10*np.random.random(1050)

observed_events = np.array(signal_events.tolist() + background_events.tolist())
observed_sample = np.histogram(observed_events,bins=nbins)[0]


# Plot these samples,if you like
plt.figure(figsize=(12,4))
plt.subplot(1,3,1)
plt.hist(observed_events,bins=nbins,label='Observations')
plt.legend()
plt.subplot(1,2)
plt.hist(MC_signal_events,label='MC signal')
plt.legend()
plt.subplot(1,3)
plt.hist(MC_background_events,label='MC background')
plt.legend()

# Use a very naive estimate of the background
# uncertainties
bkg_uncerts = np.sqrt(bkg_data)

print("Defining the PDF.......")
pdf = pyhf.simplemodels.hepdata_like(signal_data=signal_data.tolist(),\
                                     bkg_data=bkg_data.tolist(),\
                                     bkg_uncerts=bkg_uncerts.tolist())

print("Fit.......")
data = pyhf.tensorlib.astensor(observed_sample.tolist() + pdf.config.auxdata)

bestfit_pars,twice_nll = pyhf.infer.mle.fit(data,pdf,return_fitted_val=True)

print(bestfit_pars)
print(twice_nll)

plt.show()

解决方法

注意:此答案基于pyhf v0.5.2。

好的,因此看来您已经成功地确定了大部分重要零件。但是,有两种不同的方法可以执行此操作,具体取决于您喜欢的设置方式。在这两种情况下,我都假设您想要不受限制的适合度,并且您想要...

  1. 使信号+背景模型适合观测数据

  2. 使背景模型适合观察的数据

首先,让我们简要讨论不确定性。目前,张量背景默认为numpy,优化程序默认为scipy。查看文档:

但是,scipy优化器目前的一个不幸缺点是它无法返回不确定性。在适合之前(尽管我们通常建议尽早建议),您需要在代码中的任何地方进行操作,而不是使用minuit优化器:

pyhf.set_backend('numpy','minuit')

这将为您提供能够获得相关矩阵,拟合参数的不确定性以及粗麻布等漂亮功能。我们也正在努力使它与scipy保持一致,但是目前尚不成熟。

所有优化都通过我们的optimizer API进行,您目前可以通过文档中的mixin here查看。具体来说,签名是

minimize(
    objective,data,pdf,init_pars,par_bounds,fixed_vals=None,return_fitted_val=False,return_result_obj=False,do_grad=None,do_stitch=False,**kwargs)

这里有很多选择。让我们只关注一个事实,我们可以传递的关键字参数之一是return_uncertainties,它将通过为想要的拟合参数不确定性添加一列来更改最佳拟合参数。


1。信号+背景

在这种情况下,我们只想使用默认模型

result,twice_nll = pyhf.infer.mle.fit(
    data,return_uncertainties=True,return_fitted_val=True
)

bestfit_pars,errors = result.T

2。仅背景

在这种情况下,我们需要关闭信号。我们这样做的方法是将感兴趣的参数(POI)设置为0.0。然后,我们可以类似的方式获得仅背景模型的拟合参数,但使用fixed_poi_fit而不是无约束拟合:

result,twice_nll = pyhf.infer.mle.fixed_poi_fit(
    0.0,errors = result.T

请注意,这只是完成以下不受限制的拟合的一种简单方法

bkg_params = pdf.config.suggested_init()
fixed_params = pdf.config.suggested_fixed()

bkg_params[pdf.config.poi_index] = 0.0
fixed_params[pdf.config.poi_index] = True

result,init_pars=bkg_params,fixed_params=fixed_params,errors = result.T

希望这可以进一步澄清问题!

,

Giordon的解决方案应该回答您所有的问题,但是我想我也应该写出代码来基本上解决我们所能解决的所有问题。 我还可以自由更改一些值,以使信号不会太强,以至于观察到的CL值离巴西波段的右边并不远(结果并没有错,但是在那时谈论使用发现测试统计信息然后设置限制可能更有意义。:))

环境

在此示例中,我将设置一个干净的Python 3虚拟环境,然后安装依赖项(这里我们将使用pyhf v0.5.2

$ python3 -m venv "${HOME}/.venvs/question"
$ . "${HOME}/.venvs/question/bin/activate"
(question) $ cat requirements.txt
pyhf[minuit,contrib]~=0.5.2
black
(question) $ python -m pip install -r requirements.txt

代码

虽然我们无法轻易获得信号事件数量和背景事件的最佳拟合值,但我们绝对可以推断出信号强度的最佳拟合值。 以下代码段(仅由于可视化而使之很长)应该解决您问题的所有要点。

# answer.py
import numpy as np
import pyhf
import matplotlib.pyplot as plt
import pyhf.contrib.viz.brazil

# Goals:
#    - Fit the model to the observed data
#    - Infer the best fit signal strength given the model
#    - Get the uncertainties on the best fit signal strength
#    - Calculate an 95% CL upper limit on the signal strength


def plot_hist(ax,bins,bottom=0,color=None,label=None):
    bin_width = bins[1] - bins[0]
    bin_leftedges = bins[:-1]
    bin_centers = [edge + bin_width / 2.0 for edge in bin_leftedges]
    ax.bar(
        bin_centers,bin_width,bottom=bottom,alpha=0.5,color=color,label=label
    )


def plot_data(ax,label="Data"):
    bin_width = bins[1] - bins[0]
    bin_leftedges = bins[:-1]
    bin_centers = [edge + bin_width / 2.0 for edge in bin_leftedges]
    ax.scatter(bin_centers,color="black",label=label)


def invert_interval(test_mus,hypo_tests,test_size=0.05):
    # This will be taken care of in v0.5.3
    cls_obs = np.array([test[0] for test in hypo_tests]).flatten()
    cls_exp = [
        np.array([test[1][idx] for test in hypo_tests]).flatten() for idx in range(5)
    ]
    crossing_test_stats = {"exp": [],"obs": None}
    for cls_exp_sigma in cls_exp:
        crossing_test_stats["exp"].append(
            np.interp(
                test_size,list(reversed(cls_exp_sigma)),list(reversed(test_mus))
            )
        )
    crossing_test_stats["obs"] = np.interp(
        test_size,list(reversed(cls_obs)),list(reversed(test_mus))
    )
    return crossing_test_stats


def main():
    np.random.seed(0)
    pyhf.set_backend("numpy","minuit")

    observable_range = [0.0,10.0]
    bin_width = 0.5
    _bins = np.arange(observable_range[0],observable_range[1] + bin_width,bin_width)

    n_bkg = 2000
    n_signal = int(np.sqrt(n_bkg))

    # Generate simulation
    bkg_simulation = 10 * np.random.random(n_bkg)
    signal_simulation = np.random.normal(5,1.0,n_signal)

    bkg_sample,_ = np.histogram(bkg_simulation,bins=_bins)
    signal_sample,_ = np.histogram(signal_simulation,bins=_bins)

    # Generate observations
    signal_events = np.random.normal(5,int(n_signal * 0.8))
    bkg_events = 10 * np.random.random(int(n_bkg + np.sqrt(n_bkg)))

    observed_events = np.array(signal_events.tolist() + bkg_events.tolist())
    observed_sample,_ = np.histogram(observed_events,bins=_bins)

    # Visualize the simulation and observations
    fig,ax = plt.subplots()
    fig.set_size_inches(7,5)

    plot_hist(ax,_bins,bkg_sample,label="Background")
    plot_hist(ax,signal_sample,bottom=bkg_sample,label="Signal")
    plot_data(ax,observed_sample)
    ax.legend(loc="best")
    ax.set_ylim(top=np.max(observed_sample) * 1.4)
    ax.set_xlabel("Observable")
    ax.set_ylabel("Count")
    fig.savefig("components.png")

    # Build the model
    bkg_uncerts = np.sqrt(bkg_sample)
    model = pyhf.simplemodels.hepdata_like(
        signal_data=signal_sample.tolist(),bkg_data=bkg_sample.tolist(),bkg_uncerts=bkg_uncerts.tolist(),)
    data = pyhf.tensorlib.astensor(observed_sample.tolist() + model.config.auxdata)

    # Perform inference
    fit_result = pyhf.infer.mle.fit(data,model,return_uncertainties=True)
    bestfit_pars,par_uncerts = fit_result.T
    print(
        f"best fit parameters:\
        \n * signal strength: {bestfit_pars[0]} +/- {par_uncerts[0]}\
        \n * nuisance parameters: {bestfit_pars[1:]}\
        \n * nuisance parameter uncertainties: {par_uncerts[1:]}"
    )

    # Perform hypothesis test scan
    _start = 0.0
    _stop = 5
    _step = 0.1
    poi_tests = np.arange(_start,_stop + _step,_step)

    print("\nPerforming hypothesis tests\n")
    hypo_tests = [
        pyhf.infer.hypotest(
            mu_test,return_expected_set=True,return_test_statistics=True,qtilde=True,)
        for mu_test in poi_tests
    ]

    # Upper limits on signal strength
    results = invert_interval(poi_tests,hypo_tests)

    print(f"Observed Limit on µ: {results['obs']:.2f}")
    print("-----")
    for idx,n_sigma in enumerate(np.arange(-2,3)):
        print(
            "Expected {}Limit on µ: {:.3f}".format(
                "       " if n_sigma == 0 else "({} σ) ".format(n_sigma),results["exp"][idx],)
        )

    # Visualize the "Brazil band"
    fig,5)

    ax.set_title("Hypothesis Tests")
    ax.set_ylabel(r"$\mathrm{CL}_{s}$")
    ax.set_xlabel(r"$\mu$")

    pyhf.contrib.viz.brazil.plot_results(ax,poi_tests,hypo_tests)
    fig.savefig("brazil_band.png")


if __name__ == "__main__":
    main()

运行时会给出

(question) $ python answer.py
best fit parameters:        
 * signal strength: 1.5884737977889158 +/- 0.7803435235862329        
 * nuisance parameters: [0.99020988 1.06040191 0.90488207 1.03531383 1.09093327 1.00942088
 1.07789316 1.01125627 1.06202964 0.95780043 0.94990993 1.04893286
 1.0560711  0.9758487  0.93692481 1.04683181 1.05785515 0.92381263
 0.93812855 0.96751869]        
 * nuisance parameter uncertainties: [0.06966439 0.07632218 0.0611428  0.07230328 0.07872258 0.06899675
 0.07472849 0.07403246 0.07613661 0.08606657 0.08002775 0.08655314
 0.07564512 0.07308117 0.06743479 0.07383134 0.07460864 0.06632003
 0.06683251 0.06270965]

Performing hypothesis tests

/home/stackoverflow/.venvs/question/lib/python3.7/site-packages/pyhf/infer/calculators.py:229: RuntimeWarning: invalid value encountered in double_scalars
  teststat = (qmu - qmu_A) / (2 * self.sqrtqmuA_v)
Observed Limit on µ: 2.89
-----
Expected (-2 σ) Limit on µ: 0.829
Expected (-1 σ) Limit on µ: 1.110
Expected        Limit on µ: 1.542
Expected (1 σ) Limit on µ: 2.147
Expected (2 σ) Limit on µ: 2.882

components.png

brazil_band.png

让我们知道您是否还有其他疑问!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。