You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

扩展分箱模型使用compute_sweights计算s权重时遇异常

问题

使用compute_sweights()计算扩展分箱模型的s权重时,无分箱PDF场景下运行正常,但分箱PDF即便拟合效果良好,仍会抛出ModelNotFittedToData错误。示例代码如下:

import numpy as np
import os
os.environ["ZFIT_DISABLE_TF_WARNINGS"] = "1"
import zfit
from hepstats.splot import compute_sweights

class FirstOrderPoly(zfit.pdf.ZPDF):
    """First order polynomial `a * x + b`"""
    _PARAMS = ['a']
    @zfit.supports(norm=False)
    def _pdf(self, x, norm, params):
        del norm
        data = x[0]
        a = params['a']
        return 1 + a * data
def cdf_poly(limit, a):
    return limit + 0.5 * a * limit ** 2
def integral_func(limits, norm, params, model):
    del norm, model
    a = params['a']
    lower, upper = limits.v1.limits
    integral = cdf_poly(upper, a) - cdf_poly(lower, a)
    return integral
integral_limits = zfit.Space(axes=(0,), limits=(zfit.Space.ANY, zfit.Space.ANY))
FirstOrderPoly.register_analytic_integral(func=integral_func, limits=integral_limits)

binned = True

nbkg = 20000
nsig = 45000
bounds = (1920, 2050)
np.random.seed(0)
bkg = np.random.uniform(1920, 2050, nbkg)
peak = np.random.normal(1969, 6.75, nsig)
mass = np.concatenate((bkg,peak))
sel = (mass > bounds[0]) & (mass < bounds[1])
mass = mass[sel]
sorter = np.argsort(mass)
mass = mass[sorter]

obs = zfit.Space("x", limits=bounds)
mean = zfit.Parameter("mean", 1969, 1920, 2050)
sigma = zfit.Parameter("sigma", 6.75, 0.02, 10)
bgA = zfit.Parameter("bgA", 0, -1.0, 1.0)
Nsig = zfit.Parameter("Nsig", nsig, 0.0, len(mass))
Nbkg = zfit.Parameter("Nbkg", nbkg, 0.0, len(mass))

if binned:
    data = zfit.Data(data=mass, obs=obs).to_binned(100)
    signal = zfit.pdf.Gauss(obs=obs, mu=mean, sigma=sigma).create_extended(Nsig).to_binned(data.space)
    bg = FirstOrderPoly(obs=obs, a=bgA).create_extended(Nbkg).to_binned(data.space)
    tot_model = zfit.pdf.BinnedSumPDF(pdfs=[signal, bg])
    nll = zfit.loss.ExtendedBinnedNLL(model=tot_model, data=data)
else:
    data = zfit.Data(data=mass, obs=obs)
    signal = zfit.pdf.Gauss(obs=obs, mu=mean, sigma=sigma).create_extended(Nsig)
    bg = FirstOrderPoly(obs=obs, a=bgA).create_extended(Nbkg)
    tot_model = zfit.pdf.SumPDF(pdfs=[signal, bg])
    nll = zfit.loss.ExtendedUnbinnedNLL(model=tot_model, data=data)
minimizer = zfit.minimize.Minuit()
result = minimizer.minimize(loss=nll)
result.hesse()
sweights = compute_sweights(tot_model, data)[Nsig]

错误由compute_sweights()内部依赖的eval_pdf()接口触发,该接口无法正确处理分箱PDF的输入逻辑。请问这是bug,还是需要换一种方式定义扩展无分箱复合模型?

解答

这不是bug,而是compute_sweights()当前仅支持无分箱的扩展模型,它依赖的底层接口无法直接兼容分箱PDF(如BinnedSumPDF)和分箱数据。

推荐两种解决方法:

方法一:分箱拟合+无分箱计算s权重

保持分箱拟合的代码不变,在计算s权重时,基于拟合得到的参数值重新定义无分箱扩展模型,传入原始无分箱数据计算权重:

# 拟合完成后替换原sweights计算代码
if binned:
    # 重新构建无分箱扩展模型
    unbinned_signal = zfit.pdf.Gauss(obs=obs, mu=mean, sigma=sigma).create_extended(Nsig)
    unbinned_bg = FirstOrderPoly(obs=obs, a=bgA).create_extended(Nbkg)
    unbinned_model = zfit.pdf.SumPDF(pdfs=[unbinned_signal, unbinned_bg])
    # 使用原始无分箱数据
    unbinned_data = zfit.Data(data=mass, obs=obs)
    sweights = compute_sweights(unbinned_model, unbinned_data)[Nsig]
else:
    sweights = compute_sweights(tot_model, data)[Nsig]

这种方法既保留了分箱拟合的精度优势,又满足compute_sweights()对无分箱模型的要求。

方法二:手动实现分箱模型的s权重计算

若必须基于分箱数据计算,可参考s权重的数学公式手动实现:

  • 计算每个分箱内信号、背景的PDF值
  • 构建并求逆协方差矩阵
  • 结合分箱计数计算对应s权重,再映射到原始数据点

此方法复杂度较高,仅在特殊场景下使用。

内容的提问来源于stack exchange,提问作者Wren

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 22:07:33